feat: work shifts by QR codes
This commit is contained in:
		@@ -7,6 +7,8 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
 | 
			
		||||
export { OpenAPI } from './core/OpenAPI';
 | 
			
		||||
export type { OpenAPIConfig } from './core/OpenAPI';
 | 
			
		||||
 | 
			
		||||
export type { ActiveWorkShiftSchema } from './models/ActiveWorkShiftSchema';
 | 
			
		||||
export type { ActiveWorkShiftsResponse } from './models/ActiveWorkShiftsResponse';
 | 
			
		||||
export type { AuthLoginRequest } from './models/AuthLoginRequest';
 | 
			
		||||
export type { AuthLoginResponse } from './models/AuthLoginResponse';
 | 
			
		||||
export type { BarcodeAttributeSchema } from './models/BarcodeAttributeSchema';
 | 
			
		||||
@@ -140,8 +142,11 @@ export type { DeletePositionRequest } from './models/DeletePositionRequest';
 | 
			
		||||
export type { DeletePositionResponse } from './models/DeletePositionResponse';
 | 
			
		||||
export type { DeletePriceCategoryRequest } from './models/DeletePriceCategoryRequest';
 | 
			
		||||
export type { DeletePriceCategoryResponse } from './models/DeletePriceCategoryResponse';
 | 
			
		||||
export type { DeleteShiftResponse } from './models/DeleteShiftResponse';
 | 
			
		||||
export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest';
 | 
			
		||||
export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse';
 | 
			
		||||
export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
 | 
			
		||||
export type { FinishShiftResponse } from './models/FinishShiftResponse';
 | 
			
		||||
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
 | 
			
		||||
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
 | 
			
		||||
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
 | 
			
		||||
@@ -225,6 +230,7 @@ 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 { StartShiftResponse } from './models/StartShiftResponse';
 | 
			
		||||
export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest';
 | 
			
		||||
export type { TaskInfoResponse } from './models/TaskInfoResponse';
 | 
			
		||||
export type { TimeTrackingData } from './models/TimeTrackingData';
 | 
			
		||||
@@ -264,3 +270,4 @@ export { ShippingWarehouseService } from './services/ShippingWarehouseService';
 | 
			
		||||
export { TaskService } from './services/TaskService';
 | 
			
		||||
export { TimeTrackingService } from './services/TimeTrackingService';
 | 
			
		||||
export { UserService } from './services/UserService';
 | 
			
		||||
export { WorkShiftsService } from './services/WorkShiftsService';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								src/client/models/ActiveWorkShiftSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/ActiveWorkShiftSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { UserSchema } from './UserSchema';
 | 
			
		||||
export type ActiveWorkShiftSchema = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    startedAt: string;
 | 
			
		||||
    user: UserSchema;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/ActiveWorkShiftsResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/ActiveWorkShiftsResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { ActiveWorkShiftSchema } from './ActiveWorkShiftSchema';
 | 
			
		||||
export type ActiveWorkShiftsResponse = {
 | 
			
		||||
    shifts: Array<ActiveWorkShiftSchema>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/DeleteShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/DeleteShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type DeleteShiftResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/FinishShiftByIdResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/FinishShiftByIdResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type FinishShiftByIdResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/FinishShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/FinishShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type FinishShiftResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/StartShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/StartShiftResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type StartShiftResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										130
									
								
								src/client/services/WorkShiftsService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/client/services/WorkShiftsService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { ActiveWorkShiftsResponse } from '../models/ActiveWorkShiftsResponse';
 | 
			
		||||
import type { DeleteShiftResponse } from '../models/DeleteShiftResponse';
 | 
			
		||||
import type { FinishShiftByIdResponse } from '../models/FinishShiftByIdResponse';
 | 
			
		||||
import type { FinishShiftResponse } from '../models/FinishShiftResponse';
 | 
			
		||||
import type { StartShiftResponse } from '../models/StartShiftResponse';
 | 
			
		||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
			
		||||
import { OpenAPI } from '../core/OpenAPI';
 | 
			
		||||
import { request as __request } from '../core/request';
 | 
			
		||||
export class WorkShiftsService {
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate Qr Code
 | 
			
		||||
     * @returns any Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static generateQrCode({
 | 
			
		||||
        userId,
 | 
			
		||||
    }: {
 | 
			
		||||
        userId: number,
 | 
			
		||||
    }): CancelablePromise<any> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: '/work-shifts/generate-qr-code/{user_id}',
 | 
			
		||||
            path: {
 | 
			
		||||
                'user_id': userId,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Start Shift
 | 
			
		||||
     * @returns StartShiftResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static startShift({
 | 
			
		||||
        userId,
 | 
			
		||||
    }: {
 | 
			
		||||
        userId: number,
 | 
			
		||||
    }): CancelablePromise<StartShiftResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/work-shifts/start-shift/{user_id}',
 | 
			
		||||
            path: {
 | 
			
		||||
                'user_id': userId,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Finish Shift
 | 
			
		||||
     * @returns FinishShiftResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static finishShift({
 | 
			
		||||
        userId,
 | 
			
		||||
    }: {
 | 
			
		||||
        userId: number,
 | 
			
		||||
    }): CancelablePromise<FinishShiftResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/work-shifts/finish-shift/{user_id}',
 | 
			
		||||
            path: {
 | 
			
		||||
                'user_id': userId,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Finish Work Shift By Id
 | 
			
		||||
     * @returns FinishShiftByIdResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static finishWorkShiftById({
 | 
			
		||||
        shiftId,
 | 
			
		||||
    }: {
 | 
			
		||||
        shiftId: number,
 | 
			
		||||
    }): CancelablePromise<FinishShiftByIdResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/work-shifts/finish-shift-by-id/{shift_id}',
 | 
			
		||||
            path: {
 | 
			
		||||
                'shift_id': shiftId,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get Active Shifts
 | 
			
		||||
     * @returns ActiveWorkShiftsResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static getActiveShifts(): CancelablePromise<ActiveWorkShiftsResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: '/work-shifts/get-active-shifts',
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete Work Shift
 | 
			
		||||
     * @returns DeleteShiftResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static deleteWorkShift({
 | 
			
		||||
        shiftId,
 | 
			
		||||
    }: {
 | 
			
		||||
        shiftId: number,
 | 
			
		||||
    }): CancelablePromise<DeleteShiftResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'DELETE',
 | 
			
		||||
            url: '/work-shifts/delete-shift/{shift_id}',
 | 
			
		||||
            path: {
 | 
			
		||||
                'shift_id': shiftId,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/modals/ScanningModal/ScanningModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/modals/ScanningModal/ScanningModal.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
import { ContextModalProps } from "@mantine/modals";
 | 
			
		||||
import { Text } from "@mantine/core";
 | 
			
		||||
import { useWindowEvent } from "@mantine/hooks";
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    label: string;
 | 
			
		||||
    onScan: (value: string) => void;
 | 
			
		||||
    closeOnScan?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ScanningModal = ({
 | 
			
		||||
                           context,
 | 
			
		||||
                           id,
 | 
			
		||||
                           innerProps,
 | 
			
		||||
                       }: ContextModalProps<Props>) => {
 | 
			
		||||
    const [inputValues, setInputValues] = useState<string>("");
 | 
			
		||||
 | 
			
		||||
    const { label, onScan, closeOnScan } = innerProps;
 | 
			
		||||
 | 
			
		||||
    useWindowEvent("keydown", (event) => {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        console.log(inputValues);
 | 
			
		||||
        setInputValues(prevState => prevState + event.key);
 | 
			
		||||
        if (event.key === "Enter") {
 | 
			
		||||
            onScan(inputValues);
 | 
			
		||||
            if (closeOnScan) {
 | 
			
		||||
                context.closeContextModal(id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Text>{label}</Text>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ScanningModal;
 | 
			
		||||
@@ -21,6 +21,7 @@ import SelectDealProductsModal from "../pages/LeadsPage/modals/SelectDealProduct
 | 
			
		||||
import ShippingWarehouseForm from "../pages/ShippingWarehousesPage/modals/ShippingWarehouseForm.tsx";
 | 
			
		||||
import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFormModal/MarketplaceFormModal.tsx";
 | 
			
		||||
import ServicePriceCategoryForm from "../pages/ServicesPage/modals/ServicePriceCategoryForm.tsx";
 | 
			
		||||
import ScanningModal from "./ScanningModal/ScanningModal.tsx";
 | 
			
		||||
 | 
			
		||||
export const modals = {
 | 
			
		||||
    enterDeadline: EnterDeadlineModal,
 | 
			
		||||
@@ -46,4 +47,5 @@ export const modals = {
 | 
			
		||||
    shippingWarehouseForm: ShippingWarehouseForm,
 | 
			
		||||
    marketplaceFormModal: MarketplaceFormModal,
 | 
			
		||||
    servicePriceCategoryForm: ServicePriceCategoryForm,
 | 
			
		||||
    scanningModal: ScanningModal,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import PageBlock from "../../components/PageBlock/PageBlock.tsx";
 | 
			
		||||
import {
 | 
			
		||||
    IconBriefcase,
 | 
			
		||||
    IconCalendarUser,
 | 
			
		||||
    IconCurrencyDollar,
 | 
			
		||||
    IconCurrencyDollar, IconQrcode,
 | 
			
		||||
    IconUser,
 | 
			
		||||
} from "@tabler/icons-react";
 | 
			
		||||
import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
 | 
			
		||||
@@ -12,6 +12,7 @@ import UsersTab from "./tabs/Users/UsersTab.tsx";
 | 
			
		||||
import { motion } from "framer-motion";
 | 
			
		||||
import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
 | 
			
		||||
import WorkTimeTable from "./tabs/WorkTimeTable/ui/WorkTimeTable.tsx";
 | 
			
		||||
import { WorkShiftsTab } from "./tabs/WorkShifts/WorkShiftsTab.tsx";
 | 
			
		||||
 | 
			
		||||
const AdminPage = () => {
 | 
			
		||||
    return (
 | 
			
		||||
@@ -42,6 +43,11 @@ const AdminPage = () => {
 | 
			
		||||
                            leftSection={<IconCalendarUser />}>
 | 
			
		||||
                            Рабочее время
 | 
			
		||||
                        </Tabs.Tab>
 | 
			
		||||
                        <Tabs.Tab
 | 
			
		||||
                            value={"workShifts"}
 | 
			
		||||
                            leftSection={<IconQrcode />}>
 | 
			
		||||
                            Смены
 | 
			
		||||
                        </Tabs.Tab>
 | 
			
		||||
                    </Tabs.List>
 | 
			
		||||
                    <Tabs.Panel value={"users"}>
 | 
			
		||||
                        <motion.div
 | 
			
		||||
@@ -75,6 +81,14 @@ const AdminPage = () => {
 | 
			
		||||
                            <WorkTimeTable />
 | 
			
		||||
                        </motion.div>
 | 
			
		||||
                    </Tabs.Panel>
 | 
			
		||||
                    <Tabs.Panel value={"workShifts"}>
 | 
			
		||||
                        <motion.div
 | 
			
		||||
                            initial={{ opacity: 0 }}
 | 
			
		||||
                            animate={{ opacity: 1 }}
 | 
			
		||||
                            transition={{ duration: 0.2 }}>
 | 
			
		||||
                            <WorkShiftsTab />
 | 
			
		||||
                        </motion.div>
 | 
			
		||||
                    </Tabs.Panel>
 | 
			
		||||
                </Tabs>
 | 
			
		||||
            </PageBlock>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,115 @@
 | 
			
		||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import { useActiveShiftsTableColumns } from "./columns.tsx";
 | 
			
		||||
import { ActionIcon, Flex, Stack, Text, Title, Tooltip } from "@mantine/core";
 | 
			
		||||
import { IconCheck, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import { ActiveWorkShiftSchema, WorkShiftsService } from "../../../../client";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import { formatDate } from "../../../../types/utils.ts";
 | 
			
		||||
import { MRT_TableOptions } from "mantine-react-table";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    activeShifts: ActiveWorkShiftSchema[];
 | 
			
		||||
    fetchActiveShifts: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ActiveShiftsTable = ({ activeShifts, fetchActiveShifts }: Props) => {
 | 
			
		||||
    const columns = useActiveShiftsTableColumns();
 | 
			
		||||
 | 
			
		||||
    const onDelete = (workShift: ActiveWorkShiftSchema) => {
 | 
			
		||||
        WorkShiftsService.deleteWorkShift({
 | 
			
		||||
            shiftId: workShift.id,
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                notifications.guess(ok, { message });
 | 
			
		||||
                fetchActiveShifts();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => {
 | 
			
		||||
                console.log(err);
 | 
			
		||||
            });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onDeleteClick = (workShift: ActiveWorkShiftSchema) => {
 | 
			
		||||
        modals.openConfirmModal({
 | 
			
		||||
            title: "Удаление записи о начале смены",
 | 
			
		||||
            children: (
 | 
			
		||||
                <Text size="sm">
 | 
			
		||||
                    Вы уверены что хотите удалить запись о начале смены работника{" "}
 | 
			
		||||
                    {workShift.user.firstName} {workShift.user.secondName} от{" "}
 | 
			
		||||
                    {formatDate(workShift.startedAt)}
 | 
			
		||||
                </Text>
 | 
			
		||||
            ),
 | 
			
		||||
            labels: { confirm: "Да", cancel: "Нет" },
 | 
			
		||||
            confirmProps: { color: "red" },
 | 
			
		||||
            onConfirm: () => onDelete(workShift),
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onShiftFinish = (workShift: ActiveWorkShiftSchema) => {
 | 
			
		||||
        WorkShiftsService.finishWorkShiftById({
 | 
			
		||||
            shiftId: workShift.id,
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                notifications.guess(ok, { message });
 | 
			
		||||
                fetchActiveShifts();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onShiftFinishClick = (workShift: ActiveWorkShiftSchema) => {
 | 
			
		||||
        modals.openConfirmModal({
 | 
			
		||||
            title: "Завершение смены",
 | 
			
		||||
            children: (
 | 
			
		||||
                <Text size="sm">
 | 
			
		||||
                    Вы уверены что хотите завершить смену работника{" "}
 | 
			
		||||
                    {workShift.user.firstName} {workShift.user.secondName} от{" "}
 | 
			
		||||
                    {formatDate(workShift.startedAt)}
 | 
			
		||||
                </Text>
 | 
			
		||||
            ),
 | 
			
		||||
            labels: { confirm: "Да", cancel: "Нет" },
 | 
			
		||||
            confirmProps: { color: "red" },
 | 
			
		||||
            onConfirm: () => onShiftFinish(workShift),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Stack mx={"xs"}>
 | 
			
		||||
            <Title order={4} mt={"md"}>
 | 
			
		||||
                Активные смены
 | 
			
		||||
            </Title>
 | 
			
		||||
            <BaseTable
 | 
			
		||||
                data={activeShifts}
 | 
			
		||||
                columns={columns}
 | 
			
		||||
                restProps={
 | 
			
		||||
                    {
 | 
			
		||||
                        enablePagination: true,
 | 
			
		||||
                        enableRowActions: true,
 | 
			
		||||
                        renderRowActions: ({ row }) => (
 | 
			
		||||
                            <Flex gap="md">
 | 
			
		||||
                                <Tooltip label="Удалить">
 | 
			
		||||
                                    <ActionIcon
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                            onDeleteClick(row.original)
 | 
			
		||||
                                        }
 | 
			
		||||
                                        variant={"default"}>
 | 
			
		||||
                                        <IconTrash />
 | 
			
		||||
                                    </ActionIcon>
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                                <Tooltip label="Завершить смену">
 | 
			
		||||
                                    <ActionIcon
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                            onShiftFinishClick(row.original)
 | 
			
		||||
                                        }
 | 
			
		||||
                                        variant={"default"}>
 | 
			
		||||
                                        <IconCheck />
 | 
			
		||||
                                    </ActionIcon>
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                            </Flex>
 | 
			
		||||
                        ),
 | 
			
		||||
                    } as MRT_TableOptions<ActiveWorkShiftSchema>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
        </Stack>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										29
									
								
								src/pages/AdminPage/components/ActiveShiftsTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/pages/AdminPage/components/ActiveShiftsTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import { useMemo } from "react";
 | 
			
		||||
import { MRT_ColumnDef } from "mantine-react-table";
 | 
			
		||||
import { ActiveWorkShiftSchema } from "../../../../client";
 | 
			
		||||
 | 
			
		||||
export const useActiveShiftsTableColumns = () => {
 | 
			
		||||
    return useMemo<MRT_ColumnDef<ActiveWorkShiftSchema>[]>(
 | 
			
		||||
        () => [
 | 
			
		||||
            {
 | 
			
		||||
                header: "Начало смены",
 | 
			
		||||
                Cell: ({ row }) =>
 | 
			
		||||
                    new Date(row.original.startedAt).toLocaleString("ru-RU"),
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                header: "ФИО",
 | 
			
		||||
                Cell: ({ row }) =>
 | 
			
		||||
                    `${row.original.user.firstName} ${row.original.user.secondName}`,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                header: "Роль",
 | 
			
		||||
                accessorKey: "user.role.name",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                header: "Должность",
 | 
			
		||||
                accessorKey: "user.position.name",
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        []
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -4,7 +4,7 @@ import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import { FC } from "react";
 | 
			
		||||
import { ActionIcon, Button, Flex, rem, Text, Tooltip } from "@mantine/core";
 | 
			
		||||
import { useUsersTableColumns } from "./columns.tsx";
 | 
			
		||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import { IconEdit, IconQrcode, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import { MRT_TableOptions } from "mantine-react-table";
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +53,13 @@ const UsersTable: FC<Props> = ({ items, onChange, onDelete, onCreate }) => {
 | 
			
		||||
            size: "md",
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    const onGenerateQrClick = (user: UserSchema) => {
 | 
			
		||||
        const pdfWindow = window.open(
 | 
			
		||||
            `${import.meta.env.VITE_API_URL}/work-shifts/generate-qr-code/${user.id}`,
 | 
			
		||||
        );
 | 
			
		||||
        if (!pdfWindow) return;
 | 
			
		||||
        pdfWindow.print();
 | 
			
		||||
    };
 | 
			
		||||
    return (
 | 
			
		||||
        <BaseTable
 | 
			
		||||
            data={items}
 | 
			
		||||
@@ -92,6 +99,15 @@ const UsersTable: FC<Props> = ({ items, onChange, onDelete, onCreate }) => {
 | 
			
		||||
                                    <IconTrash />
 | 
			
		||||
                                </ActionIcon>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                            <Tooltip
 | 
			
		||||
                                onClick={() => {
 | 
			
		||||
                                    onGenerateQrClick(row.original);
 | 
			
		||||
                                }}
 | 
			
		||||
                                label="QR-код">
 | 
			
		||||
                                <ActionIcon variant={"default"}>
 | 
			
		||||
                                    <IconQrcode />
 | 
			
		||||
                                </ActionIcon>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                        </Flex>
 | 
			
		||||
                    ),
 | 
			
		||||
                } as MRT_TableOptions<UserSchema>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import { Button, Group, Stack } from "@mantine/core";
 | 
			
		||||
import { ActiveWorkShiftSchema, WorkShiftsService } from "../../../../client";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import { ActiveShiftsTable } from "../../components/ActiveShiftsTable/ActiveShiftsTable.tsx";
 | 
			
		||||
 | 
			
		||||
export const WorkShiftsTab = () => {
 | 
			
		||||
    let inputType: "StartShift" | "FinishShift" = "StartShift";
 | 
			
		||||
    const [activeShifts, setActiveShifts] = useState<ActiveWorkShiftSchema[]>([]);
 | 
			
		||||
 | 
			
		||||
    const fetchActiveShifts = () => {
 | 
			
		||||
        WorkShiftsService.getActiveShifts()
 | 
			
		||||
            .then(res => {
 | 
			
		||||
                setActiveShifts(res.shifts);
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        fetchActiveShifts();
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    const onInputFinish = (userIdInput: string) => {
 | 
			
		||||
        const userId = parseInt(userIdInput);
 | 
			
		||||
        if (isNaN(userId)) {
 | 
			
		||||
            notifications.error({ message: "Ошибка, некорректные данные в QR-коде" });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (inputType === "StartShift") {
 | 
			
		||||
            WorkShiftsService.startShift({
 | 
			
		||||
                userId: userId!,
 | 
			
		||||
            })
 | 
			
		||||
                .then(async ({ ok, message }) => {
 | 
			
		||||
                    notifications.guess(ok, { message });
 | 
			
		||||
                    fetchActiveShifts();
 | 
			
		||||
                })
 | 
			
		||||
                .catch(err => console.log(err));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        WorkShiftsService.finishShift({
 | 
			
		||||
            userId: userId!,
 | 
			
		||||
        })
 | 
			
		||||
            .then(async ({ ok, message }) => {
 | 
			
		||||
                notifications.guess(ok, { message });
 | 
			
		||||
                fetchActiveShifts();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onScanningStart = () => {
 | 
			
		||||
        modals.openContextModal({
 | 
			
		||||
            modal: "scanningModal",
 | 
			
		||||
            innerProps: {
 | 
			
		||||
                label: "Отсканируйте QR-код",
 | 
			
		||||
                onScan: onInputFinish,
 | 
			
		||||
                closeOnScan: true,
 | 
			
		||||
            },
 | 
			
		||||
            withCloseButton: false,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onShiftStart = () => {
 | 
			
		||||
        inputType = "StartShift";
 | 
			
		||||
        onScanningStart();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onShiftFinish = () => {
 | 
			
		||||
        inputType = "FinishShift";
 | 
			
		||||
        onScanningStart();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Stack>
 | 
			
		||||
            <Group ml={"xs"} mt={"xs"}>
 | 
			
		||||
                <Button variant={"default"} onClick={onShiftStart}>
 | 
			
		||||
                    Начать смену
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Button variant={"default"} onClick={onShiftFinish}>
 | 
			
		||||
                    Закончить смену
 | 
			
		||||
                </Button>
 | 
			
		||||
            </Group>
 | 
			
		||||
            <ActiveShiftsTable
 | 
			
		||||
                activeShifts={activeShifts}
 | 
			
		||||
                fetchActiveShifts={fetchActiveShifts}
 | 
			
		||||
            />
 | 
			
		||||
        </Stack>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user