Skip to content

Commit

Permalink
refactor: optimize timesheet filtering and view logic (#3520)
Browse files Browse the repository at this point in the history
  • Loading branch information
Innocent-Akim authored Jan 17, 2025
1 parent 0294f61 commit 7e8e15e
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 23 deletions.
50 changes: 27 additions & 23 deletions apps/web/app/[locale]/timesheet/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,7 @@ import { fullWidthState } from '@app/stores/fullWidth';
import { useAtomValue } from 'jotai';

import { ArrowLeftIcon } from 'assets/svg';
import {
CalendarView,
CalendarViewIcon,
FilterStatus,
ListViewIcon,
MemberWorkIcon,
MenHoursIcon,
PendingTaskIcon,
SelectedTimesheet,
TimesheetCard,
TimesheetFilter,
TimesheetView
} from './components';
import { CalendarView, CalendarViewIcon, ListViewIcon, MemberWorkIcon, MenHoursIcon, PendingTaskIcon, SelectedTimesheet, TimesheetCard, TimesheetFilter, TimesheetView } from './components';
import { GoSearch } from 'react-icons/go';

import { differenceBetweenHours, getGreeting, secondsToTime } from '@/app/helpers';
Expand All @@ -41,6 +29,8 @@ import { endOfMonth, startOfMonth } from 'date-fns';
import TimesheetDetailModal from './components/TimesheetDetailModal';
import { useTimesheetPagination } from '@/app/hooks/features/useTimesheetPagination';
import TimesheetPagination from './components/TimesheetPagination';
import { useTimesheetFilters } from '@/app/hooks/features/useTimesheetFilters';
import { useTimesheetViewData } from '@/app/hooks/features/useTimesheetViewData';

type TimesheetViewMode = 'ListView' | 'CalendarView';
export type TimesheetDetailMode = 'Pending' | 'MenHours' | 'MemberWork';
Expand All @@ -61,11 +51,7 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb

const { isTrackingEnabled, activeTeam } = useOrganizationTeams();
const [search, setSearch] = useState<string>('');
const [filterStatus, setFilterStatus] = useLocalStorageState<FilterStatus>('timesheet-filter-status', 'All Tasks');
const [timesheetDetailMode, setTimesheetDetailMode] = useLocalStorageState<TimesheetDetailMode>(
'timesheet-detail-mode',
'Pending'
);
const [timesheetDetailMode, setTimesheetDetailMode] = useLocalStorageState<TimesheetDetailMode>('timesheet-detail-mode', 'Pending');
const [timesheetNavigator, setTimesheetNavigator] = useLocalStorageState<TimesheetViewMode>(
'timesheet-viewMode',
'ListView'
Expand Down Expand Up @@ -107,6 +93,20 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
data: filterDataTimesheet,
pageSize: TIMESHEET_PAGE_SIZE
});
const viewData = useTimesheetViewData({
timesheetNavigator,
timesheetGroupByDays,
paginatedGroups,
filterDataTimesheet
});

const {
activeStatus,
setActiveStatus,
filteredData,
statusData
} = useTimesheetFilters(viewData);


React.useEffect(() => {
getOrganizationProjects();
Expand Down Expand Up @@ -260,9 +260,9 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
</div>
<TimesheetFilter
user={user}
data={statusTimesheet}
onChangeStatus={setFilterStatus}
filterStatus={filterStatus}
data={statusData}
onChangeStatus={setActiveStatus}
filterStatus={activeStatus}
initDate={{
initialRange: dateRange,
onChange(range) {
Expand All @@ -282,12 +282,16 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
<Container fullWidth={fullWidth} className="py-5 mt-3 h-full">
<div className="rounded-lg border border-gray-200 dark:border-gray-800">
{timesheetNavigator === 'ListView' ? (
<TimesheetView user={user} data={paginatedGroups} loading={loadingTimesheet} />
<TimesheetView
user={user}
data={filteredData}
loading={loadingTimesheet}
/>
) : (
<>
<CalendarView
user={user}
data={shouldRenderPagination ? paginatedGroups : filterDataTimesheet}
data={filteredData}
loading={loadingTimesheet}
/>
{selectTimesheetId.length > 0 && (
Expand Down
64 changes: 64 additions & 0 deletions apps/web/app/hooks/features/useTimesheetFilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useMemo } from 'react';
import { FilterStatus } from '@/app/[locale]/timesheet/[memberId]/components/FilterWithStatus';
import { GroupedTimesheet } from './useTimesheet';
import { TimesheetLog, TimesheetStatus } from '@/app/interfaces';
import { useLocalStorageState } from '../useLocalStorageState';

export const useTimesheetFilters = (data?: GroupedTimesheet[]) => {
const [activeStatus, setActiveStatus] = useLocalStorageState<FilterStatus>('timesheet-filter-status', 'All Tasks');


const filteredData = useMemo(() => {
if (!data) return [];

return data.map(group => {
type FilterStatusWithoutAll = Exclude<FilterStatus, 'All Tasks'>;

const statusMap: Record<FilterStatusWithoutAll, TimesheetStatus> = {
'Pending': 'PENDING',
'Approved': 'APPROVED',
'In review': 'IN REVIEW',
'Draft': 'DRAFT',
'Rejected': 'DENIED'
};

const filteredTasks = group.tasks.filter(task => {
if (activeStatus === 'All Tasks') {
return true;
}
return task.timesheet.status === statusMap[activeStatus as FilterStatusWithoutAll];
});

return {
...group,
tasks: filteredTasks
};
}).filter(group => group.tasks.length > 0);
}, [data, activeStatus]);

const statusData = useMemo(() => {
const emptyStatusData: Record<TimesheetStatus, TimesheetLog[]> = {
'DRAFT': [],
'PENDING': [],
'IN REVIEW': [],
'DENIED': [],
'APPROVED': []
};

if (!data) return emptyStatusData;

const allTasks = data.flatMap(group => group.tasks);
return allTasks.reduce((acc, task) => {
const status = task.timesheet.status as TimesheetStatus;
acc[status].push(task);
return acc;
}, { ...emptyStatusData });
}, [data]);

return {
activeStatus,
setActiveStatus,
filteredData,
statusData
};
};
29 changes: 29 additions & 0 deletions apps/web/app/hooks/features/useTimesheetViewData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useMemo } from 'react';
import { GroupedTimesheet } from './useTimesheet';

type ViewMode = 'ListView' | 'CalendarView';
type GroupByDays = 'Daily' | 'Weekly' | 'Monthly';

interface TimesheetViewDataProps {
timesheetNavigator: ViewMode;
timesheetGroupByDays: GroupByDays;
paginatedGroups?: GroupedTimesheet[];
filterDataTimesheet?: GroupedTimesheet[];
}

export const useTimesheetViewData = ({
timesheetNavigator,
timesheetGroupByDays,
paginatedGroups,
filterDataTimesheet
}: TimesheetViewDataProps) => {
const viewData = useMemo(() => {
const shouldUsePaginatedGroups =
timesheetNavigator === 'ListView' ||
(timesheetGroupByDays === 'Daily' && timesheetNavigator === 'CalendarView');

return shouldUsePaginatedGroups ? paginatedGroups : filterDataTimesheet;
}, [timesheetNavigator, timesheetGroupByDays, paginatedGroups, filterDataTimesheet]);

return viewData;
};

0 comments on commit 7e8e15e

Please sign in to comment.