diff --git a/src/pages/AdminPage/components/PaymentRecordsTable/columns.tsx b/src/pages/AdminPage/components/PaymentRecordsTable/columns.tsx index b8bc6b9..1baa5f2 100644 --- a/src/pages/AdminPage/components/PaymentRecordsTable/columns.tsx +++ b/src/pages/AdminPage/components/PaymentRecordsTable/columns.tsx @@ -3,36 +3,67 @@ import { MRT_ColumnDef } from "mantine-react-table"; import { PaymentRecordGetSchema } from "../../../../client"; import { PaySchemeType } from "../../../../shared/enums/PaySchemeType.ts"; import { getPluralForm } from "../../../../shared/lib/utils.ts"; -import { formatDate } from "../../../../types/utils.ts"; +import { floatHoursToHoursAndMinutes, formatDate } from "../../../../types/utils.ts"; import { isEqual } from "lodash"; export const usePaymentRecordsTableColumns = () => { + const getPluralMonths = (months: number) => { + return getPluralForm( + months, + "месяц", + "месяца", + "месяцев", + ); + }; + const getPluralDays = (days: number) => { + return getPluralForm( + days, + "день", + "дня", + "дней", + ); + }; + const getPluralHours = (hours: number) => { + return getPluralForm( + hours, + "час", + "часа", + "часов", + ); + }; + const getPluralMinutes = (minutes: number) => { + return getPluralForm( + minutes, + "минута", + "минуты", + "минут", + ); + }; + const getWorkUnitsText = (paymentRecord: PaymentRecordGetSchema) => { const payrollScheme = paymentRecord.payrollScheme; - if (payrollScheme.key === PaySchemeType.HOURLY) { - return getPluralForm( - paymentRecord.workUnits, - "час", - "часа", - "часов" - ); - } else if (payrollScheme.key === PaySchemeType.DAILY) { - return getPluralForm( - paymentRecord.workUnits, - "день", - "дня", - "дней" - ); + if (payrollScheme.key === PaySchemeType.DAILY) { + return getPluralDays(paymentRecord.workUnits); } else if (payrollScheme.key === PaySchemeType.MONTHLY) { - return getPluralForm( - paymentRecord.workUnits, - "месяц", - "месяца", - "месяцев" - ); + return getPluralMonths(paymentRecord.workUnits); } return ""; }; + + const getWorkUnits = (paymentRecord: PaymentRecordGetSchema) => { + const payrollScheme = paymentRecord.payrollScheme; + if (payrollScheme.key === PaySchemeType.HOURLY) { + const [hours, minutes] = floatHoursToHoursAndMinutes(paymentRecord.workUnits); + const hoursPlural = getPluralHours(hours); + if (minutes === 0) { + return `${hours} ${hoursPlural}`; + } + const minutesPlural = getPluralMinutes(minutes); + return `${hours} ${hoursPlural} ${minutes} ${minutesPlural}`; + } + + return `${paymentRecord.workUnits} ${getWorkUnitsText(paymentRecord)}`; + }; const getDateRangesText = (paymentRecord: PaymentRecordGetSchema) => { if ( paymentRecord.endDate && @@ -62,18 +93,17 @@ export const usePaymentRecordsTableColumns = () => { }, { header: "Количество", - Cell: ({ row }) => - `${row.original.workUnits} ${getWorkUnitsText(row.original)}`, + Cell: ({ row }) => getWorkUnits(row.original), }, { header: "Сумма начисления", - Cell: ({ row }) => row.original.amount.toLocaleString("ru-RU"), + Cell: ({ row }) => Math.round(row.original.amount).toLocaleString("ru-RU"), }, { header: "Временной промежуток", Cell: ({ row }) => getDateRangesText(row.original), }, ], - [] + [], ); }; diff --git a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx index 2f1b5b7..13c175a 100644 --- a/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx +++ b/src/pages/AdminPage/tabs/WorkTimeTable/hooks/useWorkTableColumns.tsx @@ -1,11 +1,13 @@ import { useMemo } from "react"; import { MRT_ColumnDef } from "mantine-react-table"; import { getDayOfWeek } from "../../../../../shared/lib/date.ts"; -import { Box, Flex, NumberInput, rem, Text } from "@mantine/core"; +import { Box, Flex, Input, rem, Text } from "@mantine/core"; import { isNumber, last } from "lodash"; import { processSelectedCells } from "../../../../../shared/lib/interpolateCells.ts"; import { TimeTrackingData } from "../../../../../client"; import dayjs from "dayjs"; +import { IMaskInput } from "react-imask"; +import { floatHoursToHoursAndMinutes } from "../../../../../types/utils.ts"; export type EmployeeData = { name: string; @@ -20,7 +22,7 @@ export type EmployeeData = { type Props = { month: Date; data: EmployeeData[]; - onUpdate: (date: Date, userId: number, value: number) => void; + onUpdate: (date: Date, userId: number, value: string) => void; selectedCells: string[]; setSelectedCells: (cells: string[]) => void; selectedBoundaries: [Date | null, Date | null]; @@ -62,6 +64,13 @@ const useWorkTableColumns = ({ return {}; }; + const formatValue = (value: number) => { + const [hours, minutes] = floatHoursToHoursAndMinutes(value); + const hoursStr = String(hours).padStart(2, "0"); + const minutesStr = String(minutes).padStart(2, "0"); + return `${hoursStr}:${minutesStr}`; + }; + return useMemo[]>( () => [ { @@ -76,7 +85,7 @@ const useWorkTableColumns = ({ }, ...range.map(date => ({ - size: 80, + size: 100, accessorKey: date.date().toString(), header: date.date().toString(), enableSorting: false, @@ -112,35 +121,38 @@ const useWorkTableColumns = ({ Cell: ({ cell, row }) => { return ( - - isNumber(event) && + /^\d\d:\d\d$/.test(event.currentTarget.value) && onUpdate( date.toDate(), row.original.userId, - event + event.currentTarget.value, ) } styles={{ input: { textAlign: "center" } }} - hideControls - value={cell.renderValue()} + value={formatValue(cell.renderValue() as number)} /> ); }, })), { - header: "Всего часов", + header: "Всего времени", Cell: ({ row }) => { - return Object.entries(row.original).reduce( - (acc, [key, value]) => { - if (isNaN(parseInt(key)) || !isNumber(value)) + return formatValue( + Object.entries(row.original).reduce( + (acc, [key, value]) => { + if (isNaN(parseInt(key)) || !isNumber(value)) + return acc; + acc += value; return acc; - acc += value; - return acc; - }, - 0 + }, + 0, + ), ); }, }, @@ -149,17 +161,19 @@ const useWorkTableColumns = ({ header: "Итоговая сумма заработка", Cell: ({ row }) => { - return (row.original.data || []).reduce((acc, value) => { - acc += value.amount; - return acc; - }, 0); + return Math.floor( + (row.original.data || []).reduce((acc, value) => { + acc += value.amount; + return acc; + }, 0), + ); }, Footer: ( - Всего: {totalAmount.toLocaleString("ru-RU")} + Всего: {Math.floor(totalAmount).toLocaleString("ru-RU")} ), }, ], - [month, selectedCells, selectedBoundaries, totalAmount] + [month, selectedCells, selectedBoundaries, totalAmount], ); }; diff --git a/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx b/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx index c27592b..07cca32 100644 --- a/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx +++ b/src/pages/AdminPage/tabs/WorkTimeTable/ui/WorkTimeTable.tsx @@ -22,6 +22,7 @@ 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"; const WorkTimeTable = () => { const [data, setData] = useState([]); @@ -121,9 +122,16 @@ const WorkTimeTable = () => { ); }; - const optimisticUpdate = (date: Date, userId: number, value: number) => { + const optimisticUpdate = (date: Date, userId: number, value: string) => { 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 => prevState.map(record => { if (record.userId !== userId) return record; @@ -135,7 +143,7 @@ const WorkTimeTable = () => { TimeTrackingService.updateTimeTrackingRecord({ requestBody: { date: dateWithoutTimezone(date), - hours: value, + hours, userId: user.id, }, }).then(async ({ ok, message }) => { diff --git a/src/types/utils.ts b/src/types/utils.ts index ad38d31..4d8c78a 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -42,3 +42,18 @@ export function ObjectStateToTableProps( onCreate: state.onCreate, }; } + +export const floatHoursToHoursAndMinutes = (hours: number): number[] => { + const resHours = Math.floor(hours); + const minutes = Math.round((hours - resHours) * 60); + return [resHours, minutes]; +}; + +export const strTimeToFloatHours = (time: string): number => { + const values = time.split(":"); + if (values.length !== 2) return -1; + const hours = parseInt(values[0]); + const minutes = parseInt(values[1]); + + return hours + minutes / 60; +} \ No newline at end of file