Skip to content

Commit

Permalink
[Feat]: Implement Timesheet logs API integration with State managemen…
Browse files Browse the repository at this point in the history
…t 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 <[email protected]>
  • Loading branch information
Innocent-Akim and evereq authored Nov 8, 2024
1 parent 728f961 commit 0b5b01d
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<>
{
Expand All @@ -29,7 +30,7 @@ export function TimesheetFilter({ closeModal, isOpen, openModal }: ITimesheetFil

<div className="flex gap-2">
<FrequencySelect />
<TimesheetFilterDate />
<TimesheetFilterDate {...initDate} />
<TimeSheetFilterPopover />
<Button
onClick={openModal}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import {
} from "@components/ui/popover"
import { CalendarIcon } from "@radix-ui/react-icons"
import { format } from "date-fns"
import React, { useState } from "react"
import React, { useEffect, useState } from "react"
import { MdKeyboardArrowRight } from "react-icons/md"
import { PiCalendarDotsThin } from "react-icons/pi"

interface DatePickerInputProps {
date: Date | null;
label: string;
}
interface TimesheetFilterDateProps {
export interface TimesheetFilterDateProps {
onChange?: (range: { from: Date | null; to: Date | null }) => void;
initialRange?: { from: Date | null; to: Date | null };
minDate?: Date;
Expand Down Expand Up @@ -81,6 +81,11 @@ export function TimesheetFilterDate({
break;
}
};
useEffect(() => {
if (dateRange.from && dateRange.to) {
onChange?.(dateRange);
}
}, [dateRange, onChange]);

const actionButtonClass = "h-4 border-none dark:bg-dark--theme-light text-primary hover:bg-transparent hover:underline"

Expand Down
28 changes: 24 additions & 4 deletions apps/web/app/[locale]/timesheet/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
import React, { useMemo } from 'react';
import { useParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { TranslationHooks } from 'next-intl';

import { withAuthentication } from 'lib/app/authenticator';
import { Breadcrumb, Container } from 'lib/components';
import { MainLayout } from 'lib/layout';

import { useAuthenticateUser, useDailyPlan, useLocalStorageState, useModal, useOrganizationTeams } from '@app/hooks';
import { clsxm } from '@app/utils';
import { fullWidthState } from '@app/stores/fullWidth';
import { useAtomValue } from 'jotai';

import { ArrowLeftIcon } from 'assets/svg';
import { CalendarView, TimesheetCard, TimesheetFilter, TimesheetView } from './components';
import { CalendarDaysIcon, Clock, User2 } from 'lucide-react';
import { GrTask } from "react-icons/gr";
import { GoSearch } from "react-icons/go";
import { GrTask } from 'react-icons/gr';
import { GoSearch } from 'react-icons/go';

import { getGreeting } from '@/app/helpers';
import { TranslationHooks } from 'next-intl';
import { useTimesheet } from '@/app/hooks/features/useTimesheet';
import { endOfDay, startOfDay } from 'date-fns';

type TimesheetViewMode = "ListView" | "CalendarView";

Expand All @@ -31,7 +37,16 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
const t = useTranslations();
const { user } = useAuthenticateUser();

const [dateRange, setDateRange] = React.useState<{ from: Date | null; to: Date | null }>({
from: startOfDay(new Date()),
to: endOfDay(new Date()),
});

const { sortedPlans } = useDailyPlan();
const { timesheet } = useTimesheet({
startDate: dateRange.from ?? "",
endDate: dateRange.to ?? ""
});
const {
isOpen: isManualTimeModalOpen,
openModal: openManualTimeModal,
Expand Down Expand Up @@ -134,6 +149,12 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
{/* <DropdownMenuDemo /> */}
<div className='flex flex-col overflow-y-auto w-full border-1 rounded-lg bg-[#FFFFFF] dark:bg-dark--theme p-4 mt-4'>
<TimesheetFilter
initDate={{
initialRange: dateRange,
onChange(range) {
setDateRange(range)
},
}}
closeModal={closeManualTimeModal}
openModal={openManualTimeModal}
isOpen={isManualTimeModalOpen}
Expand Down Expand Up @@ -172,4 +193,3 @@ const ViewToggleButton: React.FC<ViewToggleButtonProps> = ({
<span>{mode === 'ListView' ? t('pages.timesheet.VIEWS.LIST') : t('pages.timesheet.VIEWS.CALENDAR')}</span>
</button>
);
// 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
6 changes: 3 additions & 3 deletions apps/web/app/api/timer/timesheet/daily/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
50 changes: 50 additions & 0 deletions apps/web/app/hooks/features/useTimesheet.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
44 changes: 43 additions & 1 deletion apps/web/app/services/client/api/timer/timer-log.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ITimerStatus } from '@app/interfaces';
import { ITimeSheet, ITimerStatus } from '@app/interfaces';
import { get } from '../../axios';

export async function getTimerLogs(
Expand All @@ -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<ITimeSheet[]>(endpoint, { tenantId });
}
19 changes: 17 additions & 2 deletions apps/web/app/services/server/requests/timesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ITimeSheet[]>({
path: `/timesheet/time-log/report/daily?${queries.toString()}`,
path: `/timesheet/time-log?activityLevel?${queries.toString()}`,
method: 'GET',
bearer_token,
tenantId: params.tenantId
Expand Down
3 changes: 3 additions & 0 deletions apps/web/app/stores/time-logs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ITimerLogsDailyReport } from '@app/interfaces/timer/ITimerLogs';
import { atom } from 'jotai';
import { ITimeSheet } from '../interfaces';

export const timerLogsDailyReportState = atom<ITimerLogsDailyReport[]>([]);

export const timesheetRapportState = atom<ITimeSheet[]>([])

0 comments on commit 0b5b01d

Please sign in to comment.