From cf3f79d30798df0feb0cdad22383bca3eeb4ac6b Mon Sep 17 00:00:00 2001 From: hudy9x <95471659+hudy9x@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:45:02 +0700 Subject: [PATCH] refactor: replace timeline with calendar in goal view (#149) --- .../src/components/Timeline/index.tsx | 11 ++- .../src/components/Timeline/style.css | 7 +- .../shared-ui/src/components/Timeline/type.ts | 1 + .../project/[projectId]/calendar/style.css | 40 +++++++- .../ui-app/app/_components/ProgressBar.tsx | 8 +- .../Project/Vision/VisionContainer.tsx | 4 +- .../_features/Project/Vision/VisionCreate.tsx | 2 +- .../_features/Project/Vision/VisionList.tsx | 2 +- .../Project/Vision/VisionListHeader.tsx | 2 +- .../app/_features/Project/Vision/context.tsx | 16 +++- .../app/_features/Project/Vision/index.tsx | 16 +++- .../Project/VisionTimeline/index.tsx | 95 +++++++++++++++---- 12 files changed, 166 insertions(+), 38 deletions(-) diff --git a/packages/shared-ui/src/components/Timeline/index.tsx b/packages/shared-ui/src/components/Timeline/index.tsx index c7b09d78..a7d7aebe 100644 --- a/packages/shared-ui/src/components/Timeline/index.tsx +++ b/packages/shared-ui/src/components/Timeline/index.tsx @@ -10,6 +10,7 @@ export default function Timeline({ month, year, items, + height = '2.75rem', children, onChange }: ITimelineProps) { @@ -24,7 +25,7 @@ export default function Timeline({ let totalDates = 0 const colWidth = '2rem' - const colHeight = '2.75rem' + const colHeight = height // calculate month columns - start let startMonth = 1 @@ -104,10 +105,10 @@ export default function Timeline({
- + {/* */}
@@ -131,7 +132,7 @@ export default function Timeline({ })}
@@ -151,7 +152,7 @@ export default function Timeline({ }) })}
-
+
ReactNode diff --git a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/style.css b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/style.css index 651656cc..eb8d0fe7 100644 --- a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/style.css +++ b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/style.css @@ -1,7 +1,7 @@ .calendar-wrapper { @apply flex flex-col justify-between divide-y dark:divide-gray-700 relative; /* height: calc(100vh - 153px); */ - height: 800px + height: 800px; } .week-view.calendar-wrapper { @@ -68,3 +68,41 @@ .week-view .calendar-task-item { @apply whitespace-pre-wrap; } + +/* Copy from /components/calendar/style.css - considers to remove one of them */ +.calendar-container { + @apply flex flex-col justify-between divide-y dark:divide-gray-700 relative; + @apply border bg-white rounded-md shadow-lg shadow-indigo-100; + @apply dark:bg-gray-900 dark:border-gray-700 dark:shadow-gray-900; +} + +.cal-header-day { + @apply px-3 py-1 text-sm text-gray-700 dark:text-gray-500; +} + +.cal-week { + @apply grid grid-cols-7 h-full divide-x dark:divide-gray-700 dark:text-gray-400; +} + +.cal-day { + @apply absolute top-1 left-2; +} + +.cal-day-in-week { + @apply bg-white dark:bg-gray-900 relative; + @apply text-xs text-gray-600 dark:text-gray-400; +} + +.cal-day-in-week.today .cal-day { + @apply text-indigo-500 font-bold; +} + +.cal-day-in-week.is-sunsat { + @apply bg-gray-50 dark:bg-black/40; +} + +.cal-day-in-week.not-in-month { + @apply bg-indigo-50/50 text-gray-400; + @apply dark:bg-gray-800; +} +/* Copy from components/calendar/style.css */ diff --git a/packages/ui-app/app/_components/ProgressBar.tsx b/packages/ui-app/app/_components/ProgressBar.tsx index ca93b2e5..ac8d595c 100644 --- a/packages/ui-app/app/_components/ProgressBar.tsx +++ b/packages/ui-app/app/_components/ProgressBar.tsx @@ -1,9 +1,12 @@ interface IProgressBar { progress: number + variant?: 'rounded' | 'square' color?: 'red' | 'green' | 'blue' | 'yellow' | 'dark' | 'indigo' } + export default function ProgressBar({ progress, + variant = 'rounded', color = 'blue' }: IProgressBar) { const colors = { @@ -16,14 +19,15 @@ export default function ProgressBar({ } const c = colors[color as keyof typeof colors] + const shape = variant === 'rounded' ? 'rounded-full' : 'rounded' return (
+ className={`progressbar w-full bg-gray-200 ${shape} dark:bg-gray-700`}>
0 ? 'p-0.5' : 'py-0.5' - } leading-none rounded-full`} + } leading-none ${shape}`} style={{ width: `${progress || 0}%` }}> {progress >= 20 ? ( <>{progress + '%'} diff --git a/packages/ui-app/app/_features/Project/Vision/VisionContainer.tsx b/packages/ui-app/app/_features/Project/Vision/VisionContainer.tsx index da2ee6f6..08d25356 100644 --- a/packages/ui-app/app/_features/Project/Vision/VisionContainer.tsx +++ b/packages/ui-app/app/_features/Project/Vision/VisionContainer.tsx @@ -3,6 +3,7 @@ import VisionListTask from './VisionListTask' import { DndContext, DragEndEvent } from '@dnd-kit/core' import { useServiceTaskUpdate } from '@/hooks/useServiceTaskUpdate' import VisionCalendarContainer from './VisionCalendaContainer' +import VisionTimeline from '../VisionTimeline' export default function VisionContainer({ visible }: { visible: boolean }) { const { updateTaskData } = useServiceTaskUpdate() @@ -30,7 +31,8 @@ export default function VisionContainer({ visible }: { visible: boolean }) { - + {/* */} +
) diff --git a/packages/ui-app/app/_features/Project/Vision/VisionCreate.tsx b/packages/ui-app/app/_features/Project/Vision/VisionCreate.tsx index cb1a526e..71689f62 100644 --- a/packages/ui-app/app/_features/Project/Vision/VisionCreate.tsx +++ b/packages/ui-app/app/_features/Project/Vision/VisionCreate.tsx @@ -59,5 +59,5 @@ export default function VisionCreate() { createNewVision(visionData) } - return + return } diff --git a/packages/ui-app/app/_features/Project/Vision/VisionList.tsx b/packages/ui-app/app/_features/Project/Vision/VisionList.tsx index 7b3fd1d3..09a9ec81 100644 --- a/packages/ui-app/app/_features/Project/Vision/VisionList.tsx +++ b/packages/ui-app/app/_features/Project/Vision/VisionList.tsx @@ -39,7 +39,7 @@ export default function VisionList() {
-

No vision found !

+

No goal found !

) : null} diff --git a/packages/ui-app/app/_features/Project/Vision/VisionListHeader.tsx b/packages/ui-app/app/_features/Project/Vision/VisionListHeader.tsx index 365d7be0..b23a61d9 100644 --- a/packages/ui-app/app/_features/Project/Vision/VisionListHeader.tsx +++ b/packages/ui-app/app/_features/Project/Vision/VisionListHeader.tsx @@ -19,7 +19,7 @@ export default function VisionListHeader({
- All visions: {total} + All goals: {total}
Done: {done} diff --git a/packages/ui-app/app/_features/Project/Vision/context.tsx b/packages/ui-app/app/_features/Project/Vision/context.tsx index 123f5622..44ed650b 100644 --- a/packages/ui-app/app/_features/Project/Vision/context.tsx +++ b/packages/ui-app/app/_features/Project/Vision/context.tsx @@ -23,7 +23,13 @@ interface IVisionContextProps { taskDone: number taskTotal: number visionByDays: VisionByDays - visionProgress: { [key: string]: { total: number; done: number } } + visionProgress: { + [key: string]: { + total: number + done: number + assigneeIds: string[] + } + } filter: IVisionFilter setFilter: Dispatch> setSelected: Dispatch> @@ -88,6 +94,7 @@ export const useVisionContext = () => { const getVisionProgress = (id: string) => { const progress = visionProgress[id] + console.log(visionProgress) if (!progress) { return 0 } @@ -95,6 +102,10 @@ export const useVisionContext = () => { return convertToProgress(progress.done, progress.total) } + const getVisionData = (id: string) => { + return visionProgress[id] + } + const getVisionByDay = (key: string) => { return visionByDays[key] || [] } @@ -176,6 +187,7 @@ export const useVisionContext = () => { mode, setMode, setSelected, - getVisionProgress + getVisionProgress, + getVisionData, } } diff --git a/packages/ui-app/app/_features/Project/Vision/index.tsx b/packages/ui-app/app/_features/Project/Vision/index.tsx index e25c3600..21c36e22 100644 --- a/packages/ui-app/app/_features/Project/Vision/index.tsx +++ b/packages/ui-app/app/_features/Project/Vision/index.tsx @@ -37,22 +37,30 @@ const useVisionProgress = ({ visions }: { visions: VisionField[] }) => { const { tasks } = useTaskStore() const { statusDoneId } = useProjectStatusStore() // - const visionProgress: { [key: string]: { total: number; done: number } } = {} + const visionProgress: { + [key: string]: { total: number; done: number; assigneeIds: string[] } + } = {} let taskTotal = 0 let taskDone = 0 visions.forEach(v => { - visionProgress[v.id] = { total: 0, done: 0 } + visionProgress[v.id] = { total: 0, done: 0, assigneeIds: [] } }) tasks.forEach(task => { - const { visionId, done, taskStatusId } = task + const { visionId, done, taskStatusId, assigneeIds } = task if (!visionId || !visionProgress[visionId]) return taskTotal += 1 visionProgress[visionId].total += 1 + if (assigneeIds.length) { + assigneeIds.forEach(assigneeId => { + visionProgress[visionId].assigneeIds.push(assigneeId) + }) + } + if (taskStatusId === statusDoneId) { visionProgress[visionId].done += 1 taskDone += 1 @@ -148,7 +156,7 @@ export default function ProjectVision() { setSelected }}> - + {/* */} ) } diff --git a/packages/ui-app/app/_features/Project/VisionTimeline/index.tsx b/packages/ui-app/app/_features/Project/VisionTimeline/index.tsx index 617f8c37..e2b2af90 100644 --- a/packages/ui-app/app/_features/Project/VisionTimeline/index.tsx +++ b/packages/ui-app/app/_features/Project/VisionTimeline/index.tsx @@ -2,30 +2,40 @@ import './style.css' import { useVisionContext } from '../Vision/context' import VisionViewMode from '../Vision/VisionViewMode' -import { Loading, Timeline } from '@shared/ui' +import { Avatar, Loading, Timeline } from '@shared/ui' import VisionMonthNavigator from '../Vision/VisionMonthNavigator' +import MemberAvatar from '@/components/MemberAvatar' +import { HiOutlineFlag } from 'react-icons/hi2' +import { dateFormat } from '@shared/libs' +import ProgressBar from '@/components/ProgressBar' +import { isSameDay } from 'date-fns' export default function VisionTimeline({ visible }: { visible: boolean }) { - const { visions, filter, loading, getVisionProgress, updateVision } = - useVisionContext() + const { + visions, + filter, + loading, + getVisionProgress, + getVisionData, + updateVision + } = useVisionContext() const ed = new Date() return (
- -
-
- -
- -
+ className={`vision-timeline-container w-full px-3 pt-3 relative ${visible ? '' : 'hidden' + }`}> + + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + {/* */} + {/*
*/} { updateVision({ @@ -44,14 +54,61 @@ export default function VisionTimeline({ visible }: { visible: boolean }) { } })}> {data => { + const { start, end } = data const progress = getVisionProgress(data.id) + const visionData = getVisionData(data.id) + const assigneeIds = Array.from(new Set(visionData.assigneeIds)) + + const displayAssignees = assigneeIds.slice(0, 3) + const restAssignees = assigneeIds.slice(3).length + const isSame = isSameDay(start, end) + const hasTask = visionData.done || visionData.total return ( -
- {data.title} -
+
+
+
+
+ {data.title} +
+ +
{dateFormat(start, 'MMM dd')}
+ - +
{dateFormat(end, 'MMM dd')}
+
+
+
+ {displayAssignees.map(assigneeId => { + return ( + + ) + })} + + {restAssignees ? ( +
+ +3 +
+ ) : null} +
+
+ {hasTask ? ( +
+ +
+ {visionData.total} Total + {visionData.done} Done +
+
+ ) : null} +
) }}