diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx index 724bb0e9c..ca6cd2900 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx @@ -1,7 +1,143 @@ +import { GroupedTimesheet, useTimesheet } from "@/app/hooks/features/useTimesheet"; +import { clsxm } from "@/app/utils"; +import { statusColor } from "@/lib/components"; +import { DisplayTimeForTimesheet, TaskNameInfoDisplay, TotalDurationByDate, TotalTimeDisplay } from "@/lib/features"; +import { AccordionContent, AccordionItem, AccordionTrigger } from "@components/ui/accordion"; +import { Accordion } from "@radix-ui/react-accordion"; +import { TranslationHooks, useTranslations } from "next-intl"; +import React from "react"; +import { EmployeeAvatar } from "./CompactTimesheetComponent"; +import { formatDate } from "@/app/helpers"; +import { ClockIcon } from "lucide-react"; -export function CalendarView() { +export function CalendarView({ data }: { data?: GroupedTimesheet[] }) { + const t = useTranslations(); return ( -
+
+ {data ? ( + data.length > 0 ? ( + + ) : ( +
+

{t('pages.timesheet.NO_ENTRIES_FOUND')}

+
+ ) + ) : ( +
+

{t('pages.timesheet.LOADING')}

+
+ )} +
+ ); +} + +const CalendarDataView = ({ data, t }: { data?: GroupedTimesheet[], t: TranslationHooks }) => { + const { getStatusTimesheet } = useTimesheet({}); + + return ( +
+
+ {data?.map((plan, index) => { + return
+
+
+ {formatDate(plan.date)} +
+
+ Total{" : "} + + +
+
+ + {Object.entries(getStatusTimesheet(plan.tasks)).map(([status, rows]) => ( + rows.length > 0 && status && + +
+
+
+
+ + {status === 'DENIED' ? 'REJECTED' : status} + + ({rows.length}) +
+
+
+ + +
+
+
+ + {rows.map((task) => ( +
+
+
+ + {task.employee.fullName} +
+ +
+ +
+ {task.project && task.project.name} +
+
+ ))} +
+
+ ))} +
+
+ } + )} +
) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/CompactTimesheetComponent.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/CompactTimesheetComponent.tsx new file mode 100644 index 000000000..03f80d818 --- /dev/null +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/CompactTimesheetComponent.tsx @@ -0,0 +1,46 @@ +import React from "react"; + +export const EmployeeAvatar = ({ imageUrl }: { imageUrl: string }) => { + const [isLoading, setIsLoading] = React.useState(true); + + return ( +
+ {isLoading && ( +
+ +
+ )} + Employee setIsLoading(false)} + onError={() => setIsLoading(false)} + /> +
+ ); +}; + + +const LoadingSpinner = ({ className }: { className?: string }) => ( + + + + +); diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx index 06948073c..35543a172 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx @@ -5,8 +5,10 @@ export * from './TimesheetFilter'; export * from './FrequencySelect'; export * from './FilterWithStatus'; export * from './TimesheetFilterDate'; -export * from './TimeSheetFilterPopover' +export * from './TimeSheetFilterPopover'; export * from './TimesheetAction'; export * from './RejectSelectedModal'; export * from './EditTaskModal'; -export * from './TimesheetLoader' +export * from './CompactTimesheetComponent'; +export * from './TimesheetLoader'; + diff --git a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx index 863759acc..a90e0378a 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx @@ -198,7 +198,7 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb ) : ( - + )}
diff --git a/apps/web/lib/components/types.ts b/apps/web/lib/components/types.ts index 30c0412ef..a5a822801 100644 --- a/apps/web/lib/components/types.ts +++ b/apps/web/lib/components/types.ts @@ -1,6 +1,7 @@ export type IVariant = 'primary' | 'outline' | 'ghost' | 'light' | 'dark'; type StatusColorScheme = { bg: string; + border: string, text: string; bgOpacity: string; }; @@ -8,31 +9,37 @@ type StatusColorScheme = { const STATUS_COLORS: Record = { PENDING: { bg: 'bg-[#FBB650]', + border: 'rgb(251, 182, 80)', text: 'text-[#FBB650]', bgOpacity: 'rgba(251, 182, 80, 0.1)', }, APPROVED: { bg: 'bg-[#30B366]', + border: 'rgba(48, 179, 102)', text: 'text-[#30B366]', bgOpacity: 'rgba(48, 179, 102, 0.1)', }, DENIED: { bg: 'bg-[#dc2626]', + border: 'rgba(220, 38, 38)', text: 'text-[#dc2626]', bgOpacity: 'rgba(220, 38, 38, 0.1)', }, DRAFT: { bg: 'bg-gray-300', + border: 'rgba(220, 220, 220)', text: 'text-gray-500', bgOpacity: 'rgba(220, 220, 220, 0.1)', }, 'IN REVIEW': { bg: 'bg-blue-500', + border: 'rgba(59, 130, 246)', text: 'text-blue-500', bgOpacity: 'rgba(59, 130, 246, 0.1)', }, DEFAULT: { bg: 'bg-gray-100', + border: 'rgba(243, 244, 246)', text: 'text-gray-400', bgOpacity: 'rgba(243, 244, 246, 0.1)', }, diff --git a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx index 5326b964c..3860a9d19 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -54,6 +54,7 @@ import { RejectSelectedModal, StatusAction, StatusType, + EmployeeAvatar, getTimesheetButtons, statusTable } from '@/app/[locale]/timesheet/[memberId]/components'; @@ -335,10 +336,8 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { {task.project && task.project.name}
- {task.employee.fullName}
diff --git a/apps/web/lib/features/task/task-displays.tsx b/apps/web/lib/features/task/task-displays.tsx index 0bca22f27..c87453592 100644 --- a/apps/web/lib/features/task/task-displays.tsx +++ b/apps/web/lib/features/task/task-displays.tsx @@ -82,7 +82,7 @@ export const DisplayTimeForTimesheet = ({ duration }: { duration: number }) => { return (
-
+
{formatTime(hours, minute)}
@@ -104,7 +104,7 @@ TotalTimeDisplay.displayName = 'TotalTimeDisplay'; export const TotalDurationByDate = React.memo( - ({ timesheetLog, createdAt }: { timesheetLog: TimesheetLog[]; createdAt: Date | string }) => { + ({ timesheetLog, createdAt, className }: { timesheetLog: TimesheetLog[]; createdAt: Date | string, className?: string }) => { const targetDateISO = new Date(createdAt).toISOString(); const filteredLogs = timesheetLog.filter( (item) => formatDate(item.timesheet.createdAt) === formatDate(targetDateISO)); @@ -112,7 +112,7 @@ export const TotalDurationByDate = React.memo( (total, log) => total + (log.timesheet?.duration || 0), 0); const { h: hours, m: minutes } = secondsToTime(totalDurationInSeconds); return ( -
+
{formatTime(hours, minutes)}
);