From 0b5b01db38c5a7f24c76dee485baf6cf3c2be220 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:44:30 +0200 Subject: [PATCH] [Feat]: Implement Timesheet logs API integration with State management and query handling (#3254) * feat:Implement timesheet logs API integration with state management and query handling * fix: coderabbitai * Update page.tsx --------- Co-authored-by: Ruslan Konviser --- .../[memberId]/components/TimesheetFilter.tsx | 7 +-- .../components/TimesheetFilterDate.tsx | 9 +++- .../[locale]/timesheet/[memberId]/page.tsx | 28 +++++++++-- .../app/api/timer/timesheet/daily/route.ts | 6 +-- apps/web/app/hooks/features/useTimesheet.ts | 50 +++++++++++++++++++ .../services/client/api/timer/timer-log.ts | 44 +++++++++++++++- .../app/services/server/requests/timesheet.ts | 19 ++++++- apps/web/app/stores/time-logs.ts | 3 ++ 8 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 apps/web/app/hooks/features/useTimesheet.ts diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx index 1c4418ea1..a53934536 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx @@ -1,13 +1,14 @@ import { FilterWithStatus } from './FilterWithStatus'; -import { FrequencySelect, TimeSheetFilterPopover, TimesheetFilterDate } from '.'; +import { FrequencySelect, TimeSheetFilterPopover, TimesheetFilterDate, TimesheetFilterDateProps } from '.'; import { Button } from 'lib/components'; import { AddManualTimeModal } from '@/lib/features/manual-time/add-manual-time-modal'; interface ITimesheetFilter { isOpen: boolean, openModal: () => void, closeModal: () => void + initDate?: TimesheetFilterDateProps } -export function TimesheetFilter({ closeModal, isOpen, openModal }: ITimesheetFilter) { +export function TimesheetFilter({ closeModal, isOpen, openModal, initDate }: ITimesheetFilter,) { return ( <> { @@ -29,7 +30,7 @@ export function TimesheetFilter({ closeModal, isOpen, openModal }: ITimesheetFil
- + ); -// https://demo.gauzy.co/#/pages/employees/timesheets/daily?organizationId=e4a6eeb6-3f13-4712-b880-34aa9ef1dd2f&date=2024-11-02&date_end=2024-11-02&unit_of_time=day&is_custom_date=false diff --git a/apps/web/app/api/timer/timesheet/daily/route.ts b/apps/web/app/api/timer/timesheet/daily/route.ts index dc84bf6aa..759139c47 100644 --- a/apps/web/app/api/timer/timesheet/daily/route.ts +++ b/apps/web/app/api/timer/timesheet/daily/route.ts @@ -14,15 +14,15 @@ export async function GET(req: Request) { { status: 400 } ); } - const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + const { $res, user, tenantId, organizationId, access_token, } = await authenticatedGuard(req, res); if (!user) return $res('Unauthorized'); try { const { data } = await getTaskTimesheetRequest({ tenantId, organizationId, - employeeIds: [], startDate, - endDate + endDate, + }, access_token); if (!data) { diff --git a/apps/web/app/hooks/features/useTimesheet.ts b/apps/web/app/hooks/features/useTimesheet.ts new file mode 100644 index 000000000..91a8b473a --- /dev/null +++ b/apps/web/app/hooks/features/useTimesheet.ts @@ -0,0 +1,50 @@ +import { useAuthenticateUser } from './useAuthenticateUser'; +import { useAtom } from 'jotai'; +import { timesheetRapportState } from '@/app/stores/time-logs'; +import { useQuery } from '../useQuery'; +import { useCallback, useEffect } from 'react'; +import { getTaskTimesheetLogsApi } from '@/app/services/client/api/timer/timer-log'; +import moment from 'moment'; + +interface TimesheetParams { + startDate: Date | string; + endDate: Date | string; +} + +export function useTimesheet({ + startDate, + endDate, +}: TimesheetParams) { + const { user } = useAuthenticateUser(); + const [timesheet, setTimesheet] = useAtom(timesheetRapportState); + + const { loading: loadingTimesheet, queryCall: queryTimesheet } = useQuery(getTaskTimesheetLogsApi); + + const getTaskTimesheet = useCallback( + ({ startDate, endDate }: TimesheetParams) => { + if (!user) return; + const from = moment(startDate).format('YYYY-MM-DD'); + const to = moment(endDate).format('YYYY-MM-DD') + queryTimesheet({ + startDate: from, + endDate: to, + organizationId: user.employee?.organizationId, + tenantId: user.tenantId ?? '', + timeZone: user.timeZone?.split('(')[0].trim(), + }).then((response) => { + setTimesheet(response.data); + }).catch((error) => { + console.error('Error fetching timesheet:', error); + }); + }, + [user, queryTimesheet, setTimesheet] + ); + useEffect(() => { + getTaskTimesheet({ startDate, endDate }); + }, [getTaskTimesheet, startDate, endDate]); + return { + loadingTimesheet, + timesheet, + getTaskTimesheet, + }; +} diff --git a/apps/web/app/services/client/api/timer/timer-log.ts b/apps/web/app/services/client/api/timer/timer-log.ts index f8a88bbb8..237e840e1 100644 --- a/apps/web/app/services/client/api/timer/timer-log.ts +++ b/apps/web/app/services/client/api/timer/timer-log.ts @@ -1,4 +1,4 @@ -import { ITimerStatus } from '@app/interfaces'; +import { ITimeSheet, ITimerStatus } from '@app/interfaces'; import { get } from '../../axios'; export async function getTimerLogs( @@ -13,3 +13,45 @@ export async function getTimerLogs( } // todayStart, todayEnd; + + +export async function getTaskTimesheetLogsApi({ + organizationId, + tenantId, + startDate, + endDate, + timeZone +}: { + organizationId: string, + tenantId: string, + startDate: string | Date, + endDate: string | Date, + timeZone?: string +}) { + + if (!organizationId || !tenantId || !startDate || !endDate) { + throw new Error('Required parameters missing: organizationId, tenantId, startDate, and endDate are required'); + } + const start = typeof startDate === 'string' ? new Date(startDate).toISOString() : startDate.toISOString(); + const end = typeof endDate === 'string' ? new Date(endDate).toISOString() : endDate.toISOString(); + if (isNaN(new Date(start).getTime()) || isNaN(new Date(end).getTime())) { + throw new Error('Invalid date format provided'); + } + + const params = new URLSearchParams({ + 'activityLevel[start]': '0', + 'activityLevel[end]': '100', + organizationId, + tenantId, + startDate: start, + endDate: end, + timeZone: timeZone || '', + 'relations[0]': 'project', + 'relations[1]': 'task', + 'relations[2]': 'organizationContact', + 'relations[3]': 'employee.user' + }); + + const endpoint = `/timesheet/time-log?${params.toString()}`; + return get(endpoint, { tenantId }); +} diff --git a/apps/web/app/services/server/requests/timesheet.ts b/apps/web/app/services/server/requests/timesheet.ts index be7662ec1..a7a254880 100644 --- a/apps/web/app/services/server/requests/timesheet.ts +++ b/apps/web/app/services/server/requests/timesheet.ts @@ -54,11 +54,26 @@ export function taskActivityRequest(params: TTaskActivityParams, bearer_token: s }); } +/** + * Parameters for timesheet API requests + * @property organizationId - Organization identifier + * @property tenantId - Tenant identifier + * @property startDate - Start date for timesheet period + * @property endDate - End date for timesheet period + * @property timeZone - Optional timezone for date calculations (defaults to UTC) + */ +type ITimesheetProps = { + organizationId: string; + tenantId: string; + startDate: string; + endDate: string; + timeZone?: string; +} -export function getTaskTimesheetRequest(params: TTasksTimesheetStatisticsParams, bearer_token: string) { +export function getTaskTimesheetRequest(params: ITimesheetProps, bearer_token: string) { const queries = qs.stringify(params); return serverFetch({ - path: `/timesheet/time-log/report/daily?${queries.toString()}`, + path: `/timesheet/time-log?activityLevel?${queries.toString()}`, method: 'GET', bearer_token, tenantId: params.tenantId diff --git a/apps/web/app/stores/time-logs.ts b/apps/web/app/stores/time-logs.ts index 6d7c5f9b4..3cf5395ea 100644 --- a/apps/web/app/stores/time-logs.ts +++ b/apps/web/app/stores/time-logs.ts @@ -1,4 +1,7 @@ import { ITimerLogsDailyReport } from '@app/interfaces/timer/ITimerLogs'; import { atom } from 'jotai'; +import { ITimeSheet } from '../interfaces'; export const timerLogsDailyReportState = atom([]); + +export const timesheetRapportState = atom([])