From 37136b83bcfcf27519840523d69d2d9db24f392b Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Fri, 9 May 2025 16:19:29 +0400 Subject: [PATCH] feat: worktime table with dates range --- src/client/models/CardSummary.ts | 2 +- .../models/GetTimeTrackingRecordsRequest.ts | 4 +- .../hooks/useWorkTableColumns.tsx | 16 ++-- .../WorkTimeTable/hooks/useWorkTableState.tsx | 60 ++++++------ .../tabs/WorkTimeTable/ui/WorkTimeTable.tsx | 94 ++++++------------- src/shared/lib/date.ts | 17 ---- 6 files changed, 67 insertions(+), 126 deletions(-) diff --git a/src/client/models/CardSummary.ts b/src/client/models/CardSummary.ts index ee8e232..55f8b99 100644 --- a/src/client/models/CardSummary.ts +++ b/src/client/models/CardSummary.ts @@ -24,7 +24,7 @@ export type CardSummary = { attributes: Array; shipmentWarehouseId: (number | null); shipmentWarehouseName: (string | null); - billRequests?: Array; + billRequests: Array; group?: (CardGroupSchema | null); }; diff --git a/src/client/models/GetTimeTrackingRecordsRequest.ts b/src/client/models/GetTimeTrackingRecordsRequest.ts index f999e43..b434aa1 100644 --- a/src/client/models/GetTimeTrackingRecordsRequest.ts +++ b/src/client/models/GetTimeTrackingRecordsRequest.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type GetTimeTrackingRecordsRequest = { - date: string; - userIds: Array; + dateFrom: string; + dateTo: string; }; diff --git a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx index f1df73a..669ccc4 100644 --- a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx +++ b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx @@ -7,7 +7,7 @@ import { processSelectedCells } from "../../../../../shared/lib/interpolateCells import { TimeTrackingData } from "../../../../../client"; import dayjs from "dayjs"; import { IMaskInput } from "react-imask"; -import { floatHoursToHoursAndMinutes } from "../../../../../types/utils.ts"; +import { dateToString, floatHoursToHoursAndMinutes } from "../../../../../types/utils.ts"; export type EmployeeData = { name: string; @@ -20,21 +20,17 @@ export type EmployeeData = { [key: string]: number | string; }; type Props = { - month: Date; data: EmployeeData[]; onUpdate: (date: Date, userId: number, value: string) => void; selectedCells: string[]; setSelectedCells: (cells: string[]) => void; - selectedBoundaries: [Date | null, Date | null]; range: dayjs.Dayjs[]; }; const useWorkTableColumns = ({ - month, onUpdate, data, selectedCells, setSelectedCells, - selectedBoundaries, range, }: Props) => { const totalAmount = useMemo(() => { @@ -48,7 +44,7 @@ const useWorkTableColumns = ({ } return acc; }, 0); - }, [data, month, selectedCells, selectedBoundaries]); + }, [data, selectedCells, range]); const getBorderStyles = (cellId: string) => { if (selectedCells.length <= 1) return {}; if (selectedCells[0] === cellId) @@ -86,8 +82,8 @@ const useWorkTableColumns = ({ ...range.map(date => ({ size: 100, - accessorKey: date.date().toString(), - header: date.date().toString(), + accessorKey: dateToString(date.toDate()) ?? "", + header: dateToString(date.toDate()) ?? "", enableSorting: false, enableColumnActions: false, Header: ( @@ -130,7 +126,7 @@ const useWorkTableColumns = ({ row.original.userId, value, ); - } + }; return ( @@ -178,7 +174,7 @@ const useWorkTableColumns = ({ ), }, ], - [month, selectedCells, selectedBoundaries, totalAmount], + [selectedCells, range, totalAmount], ); }; diff --git a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableState.tsx b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableState.tsx index f738c86..f1d99e7 100644 --- a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableState.tsx +++ b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableState.tsx @@ -1,49 +1,45 @@ import { useEffect, useState } from "react"; import { TimeTrackingRecord, TimeTrackingService } from "../../../../../client"; -import { - dateWithoutTimezone, - getDatesInMonth, -} from "../../../../../shared/lib/date.ts"; -import { last } from "lodash"; +import { getDefaultEndDate } from "../../WorkShiftsPlanning/utils/utils.tsx"; +import { dateToString } from "../../../../../types/utils.ts"; -const getDateBoundaries = (month: Date) => { - return [ - getDatesInMonth(month)[0].toDate(), - last(getDatesInMonth(month))?.toDate(), - ]; -}; const useWorkTableState = () => { - const [month, setMonth] = useState( - new Date(new Date().getFullYear(), new Date().getMonth(), 1) - ); + const [dateRange, setDateRange] = useState< + [Date | null, Date | null] + >([new Date(), getDefaultEndDate()]); const [trackingRecords, setTrackingRecords] = useState< TimeTrackingRecord[] >([]); - const [dateBoundaries, setDateBoundaries] = useState( - getDateBoundaries(month) - ); - const refetch = async () => { - return TimeTrackingService.getTimeTrackingRecords({ - requestBody: { - date: dateWithoutTimezone(month), - userIds: [], - }, - }).then(response => setTrackingRecords(response.records)); + const refetch = () => { + const ending = "T00:00:00"; + const dateFrom = dateToString(dateRange[0]); + const dateTo = dateToString(dateRange[1]); + if (!(dateFrom && dateTo)) return; + + TimeTrackingService + .getTimeTrackingRecords({ + requestBody: { + dateFrom: dateFrom + ending, + dateTo: dateTo + ending, + }, + }) + .then(response => { + console.log(response.records); + setTrackingRecords(response.records); + }) + .catch(err => console.log(err)); }; useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - refetch().then(_ => { - setDateBoundaries(getDateBoundaries(month)); - }); - }, [month]); + refetch(); + }, [dateRange]); + return { - month, - setMonth, + dateRange, + setDateRange, refetch, trackingRecords, - dateBoundaries, }; }; diff --git a/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx b/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx index 0afc4e5..709fd27 100644 --- a/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx +++ b/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx @@ -1,56 +1,44 @@ import { ActionIcon, Flex, MultiSelect, rem, Tooltip } from "@mantine/core"; -import { DatePickerInput, MonthPickerInput } from "@mantine/dates"; +import { DatePickerInput } from "@mantine/dates"; import useWorkTableState from "../hooks/useWorkTableState.tsx"; import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; import { useEffect, useState } from "react"; import useWorkTableColumns, { EmployeeData } from "../hooks/useWorkTableColumns.tsx"; import { TimeTrackingRecord, TimeTrackingService, UserSchema } from "../../../../../client"; -import { dateWithoutTimezone, getDatesBetween, getDatesInMonth } from "../../../../../shared/lib/date.ts"; +import { getDatesBetween } from "../../../../../shared/lib/date.ts"; import useUsersList from "../../../hooks/useUsersList.tsx"; import { notifications } from "../../../../../shared/lib/notifications.ts"; import { PaySchemeType } from "../../../../../shared/enums/PaySchemeType.ts"; import { IconEyeOff } from "@tabler/icons-react"; import { MRT_TableOptions } from "mantine-react-table"; import { difference, omit } from "lodash"; -import { strTimeToFloatHours } from "../../../../../types/utils.ts"; +import { dateToString, strTimeToFloatHours } from "../../../../../types/utils.ts"; import { useListState } from "@mantine/hooks"; const WorkTimeTable = () => { const [data, setData] = useState([]); - const { dateBoundaries, month, setMonth, trackingRecords, refetch } = - useWorkTableState(); + const { dateRange, setDateRange, trackingRecords, refetch } = useWorkTableState(); const [shownUsers, shownUsersHandlers] = useListState([]); - const [selectedBoundaries, setSelectedBoundaries] = useState< - [Date | null, Date | null] - >([null, null]); const users = useUsersList().objects.filter( user => user.payRate?.payrollScheme.key === PaySchemeType.HOURLY, ); - const getRange = () => { - const startDate = selectedBoundaries[0]; - const endDate = selectedBoundaries[1]; - if (startDate && endDate) { - return getDatesBetween(startDate, endDate); - } else { - return getDatesInMonth(month); - } + if (!(dateRange.length === 2 && dateRange[0] && dateRange[1])) return []; + const startDate = dateRange[0]; + const endDate = dateRange[1]; + return getDatesBetween(startDate, endDate); }; const range = getRange(); const transformTrackingRecordsToData = ( trackingRecords: TimeTrackingRecord[], ): EmployeeData[] => { - if (!month) return []; - const rangeDays = range.map(r => r.date()); + if (!(dateRange.length === 2 && dateRange[0] && dateRange[1])) return []; + const dateFrom = dateRange[0]; + const dateTo = dateRange[1]; + const dates = getDatesBetween(dateFrom, dateTo); - trackingRecords = trackingRecords.map(tr => ({ - ...tr, - data: tr.data.filter(d => - rangeDays.includes(new Date(d.date).getDate()), - ), - })); const existingUserIds = trackingRecords.map(tr => tr.user.id); const firstResult = trackingRecords.map(record => ({ name: `${record.user.firstName} ${record.user.secondName}`, @@ -59,14 +47,14 @@ const WorkTimeTable = () => { totalAmount: record.totalAmount, data: record.data, ...Object.fromEntries( - getDatesInMonth(month).reduce((acc, day) => { - return acc.set(day.date().toString(), 0); + dates.reduce((acc, day) => { + return acc.set(dateToString(day.toDate()) ?? "", 0); }, new Map()), ), ...Object.fromEntries( record.data.reduce((acc, recordData) => { return acc.set( - new Date(recordData.date).getDate().toString(), + recordData.date, recordData.hours, ); }, new Map()), @@ -80,7 +68,7 @@ const WorkTimeTable = () => { comment: user.comment, totalAmount: 0, ...Object.fromEntries( - getDatesInMonth(month).reduce((acc, day) => { + dates.reduce((acc, day) => { return acc.set(day.date().toString(), 0); }, new Map()), ), @@ -93,13 +81,12 @@ const WorkTimeTable = () => { // @ts-expect-error .concat(restUsersResult) .filter(r => shownUserIds.includes(r.userId)); - const firstDate = selectedBoundaries[0]; - const lastDate = selectedBoundaries[1]; - if (firstDate && lastDate) { - const allDays = getDatesInMonth(month).map(d => + + if (dateFrom && dateTo) { + const allDays = dates.map(d => d.date().toString(), ); - const allowedDays = getDatesBetween(firstDate, lastDate).map(d => + const allowedDays = getDatesBetween(dateFrom, dateTo).map(d => d.date().toString(), ); const omitDays = difference(allDays, allowedDays); @@ -117,10 +104,7 @@ const WorkTimeTable = () => { const user = users.find(user => user.id === userId); if (!user) return; - console.log(value); - const hours = strTimeToFloatHours(value); - console.log(hours); if (hours === -1) return; setData(prevState => @@ -128,12 +112,13 @@ const WorkTimeTable = () => { if (record.userId !== userId) return record; record[date.getDate()] = value; return record; - }) + }), ); + const ending = "T00:00:00"; TimeTrackingService.updateTimeTrackingRecord({ requestBody: { - date: dateWithoutTimezone(date), + date: dateToString(date) + ending, hours, userId: user.id, }, @@ -141,13 +126,11 @@ const WorkTimeTable = () => { if (!ok) { notifications.guess(ok, { message }); } - await refetch(); + refetch(); }); }; const columns = useWorkTableColumns({ - month, - selectedBoundaries, data, onUpdate: optimisticUpdate, selectedCells: [], @@ -158,11 +141,7 @@ const WorkTimeTable = () => { useEffect(() => { setData(transformTrackingRecordsToData(trackingRecords)); - }, [trackingRecords, shownUsers, selectedBoundaries]); - - useEffect(() => { - setSelectedBoundaries([null, null]); - }, [month]); + }, [trackingRecords, shownUsers, dateRange]); return ( { /> - event && setMonth(event)} - value={month} - placeholder={"Выберите месяц"} + value={dateRange} + onChange={(value) => { + setDateRange(value); + }} /> diff --git a/src/shared/lib/date.ts b/src/shared/lib/date.ts index 67cc31a..9647d9f 100644 --- a/src/shared/lib/date.ts +++ b/src/shared/lib/date.ts @@ -5,23 +5,6 @@ export const dateWithoutTimezone = (date: Date) => { return new Date(date.valueOf() - tzoffset).toISOString().slice(0, -1); }; -export const getDatesInMonth = (inputDate: Date) => { - const month = inputDate.getMonth(); - const year = inputDate.getFullYear(); - - // Create a Day.js object for the first day of the specified month and year - let date = dayjs(new Date(year, month, 1)); - const dates = []; - - // Iterate through the days of the month - while (date.month() === month) { - dates.push(date); - date = date.add(1, "day"); - } - - return dates; -}; - export const getDayOfWeek = (day: number): string => { switch (day) { case 0: