Skip to content

Commit

Permalink
feat(timesheet): add grouping by week and month functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Innocent-Akim committed Nov 22, 2024
1 parent 6af3dc0 commit 01e6109
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export function AddTaskModal({ closeModal, isOpen }: IAddTaskModalProps) {
const [isBillable, setIsBillable] = React.useState<boolean>(true);

const projectItemsLists = {
Project: activeTeam?.projects as [],
Project: activeTeam?.projects ?? [],
};

const handleSelectedValuesChange = (values: { [key: string]: Item | null }) => {
// Handle value changes
};
const selectedValues = {
Preject: null,
Project: null,
};
const handleChange = (field: string, selectedItem: Item | null) => {
// Handle field changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export function FilterWithStatus({
};

const buttonData = Object.entries({
'All Tasks': Object.values(data!).reduce((total, tasks) => total + tasks.length, 0),
Pending: data!.PENDING.length,
Approved: data!.APPROVED.length,
'In review': data!['IN REVIEW'].length,
Draft: data!.DRAFT.length,
Rejected: data!.DENIED.length,
'All Tasks': React.useMemo(() => Object.values(data!).reduce((total, tasks) => total + tasks.length, 0), [data]),
Pending: data?.PENDING.length ?? 0,
Approved: data?.APPROVED.length ?? 0,
'In review': data!['IN REVIEW']?.length ?? 0,
Draft: data?.DRAFT.length ?? 0,
Rejected: data?.DENIED.length ?? 0,
}).map(([label, count]) => ({
label: label as FilterStatus,
count,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TimesheetStatus } from "@/app/interfaces";
import { TimesheetFilterByDays, TimesheetStatus } from "@/app/interfaces";
import { clsxm } from "@/app/utils";
import { TranslationHooks } from "next-intl";
import { ReactNode } from "react";
Expand Down Expand Up @@ -31,6 +31,7 @@ export type StatusType = "Pending" | "Approved" | "Denied";
export type StatusAction = "Deleted" | "Approved" | "Denied";



// eslint-disable-next-line @typescript-eslint/no-empty-function
export const getTimesheetButtons = (status: StatusType, t: TranslationHooks, disabled: boolean, onClick: (action: StatusAction) => void) => {

Expand Down Expand Up @@ -69,3 +70,9 @@ export const statusTable: { label: TimesheetStatus; description: string }[] = [
{ label: "DRAFT", description: "The item is saved as draft" },
{ label: "DENIED", description: "The item has been rejected" },
];

export const DailyTable: { label: TimesheetFilterByDays; description: string }[] = [
{ label: "Daily", description: 'Group by Daily' },
{ label: "Weekly", description: 'Group by Weekly' },
{ label: "Monthly", description: 'Group by Monthly' },
];
6 changes: 3 additions & 3 deletions apps/web/app/[locale]/timesheet/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,23 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
to: endOfDay(new Date())
});

const { timesheet, statusTimesheet } = useTimesheet({
const { timesheet, statusTimesheet, timesheetGroupByMonth, timesheetGroupByWeek } = useTimesheet({
startDate: dateRange.from ?? '',
endDate: dateRange.to ?? ''
});

const lowerCaseSearch = useMemo(() => search?.toLowerCase() ?? '', [search]);
const filterDataTimesheet = useMemo(
() =>
timesheet.filter((v) =>
timesheetGroupByWeek.filter((v) =>
v.tasks.some(
(task) =>
task.task?.title?.toLowerCase()?.includes(lowerCaseSearch) ||
task.employee?.fullName?.toLowerCase()?.includes(lowerCaseSearch) ||
task.project?.name?.toLowerCase()?.includes(lowerCaseSearch)
)
),
[timesheet, lowerCaseSearch]
[timesheet, timesheetGroupByMonth, timesheetGroupByWeek, lowerCaseSearch]
);

const {
Expand Down
65 changes: 65 additions & 0 deletions apps/web/app/hooks/features/useTimesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,69 @@ const groupByDate = (items: TimesheetLog[]): GroupedTimesheet[] => {
.map(([date, tasks]) => ({ date, tasks }))
.sort((a, b) => b.date.localeCompare(a.date));
}
const getWeekYearKey = (date: Date): string => {
const startOfYear = new Date(date.getFullYear(), 0, 1);
const daysSinceStart = Math.floor((date.getTime() - startOfYear.getTime()) / (1000 * 60 * 60 * 24));
const week = Math.ceil((daysSinceStart + startOfYear.getDay() + 1) / 7);
return `${date.getFullYear()}-W${week}`;
};

const groupByWeek = (items: TimesheetLog[]): GroupedTimesheet[] => {
if (!items?.length) return [];
type GroupedMap = Record<string, TimesheetLog[]>;

const groupedByWeek = items.reduce<GroupedMap>((acc, item) => {
if (!item?.timesheet?.createdAt) {
console.warn('Skipping item with missing timesheet or createdAt:', item);
return acc;
}
try {
const date = new Date(item.timesheet.createdAt);
const weekKey = getWeekYearKey(date);
if (!acc[weekKey]) acc[weekKey] = [];
acc[weekKey].push(item);
} catch (error) {
console.error(
`Failed to process date for timesheet ${item.timesheet.id}:`,
{ createdAt: item.timesheet.createdAt, error }
);
}
return acc;
}, {});

return Object.entries(groupedByWeek)
.map(([week, tasks]) => ({ date: week, tasks }))
.sort((a, b) => b.date.localeCompare(a.date));
};

const groupByMonth = (items: TimesheetLog[]): GroupedTimesheet[] => {
if (!items?.length) return [];
type GroupedMap = Record<string, TimesheetLog[]>;

const groupedByMonth = items.reduce<GroupedMap>((acc, item) => {
if (!item?.timesheet?.createdAt) {
console.warn('Skipping item with missing timesheet or createdAt:', item);
return acc;
}
try {
const date = new Date(item.timesheet.createdAt);
const monthKey = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}`;
if (!acc[monthKey]) acc[monthKey] = [];
acc[monthKey].push(item);
} catch (error) {
console.error(
`Failed to process date for timesheet ${item.timesheet.id}:`,
{ createdAt: item.timesheet.createdAt, error }
);
}
return acc;
}, {});

return Object.entries(groupedByMonth)
.map(([month, tasks]) => ({ date: month, tasks }))
.sort((a, b) => b.date.localeCompare(a.date));
};



export function useTimesheet({
Expand Down Expand Up @@ -169,6 +232,8 @@ export function useTimesheet({
return {
loadingTimesheet,
timesheet: groupByDate(timesheet),
timesheetGroupByWeek: groupByWeek(timesheet),
timesheetGroupByMonth: groupByMonth(timesheet),
getTaskTimesheet,
loadingDeleteTimesheet,
deleteTaskTimesheet,
Expand Down
4 changes: 4 additions & 0 deletions apps/web/app/interfaces/ITask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export type TimesheetStatus =
| "DENIED"
| "APPROVED";

export type TimesheetFilterByDays =
| "Daily"
| "Weekly"
| "Monthly"

export type ITaskStatusStack = {
status: ITaskStatus;
Expand Down

0 comments on commit 01e6109

Please sign in to comment.