From ae9cb254aa212a31805ecb7dc0f056458e71a489 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Mon, 18 Nov 2024 21:21:07 +0200 Subject: [PATCH] feat: add table view for daily plan tasks (#3335) --- apps/web/components/ui/data-table.tsx | 6 +- .../features/task/daily-plan/future-tasks.tsx | 220 ++++++++++-------- .../features/task/daily-plan/past-tasks.tsx | 158 +++++++------ .../cells/task-action-menu-cell.tsx | 51 ++++ .../table-view/cells/task-estimation-cell.tsx | 24 ++ .../table-view/cells/task-info-cell.tsx | 7 + .../table-view/cells/task-times-cell.tsx | 31 +++ .../task/daily-plan/table-view/index.tsx | 75 ++++++ apps/web/lib/features/task/task-card.tsx | 4 +- .../lib/features/team-members-table-view.tsx | 1 - apps/web/lib/features/user-profile-plans.tsx | 163 +++++++------ 11 files changed, 486 insertions(+), 254 deletions(-) create mode 100644 apps/web/lib/features/task/daily-plan/table-view/cells/task-action-menu-cell.tsx create mode 100644 apps/web/lib/features/task/daily-plan/table-view/cells/task-estimation-cell.tsx create mode 100644 apps/web/lib/features/task/daily-plan/table-view/cells/task-info-cell.tsx create mode 100644 apps/web/lib/features/task/daily-plan/table-view/cells/task-times-cell.tsx create mode 100644 apps/web/lib/features/task/daily-plan/table-view/index.tsx diff --git a/apps/web/components/ui/data-table.tsx b/apps/web/components/ui/data-table.tsx index 09c74e340..8a9ceaf8e 100644 --- a/apps/web/components/ui/data-table.tsx +++ b/apps/web/components/ui/data-table.tsx @@ -86,9 +86,9 @@ function DataTable({ columns, data, footerRows, isHeader }: DataT {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} diff --git a/apps/web/lib/features/task/daily-plan/future-tasks.tsx b/apps/web/lib/features/task/daily-plan/future-tasks.tsx index 0b5f6ac59..65cb07acf 100644 --- a/apps/web/lib/features/task/daily-plan/future-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/future-tasks.tsx @@ -16,6 +16,7 @@ import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; import { IDailyPlan } from '@app/interfaces'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { useDateRange } from '@app/hooks/useDateRange'; +import DailyPlanTasksTableView from './table-view'; export function FutureTasks({ profile }: { profile: any }) { const { deleteDailyPlan, deleteDailyPlanLoading, futurePlans } = useDailyPlan(); @@ -57,115 +58,130 @@ export function FutureTasks({ profile }: { profile: any }) { - - {(provided) => ( -
    - {plan.tasks?.map((task, index) => - view === 'CARDS' ? ( - - {(provided) => ( -
    - -
    - )} -
    - ) : ( - - {(provided) => ( -
    - -
    - )} -
    - ) - )} - <>{provided.placeholder} - {canSeeActivity ? ( -
    - + ) : ( + + {(provided) => ( +
      + {plan.tasks?.map((task, index) => + view === 'CARDS' ? ( + + {(provided) => ( +
      + +
      + )} +
      + ) : ( + + {(provided) => ( +
      + +
      + )} +
      + ) + )} + <>{provided.placeholder} + {canSeeActivity ? ( +
      + { + setPopupOpen((prev) => !prev); + setCurrentDeleteIndex(index); + }} + variant="outline" + className="px-4 py-2 text-sm font-medium text-red-600 border border-red-600 rounded-md bg-light--theme-light dark:!bg-dark--theme-light" + > + Delete this plan + + } + > + {/*button confirm*/} + {/*button cancel*/} + - } - > - {/*button confirm*/} - - {/*button cancel*/} - - -
      - ) : ( - <> - )} -
    - )} -
    +
    +
    + ) : ( + <> + )} +
+ )} +
+ )}
))} diff --git a/apps/web/lib/features/task/daily-plan/past-tasks.tsx b/apps/web/lib/features/task/daily-plan/past-tasks.tsx index 29ce71c3e..d4fdf8cd1 100644 --- a/apps/web/lib/features/task/daily-plan/past-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/past-tasks.tsx @@ -13,6 +13,7 @@ import { useEffect, useState } from 'react'; import { IDailyPlan } from '@app/interfaces'; import { DragDropContext, Draggable, Droppable, DroppableProvided, DroppableStateSnapshot } from 'react-beautiful-dnd'; import { useDateRange } from '@app/hooks/useDateRange'; +import DailyPlanTasksTableView from './table-view'; export function PastTasks({ profile, currentTab = 'Past Tasks' }: { profile: any; currentTab?: FilterTabs }) { const { pastPlans } = useDailyPlan(); @@ -51,79 +52,90 @@ export function PastTasks({ profile, currentTab = 'Past Tasks' }: { profile: any {/* Plan header */} - - {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => ( -
    - {plan.tasks?.map((task, index) => - view === 'CARDS' ? ( - - {(provided) => ( -
    - -
    - )} -
    - ) : ( - - {(provided) => ( -
    - -
    - )} -
    - ) - )} -
- )} -
+ {view === 'TABLE' ? ( + + ) : ( + + {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => ( +
    + {plan.tasks?.map((task, index) => + view === 'CARDS' ? ( + + {(provided) => ( +
    + +
    + )} +
    + ) : ( + + {(provided) => ( +
    + +
    + )} +
    + ) + )} +
+ )} +
+ )}
))} diff --git a/apps/web/lib/features/task/daily-plan/table-view/cells/task-action-menu-cell.tsx b/apps/web/lib/features/task/daily-plan/table-view/cells/task-action-menu-cell.tsx new file mode 100644 index 000000000..89643f9df --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/table-view/cells/task-action-menu-cell.tsx @@ -0,0 +1,51 @@ +import { ITeamTask } from '@/app/interfaces'; +import { CellContext } from '@tanstack/react-table'; +import { ActiveTaskStatusDropdown } from '../../../task-status'; +import { useMemo, useState } from 'react'; +import { I_UserProfilePage, useOrganizationTeams, useTeamMemberCard } from '@/app/hooks'; +import { get } from 'lodash'; +import { TaskCardMenu } from '../../../task-card'; + +export default function TaskActionMenuCell(props: CellContext) { + const [loading, setLoading] = useState(false); + const { activeTeam } = useOrganizationTeams(); + const members = useMemo(() => activeTeam?.members || [], [activeTeam?.members]); + const profile = get(props.column, 'columnDef.meta.profile') as unknown as I_UserProfilePage; + const plan = get(props.column, 'columnDef.meta.plan'); + const planMode = get(props.column, 'columnDef.meta.planMode'); + const currentMember = useMemo( + () => + members.find((m) => { + return m.employee.user?.id === profile?.userProfile?.id; + }), + [members, profile?.userProfile?.id] + ); + const memberInfo = useTeamMemberCard(currentMember || undefined); + + return ( +
+ {/* Active Task Status Dropdown (It's a dropdown that allows the user to change the status of the task.)*/} +
+ setLoading(load)} + className="min-w-[10.625rem] text-sm" + /> +
+ {/* TaskCardMenu */} +
+ {props.row.original && currentMember && ( + + )} +
+
+ ); +} diff --git a/apps/web/lib/features/task/daily-plan/table-view/cells/task-estimation-cell.tsx b/apps/web/lib/features/task/daily-plan/table-view/cells/task-estimation-cell.tsx new file mode 100644 index 000000000..51a933455 --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/table-view/cells/task-estimation-cell.tsx @@ -0,0 +1,24 @@ +import { I_UserProfilePage, useOrganizationTeams, useTeamMemberCard, useTMCardTaskEdit } from '@/app/hooks'; +import { ITeamTask } from '@/app/interfaces'; +import { TaskEstimateInfo } from '@/lib/features/team/user-team-card/task-estimate'; +import { CellContext } from '@tanstack/react-table'; +import { get } from 'lodash'; +import { useMemo } from 'react'; + +export default function DailyPlanTaskEstimationCell(props: CellContext) { + const plan = get(props.column, 'columnDef.meta.plan'); + const profile = get(props.column, 'columnDef.meta.profile') as unknown as I_UserProfilePage; + const { activeTeam } = useOrganizationTeams(); + const members = useMemo(() => activeTeam?.members || [], [activeTeam?.members]); + const currentMember = useMemo( + () => + members.find((m) => { + return m.employee.user?.id === profile?.userProfile?.id; + }), + [members, profile?.userProfile?.id] + ); + const memberInfo = useTeamMemberCard(currentMember || undefined); + const taskEdition = useTMCardTaskEdit(props.row.original); + + return ; +} diff --git a/apps/web/lib/features/task/daily-plan/table-view/cells/task-info-cell.tsx b/apps/web/lib/features/task/daily-plan/table-view/cells/task-info-cell.tsx new file mode 100644 index 000000000..d31827ce7 --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/table-view/cells/task-info-cell.tsx @@ -0,0 +1,7 @@ +import { CellContext } from '@tanstack/react-table'; +import { TaskInfo } from '../../../task-card'; +import { ITeamTask } from '@/app/interfaces'; + +export default function DailyPlanTaskInfoCell(props: CellContext) { + return ; +} diff --git a/apps/web/lib/features/task/daily-plan/table-view/cells/task-times-cell.tsx b/apps/web/lib/features/task/daily-plan/table-view/cells/task-times-cell.tsx new file mode 100644 index 000000000..2ba60368f --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/table-view/cells/task-times-cell.tsx @@ -0,0 +1,31 @@ +import { CellContext } from '@tanstack/react-table'; +import { TaskTimes } from '../../../task-times'; +import { ITeamTask } from '@/app/interfaces'; +import { I_UserProfilePage, useOrganizationTeams, useTeamMemberCard } from '@/app/hooks'; +import get from 'lodash/get'; +import { useMemo } from 'react'; + +export default function DailyPlanTaskTimesCell(props: CellContext) { + const profile = get(props.column, 'columnDef.meta.profile') as unknown as I_UserProfilePage; + const { activeTeam } = useOrganizationTeams(); + const members = useMemo(() => activeTeam?.members || [], [activeTeam?.members]); + const currentMember = useMemo( + () => + members.find((m) => { + return m.employee.user?.id === profile?.userProfile?.id; + }), + [members, profile?.userProfile?.id] + ); + const memberInfo = useTeamMemberCard(currentMember || undefined); + + return ( + + ); +} diff --git a/apps/web/lib/features/task/daily-plan/table-view/index.tsx b/apps/web/lib/features/task/daily-plan/table-view/index.tsx new file mode 100644 index 000000000..7adc2dc79 --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/table-view/index.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import DataTable from '@components/ui/data-table'; +import { ColumnDef } from '@tanstack/react-table'; +import { IDailyPlan, ITeamTask } from '@app/interfaces'; +import DailyPlanTaskEstimationCell from './cells/task-estimation-cell'; +import DailyPlanTaskInfoCell from './cells/task-info-cell'; +import DailyPlanTaskTimesCell from './cells/task-times-cell'; +import TaskActionMenuCell from './cells/task-action-menu-cell'; +import { FilterTabs, I_UserProfilePage } from '@/app/hooks'; + +interface IDailyPlanTasksTableViewProps { + data: ITeamTask[]; + plan: IDailyPlan; + profile: I_UserProfilePage; + planMode?: FilterTabs; +} + +/** + * Table view of daily plan tasks + * + * @param {Object} props - THe props object + * @param {ITeamTask[]} props.data - The tasks + * @param {I_UserProfilePage} props.profile - The user profile page + * @param {FilterTabs} props.planMode - The plan mode to display + * + * @returns {JSX.Element} - The table view of daily plan tasks + */ +export default function DailyPlanTasksTableView(props: IDailyPlanTasksTableViewProps) { + const { data, plan, profile, planMode } = props; + + const columns = React.useMemo[]>( + () => [ + { + id: 'task', + header: 'Task', + tooltip: '', + cell: DailyPlanTaskInfoCell + }, + { + id: 'estimate', + header: 'Estimate', + tooltip: '', + cell: DailyPlanTaskEstimationCell, + meta: { + plan, + profile + } + }, + { + id: 'workedOn', + header: 'Worked On', + tooltip: '', + cell: DailyPlanTaskTimesCell, + meta: { + profile + } + }, + { + id: 'action', + header: 'Action', + tooltip: '', + cell: TaskActionMenuCell, + meta: { + plan, + profile, + planMode + } + } + ], + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + return ; +} diff --git a/apps/web/lib/features/task/task-card.tsx b/apps/web/lib/features/task/task-card.tsx index ae4d67e6f..3eae62ec9 100644 --- a/apps/web/lib/features/task/task-card.tsx +++ b/apps/web/lib/features/task/task-card.tsx @@ -480,7 +480,7 @@ function TimerButtonCall({ //* Task Estimate info * //* Task Info FC * -function TaskInfo({ +export function TaskInfo({ className, task, taskBadgeClassName, @@ -526,7 +526,7 @@ function TaskInfo({ /** * It's a dropdown menu that allows the user to remove the task. */ -function TaskCardMenu({ +export function TaskCardMenu({ task, loading, memberInfo, diff --git a/apps/web/lib/features/team-members-table-view.tsx b/apps/web/lib/features/team-members-table-view.tsx index af2d3d471..50df9227d 100644 --- a/apps/web/lib/features/team-members-table-view.tsx +++ b/apps/web/lib/features/team-members-table-view.tsx @@ -72,7 +72,6 @@ const TeamMembersTableView = ({ return ( <> - []} diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index fbdac86f3..754622dd6 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -50,6 +50,7 @@ import TaskBlockCard from './task/task-block-card'; import { TaskCard } from './task/task-card'; import moment from 'moment'; import { usePathname } from 'next/navigation'; +import DailyPlanTasksTableView from './task/daily-plan/table-view'; export type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; type FilterOutstanding = 'ALL' | 'DATE'; @@ -333,79 +334,95 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current - - {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => ( -
    - {plan.tasks?.map((task, index) => - view === 'CARDS' ? ( - - {(provided) => ( -
    - -
    - )} -
    - ) : ( - - {(provided) => ( -
    - -
    - )} -
    - ) - )} - <>{provided.placeholder} -
- )} -
+ + {view === 'TABLE' ? ( + + ) : ( + + {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => ( +
    + {plan.tasks?.map((task, index) => + view === 'CARDS' ? ( + + {(provided) => ( +
    + +
    + )} +
    + ) : ( + + {(provided) => ( +
    + +
    + )} +
    + ) + )} + <>{provided.placeholder} +
+ )} +
+ )}
))}