diff --git a/src/client/index.ts b/src/client/index.ts index c313dca..1eb435d 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -149,6 +149,8 @@ export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWare export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse'; export type { ExpenseSchemaBase } from './models/ExpenseSchemaBase'; export type { ExpenseTagSchema } from './models/ExpenseTagSchema'; +export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse'; +export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse'; export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse'; export type { FinishShiftResponse } from './models/FinishShiftResponse'; export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse'; @@ -245,6 +247,8 @@ export type { ServiceUpdateCategoryResponse } from './models/ServiceUpdateCatego export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest'; export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse'; export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema'; +export type { StartPauseByShiftIdResponse } from './models/StartPauseByShiftIdResponse'; +export type { StartPauseByUserIdResponse } from './models/StartPauseByUserIdResponse'; export type { StartShiftResponse } from './models/StartShiftResponse'; export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest'; export type { TaskInfoResponse } from './models/TaskInfoResponse'; @@ -274,6 +278,7 @@ export type { UserCreate } from './models/UserCreate'; export type { UserSchema } from './models/UserSchema'; export type { UserUpdate } from './models/UserUpdate'; export type { ValidationError } from './models/ValidationError'; +export type { WorkShiftRowSchema } from './models/WorkShiftRowSchema'; export type { WorkShiftSchema } from './models/WorkShiftSchema'; export { AuthService } from './services/AuthService'; diff --git a/src/client/models/FinishPauseByShiftIdResponse.ts b/src/client/models/FinishPauseByShiftIdResponse.ts new file mode 100644 index 0000000..ed8d6ee --- /dev/null +++ b/src/client/models/FinishPauseByShiftIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type FinishPauseByShiftIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/FinishPauseByUserIdResponse.ts b/src/client/models/FinishPauseByUserIdResponse.ts new file mode 100644 index 0000000..035abd3 --- /dev/null +++ b/src/client/models/FinishPauseByUserIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type FinishPauseByUserIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/GetWorkShiftsResponse.ts b/src/client/models/GetWorkShiftsResponse.ts index d557b26..b6299f9 100644 --- a/src/client/models/GetWorkShiftsResponse.ts +++ b/src/client/models/GetWorkShiftsResponse.ts @@ -3,9 +3,9 @@ /* tslint:disable */ /* eslint-disable */ import type { PaginationInfoSchema } from './PaginationInfoSchema'; -import type { WorkShiftSchema } from './WorkShiftSchema'; +import type { WorkShiftRowSchema } from './WorkShiftRowSchema'; export type GetWorkShiftsResponse = { - shifts: Array; + shifts: Array; paginationInfo: PaginationInfoSchema; }; diff --git a/src/client/models/StartPauseByShiftIdResponse.ts b/src/client/models/StartPauseByShiftIdResponse.ts new file mode 100644 index 0000000..d2ddd85 --- /dev/null +++ b/src/client/models/StartPauseByShiftIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type StartPauseByShiftIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/StartPauseByUserIdResponse.ts b/src/client/models/StartPauseByUserIdResponse.ts new file mode 100644 index 0000000..4b6b3ec --- /dev/null +++ b/src/client/models/StartPauseByUserIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type StartPauseByUserIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/WorkShiftRowSchema.ts b/src/client/models/WorkShiftRowSchema.ts new file mode 100644 index 0000000..b7f805a --- /dev/null +++ b/src/client/models/WorkShiftRowSchema.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { WorkShiftSchema } from './WorkShiftSchema'; +export type WorkShiftRowSchema = { + workShift: WorkShiftSchema; + totalHours?: (number | null); + pauseHours?: (number | null); +}; + diff --git a/src/client/models/WorkShiftSchema.ts b/src/client/models/WorkShiftSchema.ts index 40ef15e..4ca4c97 100644 --- a/src/client/models/WorkShiftSchema.ts +++ b/src/client/models/WorkShiftSchema.ts @@ -7,7 +7,7 @@ export type WorkShiftSchema = { id: number; startedAt: string; finishedAt?: (string | null); - hours?: (number | null); + isPaused?: (boolean | null); user: UserSchema; }; diff --git a/src/client/services/WorkShiftsService.ts b/src/client/services/WorkShiftsService.ts index 6b12bd7..b32dedc 100644 --- a/src/client/services/WorkShiftsService.ts +++ b/src/client/services/WorkShiftsService.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { DeleteShiftResponse } from '../models/DeleteShiftResponse'; +import type { FinishPauseByShiftIdResponse } from '../models/FinishPauseByShiftIdResponse'; +import type { FinishPauseByUserIdResponse } from '../models/FinishPauseByUserIdResponse'; import type { FinishShiftByIdResponse } from '../models/FinishShiftByIdResponse'; import type { FinishShiftResponse } from '../models/FinishShiftResponse'; import type { GetWorkShiftsResponse } from '../models/GetWorkShiftsResponse'; +import type { StartPauseByShiftIdResponse } from '../models/StartPauseByShiftIdResponse'; +import type { StartPauseByUserIdResponse } from '../models/StartPauseByUserIdResponse'; import type { StartShiftResponse } from '../models/StartShiftResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; @@ -145,4 +149,88 @@ export class WorkShiftsService { }, }); } + /** + * Start Pause By Shift Id + * @returns StartPauseByShiftIdResponse Successful Response + * @throws ApiError + */ + public static startPauseByShiftId({ + shiftId, + }: { + shiftId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/start/{shift_id}', + path: { + 'shift_id': shiftId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Start Pause By User Id + * @returns StartPauseByUserIdResponse Successful Response + * @throws ApiError + */ + public static startPauseByUserId({ + userId, + }: { + userId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/start/for-user/{user_id}', + path: { + 'user_id': userId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Finish Pause By Shift Id + * @returns FinishPauseByShiftIdResponse Successful Response + * @throws ApiError + */ + public static finishPauseByShiftId({ + shiftId, + }: { + shiftId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/finish/{shift_id}', + path: { + 'shift_id': shiftId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Finish Pause By User Id + * @returns FinishPauseByUserIdResponse Successful Response + * @throws ApiError + */ + public static finishPauseByUserId({ + userId, + }: { + userId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/finish/for-user/{shift_id}', + query: { + 'user_id': userId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx b/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx index 5cb48a1..31e1d70 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx @@ -1,17 +1,18 @@ import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; import { useShiftsTableColumns } from "../hooks/columns.tsx"; import { ActionIcon, Flex, Text, Tooltip } from "@mantine/core"; -import { IconCheck, IconTrash } from "@tabler/icons-react"; -import { WorkShiftSchema, WorkShiftsService } from "../../../../../client"; +import { IconCheck, IconPlayerPause, IconPlayerPlay, IconTrash } from "@tabler/icons-react"; +import { WorkShiftRowSchema, WorkShiftsService } from "../../../../../client"; import { modals } from "@mantine/modals"; import { formatDate } from "../../../../../types/utils.ts"; -import { MRT_TableOptions } from "mantine-react-table"; +import { MRT_Row, MRT_TableOptions } from "mantine-react-table"; import { notifications } from "../../../../../shared/lib/notifications.ts"; import { ShiftsTableType } from "../../../components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx"; +import { ReactNode } from "react"; type Props = { - shifts: WorkShiftSchema[]; + shifts: WorkShiftRowSchema[]; fetchShifts: () => void; shiftsTableType: ShiftsTableType; } @@ -24,9 +25,9 @@ export const ShiftsTable = ({ const isActiveShiftsTable = shiftsTableType === ShiftsTableType.ACTIVE; const columns = useShiftsTableColumns({ isActiveShiftsTable }); - const onDelete = (workShift: WorkShiftSchema) => { + const onDelete = (workShiftRow: WorkShiftRowSchema) => { WorkShiftsService.deleteWorkShift({ - shiftId: workShift.id, + shiftId: workShiftRow.workShift.id, }) .then(({ ok, message }) => { notifications.guess(ok, { message }); @@ -35,25 +36,25 @@ export const ShiftsTable = ({ .catch(err => console.log(err)); }; - const onDeleteClick = (workShift: WorkShiftSchema) => { + const onDeleteClick = (workShiftRow: WorkShiftRowSchema) => { modals.openConfirmModal({ title: "Удаление смены", children: ( Вы уверены что хотите удалить смену работника{" "} - {workShift.user.firstName} {workShift.user.secondName} от{" "} - {formatDate(workShift.startedAt)} + {workShiftRow.workShift.user.firstName} {workShiftRow.workShift.user.secondName} от{" "} + {formatDate(workShiftRow.workShift.startedAt)} ), labels: { confirm: "Да", cancel: "Нет" }, confirmProps: { color: "red" }, - onConfirm: () => onDelete(workShift), + onConfirm: () => onDelete(workShiftRow), }); }; - const onShiftFinish = (workShift: WorkShiftSchema) => { + const onShiftFinish = (workShiftRow: WorkShiftRowSchema) => { WorkShiftsService.finishWorkShiftById({ - shiftId: workShift.id, + shiftId: workShiftRow.workShift.id, }) .then(({ ok, message }) => { notifications.guess(ok, { message }); @@ -62,22 +63,95 @@ export const ShiftsTable = ({ .catch(err => console.log(err)); }; - const onShiftFinishClick = (workShift: WorkShiftSchema) => { + const onShiftFinishClick = (workShiftRow: WorkShiftRowSchema) => { modals.openConfirmModal({ title: "Завершение смены", children: ( Вы уверены что хотите завершить смену работника{" "} - {workShift.user.firstName} {workShift.user.secondName} от{" "} - {formatDate(workShift.startedAt)} + {workShiftRow.workShift.user.firstName} {workShiftRow.workShift.user.secondName} от{" "} + {formatDate(workShiftRow.workShift.startedAt)} ), labels: { confirm: "Да", cancel: "Нет" }, confirmProps: { color: "red" }, - onConfirm: () => onShiftFinish(workShift), + onConfirm: () => onShiftFinish(workShiftRow), }); }; + const onShiftPauseClick = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.startPauseByShiftId({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const onShiftResumeClick = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.finishPauseByShiftId({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const getAction = ( + label: string, + func: () => void, + icon: ReactNode, + ) => { + return ( + + + {icon} + + + ); + }; + + const getRowActions = (row: MRT_Row) => { + const actions = [ + getAction("Удалить", () => onDeleteClick(row.original), ), + ]; + + if (isActiveShiftsTable) { + actions.push( + getAction( + "Завершить смену", + () => onShiftFinishClick(row.original), + , + ), + ); + if (row.original.workShift.isPaused) { + actions.push( + getAction( + "Продолжить смену", + () => onShiftResumeClick(row.original), + , + ), + ); + } else { + actions.push( + getAction( + "Поставить смену на паузу", + () => onShiftPauseClick(row.original), + , + ), + ); + } + } + + return actions; + }; + return ( { return ( - - - onDeleteClick(row.original) - } - variant={"default"}> - - - - {isActiveShiftsTable && ( - - - onShiftFinishClick(row.original) - } - variant={"default"}> - - - - )} + {...getRowActions(row)} ); }, - } as MRT_TableOptions + } as MRT_TableOptions } /> ); diff --git a/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx b/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx index 4e3ba1a..c40fc01 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx @@ -9,6 +9,8 @@ const WorkShiftInput = ({ fetchShifts }: Props) => { const { onShiftStart, onShiftFinish, + onShiftResume, + onShiftPause, } = useWorkShiftInput({ fetchShifts }); return ( @@ -19,6 +21,12 @@ const WorkShiftInput = ({ fetchShifts }: Props) => { + + ); }; diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx index 39dce21..31f615d 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { MRT_ColumnDef, MRT_Row } from "mantine-react-table"; -import { WorkShiftSchema } from "../../../../../client"; +import { WorkShiftRowSchema } from "../../../../../client"; type Props = { @@ -8,12 +8,9 @@ type Props = { } export const useShiftsTableColumns = ({ isActiveShiftsTable }: Props) => { - const getWorkedHoursString = (startedAtStr: string, finishedAtStr: string) => { - const finishedAt = new Date(finishedAtStr); - const startedAt = new Date(startedAtStr); - const diff: number = finishedAt.getTime() - startedAt.getTime(); - const hours = Math.floor(diff / 3_600_000); - const minutes = Math.round(diff % 3_600_000 / 60_000); + const getWorkedHoursString = (seconds: number) => { + const hours = Math.floor(seconds / 3_600); + const minutes = Math.floor(seconds % 3_600 / 60); if (hours === 0) { return `${minutes} мин.`; } @@ -24,38 +21,50 @@ export const useShiftsTableColumns = ({ isActiveShiftsTable }: Props) => { return isActiveShiftsTable ? [] : [ { header: "Конец смены", - accessorKey: "finishedAt", - Cell: ({ row }: { row: MRT_Row }) => - row.original.finishedAt && new Date(row.original.finishedAt).toLocaleString("ru-RU"), + accessorKey: "workShift.finishedAt", + Cell: ({ row }: { row: MRT_Row }) => + row.original.workShift.finishedAt && new Date(row.original.workShift.finishedAt).toLocaleString("ru-RU"), + }, + { + header: "Длительность смены", + accessorKey: "totalHours", + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString(row.original.totalHours ?? 0), + }, + { + header: "Перерывы", + accessorKey: "pauseHours", + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString(row.original.pauseHours ?? 0), }, { header: "Отработано", - Cell: ({ row }: { row: MRT_Row }) => - getWorkedHoursString(row.original.startedAt, row.original.finishedAt ?? ""), + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString((row.original.totalHours ?? 0) - (row.original.pauseHours ?? 0)), }, - ]; + ] as MRT_ColumnDef[]; }; - return useMemo[]>( + return useMemo[]>( () => [ { header: "ФИО", - Cell: ({ row }: { row: MRT_Row }) => - `${row.original.user.firstName} ${row.original.user.secondName}`, + Cell: ({ row }: { row: MRT_Row }) => + `${row.original.workShift.user.firstName} ${row.original.workShift.user.secondName}`, }, { header: "Роль", - accessorKey: "user.role.name", + accessorKey: "workShift.user.role.name", }, { header: "Должность", - accessorKey: "user.position.name", + accessorKey: "workShift.user.position.name", }, { header: "Начало смены", - accessorKey: "startedAt", + accessorKey: "workShift.startedAt", Cell: ({ row }) => - new Date(row.original.startedAt).toLocaleString("ru-RU"), + new Date(row.original.workShift.startedAt).toLocaleString("ru-RU"), }, ...getColumnsForHistory(), ], diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx index bf0c8fe..3dfeb89 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx @@ -6,8 +6,22 @@ type Props = { fetchShifts: () => void; } +enum InputType { + START_SHIFT, + FINISH_SHIFT, + RESUME_SHIFT, + PAUSE_SHIFT, +} + const useWorkShiftInput = ({ fetchShifts }: Props) => { - let inputType: "StartShift" | "FinishShift" = "StartShift"; + let inputType: InputType = InputType.START_SHIFT; + + const workShiftMethods = { + [InputType.START_SHIFT]: WorkShiftsService.startShift, + [InputType.FINISH_SHIFT]: WorkShiftsService.finishShift, + [InputType.RESUME_SHIFT]: WorkShiftsService.finishPauseByUserId, + [InputType.PAUSE_SHIFT]: WorkShiftsService.startPauseByUserId, + }; const onInputFinish = (userIdInput: string) => { const userId = parseInt(userIdInput); @@ -16,21 +30,7 @@ const useWorkShiftInput = ({ fetchShifts }: Props) => { return; } - if (inputType === "StartShift") { - WorkShiftsService.startShift({ - userId: userId!, - }) - .then(async ({ ok, message }) => { - notifications.guess(ok, { message }); - fetchShifts(); - }) - .catch(err => console.log(err)); - return; - } - - WorkShiftsService.finishShift({ - userId: userId!, - }) + workShiftMethods[inputType]({ userId }) .then(async ({ ok, message }) => { notifications.guess(ok, { message }); fetchShifts(); @@ -51,18 +51,30 @@ const useWorkShiftInput = ({ fetchShifts }: Props) => { }; const onShiftStart = () => { - inputType = "StartShift"; + inputType = InputType.START_SHIFT; onScanningStart(); }; const onShiftFinish = () => { - inputType = "FinishShift"; + inputType = InputType.FINISH_SHIFT; + onScanningStart(); + }; + + const onShiftResume = () => { + inputType = InputType.RESUME_SHIFT; + onScanningStart(); + }; + + const onShiftPause = () => { + inputType = InputType.PAUSE_SHIFT; onScanningStart(); }; return { onShiftStart, onShiftFinish, + onShiftResume, + onShiftPause, }; }; diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx index 8b71f75..ae3edd0 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx @@ -1,12 +1,12 @@ import { useEffect, useState } from "react"; -import { WorkShiftSchema, WorkShiftsService } from "../../../../../client"; +import { WorkShiftRowSchema, WorkShiftsService } from "../../../../../client"; import { ShiftsTableType } from "../../../components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx"; const useWorkShiftsTable = () => { const [totalPages, setTotalPages] = useState(1); const [page, setPage] = useState(1); - const [shifts, setShifts] = useState([]); + const [shifts, setShifts] = useState([]); const [shiftsTableType, setShiftsTableType] = useState(ShiftsTableType.ACTIVE); const [isLoading, setIsLoading] = useState(false); @@ -18,6 +18,7 @@ const useWorkShiftsTable = () => { itemsPerPage: 10, }) .then(res => { + console.log(res.shifts); setShifts(res.shifts); setTotalPages(res.paginationInfo.totalPages); }) diff --git a/src/types/utils.ts b/src/types/utils.ts index b917ac1..d30f9c8 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -45,7 +45,7 @@ export function ObjectStateToTableProps( export const floatHoursToHoursAndMinutes = (hours: number): number[] => { const resHours = Math.floor(hours); - const minutes = Math.round((hours - resHours) * 60); + const minutes = Math.floor((hours - resHours) * 60); return [resHours, minutes]; };