diff --git a/apps/web/app/hooks/features/useTeamTasks.ts b/apps/web/app/hooks/features/useTeamTasks.ts index 8d29b6936..cef6a0bcb 100644 --- a/apps/web/app/hooks/features/useTeamTasks.ts +++ b/apps/web/app/hooks/features/useTeamTasks.ts @@ -10,10 +10,12 @@ import { deleteTaskAPI, getTeamTasksAPI, updateTaskAPI, - deleteEmployeeFromTasksAPI + deleteEmployeeFromTasksAPI, + getTasksByIdAPI } from '@app/services/client/api'; import { activeTeamState, + detailedTaskState, memberActiveTaskIdState, userState } from '@app/stores'; @@ -36,6 +38,7 @@ export function useTeamTasks() { const setAllTasks = useSetRecoilState(teamTasksState); const tasks = useRecoilValue(tasksByTeamState); + const [detailedTask, setDetailedTask] = useRecoilState(detailedTaskState); // const allTaskStatistics = useRecoilValue(allTaskStatisticsState); const tasksRef = useSyncRef(tasks); @@ -53,6 +56,8 @@ export function useTeamTasks() { // Queries hooks const { queryCall, loading } = useQuery(getTeamTasksAPI); + const { queryCall: getTasksByIdQueryCall, loading: getTasksByIdLoading } = + useQuery(getTasksByIdAPI); const { queryCall: deleteQueryCall, loading: deleteLoading } = useQuery(deleteTaskAPI); @@ -68,6 +73,16 @@ export function useTeamTasks() { loading: deleteEmployeeFromTasksLoading } = useQuery(deleteEmployeeFromTasksAPI); + const getTaskById = useCallback( + (taskId: string) => { + return getTasksByIdQueryCall(taskId).then((res) => { + setDetailedTask(res?.data?.data || null); + return res; + }); + }, + [getTasksByIdQueryCall, setDetailedTask] + ); + const deepCheckAndUpdateTasks = useCallback( (responseTasks: ITeamTask[], deepCheck?: boolean) => { if (responseTasks && responseTasks.length) { @@ -195,11 +210,17 @@ export function useTeamTasks() { const updateTask = useCallback( (task: Partial & { id: string }) => { return updateQueryCall(task.id, task).then((res) => { - deepCheckAndUpdateTasks(res?.data?.items || [], true); + const updatedTasks = res?.data?.items || []; + deepCheckAndUpdateTasks(updatedTasks, true); + + if (detailedTask) { + getTaskById(detailedTask.id); + } + return res; }); }, - [updateQueryCall, deepCheckAndUpdateTasks] + [updateQueryCall, deepCheckAndUpdateTasks, detailedTask, getTaskById] ); const updateTitle = useCallback( @@ -363,6 +384,9 @@ export function useTeamTasks() { setAllTasks, loadTeamTasksData, deleteEmployeeFromTasks, - deleteEmployeeFromTasksLoading + deleteEmployeeFromTasksLoading, + getTaskById, + getTasksByIdLoading, + detailedTask }; } diff --git a/apps/web/app/interfaces/ITask.ts b/apps/web/app/interfaces/ITask.ts index fc2d3c377..029c6ca5d 100644 --- a/apps/web/app/interfaces/ITask.ts +++ b/apps/web/app/interfaces/ITask.ts @@ -34,6 +34,7 @@ export type ITeamTask = { parentId?: string; parent?: ITeamTask; issueType?: string; + rootEpic?: ITeamTask | null; } & Omit; type SelectedTeam = Pick< diff --git a/apps/web/app/services/server/requests/tasks.ts b/apps/web/app/services/server/requests/tasks.ts index 1378859cd..c94503cb9 100644 --- a/apps/web/app/services/server/requests/tasks.ts +++ b/apps/web/app/services/server/requests/tasks.ts @@ -81,7 +81,7 @@ export function getTaskByIdRequest({ 'join[alias]': 'task', 'join[leftJoinAndSelect][members]': 'task.members', 'join[leftJoinAndSelect][user]': 'members.user', - rootEpic: 'true' + includeRootEpic: 'true' } as Record; relations.forEach((rl, i) => { diff --git a/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx b/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx index 20d440f29..ee2ef6406 100644 --- a/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx +++ b/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx @@ -124,9 +124,7 @@ const TaskSecondaryInfo = () => { /> )} - {task && task.parentId && task.parent?.issueType === 'Epic' && ( - - )} + {task && } {/* Task Status */} @@ -245,22 +243,25 @@ const TaskSecondaryInfo = () => { const EpicParent = ({ task }: { task: ITeamTask }) => { const { trans } = useTranslation('taskDetails'); - return (task.issueType === 'Task' || - task.issueType === 'Bug' || - task.issueType === 'Story') && - task.parentId && - task.parent?.issueType === 'Epic' ? ( + if (task.issueType === 'Story') { + return <>; + } + + return (!task.issueType || + task.issueType === 'Task' || + task.issueType === 'Bug') && + task?.rootEpic ? ( - +
-
{`#${task.parent?.taskNumber} ${task.parent?.title}`}
+
{`#${task?.rootEpic?.number} ${task?.rootEpic?.title}`}
diff --git a/apps/web/components/pages/task/title-block/task-title-block.tsx b/apps/web/components/pages/task/title-block/task-title-block.tsx index f1c5424a9..a51599fc6 100644 --- a/apps/web/components/pages/task/title-block/task-title-block.tsx +++ b/apps/web/components/pages/task/title-block/task-title-block.tsx @@ -221,6 +221,20 @@ const TaskTitleBlock = () => { )} */} {/* Parent Issue/Task Name */} + + {(!task?.issueType || + task?.issueType === 'Task' || + task?.issueType === 'Bug') && + task?.rootEpic && ( + + )} + @@ -286,7 +300,7 @@ const ParentTaskBadge = ({ task }: { task: ITeamTask | null }) => { } `} - >{`#${task.parent.taskNumber}`} + >{`#${task.parent.taskNumber || task.parent.number}`} {` - ${task.parent.title}`} @@ -295,7 +309,9 @@ const ParentTaskBadge = ({ task }: { task: ITeamTask | null }) => {
-

{`#${task.parent.taskNumber}`}

+

{`#${ + task.parent.taskNumber || task.parent.number + }`}

{task.parent.title}

diff --git a/apps/web/pages/api/tasks/[id].ts b/apps/web/pages/api/tasks/[id].ts index 0bfa30e75..d14c45447 100644 --- a/apps/web/pages/api/tasks/[id].ts +++ b/apps/web/pages/api/tasks/[id].ts @@ -22,14 +22,17 @@ export default async function handler( switch (req.method) { case 'GET': - await getTaskByIdRequest({ - taskId: taskId as string, - tenantId, - organizationId, - bearer_token: access_token - }); - break; + return $res.status(200).json( + await getTaskByIdRequest({ + taskId: taskId as string, + tenantId, + organizationId, + bearer_token: access_token + }) + ); + case 'PUT': + delete body.rootEpic; await updateTaskRequest( { data: body, diff --git a/apps/web/pages/task/[id].tsx b/apps/web/pages/task/[id].tsx index 734d89d16..172cbec64 100644 --- a/apps/web/pages/task/[id].tsx +++ b/apps/web/pages/task/[id].tsx @@ -10,8 +10,6 @@ import { withAuthentication } from 'lib/app/authenticator'; import TaskDetailsAside from '@components/pages/task/task-details-aside'; import { useEffect } from 'react'; import { useRouter } from 'next/router'; -import { useRecoilState } from 'recoil'; -import { detailedTaskState } from '@app/stores'; import TaskTitleBlock from '@components/pages/task/title-block/task-title-block'; import { ArrowLeft } from 'lib/components/svgs'; import { RelatedIssueCard } from '@components/pages/task/IssueCard'; @@ -21,11 +19,14 @@ import { ChildIssueCard } from '@components/pages/task/ChildIssueCard'; const TaskDetails = () => { const profile = useUserProfilePage(); - const { tasks } = useTeamTasks(); - const [task, setTask] = useRecoilState(detailedTaskState); const { trans } = useTranslation('taskDetails'); const router = useRouter(); const { isTrackingEnabled, activeTeam } = useOrganizationTeams(); + const { + getTaskById, + detailedTask: task, + getTasksByIdLoading + } = useTeamTasks(); const breadcrumb = [ { title: activeTeam?.name || '', href: '/' }, @@ -33,14 +34,17 @@ const TaskDetails = () => { ]; useEffect(() => { - if (router.isReady && router.query?.id && tasks.length > 0) { - const foundTask = tasks.find( - (x) => x.id === (router.query?.id as string) - ); - // console.log(foundTask); - foundTask && setTask(foundTask); + if ( + router.isReady && + // If id is passed in query param + router.query?.id && + // Either no task or task id doesn't match query id + (!task || (task && task.id !== router.query?.id)) && + !getTasksByIdLoading + ) { + getTaskById(router.query?.id as string); } - }, [tasks, router.isReady, router.query?.id, setTask]); + }, [getTaskById, router, task, getTasksByIdLoading]); return (