diff --git a/src/components/Dropdown/Dropdown.i18n/en.json b/src/components/Dropdown/Dropdown.i18n/en.json index e596990c2..2e601eac8 100644 --- a/src/components/Dropdown/Dropdown.i18n/en.json +++ b/src/components/Dropdown/Dropdown.i18n/en.json @@ -1,8 +1,9 @@ { - "Project": "", - "Owner": "", - "Priority": "", - "State": "", - "Estimate": "", - "Not chosen": "" + "Parent projects": "Parent projects", + "Project": "Project", + "Owner": "Owner", + "Priority": "Priority", + "State": "State", + "Estimate": "Estimate", + "Not chosen": "Not chosen" } diff --git a/src/components/Dropdown/Dropdown.i18n/ru.json b/src/components/Dropdown/Dropdown.i18n/ru.json index 4af5e4171..892e134f9 100644 --- a/src/components/Dropdown/Dropdown.i18n/ru.json +++ b/src/components/Dropdown/Dropdown.i18n/ru.json @@ -1,4 +1,5 @@ { + "Parent projects": "Родители", "Project": "Проект", "Owner": "Ответственный", "Priority": "Приоритет", diff --git a/src/components/GoalCreateForm/GoalCreateForm.tsx b/src/components/GoalCreateForm/GoalCreateForm.tsx index d76de42c6..8677be68c 100644 --- a/src/components/GoalCreateForm/GoalCreateForm.tsx +++ b/src/components/GoalCreateForm/GoalCreateForm.tsx @@ -1,8 +1,7 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useContext, useMemo, useState } from 'react'; import { IconUpSmallSolid, IconDownSmallSolid } from '@taskany/icons'; import { Button, Text } from '@taskany/bricks/harmony'; import { KeyCode, useKeyboard } from '@taskany/bricks'; -import { useRouter as useNextRouter } from 'next/router'; import { useRouter } from '../../hooks/router'; import { usePageContext } from '../../hooks/usePageContext'; @@ -23,6 +22,7 @@ import { goalForm, } from '../../utils/domObjects'; import { FormAction } from '../FormActions/FormActions'; +import { ProjectContext } from '../ProjectContext/ProjectContext'; import { Dropdown, DropdownPanel, DropdownTrigger } from '../Dropdown/Dropdown'; import { tr } from './GoalCreateForm.i18n'; @@ -36,10 +36,6 @@ interface GoalCreateFormProps { const GoalCreateForm: React.FC = ({ title, onGoalCreate, personal }) => { const router = useRouter(); - const { - asPath, - query: { id }, - } = useNextRouter(); const { user } = usePageContext(); const [goalCreateFormActionCache, setGoalCreateFormActionCache] = useLocalStorage('goalCreateFormAction'); const [busy, setBusy] = useState(false); @@ -50,14 +46,7 @@ const GoalCreateForm: React.FC = ({ title, onGoalCreate, pe const defaultPriority = useMemo(() => priorities?.filter((priority) => priority.default)[0], [priorities]); const [isOpen, setIsOpen] = useState(false); - const { data: project } = trpc.project.getById.useQuery( - { - id: id as string, - }, - { - enabled: Boolean(asPath.includes('/projects/') && id), - }, - ); + const { project: parent } = useContext(ProjectContext); const createOptions = [ { @@ -151,7 +140,7 @@ const GoalCreateForm: React.FC = ({ title, onGoalCreate, pe busy={busy} validitySchema={goalCommonSchema} owner={{ id: user?.activityId, user } as ActivityByIdReturnType} - parent={project ?? undefined} + parent={parent ?? undefined} personal={personal} priority={defaultPriority ?? undefined} onSubmit={createGoal} diff --git a/src/components/GoalForm/GoalForm.tsx b/src/components/GoalForm/GoalForm.tsx index 1cff681e7..394bccccf 100644 --- a/src/components/GoalForm/GoalForm.tsx +++ b/src/components/GoalForm/GoalForm.tsx @@ -37,6 +37,7 @@ import { stateCombobox, usersCombobox, } from '../../utils/domObjects'; +import { ModalEvent, dispatchModalEvent } from '../../utils/dispatchModal'; import { TagsList } from '../TagsList/TagsList'; import { GoalParentDropdown } from '../GoalParentDropdown/GoalParentDropdown'; import { UserDropdown } from '../UserDropdown/UserDropdown'; @@ -161,6 +162,10 @@ export const GoalForm: React.FC = ({ setGoalType(goalTypeMap.default); }, [setValue, goalType, parent]); + const onNewProjectClick = useCallback(() => { + dispatchModalEvent(ModalEvent.GoalCreateModal)(); + }, []); + return (
@@ -231,6 +236,7 @@ export const GoalForm: React.FC = ({ error={errorsResolver(field.name)} disabled={busy} className={s.GoalFormParentDropdown} + onNewProjectClick={onNewProjectClick} {...field} {...projectsCombobox.attr} /> diff --git a/src/components/GoalParentDropdown/GoalParentDropdown.tsx b/src/components/GoalParentDropdown/GoalParentDropdown.tsx index 89979967e..efe127de4 100644 --- a/src/components/GoalParentDropdown/GoalParentDropdown.tsx +++ b/src/components/GoalParentDropdown/GoalParentDropdown.tsx @@ -6,6 +6,7 @@ import { IconAddOutline } from '@taskany/icons'; import { trpc } from '../../utils/trpcClient'; import { Dropdown, DropdownTrigger, DropdownPanel, DropdownGuardedProps } from '../Dropdown/Dropdown'; import { ModalEvent, dispatchModalEvent } from '../../utils/dispatchModal'; +import { ModalContext } from '../ModalOnEvent'; import s from './GoalParentDropdown.module.css'; import { tr } from './GoalParentDropdown.i18n'; @@ -25,8 +26,10 @@ type GoalParentDropdownProps = { placeholder?: string; disabled?: boolean; readOnly?: boolean; + filter?: string[]; placement?: ComponentProps['placement']; onClose?: () => void; + onNewProjectClick?: () => void; } & DropdownGuardedProps; export const GoalParentDropdown = ({ @@ -37,58 +40,63 @@ export const GoalParentDropdown = ({ placement, onChange, onClose, + filter, + onNewProjectClick, ...props }: GoalParentDropdownProps) => { + const { values, filterIds } = useMemo(() => { + const res: GoalParentValue[] = []; + const values = res.concat(value || []); + + const filterIds = Array.from( + values.reduce((acum, { id }) => { + acum.add(id); + + return acum; + }, new Set(filter)), + ); + + return { + values, + filterIds, + }; + }, [value, filter]); + const [inputState, setInputState] = useState(query); - const { data: userProjects = [] } = trpc.v2.project.userProjects.useQuery(undefined, { - keepPreviousData: true, - }); + const enableSuggestion = inputState.length >= 2; - useEffect(() => { - setInputState(query); - }, [query]); + const { data: userProjects = [] } = trpc.v2.project.userProjects.useQuery( + { + take: 10, + filter: filterIds, + }, + { + keepPreviousData: true, + }, + ); - const { data } = trpc.project.suggestions.useQuery( + const { data: suggestionsProjects = [] } = trpc.project.suggestions.useQuery( { query: inputState, + filter: filterIds, }, { - enabled: inputState.length >= 2, + enabled: enableSuggestion, + keepPreviousData: true, cacheTime: 0, staleTime: 0, }, ); - const suggestions = useMemo( - () => (data && data?.length > 0 ? data : userProjects.slice(0, 10)), - [data, userProjects], - ); - - const values = useMemo(() => { - const res: GoalParentValue[] = []; - return res.concat(value || []); - }, [value]); - - const valuesMap = useMemo(() => { - return values.reduce>((acc, cur) => { - acc[cur.id] = true; - return acc; - }, {}); - }, [values]); - - const items = useMemo(() => { - if (mode === 'single') { - return suggestions; - } - - return suggestions.filter((suggestion) => !valuesMap[suggestion.id]); - }, [mode, suggestions, valuesMap]); + useEffect(() => { + setInputState(query); + }, [query]); const handleCreateProject = useCallback(() => { - dispatchModalEvent(ModalEvent.GoalCreateModal)(); + onNewProjectClick?.(); dispatchModalEvent(ModalEvent.ProjectCreateModal)(); - }, []); + }, [onNewProjectClick]); const handleClose = useCallback(() => { onClose?.(); @@ -114,7 +122,7 @@ export const GoalParentDropdown = ({ width={320} value={values} title={tr('Suggestions')} - items={items} + items={enableSuggestion ? suggestionsProjects : userProjects} placement={placement} mode={mode} selectable @@ -133,13 +141,19 @@ export const GoalParentDropdown = ({ )} > -