feat: a few shipping products in box
This commit is contained in:
18
src/pages/CardsPage/tabs/ShippingTab/ShippingTab.module.css
Normal file
18
src/pages/CardsPage/tabs/ShippingTab/ShippingTab.module.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.icon {
|
||||
width: rem(13px);
|
||||
height: auto;
|
||||
vertical-align: rem(-1px);
|
||||
margin-right: rem(8px);
|
||||
}
|
||||
|
||||
.expandIcon {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.expandIconRotated {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.productsTableBorder {
|
||||
border-bottom: solid 1px var(--mantine-color-dark-5);
|
||||
}
|
||||
@@ -1,27 +1,31 @@
|
||||
import useShippingTableColumns from "../hooks/shippingTableColumns.tsx";
|
||||
import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { BoxSchema, ShippingService } from "../../../../../client";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { IconBox, IconChevronRight, IconPlus, IconTrash } from "@tabler/icons-react";
|
||||
import useUpdateCard from "../hooks/useUpdateCard.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import clsx from "clsx";
|
||||
import classes from "../ShippingTab.module.css";
|
||||
import "mantine-datatable/styles.css";
|
||||
import { useState } from "react";
|
||||
import ShippingProductsTable from "./ShippingProductsTable.tsx";
|
||||
import { Group, rem, Text } from "@mantine/core";
|
||||
import { ShippingProductParentData } from "../types/ShippingProductData.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import InlineShippingButton from "./InlineShippingButton.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
items: BoxSchema[];
|
||||
onCreateShippingProduct: (values: ShippingProductParentData) => void;
|
||||
}
|
||||
|
||||
const BoxesTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<BoxSchema>({ isBox: true });
|
||||
const BoxesTable = ({ items, onCreateShippingProduct }: Props) => {
|
||||
const { update } = useUpdateCard();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
const [boxIds, setBoxIds] = useState<number[]>([]);
|
||||
|
||||
const onDeleteClick = (box: BoxSchema) => {
|
||||
const onDeleteBox = (boxId: number) => {
|
||||
ShippingService.deleteBox({
|
||||
boxId: box.id,
|
||||
boxId,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
@@ -30,55 +34,92 @@ const BoxesTable = ({ items }: Props) => {
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onEditClick = (box: BoxSchema) => {
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование короба",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: true,
|
||||
shippingData: {
|
||||
boxId: box.id,
|
||||
productId: box.product?.id,
|
||||
quantity: box.quantity,
|
||||
},
|
||||
},
|
||||
const onDeleteBoxClick = (box: BoxSchema) => {
|
||||
if (box.shippingProducts.length === 0) {
|
||||
onDeleteBox(box.id);
|
||||
return;
|
||||
}
|
||||
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление короба",
|
||||
children: <Text size="sm">Вы уверены что хотите удалить короб?</Text>,
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => onDeleteBox(box.id),
|
||||
});
|
||||
};
|
||||
|
||||
const getBoxContent = (box: BoxSchema) => {
|
||||
if (box.shippingProducts.length === 0) {
|
||||
return (
|
||||
<Text>Пустой</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ShippingProductsTable items={box.shippingProducts} />
|
||||
);
|
||||
};
|
||||
|
||||
const getBoxActions = (box: BoxSchema) => {
|
||||
return (
|
||||
<Group wrap={"nowrap"} gap={rem(10)}>
|
||||
<InlineShippingButton onClick={() => onCreateShippingProduct({ boxId: box.id })}>
|
||||
<IconPlus />
|
||||
Товар
|
||||
</InlineShippingButton>
|
||||
<InlineShippingButton onClick={() => onDeleteBoxClick(box)}>
|
||||
<IconTrash />
|
||||
</InlineShippingButton>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const isBoxExpandable = (box: BoxSchema) => {
|
||||
const isExpandable = box.shippingProducts.length > 0;
|
||||
if (!isExpandable && boxIds.includes(box.id)) {
|
||||
setBoxIds(boxIds.filter(boxId => boxId !== box.id));
|
||||
}
|
||||
return isExpandable;
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseTable
|
||||
data={items}
|
||||
columns={columns}
|
||||
restProps={
|
||||
<DataTable
|
||||
noHeader
|
||||
withTableBorder
|
||||
columns={[
|
||||
{
|
||||
enableSorting: false,
|
||||
enableColumnActions: false,
|
||||
enableRowActions: true,
|
||||
positionActionsColumn: "last",
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => onDeleteClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
accessor: "id",
|
||||
title: "Короб",
|
||||
noWrap: true,
|
||||
render: (box) => (
|
||||
<>
|
||||
{isBoxExpandable(box) && (
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: boxIds?.includes(box.id),
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<IconBox className={classes.icon} />
|
||||
<span>Короб К{box.id}</span>
|
||||
</>
|
||||
),
|
||||
} as MRT_TableOptions<BoxSchema>
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: getBoxActions,
|
||||
},
|
||||
]}
|
||||
records={items?.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: boxIds, onRecordIdsChange: setBoxIds },
|
||||
expandable: ({ record }) => isBoxExpandable(record),
|
||||
content: ({ record }) => getBoxContent(record),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Button, ButtonProps, Group } from "@mantine/core";
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
children?: ReactNode;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const InlineShippingButton = ({ children, onClick, ...props }: Props) => {
|
||||
return (
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
onClick && onClick();
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<Group gap="sm" wrap={"nowrap"}>
|
||||
{children}
|
||||
</Group>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineShippingButton;
|
||||
@@ -1,13 +1,10 @@
|
||||
import useShippingTableColumns from "../hooks/shippingTableColumns.tsx";
|
||||
import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { ShippingProductSchema, ShippingService } from "../../../../../client";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import useUpdateCard from "../hooks/useUpdateCard.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { ShippingProductSchema } from "../../../../../client";
|
||||
import { Box } from "@mantine/core";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import clsx from "clsx";
|
||||
import "mantine-datatable/styles.css";
|
||||
import classes from "../ShippingTab.module.css";
|
||||
|
||||
|
||||
type Props = {
|
||||
@@ -15,72 +12,18 @@ type Props = {
|
||||
}
|
||||
|
||||
const ShippingProductsTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<ShippingProductSchema>({ isBox: false });
|
||||
const { update } = useUpdateCard();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const onDeleteClick = (shippingProduct: ShippingProductSchema) => {
|
||||
ShippingService.deleteShippingProduct({
|
||||
shippingProductId: shippingProduct.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
update();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onEditClick = (shippingProduct: ShippingProductSchema) => {
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование товара на паллете",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: false,
|
||||
shippingData: {
|
||||
shippingProductId: shippingProduct.id,
|
||||
productId: shippingProduct.product.id,
|
||||
quantity: shippingProduct.quantity,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
const columns = useShippingTableColumns();
|
||||
|
||||
return (
|
||||
<BaseTable
|
||||
data={items}
|
||||
columns={columns}
|
||||
restProps={
|
||||
{
|
||||
enableSorting: false,
|
||||
enableColumnActions: false,
|
||||
enableRowActions: true,
|
||||
enableRowNumbers: true,
|
||||
positionActionsColumn: "last",
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => onDeleteClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<ShippingProductSchema>
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<DataTable
|
||||
withTableBorder
|
||||
withColumnBorders
|
||||
columns={columns}
|
||||
records={items}
|
||||
className={clsx(classes.productsTableBorder)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { Accordion, ActionIcon, Button, Center, Group, rem, Stack, Title, Tooltip } from "@mantine/core";
|
||||
import { Flex, rem, Stack } from "@mantine/core";
|
||||
import { BoxSchema, PalletSchema, ShippingProductSchema } from "../../../../../client";
|
||||
import ShippingProductsTable from "./ShippingProductsTable.tsx";
|
||||
import BoxesTable from "./BoxesTable.tsx";
|
||||
import { IconBox, IconPlus, IconSpace, IconTrash } from "@tabler/icons-react";
|
||||
import { IconChevronRight, IconPlus, IconSpace, IconTrash } from "@tabler/icons-react";
|
||||
import useShipping from "../hooks/useShipping.tsx";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import clsx from "clsx";
|
||||
import "mantine-datatable/styles.css";
|
||||
import classes from "../ShippingTab.module.css";
|
||||
import InlineShippingButton from "./InlineShippingButton.tsx";
|
||||
|
||||
const ShippingTree = () => {
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
@@ -14,91 +19,59 @@ const ShippingTree = () => {
|
||||
onCreateShippingProduct,
|
||||
onDeletePalletClick,
|
||||
palletIds,
|
||||
setPalletIds,
|
||||
} = useShipping();
|
||||
|
||||
const sortById = (data?: PalletSchema[] | BoxSchema[] | ShippingProductSchema[]) => {
|
||||
return data?.sort((a, b) => a.id - b.id);
|
||||
};
|
||||
|
||||
const getPallets = () => {
|
||||
const sortedPallets = sortById(card?.pallets) as PalletSchema[];
|
||||
const pallets = sortedPallets?.map((pallet => {
|
||||
palletIds.push(pallet.id.toString());
|
||||
return (
|
||||
<Accordion.Item key={pallet.id} value={pallet.id.toString()}>
|
||||
<Center>
|
||||
<Accordion.Control icon={<IconSpace />}>
|
||||
Паллет - П{pallet.id}
|
||||
</Accordion.Control>
|
||||
{removePalletButton(pallet.id)}
|
||||
</Center>
|
||||
<Accordion.Panel>
|
||||
{getPalletContent(pallet)}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
})) ?? [];
|
||||
|
||||
if (card?.boxes && card?.boxes.length > 0) {
|
||||
const boxes = card?.boxes.sort((b1, b2) => (b1.id - b2.id));
|
||||
const itemValue = "noPallets";
|
||||
const boxesWithoutPallet = (
|
||||
<Accordion.Item key={-1} value={itemValue}>
|
||||
<Accordion.Control icon={<IconBox />}>
|
||||
Короба без паллетов
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<BoxesTable items={boxes} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
pallets.unshift(boxesWithoutPallet);
|
||||
palletIds.push(itemValue);
|
||||
}
|
||||
|
||||
return pallets;
|
||||
};
|
||||
|
||||
const removePalletButton = (palletId: number) => {
|
||||
const removePalletButton = (pallet: PalletSchema) => {
|
||||
return (
|
||||
<Tooltip label="Удалить паллет">
|
||||
<ActionIcon
|
||||
variant={"default"}
|
||||
onClick={() => onDeletePalletClick(palletId)}
|
||||
mx={"md"}
|
||||
>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<InlineShippingButton onClick={() => onDeletePalletClick(pallet)}>
|
||||
<IconTrash />
|
||||
</InlineShippingButton>
|
||||
);
|
||||
};
|
||||
|
||||
const createBoxOrShippingProductButton = (palletId: number, isBox: boolean) => {
|
||||
const createButtonLabel = isBox ? "Добавить короб" : "Добавить товар";
|
||||
const createButtonLabel = isBox ? "Короб" : "Товар";
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant={"default"}
|
||||
<InlineShippingButton
|
||||
onClick={() => {
|
||||
if (isBox) {
|
||||
onCreateBoxInPallet(palletId);
|
||||
} else {
|
||||
onCreateShippingProduct(palletId);
|
||||
onCreateShippingProduct({ palletId });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Group gap={rem(5)}>
|
||||
<IconPlus />
|
||||
{createButtonLabel}
|
||||
</Group>
|
||||
</Button>
|
||||
<IconPlus />
|
||||
{createButtonLabel}
|
||||
</InlineShippingButton>
|
||||
);
|
||||
};
|
||||
|
||||
const getPalletContent = (pallet: PalletSchema) => {
|
||||
const isBox = pallet.boxes.length > 0;
|
||||
|
||||
const boxes = sortById(pallet.boxes) as BoxSchema[];
|
||||
const shippingProducts = sortById(pallet.shippingProducts) as ShippingProductSchema[];
|
||||
|
||||
if (isBox) {
|
||||
return (
|
||||
<BoxesTable items={boxes} onCreateShippingProduct={onCreateShippingProduct} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ShippingProductsTable items={shippingProducts} />
|
||||
);
|
||||
};
|
||||
|
||||
const getPalletActions = (pallet: PalletSchema) => {
|
||||
const isEmpty = pallet.boxes.length === 0 && pallet.shippingProducts.length === 0;
|
||||
const isBox = pallet.boxes.length > 0;
|
||||
const title = isEmpty ? "Пустой" : isBox ? "Короба" : "Товары";
|
||||
|
||||
let palletButtons;
|
||||
if (isEmpty) {
|
||||
@@ -112,39 +85,77 @@ const ShippingTree = () => {
|
||||
];
|
||||
}
|
||||
|
||||
const boxes = sortById(pallet.boxes) as BoxSchema[];
|
||||
const shippingProducts = sortById(pallet.shippingProducts) as ShippingProductSchema[];
|
||||
|
||||
let table;
|
||||
if (!isEmpty) {
|
||||
if (isBox) {
|
||||
table = (<BoxesTable items={boxes} />);
|
||||
} else {
|
||||
table = (<ShippingProductsTable items={shippingProducts} />);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap={rem(5)}>
|
||||
<Group justify={"space-between"}>
|
||||
<Title order={6}>{title}</Title>
|
||||
<Group gap={rem(10)}>
|
||||
{...palletButtons}
|
||||
</Group>
|
||||
</Group>
|
||||
{table}
|
||||
</Stack>
|
||||
<Flex
|
||||
gap={rem(10)}
|
||||
wrap={"nowrap"}
|
||||
direction={"row-reverse"}
|
||||
>
|
||||
{removePalletButton(pallet)}
|
||||
{...palletButtons}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const isPalletExpandable = (pallet: PalletSchema) => {
|
||||
const isExpandable = pallet.boxes.length !== 0 || pallet.shippingProducts.length !== 0;
|
||||
if (!isExpandable && palletIds.includes(pallet.id)) {
|
||||
setPalletIds(palletIds.filter(palletId => palletId !== pallet.id));
|
||||
}
|
||||
return isExpandable;
|
||||
};
|
||||
|
||||
if (card?.boxes?.length === 0 && card?.pallets?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
multiple={true}
|
||||
defaultValue={palletIds}
|
||||
bd={"solid 1px gray"}
|
||||
>
|
||||
{getPallets()}
|
||||
</Accordion>
|
||||
<Stack gap={0}>
|
||||
{card?.boxes && (
|
||||
<BoxesTable items={card?.boxes} onCreateShippingProduct={onCreateShippingProduct} />
|
||||
)}
|
||||
<DataTable
|
||||
noHeader
|
||||
withTableBorder
|
||||
columns={[
|
||||
{
|
||||
accessor: "id",
|
||||
title: "Паллет",
|
||||
noWrap: true,
|
||||
render: (pallet) => {
|
||||
const isExpandable = isPalletExpandable(pallet);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isExpandable && (
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: palletIds?.includes(pallet.id),
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<IconSpace className={classes.icon} />
|
||||
<span>Паллет П{pallet.id}</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: getPalletActions,
|
||||
},
|
||||
]}
|
||||
records={card?.pallets?.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: palletIds, onRecordIdsChange: setPalletIds },
|
||||
expandable: ({ record }) => isPalletExpandable(record),
|
||||
content: ({ record }) => getPalletContent(record),
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,44 +1,89 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef, MRT_RowData } from "mantine-react-table";
|
||||
import { DataTableColumn } from "mantine-datatable";
|
||||
import { ShippingProductSchema, ShippingService } from "../../../../../client";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { modals } from "@mantine/modals";
|
||||
import useUpdateCard from "./useUpdateCard.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
|
||||
type Props = {
|
||||
isBox: boolean;
|
||||
}
|
||||
const useShippingTableColumns = () => {
|
||||
const { update } = useUpdateCard();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const useShippingTableColumns = <T extends MRT_RowData>({ isBox }: Props) => {
|
||||
const hideBoxColumns = ["id"];
|
||||
const onDeleteClick = (shippingProduct: ShippingProductSchema) => {
|
||||
ShippingService.deleteShippingProduct({
|
||||
shippingProductId: shippingProduct.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
update();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
return useMemo<MRT_ColumnDef<T>[]>(
|
||||
() => [
|
||||
{
|
||||
header: "ID",
|
||||
accessorKey: "id",
|
||||
Cell: ({ row }) => `K${row.original.id}`,
|
||||
const onEditClick = (shippingProduct: ShippingProductSchema) => {
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование товара",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
shippingData: {
|
||||
shippingProductId: shippingProduct.id,
|
||||
productId: shippingProduct.product.id,
|
||||
quantity: shippingProduct.quantity,
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Название",
|
||||
accessorKey: "product.name",
|
||||
Cell: ({ row }) => row.original.product?.name ?? "-",
|
||||
},
|
||||
{
|
||||
header: "Артикул",
|
||||
accessorKey: "product.article",
|
||||
Cell: ({ row }) => row.original.product?.article ?? "-",
|
||||
},
|
||||
{
|
||||
header: "Размер",
|
||||
accessorKey: "product.size",
|
||||
Cell: ({ row }) => row.original.product?.size ?? "-",
|
||||
},
|
||||
{
|
||||
header: "Количество",
|
||||
accessorKey: "quantity",
|
||||
},
|
||||
],
|
||||
[],
|
||||
).filter(
|
||||
columnDef => isBox || !hideBoxColumns.includes(columnDef.accessorKey || ""),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
title: "Название",
|
||||
accessor: "product.name",
|
||||
render: shippingProduct => shippingProduct.product?.name ?? "-",
|
||||
},
|
||||
{
|
||||
title: "Артикул",
|
||||
accessor: "product.article",
|
||||
render: shippingProduct => shippingProduct.product?.article ?? "-",
|
||||
},
|
||||
{
|
||||
title: "Размер",
|
||||
accessor: "product.size",
|
||||
render: shippingProduct => shippingProduct.product?.size ?? "-",
|
||||
},
|
||||
{
|
||||
title: "Количество",
|
||||
accessor: "quantity",
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: shippingProduct => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => onDeleteClick(shippingProduct)}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(shippingProduct)}
|
||||
variant={"default"}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
] as DataTableColumn<ShippingProductSchema>[];
|
||||
};
|
||||
|
||||
export default useShippingTableColumns;
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { CreateBoxInCardSchema, CreateBoxInPalletSchema, ShippingService } from "../../../../../client";
|
||||
import {
|
||||
CreateBoxInCardSchema,
|
||||
CreateBoxInPalletSchema, PalletSchema,
|
||||
ShippingService,
|
||||
} from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { Text } from "@mantine/core";
|
||||
import useUpdateCard from "./useUpdateCard.tsx";
|
||||
import { useState } from "react";
|
||||
import { ShippingProductParentData } from "../types/ShippingProductData.tsx";
|
||||
|
||||
const useShipping = () => {
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
const { update } = useUpdateCard();
|
||||
const palletIds: string[] = [];
|
||||
const [palletIds, setPalletIds] = useState<number[]>([]);
|
||||
|
||||
const onCreatePalletClick = () => {
|
||||
if (!card) return;
|
||||
@@ -34,19 +40,24 @@ const useShipping = () => {
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onDeletePalletClick = (palletId: number) => {
|
||||
const onDeletePalletClick = (pallet: PalletSchema) => {
|
||||
if (!card) return;
|
||||
if (pallet.shippingProducts.length === 0 && pallet.boxes.length === 0) {
|
||||
onDeletePallet(pallet.id);
|
||||
return;
|
||||
}
|
||||
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление паллета",
|
||||
children: <Text size="sm">Вы уверены что хотите удалить паллет?</Text>,
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => onDeletePallet(palletId),
|
||||
onConfirm: () => onDeletePallet(pallet.id),
|
||||
});
|
||||
};
|
||||
|
||||
const onCreateBox = (data: CreateBoxInPalletSchema | CreateBoxInCardSchema) => {
|
||||
ShippingService.updateBox({
|
||||
ShippingService.createBox({
|
||||
requestBody: {
|
||||
data,
|
||||
},
|
||||
@@ -66,18 +77,20 @@ const useShipping = () => {
|
||||
onCreateBox({ palletId });
|
||||
};
|
||||
|
||||
const onCreateShippingProduct = (palletId: number) => {
|
||||
const onCreateShippingProduct = ({ palletId, boxId }: ShippingProductParentData) => {
|
||||
if (!card) return;
|
||||
const postfix = palletId ? "на паллет" : "в короб";
|
||||
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Добавление товара на паллет",
|
||||
title: "Добавление товара " + postfix,
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: false,
|
||||
shippingData: {
|
||||
palletId,
|
||||
boxId,
|
||||
productId: null,
|
||||
quantity: null,
|
||||
},
|
||||
@@ -92,6 +105,7 @@ const useShipping = () => {
|
||||
onCreatePalletClick,
|
||||
onDeletePalletClick,
|
||||
palletIds,
|
||||
setPalletIds,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, NumberInput, rem, Text } from "@mantine/core";
|
||||
import getRestProducts from "../utils/getRestProducts.tsx";
|
||||
import {
|
||||
CreateBoxInCardSchema,
|
||||
CreateBoxInPalletSchema,
|
||||
CreateShippingProductSchema,
|
||||
CardProductSchema,
|
||||
CardSchema,
|
||||
CreateShippingProductSchema,
|
||||
ProductSchema,
|
||||
ShippingService,
|
||||
UpdateBoxSchema,
|
||||
UpdateShippingProductSchema,
|
||||
} from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
@@ -22,7 +19,6 @@ import ShippingProductSelect from "../components/ShippingProductSelect.tsx";
|
||||
type Props = {
|
||||
updateOnSubmit: () => void;
|
||||
card: CardSchema;
|
||||
isBox: boolean;
|
||||
shippingData: Partial<ShippingData>;
|
||||
}
|
||||
|
||||
@@ -65,31 +61,13 @@ const ShippingProductModal = ({
|
||||
useEffect(() => {
|
||||
const data = getRestProducts({
|
||||
card: innerProps.card,
|
||||
unaccountedValues: innerProps.shippingData as UpdateShippingProductSchema | UpdateBoxSchema,
|
||||
shippingProductId: (innerProps.shippingData as UpdateShippingProductSchema).shippingProductId,
|
||||
});
|
||||
setRestProducts(data.restProducts);
|
||||
setRestProductSelectData(data.restProductsSelectData);
|
||||
}, [innerProps.card]);
|
||||
|
||||
const updateBox = () => {
|
||||
const data = {
|
||||
...innerProps.shippingData,
|
||||
...form.values,
|
||||
productId: form.values.product?.id,
|
||||
} as CreateBoxInPalletSchema | CreateBoxInCardSchema | UpdateBoxSchema;
|
||||
|
||||
ShippingService.updateBox({
|
||||
requestBody: { data },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message: message });
|
||||
innerProps.updateOnSubmit();
|
||||
if (ok) context.closeContextModal(id);
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const updateShippingProduct = () => {
|
||||
const onSubmit = () => {
|
||||
const data = {
|
||||
...innerProps.shippingData,
|
||||
...form.values,
|
||||
@@ -107,14 +85,6 @@ const ShippingProductModal = ({
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
if (innerProps.isBox) {
|
||||
updateBox();
|
||||
} else {
|
||||
updateShippingProduct();
|
||||
}
|
||||
};
|
||||
|
||||
const getRestQuantityText = () => {
|
||||
if (!form.values.product) return;
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import {
|
||||
CreateShippingProductSchema,
|
||||
ProductSchema,
|
||||
UpdateBoxSchema,
|
||||
UpdateShippingProductSchema,
|
||||
} from "../../../../../client";
|
||||
import { CreateShippingProductSchema, ProductSchema, UpdateShippingProductSchema } from "../../../../../client";
|
||||
|
||||
export type ShippingModalForm = {
|
||||
quantity: number;
|
||||
product?: ProductSchema | null;
|
||||
}
|
||||
|
||||
export type ShippingData =
|
||||
UpdateBoxSchema
|
||||
| CreateShippingProductSchema
|
||||
| UpdateShippingProductSchema;
|
||||
export type ShippingData = CreateShippingProductSchema | UpdateShippingProductSchema;
|
||||
|
||||
export type ShippingProductParentData = {
|
||||
palletId?: number;
|
||||
boxId?: number;
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
import { CardProductSchema, CardSchema, ProductSchema } from "../../../../../client";
|
||||
import { BoxSchema, CardProductSchema, CardSchema, ProductSchema } from "../../../../../client";
|
||||
|
||||
type UnaccountedValues = {
|
||||
boxId?: number | null;
|
||||
shippingProductId?: number | null;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
card?: CardSchema;
|
||||
unaccountedValues: UnaccountedValues;
|
||||
shippingProductId?: number | null;
|
||||
}
|
||||
|
||||
const getRestProducts = ({
|
||||
card,
|
||||
unaccountedValues,
|
||||
shippingProductId,
|
||||
}: Props) => {
|
||||
const totalProducts = new Map(
|
||||
card?.products.map(product => product && [product.product.id, product]),
|
||||
@@ -30,20 +26,23 @@ const getRestProducts = ({
|
||||
}
|
||||
};
|
||||
|
||||
card?.boxes?.forEach((box) => {
|
||||
if (!box.product || box.id === unaccountedValues.boxId) return;
|
||||
accountProduct(box.product, box.quantity);
|
||||
});
|
||||
const accountBoxes = (boxes?: BoxSchema[]) => {
|
||||
boxes?.forEach((box) => {
|
||||
box.shippingProducts.forEach((shippingProduct) => {
|
||||
if (!shippingProduct.product || shippingProduct.id === shippingProductId) return;
|
||||
accountProduct(shippingProduct.product, shippingProduct.quantity);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
accountBoxes(card?.boxes);
|
||||
|
||||
card?.pallets?.forEach(pallet => {
|
||||
pallet.shippingProducts.forEach(shippingProduct => {
|
||||
if (shippingProduct.id === unaccountedValues.shippingProductId) return;
|
||||
if (shippingProduct.id === shippingProductId) return;
|
||||
accountProduct(shippingProduct.product, shippingProduct.quantity);
|
||||
});
|
||||
pallet.boxes.forEach((box) => {
|
||||
if (!box.product || box.id === unaccountedValues.boxId) return;
|
||||
accountProduct(box.product, box.quantity);
|
||||
});
|
||||
accountBoxes(pallet.boxes);
|
||||
});
|
||||
|
||||
const restProducts = new Map<number, CardProductSchema>();
|
||||
|
||||
Reference in New Issue
Block a user