feat: scanning mode on receipt page
This commit is contained in:
		@@ -33,6 +33,7 @@ import AssignUserModal from "../pages/LeadsPage/tabs/EmployeesTab/modals/AssignU
 | 
				
			|||||||
import ResidualProductModal from "../pages/ResiduesPage/modals/ResidualProductModal/ResidualProductModal.tsx";
 | 
					import ResidualProductModal from "../pages/ResiduesPage/modals/ResidualProductModal/ResidualProductModal.tsx";
 | 
				
			||||||
import NewReceiptModal from "../pages/ReceiptPage/components/NewReceipt/modals/NewReceiptModal.tsx";
 | 
					import NewReceiptModal from "../pages/ReceiptPage/components/NewReceipt/modals/NewReceiptModal.tsx";
 | 
				
			||||||
import ReceiptModal from "../pages/ReceiptPage/components/ReceiptEditing/modals/ReceiptModal.tsx";
 | 
					import ReceiptModal from "../pages/ReceiptPage/components/ReceiptEditing/modals/ReceiptModal.tsx";
 | 
				
			||||||
 | 
					import SelectScannedProductModal from "../pages/ReceiptPage/modals/SelectScannedProductModal.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const modals = {
 | 
					export const modals = {
 | 
				
			||||||
    enterDeadline: EnterDeadlineModal,
 | 
					    enterDeadline: EnterDeadlineModal,
 | 
				
			||||||
@@ -63,6 +64,7 @@ export const modals = {
 | 
				
			|||||||
    transactionTagsModal: TransactionTagsModal,
 | 
					    transactionTagsModal: TransactionTagsModal,
 | 
				
			||||||
    shippingProductModal: ShippingProductModal,
 | 
					    shippingProductModal: ShippingProductModal,
 | 
				
			||||||
    residualProductModal: ResidualProductModal,
 | 
					    residualProductModal: ResidualProductModal,
 | 
				
			||||||
 | 
					    selectScannedProductModal: SelectScannedProductModal,
 | 
				
			||||||
    newReceiptModal: NewReceiptModal,
 | 
					    newReceiptModal: NewReceiptModal,
 | 
				
			||||||
    receiptModal: ReceiptModal,
 | 
					    receiptModal: ReceiptModal,
 | 
				
			||||||
    departmentModal: DepartmentModal,
 | 
					    departmentModal: DepartmentModal,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Accordion, ActionIcon, Center } from "@mantine/core";
 | 
					import { Accordion, ActionIcon, Center } from "@mantine/core";
 | 
				
			||||||
import { IconBox, IconPlus, IconTrash } from "@tabler/icons-react";
 | 
					import { IconBarcode, IconBox, IconPlayerPause, IconPlus, IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
					import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
				
			||||||
import { useReceiptContext } from "../contexts/ReceiptContext.tsx";
 | 
					import { useReceiptContext } from "../contexts/ReceiptContext.tsx";
 | 
				
			||||||
import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
					import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
				
			||||||
@@ -10,7 +10,17 @@ type Props = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AccordionBoxes = ({ pallet, palletIdx }: Props) => {
 | 
					const AccordionBoxes = ({ pallet, palletIdx }: Props) => {
 | 
				
			||||||
    const { boxes, boxesHandlers, onObjectEditClick, palletsHandlers, setBoxData } = useReceiptContext();
 | 
					    const {
 | 
				
			||||||
 | 
					        boxes,
 | 
				
			||||||
 | 
					        boxesHandlers,
 | 
				
			||||||
 | 
					        onObjectEditClick,
 | 
				
			||||||
 | 
					        palletsHandlers,
 | 
				
			||||||
 | 
					        setBoxData,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
 | 
					    } = useReceiptContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { boxId, isScanning } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deleteBox = (boxId: number) => {
 | 
					    const deleteBox = (boxId: number) => {
 | 
				
			||||||
        if (palletIdx && pallet) {
 | 
					        if (palletIdx && pallet) {
 | 
				
			||||||
@@ -23,9 +33,22 @@ const AccordionBoxes = ({ pallet, palletIdx }: Props) => {
 | 
				
			|||||||
    const boxActions = (box: ReceiptBox) => {
 | 
					    const boxActions = (box: ReceiptBox) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
 | 
					                <ActionIcon
 | 
				
			||||||
 | 
					                    variant={"default"}
 | 
				
			||||||
 | 
					                    onClick={() => toggleScanning(box.id, pallet?.id)}
 | 
				
			||||||
 | 
					                    mr={"sm"}
 | 
				
			||||||
 | 
					                    disabled={isScanning && box.id !== boxId}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    {isScanning && box.id === boxId ? (
 | 
				
			||||||
 | 
					                        <IconPlayerPause />
 | 
				
			||||||
 | 
					                    ) : (
 | 
				
			||||||
 | 
					                        <IconBarcode />
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                </ActionIcon>
 | 
				
			||||||
                <ActionIcon
 | 
					                <ActionIcon
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => onObjectEditClick(box, true, pallet)}
 | 
					                    onClick={() => onObjectEditClick(box, true, pallet)}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                    mr={"sm"}
 | 
					                    mr={"sm"}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <IconPlus />
 | 
					                    <IconPlus />
 | 
				
			||||||
@@ -33,6 +56,7 @@ const AccordionBoxes = ({ pallet, palletIdx }: Props) => {
 | 
				
			|||||||
                <ActionIcon
 | 
					                <ActionIcon
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => deleteBox(box.id)}
 | 
					                    onClick={() => deleteBox(box.id)}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                    mr={"sm"}
 | 
					                    mr={"sm"}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <IconTrash />
 | 
					                    <IconTrash />
 | 
				
			||||||
@@ -60,6 +84,7 @@ const AccordionBoxes = ({ pallet, palletIdx }: Props) => {
 | 
				
			|||||||
                            products={box.residualProducts}
 | 
					                            products={box.residualProducts}
 | 
				
			||||||
                            object={box}
 | 
					                            object={box}
 | 
				
			||||||
                            setObjectData={(box: ReceiptBox) => setBoxData(box, pallet)}
 | 
					                            setObjectData={(box: ReceiptBox) => setBoxData(box, pallet)}
 | 
				
			||||||
 | 
					                            disabled={isScanning}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </Accordion.Panel>
 | 
					                    </Accordion.Panel>
 | 
				
			||||||
                </Accordion.Item>
 | 
					                </Accordion.Item>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,22 @@
 | 
				
			|||||||
import { Accordion, ActionIcon, Box, Button, Center, Flex, Group } from "@mantine/core";
 | 
					import { Accordion, ActionIcon, Box, Button, Center, Flex, Group } from "@mantine/core";
 | 
				
			||||||
import { IconPlus, IconSpace, IconTrash } from "@tabler/icons-react";
 | 
					import { IconBarcode, IconPlayerPause, IconPlus, IconSpace, IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
					import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
				
			||||||
import { useReceiptContext } from "../contexts/ReceiptContext.tsx";
 | 
					import { useReceiptContext } from "../contexts/ReceiptContext.tsx";
 | 
				
			||||||
import AccordionBoxes from "./AccordionBoxes.tsx";
 | 
					import AccordionBoxes from "./AccordionBoxes.tsx";
 | 
				
			||||||
import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
					import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AccordionPallets = () => {
 | 
					const AccordionPallets = () => {
 | 
				
			||||||
    const { pallets, palletsHandlers, nextId, onObjectEditClick, setPalletData } = useReceiptContext();
 | 
					    const {
 | 
				
			||||||
 | 
					        pallets,
 | 
				
			||||||
 | 
					        palletsHandlers,
 | 
				
			||||||
 | 
					        nextId,
 | 
				
			||||||
 | 
					        onObjectEditClick,
 | 
				
			||||||
 | 
					        setPalletData,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
 | 
					    } = useReceiptContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { palletId, boxId, isScanning } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deletePallet = (palletId: number) => {
 | 
					    const deletePallet = (palletId: number) => {
 | 
				
			||||||
        palletsHandlers.filter(item => item.id !== palletId);
 | 
					        palletsHandlers.filter(item => item.id !== palletId);
 | 
				
			||||||
@@ -23,25 +33,44 @@ const AccordionPallets = () => {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const palletActions = (pallet: ReceiptPallet) => {
 | 
					    const palletActions = (pallet: ReceiptPallet) => {
 | 
				
			||||||
 | 
					        const isScanModeVisible = pallet.boxes.length === 0;
 | 
				
			||||||
        const isCreateButtonVisible = pallet.boxes.length > 0 || pallet.residualProducts.length > 0;
 | 
					        const isCreateButtonVisible = pallet.boxes.length > 0 || pallet.residualProducts.length > 0;
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
                {isCreateButtonVisible && <ActionIcon
 | 
					                {isScanModeVisible && (
 | 
				
			||||||
                    variant={"default"}
 | 
					                    <ActionIcon
 | 
				
			||||||
                    onClick={() => {
 | 
					                        variant={"default"}
 | 
				
			||||||
                        if (pallet.residualProducts.length > 0) {
 | 
					                        onClick={() => toggleScanning(undefined, pallet.id)}
 | 
				
			||||||
                            onObjectEditClick(pallet, false);
 | 
					                        mr={"sm"}
 | 
				
			||||||
                        } else {
 | 
					                        disabled={isScanning && (pallet.id !== palletId || !!boxId)}
 | 
				
			||||||
                            createBox(pallet);
 | 
					                    >
 | 
				
			||||||
                        }
 | 
					                        {isScanning && pallet.id === palletId && !boxId ? (
 | 
				
			||||||
                    }}
 | 
					                            <IconPlayerPause />
 | 
				
			||||||
                    mr={"sm"}
 | 
					                        ) : (
 | 
				
			||||||
                >
 | 
					                            <IconBarcode />
 | 
				
			||||||
                    <IconPlus />
 | 
					                        )}
 | 
				
			||||||
                </ActionIcon>}
 | 
					                    </ActionIcon>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					                {isCreateButtonVisible && (
 | 
				
			||||||
 | 
					                    <ActionIcon
 | 
				
			||||||
 | 
					                        variant={"default"}
 | 
				
			||||||
 | 
					                        onClick={() => {
 | 
				
			||||||
 | 
					                            if (pallet.residualProducts.length > 0) {
 | 
				
			||||||
 | 
					                                onObjectEditClick(pallet, false);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                createBox(pallet);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                        disabled={isScanning}
 | 
				
			||||||
 | 
					                        mr={"sm"}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        <IconPlus />
 | 
				
			||||||
 | 
					                    </ActionIcon>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
                <ActionIcon
 | 
					                <ActionIcon
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => deletePallet(pallet.id)}
 | 
					                    onClick={() => deletePallet(pallet.id)}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                    mr={"md"}
 | 
					                    mr={"md"}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <IconTrash />
 | 
					                    <IconTrash />
 | 
				
			||||||
@@ -55,6 +84,7 @@ const AccordionPallets = () => {
 | 
				
			|||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
                variant={"default"}
 | 
					                variant={"default"}
 | 
				
			||||||
                onClick={onClick}
 | 
					                onClick={onClick}
 | 
				
			||||||
 | 
					                disabled={isScanning}
 | 
				
			||||||
                flex={1}
 | 
					                flex={1}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <Group gap={"md"}>
 | 
					                <Group gap={"md"}>
 | 
				
			||||||
@@ -98,6 +128,7 @@ const AccordionPallets = () => {
 | 
				
			|||||||
                            products={pallet.residualProducts}
 | 
					                            products={pallet.residualProducts}
 | 
				
			||||||
                            object={pallet}
 | 
					                            object={pallet}
 | 
				
			||||||
                            setObjectData={object => setPalletData(object as ReceiptPallet)}
 | 
					                            setObjectData={object => setPalletData(object as ReceiptPallet)}
 | 
				
			||||||
 | 
					                            disabled={isScanning}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                </Accordion.Panel>
 | 
					                </Accordion.Panel>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,15 @@ import InlineButton from "../../../../../components/InlineButton/InlineButton.ts
 | 
				
			|||||||
import { IconPlus } from "@tabler/icons-react";
 | 
					import { IconPlus } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ReceiptEditor = () => {
 | 
					const ReceiptEditor = () => {
 | 
				
			||||||
    const { fixed, palletsHandlers, boxesHandlers, nextId } = useReceiptContext();
 | 
					    const {
 | 
				
			||||||
 | 
					        fixed,
 | 
				
			||||||
 | 
					        palletsHandlers,
 | 
				
			||||||
 | 
					        boxesHandlers,
 | 
				
			||||||
 | 
					        nextId,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					    } = useReceiptContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { isScanning } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!fixed) return;
 | 
					    if (!fixed) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,11 +39,19 @@ const ReceiptEditor = () => {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Stack>
 | 
					        <Stack>
 | 
				
			||||||
            <Flex gap="md">
 | 
					            <Flex gap="md">
 | 
				
			||||||
                <InlineButton onClick={() => createPallet()} flex={1}>
 | 
					                <InlineButton
 | 
				
			||||||
 | 
					                    onClick={() => createPallet()}
 | 
				
			||||||
 | 
					                    flex={1}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
                    <IconPlus />
 | 
					                    <IconPlus />
 | 
				
			||||||
                    Паллет
 | 
					                    Паллет
 | 
				
			||||||
                </InlineButton>
 | 
					                </InlineButton>
 | 
				
			||||||
                <InlineButton onClick={() => createBox()} flex={1}>
 | 
					                <InlineButton
 | 
				
			||||||
 | 
					                    onClick={() => createBox()}
 | 
				
			||||||
 | 
					                    flex={1}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
                    <IconPlus />
 | 
					                    <IconPlus />
 | 
				
			||||||
                    Короб
 | 
					                    Короб
 | 
				
			||||||
                </InlineButton>
 | 
					                </InlineButton>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,9 +6,10 @@ type Props = {
 | 
				
			|||||||
    products: ReceiptProduct[];
 | 
					    products: ReceiptProduct[];
 | 
				
			||||||
    object: ReceiptBox | ReceiptPallet;
 | 
					    object: ReceiptBox | ReceiptPallet;
 | 
				
			||||||
    setObjectData: (object: ReceiptBox | ReceiptPallet) => void;
 | 
					    setObjectData: (object: ReceiptBox | ReceiptPallet) => void;
 | 
				
			||||||
 | 
					    disabled: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ReceiptProducts = ({ products, object, setObjectData }: Props) => {
 | 
					const ReceiptProducts = ({ products, object, setObjectData, disabled }: Props) => {
 | 
				
			||||||
    if (products.length === 0) return;
 | 
					    if (products.length === 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deleteProduct = (productId: number) => {
 | 
					    const deleteProduct = (productId: number) => {
 | 
				
			||||||
@@ -31,6 +32,7 @@ const ReceiptProducts = ({ products, object, setObjectData }: Props) => {
 | 
				
			|||||||
                    allowDecimal={false}
 | 
					                    allowDecimal={false}
 | 
				
			||||||
                    allowNegative={false}
 | 
					                    allowNegative={false}
 | 
				
			||||||
                    w={rem(100)}
 | 
					                    w={rem(100)}
 | 
				
			||||||
 | 
					                    disabled={disabled}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </Group>
 | 
					            </Group>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -50,6 +52,7 @@ const ReceiptProducts = ({ products, object, setObjectData }: Props) => {
 | 
				
			|||||||
                        <ActionIcon
 | 
					                        <ActionIcon
 | 
				
			||||||
                            variant={"default"}
 | 
					                            variant={"default"}
 | 
				
			||||||
                            onClick={() => deleteProduct(receiptProduct.id)}
 | 
					                            onClick={() => deleteProduct(receiptProduct.id)}
 | 
				
			||||||
 | 
					                            disabled={disabled}
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
                            <IconTrash />
 | 
					                            <IconTrash />
 | 
				
			||||||
                        </ActionIcon>
 | 
					                        </ActionIcon>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,21 +4,29 @@ import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
				
			|||||||
import { modals } from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import nextId from "../utils/nextId.ts";
 | 
					import nextId from "../utils/nextId.ts";
 | 
				
			||||||
import { useListState, UseListStateHandlers } from "@mantine/hooks";
 | 
					import { useListState, UseListStateHandlers } from "@mantine/hooks";
 | 
				
			||||||
 | 
					import useScanningMode, { ScanningData } from "../../../hooks/useScanningMode.tsx";
 | 
				
			||||||
 | 
					import useBarcodesProductsMap from "../../../hooks/useBarcodesProductsMap.tsx";
 | 
				
			||||||
 | 
					import useApplyingScanningResult from "../hooks/useApplyingScannedResult.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ReceiptContextState = {
 | 
					type ReceiptContextState = {
 | 
				
			||||||
    client?: ClientSchema;
 | 
					    client?: ClientSchema;
 | 
				
			||||||
    setClient: Dispatch<SetStateAction<ClientSchema | undefined>>;
 | 
					    setClient: Dispatch<SetStateAction<ClientSchema | undefined>>;
 | 
				
			||||||
    fixed: boolean;
 | 
					    fixed: boolean;
 | 
				
			||||||
    setFixed: Dispatch<SetStateAction<boolean>>;
 | 
					    setFixed: Dispatch<SetStateAction<boolean>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pallets: ReceiptPallet[];
 | 
					    pallets: ReceiptPallet[];
 | 
				
			||||||
    palletsHandlers: UseListStateHandlers<ReceiptPallet>;
 | 
					    palletsHandlers: UseListStateHandlers<ReceiptPallet>;
 | 
				
			||||||
    boxes: ReceiptBox[];
 | 
					    boxes: ReceiptBox[];
 | 
				
			||||||
    boxesHandlers: UseListStateHandlers<ReceiptBox>;
 | 
					    boxesHandlers: UseListStateHandlers<ReceiptBox>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nextId: () => number;
 | 
					    nextId: () => number;
 | 
				
			||||||
    onObjectEditClick: (object: ReceiptBox | ReceiptPallet, isBox: boolean, parentPallet?: ReceiptPallet) => void;
 | 
					    onObjectEditClick: (object: ReceiptBox | ReceiptPallet, isBox: boolean, parentPallet?: ReceiptPallet) => void;
 | 
				
			||||||
    reset: () => void;
 | 
					    reset: () => void;
 | 
				
			||||||
    setBoxData: (box: ReceiptBox, pallet?: ReceiptPallet) => void;
 | 
					    setBoxData: (box: ReceiptBox, pallet?: ReceiptPallet) => void;
 | 
				
			||||||
    setPalletData: (pallet: ReceiptPallet) => void;
 | 
					    setPalletData: (pallet: ReceiptPallet) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scanningData: ScanningData;
 | 
				
			||||||
 | 
					    toggleScanning: (boxId?: number, palletId?: number) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ReceiptContext = createContext<ReceiptContextState | undefined>(
 | 
					const ReceiptContext = createContext<ReceiptContextState | undefined>(
 | 
				
			||||||
@@ -32,6 +40,8 @@ const useReceiptContextState = () => {
 | 
				
			|||||||
    const [pallets, palletsHandlers] = useListState<ReceiptPallet>([]);
 | 
					    const [pallets, palletsHandlers] = useListState<ReceiptPallet>([]);
 | 
				
			||||||
    const [boxes, boxesHandlers] = useListState<ReceiptBox>([]);
 | 
					    const [boxes, boxesHandlers] = useListState<ReceiptBox>([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { barcodesProductsMap } = useBarcodesProductsMap({ clientId: client?.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const setBoxData = (box: ReceiptBox, pallet?: ReceiptPallet) => {
 | 
					    const setBoxData = (box: ReceiptBox, pallet?: ReceiptPallet) => {
 | 
				
			||||||
        if (pallet) {
 | 
					        if (pallet) {
 | 
				
			||||||
            setBoxOnPalletData(box, pallet);
 | 
					            setBoxOnPalletData(box, pallet);
 | 
				
			||||||
@@ -69,6 +79,15 @@ const useReceiptContextState = () => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const editObject = (
 | 
				
			||||||
 | 
					        object: ReceiptBox | ReceiptPallet,
 | 
				
			||||||
 | 
					        isBox: boolean,
 | 
				
			||||||
 | 
					        parentPallet?: ReceiptPallet,
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					        if (isBox) setBoxData(object, parentPallet);
 | 
				
			||||||
 | 
					        setPalletData(object as ReceiptPallet);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onObjectEditClick = (
 | 
					    const onObjectEditClick = (
 | 
				
			||||||
        object: ReceiptBox | ReceiptPallet,
 | 
					        object: ReceiptBox | ReceiptPallet,
 | 
				
			||||||
        isBox: boolean,
 | 
					        isBox: boolean,
 | 
				
			||||||
@@ -83,13 +102,24 @@ const useReceiptContextState = () => {
 | 
				
			|||||||
                clientId: client.id,
 | 
					                clientId: client.id,
 | 
				
			||||||
                object: object,
 | 
					                object: object,
 | 
				
			||||||
                setObjectData: (object: ReceiptBox | ReceiptPallet) => {
 | 
					                setObjectData: (object: ReceiptBox | ReceiptPallet) => {
 | 
				
			||||||
                    if (isBox) setBoxData(object, parentPallet);
 | 
					                    editObject(object, isBox, parentPallet);
 | 
				
			||||||
                    setPalletData(object as ReceiptPallet);
 | 
					 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                barcodesProductsMap,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        onScanningFinish,
 | 
				
			||||||
 | 
					    } = useApplyingScanningResult({
 | 
				
			||||||
 | 
					        pallets,
 | 
				
			||||||
 | 
					        boxes,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					        editObject,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { scanningData, toggleScanning } = useScanningMode({ onScanningFinish });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const reset = () => {
 | 
					    const reset = () => {
 | 
				
			||||||
        palletsHandlers.setState([]);
 | 
					        palletsHandlers.setState([]);
 | 
				
			||||||
        boxesHandlers.setState([]);
 | 
					        boxesHandlers.setState([]);
 | 
				
			||||||
@@ -111,6 +141,8 @@ const useReceiptContextState = () => {
 | 
				
			|||||||
        reset,
 | 
					        reset,
 | 
				
			||||||
        setBoxData,
 | 
					        setBoxData,
 | 
				
			||||||
        setPalletData,
 | 
					        setPalletData,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
				
			||||||
 | 
					import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { ProductSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					import findProductInObject from "../utils/findProductInObject.tsx";
 | 
				
			||||||
 | 
					import nextId from "../utils/nextId.ts";
 | 
				
			||||||
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    pallets: ReceiptPallet[];
 | 
				
			||||||
 | 
					    boxes: ReceiptBox[];
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
 | 
					    editObject: (
 | 
				
			||||||
 | 
					        object: ReceiptBox | ReceiptPallet,
 | 
				
			||||||
 | 
					        isBox: boolean,
 | 
				
			||||||
 | 
					        parentPallet?: ReceiptPallet,
 | 
				
			||||||
 | 
					    ) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useApplyingScannedResult = ({
 | 
				
			||||||
 | 
					                                      pallets,
 | 
				
			||||||
 | 
					                                      boxes,
 | 
				
			||||||
 | 
					                                      barcodesProductsMap,
 | 
				
			||||||
 | 
					                                      editObject,
 | 
				
			||||||
 | 
					                                  }: Props) => {
 | 
				
			||||||
 | 
					    let isBox = false;
 | 
				
			||||||
 | 
					    let object: ReceiptPallet | ReceiptBox;
 | 
				
			||||||
 | 
					    let parentPallet: ReceiptPallet | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onProductSelect = (product: ProductSchema) => {
 | 
				
			||||||
 | 
					        const productIdx = object.residualProducts.findIndex(p => p.product.id === product.id);
 | 
				
			||||||
 | 
					        if (productIdx === -1) {
 | 
				
			||||||
 | 
					            object.residualProducts.unshift({
 | 
				
			||||||
 | 
					                id: nextId(),
 | 
				
			||||||
 | 
					                quantity: 1,
 | 
				
			||||||
 | 
					                product,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            object.residualProducts[productIdx].quantity += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        editObject(object, isBox, parentPallet);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const showSelectProductModal = (productsToSelect: ProductSchema[]) => {
 | 
				
			||||||
 | 
					        modals.openContextModal({
 | 
				
			||||||
 | 
					            modal: "selectScannedProductModal",
 | 
				
			||||||
 | 
					            title: "Выберите товар для данного штрихкода",
 | 
				
			||||||
 | 
					            withCloseButton: false,
 | 
				
			||||||
 | 
					            innerProps: {
 | 
				
			||||||
 | 
					                productsToSelect,
 | 
				
			||||||
 | 
					                onProductSelect,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const findProductsByBarcode = (scannedValue: string) => {
 | 
				
			||||||
 | 
					        const productsToSelect = barcodesProductsMap.get(scannedValue) ?? [];
 | 
				
			||||||
 | 
					        if (productsToSelect?.length === 0) {
 | 
				
			||||||
 | 
					            notifications.error({ message: `Товара с штрихкодом ${scannedValue} не найдено` });
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (productsToSelect?.length === 1) {
 | 
				
			||||||
 | 
					            onProductSelect(productsToSelect[0]);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const product = findProductInObject(object, productsToSelect);
 | 
				
			||||||
 | 
					        if (product) {
 | 
				
			||||||
 | 
					            onProductSelect(product);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return showSelectProductModal(productsToSelect);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onScanningFinish = (
 | 
				
			||||||
 | 
					        value: string,
 | 
				
			||||||
 | 
					        boxId?: number,
 | 
				
			||||||
 | 
					        palletId?: number,
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					        isBox = !!boxId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (boxId) {
 | 
				
			||||||
 | 
					            let parentPalletIdx = -1;
 | 
				
			||||||
 | 
					            if (palletId) {
 | 
				
			||||||
 | 
					                parentPalletIdx = pallets.findIndex(pallet => pallet.id === palletId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            parentPallet = parentPalletIdx === -1 ? undefined : pallets[parentPalletIdx];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const boxesToSearch = parentPallet?.boxes ?? boxes;
 | 
				
			||||||
 | 
					            const boxIdx = boxesToSearch.findIndex(box => box.id === boxId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            object = boxesToSearch[boxIdx];
 | 
				
			||||||
 | 
					            findProductsByBarcode(value);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const palletIdx = pallets.findIndex(pallet => pallet.id === palletId);
 | 
				
			||||||
 | 
					        object = pallets[palletIdx];
 | 
				
			||||||
 | 
					        findProductsByBarcode(value);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        onScanningFinish,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useApplyingScannedResult;
 | 
				
			||||||
@@ -14,17 +14,19 @@ type Props = {
 | 
				
			|||||||
    clientId: number;
 | 
					    clientId: number;
 | 
				
			||||||
    object: ReceiptBox | ReceiptPallet;
 | 
					    object: ReceiptBox | ReceiptPallet;
 | 
				
			||||||
    setObjectData: (object: ReceiptBox | ReceiptPallet) => void;
 | 
					    setObjectData: (object: ReceiptBox | ReceiptPallet) => void;
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NewReceiptModal = ({
 | 
					const NewReceiptModal = ({
 | 
				
			||||||
                          context,
 | 
					                             context,
 | 
				
			||||||
                          id,
 | 
					                             id,
 | 
				
			||||||
                          innerProps,
 | 
					                             innerProps,
 | 
				
			||||||
                      }: ContextModalProps<Props>) => {
 | 
					                         }: ContextModalProps<Props>) => {
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        setObjectData,
 | 
					        setObjectData,
 | 
				
			||||||
        object,
 | 
					        object,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
    } = innerProps;
 | 
					    } = innerProps;
 | 
				
			||||||
    const { isScanning, setIsScanning, scannedValue, setScannedValue } = useScanning();
 | 
					    const { isScanning, setIsScanning, scannedValue, setScannedValue } = useScanning();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -112,12 +114,12 @@ const NewReceiptModal = ({
 | 
				
			|||||||
            </form>
 | 
					            </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <ScanBarcode
 | 
					            <ScanBarcode
 | 
				
			||||||
                clientId={clientId}
 | 
					 | 
				
			||||||
                isScanning={isScanning}
 | 
					                isScanning={isScanning}
 | 
				
			||||||
                setIsScanning={setIsScanning}
 | 
					                setIsScanning={setIsScanning}
 | 
				
			||||||
                onProductSelect={onProductAfterScanningSelect}
 | 
					                onProductSelect={onProductAfterScanningSelect}
 | 
				
			||||||
                scannedValue={scannedValue}
 | 
					                scannedValue={scannedValue}
 | 
				
			||||||
                object={object}
 | 
					                object={object}
 | 
				
			||||||
 | 
					                barcodesProductsMap={barcodesProductsMap}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import { ReceiptBox, ReceiptPallet } from "../types/types.tsx";
 | 
				
			||||||
 | 
					import { ProductSchema, ResidualBoxSchema, ResidualPalletSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const findProductInObject = (
 | 
				
			||||||
 | 
					    object: ReceiptPallet | ReceiptBox | ResidualPalletSchema | ResidualBoxSchema,
 | 
				
			||||||
 | 
					    productsToSelect: ProductSchema[],
 | 
				
			||||||
 | 
					): ProductSchema | undefined => {
 | 
				
			||||||
 | 
					    for (let i = 0; i < productsToSelect.length; i++) {
 | 
				
			||||||
 | 
					        for (let j = 0; j < object.residualProducts.length; j++) {
 | 
				
			||||||
 | 
					            if (productsToSelect[i].id === object.residualProducts[j].product.id) {
 | 
				
			||||||
 | 
					                return productsToSelect[i];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default findProductInObject;
 | 
				
			||||||
@@ -1,17 +1,33 @@
 | 
				
			|||||||
import { Accordion, ActionIcon, Center } from "@mantine/core";
 | 
					import { Accordion, ActionIcon, Center } from "@mantine/core";
 | 
				
			||||||
import { IconBox, IconPlus, IconTrash } from "@tabler/icons-react";
 | 
					import { IconBarcode, IconBox, IconPlayerPause, IconPlus, IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
					import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
				
			||||||
import { ResidualBoxSchema, ResidualPalletSchema, ResiduesService } from "../../../../../client";
 | 
					import { ProductSchema, ResidualBoxSchema, ResidualPalletSchema, ResiduesService } from "../../../../../client";
 | 
				
			||||||
import { modals } from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { ScanningData } from "../../../hooks/useScanningMode.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    pallet: ResidualPalletSchema;
 | 
					    pallet: ResidualPalletSchema;
 | 
				
			||||||
    clientId: number;
 | 
					    clientId: number;
 | 
				
			||||||
    fetchPallet: () => void;
 | 
					    fetchPallet: () => void;
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
 | 
					    scanningData: ScanningData
 | 
				
			||||||
 | 
					    toggleScanning: (boxId?: number, palletId?: number) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AccordionBoxesOnPallet = ({ pallet, clientId, fetchPallet }: Props) => {
 | 
					const AccordionBoxesOnPallet = ({
 | 
				
			||||||
 | 
					                                    pallet,
 | 
				
			||||||
 | 
					                                    clientId,
 | 
				
			||||||
 | 
					                                    fetchPallet,
 | 
				
			||||||
 | 
					                                    barcodesProductsMap,
 | 
				
			||||||
 | 
					                                    scanningData,
 | 
				
			||||||
 | 
					                                    toggleScanning,
 | 
				
			||||||
 | 
					                                }: Props) => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        isScanning,
 | 
				
			||||||
 | 
					        boxId,
 | 
				
			||||||
 | 
					    } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deleteBox = (boxId: number) => {
 | 
					    const deleteBox = (boxId: number) => {
 | 
				
			||||||
        ResiduesService.deleteResidualBox({
 | 
					        ResiduesService.deleteResidualBox({
 | 
				
			||||||
            boxId,
 | 
					            boxId,
 | 
				
			||||||
@@ -36,6 +52,7 @@ const AccordionBoxesOnPallet = ({ pallet, clientId, fetchPallet }: Props) => {
 | 
				
			|||||||
                isBox: true,
 | 
					                isBox: true,
 | 
				
			||||||
                object: box,
 | 
					                object: box,
 | 
				
			||||||
                fetchObject: fetchPallet,
 | 
					                fetchObject: fetchPallet,
 | 
				
			||||||
 | 
					                barcodesProductsMap,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -43,10 +60,23 @@ const AccordionBoxesOnPallet = ({ pallet, clientId, fetchPallet }: Props) => {
 | 
				
			|||||||
    const boxActions = (box: ResidualBoxSchema) => {
 | 
					    const boxActions = (box: ResidualBoxSchema) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
 | 
					                <ActionIcon
 | 
				
			||||||
 | 
					                    variant={"default"}
 | 
				
			||||||
 | 
					                    onClick={() => toggleScanning(box.id, pallet?.id)}
 | 
				
			||||||
 | 
					                    mr={"sm"}
 | 
				
			||||||
 | 
					                    disabled={isScanning && box.id !== boxId}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    {isScanning && box.id === boxId ? (
 | 
				
			||||||
 | 
					                        <IconPlayerPause />
 | 
				
			||||||
 | 
					                    ) : (
 | 
				
			||||||
 | 
					                        <IconBarcode />
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                </ActionIcon>
 | 
				
			||||||
                <ActionIcon
 | 
					                <ActionIcon
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => onCreateProductClick(box)}
 | 
					                    onClick={() => onCreateProductClick(box)}
 | 
				
			||||||
                    mr={"sm"}
 | 
					                    mr={"sm"}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <IconPlus />
 | 
					                    <IconPlus />
 | 
				
			||||||
                </ActionIcon>
 | 
					                </ActionIcon>
 | 
				
			||||||
@@ -54,6 +84,7 @@ const AccordionBoxesOnPallet = ({ pallet, clientId, fetchPallet }: Props) => {
 | 
				
			|||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => deleteBox(box.id)}
 | 
					                    onClick={() => deleteBox(box.id)}
 | 
				
			||||||
                    mr={"sm"}
 | 
					                    mr={"sm"}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <IconTrash />
 | 
					                    <IconTrash />
 | 
				
			||||||
                </ActionIcon>
 | 
					                </ActionIcon>
 | 
				
			||||||
@@ -78,6 +109,7 @@ const AccordionBoxesOnPallet = ({ pallet, clientId, fetchPallet }: Props) => {
 | 
				
			|||||||
                            clientId={clientId}
 | 
					                            clientId={clientId}
 | 
				
			||||||
                            object={box}
 | 
					                            object={box}
 | 
				
			||||||
                            updateObject={fetchPallet}
 | 
					                            updateObject={fetchPallet}
 | 
				
			||||||
 | 
					                            disabled={isScanning}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </Accordion.Panel>
 | 
					                    </Accordion.Panel>
 | 
				
			||||||
                </Accordion.Item>
 | 
					                </Accordion.Item>
 | 
				
			||||||
@@ -5,9 +5,10 @@ import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			|||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    residualProduct: ResidualProductSchema;
 | 
					    residualProduct: ResidualProductSchema;
 | 
				
			||||||
    updateObject: () => void;
 | 
					    updateObject: () => void;
 | 
				
			||||||
 | 
					    disabled: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ProductQuantityField = ({ residualProduct, updateObject }: Props) => {
 | 
					const ProductQuantityField = ({ residualProduct, updateObject, disabled }: Props) => {
 | 
				
			||||||
    const updateProduct = (quantity: number) => {
 | 
					    const updateProduct = (quantity: number) => {
 | 
				
			||||||
        if (residualProduct.quantity === quantity) return;
 | 
					        if (residualProduct.quantity === quantity) return;
 | 
				
			||||||
        ResiduesService.updateResidualProduct({
 | 
					        ResiduesService.updateResidualProduct({
 | 
				
			||||||
@@ -38,6 +39,7 @@ const ProductQuantityField = ({ residualProduct, updateObject }: Props) => {
 | 
				
			|||||||
                allowDecimal={false}
 | 
					                allowDecimal={false}
 | 
				
			||||||
                allowNegative={false}
 | 
					                allowNegative={false}
 | 
				
			||||||
                w={rem(100)}
 | 
					                w={rem(100)}
 | 
				
			||||||
 | 
					                disabled={disabled}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </Group>
 | 
					        </Group>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import { Button, Flex, Stack, Text, Title } from "@mantine/core";
 | 
					import { ActionIcon, Button, Flex, Stack, Text, Title } from "@mantine/core";
 | 
				
			||||||
import useReceiptBox from "../hooks/useReceiptBox.tsx";
 | 
					import useReceiptBox from "../hooks/useReceiptBox.tsx";
 | 
				
			||||||
import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
					import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
				
			||||||
import InlineButton from "../../../../../components/InlineButton/InlineButton.tsx";
 | 
					import InlineButton from "../../../../../components/InlineButton/InlineButton.tsx";
 | 
				
			||||||
import { IconArrowLeft, IconPlus } from "@tabler/icons-react";
 | 
					import { IconArrowLeft, IconBarcode, IconPlayerPause, IconPlus } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    boxId: number;
 | 
					    boxId: number;
 | 
				
			||||||
@@ -14,8 +14,12 @@ const ReceiptBoxEditor = ({ boxId }: Props) => {
 | 
				
			|||||||
        fetchBox,
 | 
					        fetchBox,
 | 
				
			||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        onCreateProductClick,
 | 
					        onCreateProductClick,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
    } = useReceiptBox({ boxId });
 | 
					    } = useReceiptBox({ boxId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { isScanning } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!box) return <Text>Короб c ID K{boxId} не найден</Text>;
 | 
					    if (!box) return <Text>Короб c ID K{boxId} не найден</Text>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const backButton = (
 | 
					    const backButton = (
 | 
				
			||||||
@@ -23,20 +27,39 @@ const ReceiptBoxEditor = ({ boxId }: Props) => {
 | 
				
			|||||||
            variant={"default"}
 | 
					            variant={"default"}
 | 
				
			||||||
            onClick={() => window.location.reload()}
 | 
					            onClick={() => window.location.reload()}
 | 
				
			||||||
            flex={1}
 | 
					            flex={1}
 | 
				
			||||||
 | 
					            disabled={isScanning}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <IconArrowLeft />
 | 
					            <IconArrowLeft />
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getScanningModeAction = () => {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <ActionIcon
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                onClick={() => toggleScanning(box?.id)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                {isScanning ? (
 | 
				
			||||||
 | 
					                    <IconPlayerPause />
 | 
				
			||||||
 | 
					                ) : (
 | 
				
			||||||
 | 
					                    <IconBarcode />
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </ActionIcon>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Stack>
 | 
					        <Stack>
 | 
				
			||||||
            <Flex align={"center"} flex={9} gap={"md"}>
 | 
					            <Flex align={"center"} justify={"space-between"}>
 | 
				
			||||||
                {backButton}
 | 
					                <Flex align={"center"} flex={9} gap={"md"}>
 | 
				
			||||||
                <Title flex={8} order={3}>
 | 
					                    {backButton}
 | 
				
			||||||
                    Короб ID: К{box?.id}
 | 
					                    <Title flex={8} order={3}>
 | 
				
			||||||
                </Title>
 | 
					                        Короб ID: К{box?.id}
 | 
				
			||||||
 | 
					                    </Title>
 | 
				
			||||||
 | 
					                </Flex>
 | 
				
			||||||
 | 
					                {getScanningModeAction()}
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            <InlineButton onClick={onCreateProductClick}>
 | 
					            <InlineButton onClick={onCreateProductClick} disabled={isScanning}>
 | 
				
			||||||
                <IconPlus />
 | 
					                <IconPlus />
 | 
				
			||||||
                Товар
 | 
					                Товар
 | 
				
			||||||
            </InlineButton>
 | 
					            </InlineButton>
 | 
				
			||||||
@@ -44,6 +67,7 @@ const ReceiptBoxEditor = ({ boxId }: Props) => {
 | 
				
			|||||||
                object={box}
 | 
					                object={box}
 | 
				
			||||||
                clientId={clientId}
 | 
					                clientId={clientId}
 | 
				
			||||||
                updateObject={fetchBox}
 | 
					                updateObject={fetchBox}
 | 
				
			||||||
 | 
					                disabled={isScanning}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </Stack>
 | 
					        </Stack>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
import useReceiptPallet from "../hooks/useReceiptPallet.tsx";
 | 
					import useReceiptPallet from "../hooks/useReceiptPallet.tsx";
 | 
				
			||||||
import { Button, Flex, Group, Stack, Text, Title } from "@mantine/core";
 | 
					import { ActionIcon, Button, Flex, Group, Stack, Text, Title } from "@mantine/core";
 | 
				
			||||||
import { IconArrowLeft, IconPlus } from "@tabler/icons-react";
 | 
					import { IconArrowLeft, IconBarcode, IconPlayerPause, IconPlus } from "@tabler/icons-react";
 | 
				
			||||||
import InlineButton from "../../../../../components/InlineButton/InlineButton.tsx";
 | 
					import InlineButton from "../../../../../components/InlineButton/InlineButton.tsx";
 | 
				
			||||||
import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
					import ReceiptProducts from "./ReceiptProducts.tsx";
 | 
				
			||||||
import AccordionBoxesOnPallet from "./AccordionBoxes.tsx";
 | 
					import AccordionBoxesOnPallet from "./AccordionBoxesOnPallet.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    palletId: number;
 | 
					    palletId: number;
 | 
				
			||||||
@@ -16,9 +16,19 @@ const ReceiptPalletEditor = ({ palletId }: Props) => {
 | 
				
			|||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        onCreateProductClick,
 | 
					        onCreateProductClick,
 | 
				
			||||||
        onCreateBoxClick,
 | 
					        onCreateBoxClick,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
    } = useReceiptPallet({ palletId });
 | 
					    } = useReceiptPallet({ palletId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!pallet) return <Text>Паллет c ID P{palletId} не найден</Text>;
 | 
					    if (!pallet) {
 | 
				
			||||||
 | 
					        return <Text>Паллет c ID P{palletId} не найден</Text>;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        isScanning,
 | 
				
			||||||
 | 
					        boxId,
 | 
				
			||||||
 | 
					    } = scanningData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const createButtons = () => {
 | 
					    const createButtons = () => {
 | 
				
			||||||
        const isBoxes = pallet.boxes.length > 0;
 | 
					        const isBoxes = pallet.boxes.length > 0;
 | 
				
			||||||
@@ -27,13 +37,13 @@ const ReceiptPalletEditor = ({ palletId }: Props) => {
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <Group>
 | 
					            <Group>
 | 
				
			||||||
                {(isBoxes || isBoth) && (
 | 
					                {(isBoxes || isBoth) && (
 | 
				
			||||||
                    <InlineButton onClick={onCreateBoxClick} flex={1}>
 | 
					                    <InlineButton onClick={onCreateBoxClick} flex={1} disabled={isScanning}>
 | 
				
			||||||
                        <IconPlus />
 | 
					                        <IconPlus />
 | 
				
			||||||
                        Короб
 | 
					                        Короб
 | 
				
			||||||
                    </InlineButton>
 | 
					                    </InlineButton>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
                {(isProducts || isBoth) && (
 | 
					                {(isProducts || isBoth) && (
 | 
				
			||||||
                    <InlineButton onClick={onCreateProductClick} flex={1}>
 | 
					                    <InlineButton onClick={onCreateProductClick} flex={1} disabled={isScanning}>
 | 
				
			||||||
                        <IconPlus />
 | 
					                        <IconPlus />
 | 
				
			||||||
                        Товар
 | 
					                        Товар
 | 
				
			||||||
                    </InlineButton>
 | 
					                    </InlineButton>
 | 
				
			||||||
@@ -50,6 +60,9 @@ const ReceiptPalletEditor = ({ palletId }: Props) => {
 | 
				
			|||||||
                    pallet={pallet}
 | 
					                    pallet={pallet}
 | 
				
			||||||
                    clientId={clientId}
 | 
					                    clientId={clientId}
 | 
				
			||||||
                    fetchPallet={fetchPallet}
 | 
					                    fetchPallet={fetchPallet}
 | 
				
			||||||
 | 
					                    barcodesProductsMap={barcodesProductsMap}
 | 
				
			||||||
 | 
					                    scanningData={scanningData}
 | 
				
			||||||
 | 
					                    toggleScanning={toggleScanning}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -59,6 +72,7 @@ const ReceiptPalletEditor = ({ palletId }: Props) => {
 | 
				
			|||||||
                    clientId={clientId}
 | 
					                    clientId={clientId}
 | 
				
			||||||
                    object={pallet}
 | 
					                    object={pallet}
 | 
				
			||||||
                    updateObject={fetchPallet}
 | 
					                    updateObject={fetchPallet}
 | 
				
			||||||
 | 
					                    disabled={isScanning}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -74,14 +88,34 @@ const ReceiptPalletEditor = ({ palletId }: Props) => {
 | 
				
			|||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getScanningModeAction = () => {
 | 
				
			||||||
 | 
					        if (pallet.boxes.length !== 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <ActionIcon
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                onClick={() => toggleScanning(undefined, pallet?.id)}
 | 
				
			||||||
 | 
					                disabled={isScanning && !!boxId}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                {isScanning && !boxId ? (
 | 
				
			||||||
 | 
					                    <IconPlayerPause />
 | 
				
			||||||
 | 
					                ) : (
 | 
				
			||||||
 | 
					                    <IconBarcode />
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </ActionIcon>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Stack>
 | 
					        <Stack>
 | 
				
			||||||
            <Flex align={"center"} flex={9} gap={"md"}>
 | 
					            <Flex align={"center"} justify={"space-between"}>
 | 
				
			||||||
                {backButton}
 | 
					                <Flex align={"center"} flex={9} gap={"md"}>
 | 
				
			||||||
                <Title flex={8} order={3}>
 | 
					                    {backButton}
 | 
				
			||||||
                    Паллет ID: П{pallet?.id}
 | 
					                    <Title flex={8} order={3}>
 | 
				
			||||||
                </Title>
 | 
					                        Паллет ID: П{pallet?.id}
 | 
				
			||||||
 | 
					                    </Title>
 | 
				
			||||||
 | 
					                </Flex>
 | 
				
			||||||
 | 
					                {getScanningModeAction()}
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            {createButtons()}
 | 
					            {createButtons()}
 | 
				
			||||||
            {renderPalletData()}
 | 
					            {renderPalletData()}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,10 @@ type Props = {
 | 
				
			|||||||
    clientId: number | null;
 | 
					    clientId: number | null;
 | 
				
			||||||
    object: ResidualBoxSchema | ResidualPalletSchema | null;
 | 
					    object: ResidualBoxSchema | ResidualPalletSchema | null;
 | 
				
			||||||
    updateObject: () => void;
 | 
					    updateObject: () => void;
 | 
				
			||||||
 | 
					    disabled: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ReceiptProducts = ({ clientId, object, updateObject }: Props) => {
 | 
					const ReceiptProducts = ({ clientId, object, updateObject, disabled }: Props) => {
 | 
				
			||||||
    if (!object || !clientId) return;
 | 
					    if (!object || !clientId) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deleteProduct = (residualProductId: number) => {
 | 
					    const deleteProduct = (residualProductId: number) => {
 | 
				
			||||||
@@ -37,11 +38,13 @@ const ReceiptProducts = ({ clientId, object, updateObject }: Props) => {
 | 
				
			|||||||
                            <ProductQuantityField
 | 
					                            <ProductQuantityField
 | 
				
			||||||
                                residualProduct={residualProduct}
 | 
					                                residualProduct={residualProduct}
 | 
				
			||||||
                                updateObject={updateObject}
 | 
					                                updateObject={updateObject}
 | 
				
			||||||
 | 
					                                disabled={disabled}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        </Stack>
 | 
					                        </Stack>
 | 
				
			||||||
                        <ActionIcon
 | 
					                        <ActionIcon
 | 
				
			||||||
                            variant={"default"}
 | 
					                            variant={"default"}
 | 
				
			||||||
                            onClick={() => deleteProduct(residualProduct.id)}
 | 
					                            onClick={() => deleteProduct(residualProduct.id)}
 | 
				
			||||||
 | 
					                            disabled={disabled}
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
                            <IconTrash />
 | 
					                            <IconTrash />
 | 
				
			||||||
                        </ActionIcon>
 | 
					                        </ActionIcon>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { ProductSchema, ResidualBoxSchema, ResidualPalletSchema, ResiduesService } from "../../../../../client";
 | 
				
			||||||
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
 | 
					import findProductInObject from "../../NewReceipt/utils/findProductInObject.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    object: ResidualBoxSchema | ResidualPalletSchema | null;
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
 | 
					    refetch: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useApplyingScannedResult = ({
 | 
				
			||||||
 | 
					                                      object,
 | 
				
			||||||
 | 
					                                      barcodesProductsMap,
 | 
				
			||||||
 | 
					                                      refetch,
 | 
				
			||||||
 | 
					                                  }: Props) => {
 | 
				
			||||||
 | 
					    let boxIdValue: number | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onProductSelect = (product: ProductSchema) => {
 | 
				
			||||||
 | 
					        if (!object) return;
 | 
				
			||||||
 | 
					        ResiduesService.createResidualProduct({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                data: {
 | 
				
			||||||
 | 
					                    productId: product.id,
 | 
				
			||||||
 | 
					                    quantity: 1,
 | 
				
			||||||
 | 
					                    palletId: boxIdValue ? null : object.id,
 | 
				
			||||||
 | 
					                    boxId: boxIdValue,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                notifications.guess(ok, { message });
 | 
				
			||||||
 | 
					                refetch();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const showSelectProductModal = (productsToSelect: ProductSchema[]) => {
 | 
				
			||||||
 | 
					        modals.openContextModal({
 | 
				
			||||||
 | 
					            modal: "selectScannedProductModal",
 | 
				
			||||||
 | 
					            title: "Выберите товар для данного штрихкода",
 | 
				
			||||||
 | 
					            withCloseButton: false,
 | 
				
			||||||
 | 
					            innerProps: {
 | 
				
			||||||
 | 
					                productsToSelect,
 | 
				
			||||||
 | 
					                onProductSelect,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onScanningFinish = (
 | 
				
			||||||
 | 
					        value: string,
 | 
				
			||||||
 | 
					        boxId?: number,
 | 
				
			||||||
 | 
					        palletId?: number,
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					        if (!object) return;
 | 
				
			||||||
 | 
					        boxIdValue = boxId ?? null;
 | 
				
			||||||
 | 
					        let objectValue: ResidualPalletSchema | ResidualBoxSchema = object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (palletId && boxId) {
 | 
				
			||||||
 | 
					            const pallet = object as ResidualPalletSchema;
 | 
				
			||||||
 | 
					            const boxOnPallet = pallet.boxes.find(box => box.id === boxId);
 | 
				
			||||||
 | 
					            if (!boxOnPallet) return;
 | 
				
			||||||
 | 
					            objectValue = boxOnPallet;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const productsToSelect = barcodesProductsMap.get(value) ?? [];
 | 
				
			||||||
 | 
					        if (productsToSelect?.length === 0) {
 | 
				
			||||||
 | 
					            notifications.error({ message: `Товара с штрихкодом ${value} не найдено` });
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (productsToSelect?.length === 1) {
 | 
				
			||||||
 | 
					            onProductSelect(productsToSelect[0]);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const product = findProductInObject(objectValue, productsToSelect);
 | 
				
			||||||
 | 
					        if (product) {
 | 
				
			||||||
 | 
					            onProductSelect(product);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return showSelectProductModal(productsToSelect);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { onScanningFinish };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useApplyingScannedResult;
 | 
				
			||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
import { useEffect, useState } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import { ResidualBoxSchema, ResiduesService } from "../../../../../client";
 | 
					import { ResidualBoxSchema, ResiduesService } from "../../../../../client";
 | 
				
			||||||
import { modals } from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
 | 
					import useBarcodesProductsMap from "../../../hooks/useBarcodesProductsMap.tsx";
 | 
				
			||||||
 | 
					import useScanningMode from "../../../hooks/useScanningMode.tsx";
 | 
				
			||||||
 | 
					import useApplyingScannedResult from "./useApplyingScannedResult.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
@@ -27,6 +30,16 @@ const useReceiptBox = ({ boxId }: Props) => {
 | 
				
			|||||||
            .catch(err => console.log(err));
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { barcodesProductsMap } = useBarcodesProductsMap({ clientId: clientId ?? -1 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { onScanningFinish } = useApplyingScannedResult({
 | 
				
			||||||
 | 
					        object: box,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					        refetch: fetchBox,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { scanningData, toggleScanning } = useScanningMode({ onScanningFinish });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onCreateProductClick = () => {
 | 
					    const onCreateProductClick = () => {
 | 
				
			||||||
        if (!(box && clientId)) return;
 | 
					        if (!(box && clientId)) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
@@ -38,6 +51,7 @@ const useReceiptBox = ({ boxId }: Props) => {
 | 
				
			|||||||
                isBox: true,
 | 
					                isBox: true,
 | 
				
			||||||
                object: box,
 | 
					                object: box,
 | 
				
			||||||
                fetchObject: fetchBox,
 | 
					                fetchObject: fetchBox,
 | 
				
			||||||
 | 
					                barcodesProductsMap,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -47,6 +61,8 @@ const useReceiptBox = ({ boxId }: Props) => {
 | 
				
			|||||||
        fetchBox,
 | 
					        fetchBox,
 | 
				
			||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        onCreateProductClick,
 | 
					        onCreateProductClick,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,9 @@ import { useEffect, useState } from "react";
 | 
				
			|||||||
import { ResidualPalletSchema, ResiduesService } from "../../../../../client";
 | 
					import { ResidualPalletSchema, ResiduesService } from "../../../../../client";
 | 
				
			||||||
import { modals } from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import useBarcodesProductsMap from "../../../hooks/useBarcodesProductsMap.tsx";
 | 
				
			||||||
 | 
					import useApplyingScannedResult from "./useApplyingScannedResult.tsx";
 | 
				
			||||||
 | 
					import useScanningMode from "../../../hooks/useScanningMode.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    palletId: number;
 | 
					    palletId: number;
 | 
				
			||||||
@@ -27,6 +30,16 @@ const useReceiptPallet = ({ palletId }: Props) => {
 | 
				
			|||||||
            .catch(err => console.log(err));
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { barcodesProductsMap } = useBarcodesProductsMap({ clientId: clientId ?? -1 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { onScanningFinish } = useApplyingScannedResult({
 | 
				
			||||||
 | 
					        object: pallet,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					        refetch: fetchPallet,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { scanningData, toggleScanning } = useScanningMode({ onScanningFinish });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onCreateProductClick = () => {
 | 
					    const onCreateProductClick = () => {
 | 
				
			||||||
        if (!(pallet && clientId)) return;
 | 
					        if (!(pallet && clientId)) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
@@ -38,6 +51,7 @@ const useReceiptPallet = ({ palletId }: Props) => {
 | 
				
			|||||||
                isBox: false,
 | 
					                isBox: false,
 | 
				
			||||||
                object: pallet,
 | 
					                object: pallet,
 | 
				
			||||||
                fetchObject: fetchPallet,
 | 
					                fetchObject: fetchPallet,
 | 
				
			||||||
 | 
					                barcodesProductsMap,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -64,6 +78,9 @@ const useReceiptPallet = ({ palletId }: Props) => {
 | 
				
			|||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        onCreateProductClick,
 | 
					        onCreateProductClick,
 | 
				
			||||||
        onCreateBoxClick,
 | 
					        onCreateBoxClick,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ type Props = {
 | 
				
			|||||||
    object: ResidualBoxSchema | ResidualPalletSchema;
 | 
					    object: ResidualBoxSchema | ResidualPalletSchema;
 | 
				
			||||||
    fetchObject: () => void;
 | 
					    fetchObject: () => void;
 | 
				
			||||||
    isBox: boolean;
 | 
					    isBox: boolean;
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ReceiptModal = ({
 | 
					const ReceiptModal = ({
 | 
				
			||||||
@@ -25,7 +26,9 @@ const ReceiptModal = ({
 | 
				
			|||||||
        clientId,
 | 
					        clientId,
 | 
				
			||||||
        object,
 | 
					        object,
 | 
				
			||||||
        fetchObject,
 | 
					        fetchObject,
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
    } = innerProps;
 | 
					    } = innerProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { isScanning, setIsScanning, scannedValue, setScannedValue } = useScanning();
 | 
					    const { isScanning, setIsScanning, scannedValue, setScannedValue } = useScanning();
 | 
				
			||||||
    const initialValues = {
 | 
					    const initialValues = {
 | 
				
			||||||
        product: null,
 | 
					        product: null,
 | 
				
			||||||
@@ -115,12 +118,12 @@ const ReceiptModal = ({
 | 
				
			|||||||
            </form>
 | 
					            </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <ScanBarcode
 | 
					            <ScanBarcode
 | 
				
			||||||
                clientId={clientId}
 | 
					 | 
				
			||||||
                isScanning={isScanning}
 | 
					                isScanning={isScanning}
 | 
				
			||||||
                setIsScanning={setIsScanning}
 | 
					                setIsScanning={setIsScanning}
 | 
				
			||||||
                onProductSelect={onProductAfterScanningSelect}
 | 
					                onProductSelect={onProductAfterScanningSelect}
 | 
				
			||||||
                scannedValue={scannedValue}
 | 
					                scannedValue={scannedValue}
 | 
				
			||||||
                object={object}
 | 
					                object={object}
 | 
				
			||||||
 | 
					                barcodesProductsMap={barcodesProductsMap}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,28 @@
 | 
				
			|||||||
import { ProductSchema, ResidualBoxSchema, ResidualPalletSchema } from "../../../../client";
 | 
					import { ProductSchema, ResidualBoxSchema, ResidualPalletSchema } from "../../../../client";
 | 
				
			||||||
import { useEffect, useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import useProductsList from "../../../ProductsPage/hooks/useProductsList.tsx";
 | 
					 | 
				
			||||||
import { Button, Group, Radio, Stack, Text } from "@mantine/core";
 | 
					import { Button, Group, Radio, Stack, Text } from "@mantine/core";
 | 
				
			||||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
				
			||||||
import { ReceiptBox, ReceiptPallet } from "../NewReceipt/types/types.tsx";
 | 
					import { ReceiptBox, ReceiptPallet } from "../NewReceipt/types/types.tsx";
 | 
				
			||||||
 | 
					import findProductInObject from "../NewReceipt/utils/findProductInObject.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onProductSelect: (product?: ProductSchema) => void;
 | 
					    onProductSelect: (product?: ProductSchema) => void;
 | 
				
			||||||
    clientId: number;
 | 
					 | 
				
			||||||
    isScanning: boolean;
 | 
					    isScanning: boolean;
 | 
				
			||||||
    setIsScanning: (isScanning: boolean) => void;
 | 
					    setIsScanning: (isScanning: boolean) => void;
 | 
				
			||||||
    scannedValue: string;
 | 
					    scannedValue: string;
 | 
				
			||||||
    object: ReceiptBox | ReceiptPallet | ResidualPalletSchema | ResidualBoxSchema;
 | 
					    object: ReceiptBox | ReceiptPallet | ResidualPalletSchema | ResidualBoxSchema;
 | 
				
			||||||
 | 
					    barcodesProductsMap: Map<string, ProductSchema[]>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ScanBarcode = ({
 | 
					const ScanBarcode = ({
 | 
				
			||||||
                         onProductSelect,
 | 
					                         onProductSelect,
 | 
				
			||||||
                         clientId,
 | 
					 | 
				
			||||||
                         isScanning,
 | 
					                         isScanning,
 | 
				
			||||||
                         setIsScanning,
 | 
					                         setIsScanning,
 | 
				
			||||||
                         scannedValue,
 | 
					                         scannedValue,
 | 
				
			||||||
                         object,
 | 
					                         object,
 | 
				
			||||||
 | 
					                         barcodesProductsMap,
 | 
				
			||||||
                     }: Props) => {
 | 
					                     }: Props) => {
 | 
				
			||||||
    const productsData = useProductsList({ clientId, searchInput: "" });
 | 
					
 | 
				
			||||||
    const [barcodesProducts, setBarcodesProducts] = useState(new Map<string, ProductSchema[]>());
 | 
					 | 
				
			||||||
    let productsToSelect: ProductSchema[] = [];
 | 
					    let productsToSelect: ProductSchema[] = [];
 | 
				
			||||||
    const [selectedProduct, setSelectedProduct] = useState<ProductSchema>();
 | 
					    const [selectedProduct, setSelectedProduct] = useState<ProductSchema>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,38 +30,8 @@ const ScanBarcode = ({
 | 
				
			|||||||
        setIsScanning(!isScanning);
 | 
					        setIsScanning(!isScanning);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const productsToBarcodesProducts = () => {
 | 
					 | 
				
			||||||
        const data = new Map<string, ProductSchema[]>();
 | 
					 | 
				
			||||||
        productsData.products.forEach(product => {
 | 
					 | 
				
			||||||
            product.barcodes.forEach(barcode => {
 | 
					 | 
				
			||||||
                if (data.has(barcode)) {
 | 
					 | 
				
			||||||
                    data.set(barcode, [...data.get(barcode)!, product]);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    data.set(barcode, [product]);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        setBarcodesProducts(data);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
        if (productsData.products.length !== 0) {
 | 
					 | 
				
			||||||
            productsToBarcodesProducts();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }, [productsData.isLoading]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const findProductInObject = (productsToSelect: ProductSchema[]): ProductSchema | undefined => {
 | 
					 | 
				
			||||||
        for (let i = 0; i < productsToSelect.length; i++) {
 | 
					 | 
				
			||||||
            for (let j = 0; j < object.residualProducts.length; j++) {
 | 
					 | 
				
			||||||
                if (productsToSelect[i].id === object.residualProducts[j].product.id) {
 | 
					 | 
				
			||||||
                    return productsToSelect[i];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const renderScanningResults = () => {
 | 
					    const renderScanningResults = () => {
 | 
				
			||||||
        productsToSelect = barcodesProducts.get(scannedValue) ?? [];
 | 
					        productsToSelect = barcodesProductsMap.get(scannedValue) ?? [];
 | 
				
			||||||
        if (productsToSelect?.length === 0) {
 | 
					        if (productsToSelect?.length === 0) {
 | 
				
			||||||
            notifications.error({ message: `Товара с штрихкодом ${scannedValue} не найдено` });
 | 
					            notifications.error({ message: `Товара с штрихкодом ${scannedValue} не найдено` });
 | 
				
			||||||
            onProductSelect();
 | 
					            onProductSelect();
 | 
				
			||||||
@@ -72,7 +41,7 @@ const ScanBarcode = ({
 | 
				
			|||||||
            onProductSelect(productsToSelect[0]);
 | 
					            onProductSelect(productsToSelect[0]);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const product = findProductInObject(productsToSelect);
 | 
					        const product = findProductInObject(object, productsToSelect);
 | 
				
			||||||
        if (product) {
 | 
					        if (product) {
 | 
				
			||||||
            onProductSelect(product);
 | 
					            onProductSelect(product);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										42
									
								
								src/pages/ReceiptPage/hooks/useBarcodesProductsMap.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/pages/ReceiptPage/hooks/useBarcodesProductsMap.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import useProductsList from "../../ProductsPage/hooks/useProductsList.tsx";
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { ProductSchema } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    clientId?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useBarcodesProductsMap = ({ clientId }: Props) => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        products,
 | 
				
			||||||
 | 
					        isLoading: isProductsLoading,
 | 
				
			||||||
 | 
					    } = useProductsList({ clientId: clientId ?? 0, searchInput: "" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [barcodesProductsMap, setBarcodesProductsMap] = useState(new Map<string, ProductSchema[]>());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const productsToBarcodesProducts = () => {
 | 
				
			||||||
 | 
					        const data = new Map<string, ProductSchema[]>();
 | 
				
			||||||
 | 
					        products.forEach(product => {
 | 
				
			||||||
 | 
					            product.barcodes.forEach(barcode => {
 | 
				
			||||||
 | 
					                if (data.has(barcode)) {
 | 
				
			||||||
 | 
					                    data.set(barcode, [...data.get(barcode)!, product]);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    data.set(barcode, [product]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        setBarcodesProductsMap(data);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (products.length !== 0) {
 | 
				
			||||||
 | 
					            productsToBarcodesProducts();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }, [isProductsLoading]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        barcodesProductsMap,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useBarcodesProductsMap;
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/pages/ReceiptPage/hooks/useScanningMode.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/pages/ReceiptPage/hooks/useScanningMode.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					import { useWindowEvent } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ScanningData = {
 | 
				
			||||||
 | 
					    boxId?: number;
 | 
				
			||||||
 | 
					    palletId?: number;
 | 
				
			||||||
 | 
					    isScanning: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    onScanningFinish: (value: string, boxId?: number, palletId?: number) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useScanningMode = ({ onScanningFinish }: Props) => {
 | 
				
			||||||
 | 
					    const [scanningValue, setScanningValue] = useState<string>("");
 | 
				
			||||||
 | 
					    const [boxId, setBoxId] = useState<number>();
 | 
				
			||||||
 | 
					    const [palletId, setPalletId] = useState<number>();
 | 
				
			||||||
 | 
					    const [isScanning, setIsScanning] = useState<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const setIsScanningValue = (isScanning: boolean) => {
 | 
				
			||||||
 | 
					        if (!isScanning) {
 | 
				
			||||||
 | 
					            setScanningValue("");
 | 
				
			||||||
 | 
					            setBoxId(undefined);
 | 
				
			||||||
 | 
					            setPalletId(undefined);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setIsScanning(isScanning);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useWindowEvent("keydown", (event) => {
 | 
				
			||||||
 | 
					        if (!isScanning) return;
 | 
				
			||||||
 | 
					        event.preventDefault();
 | 
				
			||||||
 | 
					        setScanningValue(prevState => prevState + event.key);
 | 
				
			||||||
 | 
					        if (["\n", "\r", "Enter"].includes(event.key)) {
 | 
				
			||||||
 | 
					            onScanningFinish(scanningValue, boxId, palletId);
 | 
				
			||||||
 | 
					            setScanningValue("");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const toggleScanning = (boxId?: number, palletId?: number) => {
 | 
				
			||||||
 | 
					        if (isScanning) {
 | 
				
			||||||
 | 
					            setBoxId(undefined);
 | 
				
			||||||
 | 
					            setPalletId(undefined);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            setBoxId(boxId);
 | 
				
			||||||
 | 
					            setPalletId(palletId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setIsScanningValue(!isScanning);
 | 
				
			||||||
 | 
					        setScanningValue("");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const scanningData: ScanningData = {
 | 
				
			||||||
 | 
					        boxId,
 | 
				
			||||||
 | 
					        palletId,
 | 
				
			||||||
 | 
					        isScanning,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        scanningData,
 | 
				
			||||||
 | 
					        toggleScanning,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useScanningMode;
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/pages/ReceiptPage/modals/SelectScannedProductModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/pages/ReceiptPage/modals/SelectScannedProductModal.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
 | 
					import { Button, Group, Radio, Stack, Text } from "@mantine/core";
 | 
				
			||||||
 | 
					import { ProductSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { notifications } from "../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    productsToSelect: ProductSchema[];
 | 
				
			||||||
 | 
					    onProductSelect: (product: ProductSchema) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectScannedProductModal = ({
 | 
				
			||||||
 | 
					                                context,
 | 
				
			||||||
 | 
					                                id,
 | 
				
			||||||
 | 
					                                innerProps,
 | 
				
			||||||
 | 
					                            }: ContextModalProps<Props>) => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        productsToSelect,
 | 
				
			||||||
 | 
					        onProductSelect,
 | 
				
			||||||
 | 
					    } = innerProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [selectedProduct, setSelectedProduct] = useState<ProductSchema>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const renderProductsToSelect = () => {
 | 
				
			||||||
 | 
					        return productsToSelect.map(product => (
 | 
				
			||||||
 | 
					            <Group key={product.id} wrap={"nowrap"}>
 | 
				
			||||||
 | 
					                <Radio
 | 
				
			||||||
 | 
					                    checked={selectedProduct?.id === product.id}
 | 
				
			||||||
 | 
					                    onChange={() => setSelectedProduct(product)}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Stack>
 | 
				
			||||||
 | 
					                    <Text>{product.name}</Text>
 | 
				
			||||||
 | 
					                    {product.size && <Text>{product.size}</Text>}
 | 
				
			||||||
 | 
					                </Stack>
 | 
				
			||||||
 | 
					            </Group>
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Stack>
 | 
				
			||||||
 | 
					            {renderProductsToSelect()}
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                onClick={() => {
 | 
				
			||||||
 | 
					                    if (!selectedProduct) {
 | 
				
			||||||
 | 
					                        notifications.error({ message: "Товар не выбран" });
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        onProductSelect(selectedProduct);
 | 
				
			||||||
 | 
					                        setSelectedProduct(undefined);
 | 
				
			||||||
 | 
					                        context.closeContextModal(id);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                Выбрать
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					        </Stack>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SelectScannedProductModal;
 | 
				
			||||||
		Reference in New Issue
	
	Block a user