diff --git a/src/components/DataTableCells/ProjectsListFieldCell.tsx b/src/components/DataTableCells/ProjectsListFieldCell.tsx new file mode 100644 index 000000000..1d611c722 --- /dev/null +++ b/src/components/DataTableCells/ProjectsListFieldCell.tsx @@ -0,0 +1,43 @@ +import React, { useCallback } from 'react'; +import { FormGroup, Intent, Classes } from '@blueprintjs/core'; +import classNames from 'classnames'; + +import { CellType } from '@/constants'; +import { ProjectSuggestField } from '@/containers/Projects/components'; + +/** + * projects list field cell. + * @returns + */ +export function ProjectsListFieldCell({ + column: { id }, + row: { index, original }, + payload: { projects, updateData, errors }, +}) { + const handleProjectSelected = useCallback( + (project) => { + updateData(index, 'project_id', project.id); + }, + [updateData, index], + ); + + const error = errors?.[index]?.[id]; + return ( + + + + ); +} + +ProjectsListFieldCell.cellType = CellType.Field; diff --git a/src/components/DataTableCells/index.tsx b/src/components/DataTableCells/index.tsx index 84ef9d344..02fb0ebb4 100644 --- a/src/components/DataTableCells/index.tsx +++ b/src/components/DataTableCells/index.tsx @@ -10,6 +10,7 @@ import CheckBoxFieldCell from './CheckBoxFieldCell'; import SwitchFieldCell from './SwitchFieldCell'; import TextAreaCell from './TextAreaCell'; import BranchesListFieldCell from './BranchesListFieldCell'; +import { ProjectsListFieldCell } from './ProjectsListFieldCell'; import { TextOverviewTooltipCell } from './TextOverviewTooltipCell'; export { @@ -26,5 +27,6 @@ export { SwitchFieldCell, TextAreaCell, BranchesListFieldCell, + ProjectsListFieldCell, TextOverviewTooltipCell, }; diff --git a/src/containers/Projects/components/ProjectSuggestField.tsx b/src/containers/Projects/components/ProjectSuggestField.tsx new file mode 100644 index 000000000..ede0e53a0 --- /dev/null +++ b/src/containers/Projects/components/ProjectSuggestField.tsx @@ -0,0 +1,151 @@ +import React from 'react'; +import styled from 'styled-components'; +import intl from 'react-intl-universal'; +import { Menu, MenuItem } from '@blueprintjs/core'; +import { Suggest } from '@blueprintjs/select'; +import { FormattedMessage as T } from '@/components'; + +import classNames from 'classnames'; +import { CLASSES } from '@/constants/classes'; +import { firstLettersArgs } from '@/utils'; + +/** + * project suggest field. + * @returns + */ +export function ProjectSuggestField({ + projects, + initialProjectId, + selectedProjectId, + defaultSelectText = intl.get('select_project'), + popoverFill = false, + onProjectSelected, + ...suggestProps +}) { + const initialProject = React.useMemo( + () => projects.find((b) => b.id === initialProjectId), + [initialProjectId, projects], + ); + + const [selectedProject, setSelectedProject] = React.useState( + initialProject || null, + ); + + React.useEffect(() => { + if (typeof selectedProjectId !== 'undefined') { + const project = selectedProjectId + ? projects.find((a) => a.id === selectedProjectId) + : null; + setSelectedProject(project); + } + }, [selectedProjectId, projects, setSelectedProject]); + + /** + * @param {*} project + * @param {*} param1 + * @returns {JSX.Element} + */ + const projectsItemRenderer = (project, { handleClick, modifiers, query }) => { + return ( + + } + disabled={modifiers.disabled} + key={project.id} + text={project.name} + onClick={handleClick} + /> + + ); + }; + + /** + * + * @param {*} query + * @param {*} project + * @param {*} _index + * @param {*} exactMatch + * @returns + */ + const projectsItemPredicate = (query, project, _index, exactMatch) => { + const normalizedTitle = project.name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return ( + `${project.name}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0 + ); + } + }; + + /** + * + * @param {*} project + * @returns + */ + const projectItemSelect = React.useCallback( + (project) => { + if (project.id) { + setSelectedProject({ ...project }); + onProjectSelected && onProjectSelected(project); + } + }, + [setSelectedProject, onProjectSelected], + ); + + /** + * + * @param {*} inputVaue + * @returns + */ + const projectInputValueRenderer = (inputValue) => { + if (inputValue) { + return inputValue.name.toString(); + } + return ''; + }; + + return ( + } />} + itemRenderer={projectsItemRenderer} + itemPredicate={projectsItemPredicate} + onItemSelect={projectItemSelect} + selectedItem={selectedProject} + inputProps={{ placeholder: defaultSelectText }} + resetOnClose={true} + fill={true} + popoverProps={{ minimal: true, boundary: 'window' }} + inputValueRenderer={projectInputValueRenderer} + className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { + [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, + })} + {...suggestProps} + /> + ); +} + +const AvatarSelect = ({ text }) => { + return {firstLettersArgs(text?.name)}; +}; + +const MenuContent = styled(Menu)` + .bp3-menu { + margin: 5px; + } +`; + +const AvaterContent = styled.div` + display: inline-block; + background: #adbcc9; + text-align: center; + font-weight: 400; + color: #fff; + height: 25px; + width: 25px; + line-height: 25px; + font-size: 12px; +`; diff --git a/src/containers/Projects/components/index.ts b/src/containers/Projects/components/index.ts index 93edd1b79..5a15d6891 100644 --- a/src/containers/Projects/components/index.ts +++ b/src/containers/Projects/components/index.ts @@ -4,3 +4,4 @@ export * from './TaskSelect'; export * from './ProjectsSelect'; export * from './ProjectMultiSelect' export * from './FInputGroupComponent'; +export * from './ProjectSuggestField' \ No newline at end of file