feat: a few shipping products in box

This commit is contained in:
2025-03-01 17:05:27 +04:00
parent d37dce7980
commit 17e6c5f23a
20 changed files with 405 additions and 356 deletions

View File

@@ -132,6 +132,8 @@ export type { CreateBoardRequest } from './models/CreateBoardRequest';
export type { CreateBoardResponse } from './models/CreateBoardResponse';
export type { CreateBoxInCardSchema } from './models/CreateBoxInCardSchema';
export type { CreateBoxInPalletSchema } from './models/CreateBoxInPalletSchema';
export type { CreateBoxRequest } from './models/CreateBoxRequest';
export type { CreateBoxResponse } from './models/CreateBoxResponse';
export type { CreateCardBillRequest } from './models/CreateCardBillRequest';
export type { CreateCardBillResponse } from './models/CreateCardBillResponse';
export type { CreateCardGroupRequest } from './models/CreateCardGroupRequest';
@@ -354,9 +356,6 @@ export type { UpdateBoardOrderRequest } from './models/UpdateBoardOrderRequest';
export type { UpdateBoardOrderResponse } from './models/UpdateBoardOrderResponse';
export type { UpdateBoardRequest } from './models/UpdateBoardRequest';
export type { UpdateBoardResponse } from './models/UpdateBoardResponse';
export type { UpdateBoxRequest } from './models/UpdateBoxRequest';
export type { UpdateBoxResponse } from './models/UpdateBoxResponse';
export type { UpdateBoxSchema } from './models/UpdateBoxSchema';
export type { UpdateDepartmentRequest } from './models/UpdateDepartmentRequest';
export type { UpdateDepartmentResponse } from './models/UpdateDepartmentResponse';
export type { UpdateDepartmentSectionRequest } from './models/UpdateDepartmentSectionRequest';

View File

@@ -2,12 +2,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ProductSchema } from './ProductSchema';
import type { ShippingProductSchema } from './ShippingProductSchema';
export type BoxSchema = {
id: number;
quantity: number;
product: (ProductSchema | null);
palletId: (number | null);
cardId: (number | null);
shippingProducts: Array<ShippingProductSchema>;
};

View File

@@ -4,8 +4,7 @@
/* eslint-disable */
import type { CreateBoxInCardSchema } from './CreateBoxInCardSchema';
import type { CreateBoxInPalletSchema } from './CreateBoxInPalletSchema';
import type { UpdateBoxSchema } from './UpdateBoxSchema';
export type UpdateBoxRequest = {
data: (CreateBoxInCardSchema | CreateBoxInPalletSchema | UpdateBoxSchema);
export type CreateBoxRequest = {
data: (CreateBoxInCardSchema | CreateBoxInPalletSchema);
};

View File

@@ -2,7 +2,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UpdateBoxResponse = {
export type CreateBoxResponse = {
ok: boolean;
message: string;
};

View File

@@ -5,6 +5,7 @@
export type CreateShippingProductSchema = {
productId: (number | null);
quantity: (number | null);
palletId: number;
palletId?: (number | null);
boxId?: (number | null);
};

View File

@@ -7,6 +7,7 @@ export type ShippingProductSchema = {
id: number;
quantity: number;
product: ProductSchema;
palletId: number;
palletId: (number | null);
boxId: (number | null);
};

View File

@@ -1,10 +0,0 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UpdateBoxSchema = {
productId: (number | null);
quantity: (number | null);
boxId: (number | null);
};

View File

@@ -2,12 +2,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CreateBoxRequest } from '../models/CreateBoxRequest';
import type { CreateBoxResponse } from '../models/CreateBoxResponse';
import type { CreatePalletResponse } from '../models/CreatePalletResponse';
import type { DeleteBoxResponse } from '../models/DeleteBoxResponse';
import type { DeletePalletResponse } from '../models/DeletePalletResponse';
import type { DeleteShippingProductResponse } from '../models/DeleteShippingProductResponse';
import type { UpdateBoxRequest } from '../models/UpdateBoxRequest';
import type { UpdateBoxResponse } from '../models/UpdateBoxResponse';
import type { UpdateShippingProductRequest } from '../models/UpdateShippingProductRequest';
import type { UpdateShippingProductResponse } from '../models/UpdateShippingProductResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
@@ -98,15 +98,15 @@ export class ShippingService {
});
}
/**
* Update Box
* @returns UpdateBoxResponse Successful Response
* Create Box
* @returns CreateBoxResponse Successful Response
* @throws ApiError
*/
public static updateBox({
public static createBox({
requestBody,
}: {
requestBody: UpdateBoxRequest,
}): CancelablePromise<UpdateBoxResponse> {
requestBody: CreateBoxRequest,
}): CancelablePromise<CreateBoxResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/shipping/box',

View File

@@ -3,18 +3,14 @@ import { CardSchema } from "../../../client";
const DATETIME_TYPES = ["datetime", "date"];
const getAttributesFromCard = (card: CardSchema) => {
console.log("getAttributesFromCard");
return card.attributes.reduce(
(values, cardAttribute) => {
let value: boolean | number | string | null | Date = cardAttribute.value;
console.log(cardAttribute.attribute.type.type);
if (DATETIME_TYPES.includes(cardAttribute.attribute.type.type) && value !== null) {
value = new Date(value as string);
}
console.log("cardAttrValue", value);
console.log("value type", typeof value);
return {
...values,
[cardAttribute.attribute.name]: value,

View File

@@ -13,7 +13,7 @@ const InlineButton = ({ children, onClick, ...props }: Props) => {
onClick={onClick}
{...props}
>
<Group gap="sm">
<Group gap="sm" wrap={"nowrap"}>
{children}
</Group>
</Button>

View 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);
}

View File

@@ -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 (
<BaseTable
data={items}
columns={columns}
restProps={
{
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>
),
} as MRT_TableOptions<BoxSchema>
<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 (
<DataTable
noHeader
withTableBorder
columns={[
{
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>
</>
),
},
{
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),
}}
/>
);
};

View File

@@ -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;

View File

@@ -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}
<Box>
<DataTable
withTableBorder
withColumnBorders
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>
}
records={items}
className={clsx(classes.productsTableBorder)}
/>
</Box>
);
};

View File

@@ -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());
const removePalletButton = (pallet: PalletSchema) => {
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) => {
return (
<Tooltip label="Удалить паллет">
<ActionIcon
variant={"default"}
onClick={() => onDeletePalletClick(palletId)}
mx={"md"}
>
<InlineShippingButton onClick={() => onDeletePalletClick(pallet)}>
<IconTrash />
</ActionIcon>
</Tooltip>
</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>
</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,40 +85,78 @@ const ShippingTree = () => {
];
}
const boxes = sortById(pallet.boxes) as BoxSchema[];
const shippingProducts = sortById(pallet.shippingProducts) as ShippingProductSchema[];
return (
<Flex
gap={rem(10)}
wrap={"nowrap"}
direction={"row-reverse"}
>
{removePalletButton(pallet)}
{...palletButtons}
</Flex>
);
};
let table;
if (!isEmpty) {
if (isBox) {
table = (<BoxesTable items={boxes} />);
} else {
table = (<ShippingProductsTable items={shippingProducts} />);
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 (
<Stack gap={rem(5)}>
<Group justify={"space-between"}>
<Title order={6}>{title}</Title>
<Group gap={rem(10)}>
{...palletButtons}
</Group>
</Group>
{table}
<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>
);
};
return (
<Accordion
multiple={true}
defaultValue={palletIds}
bd={"solid 1px gray"}
>
{getPallets()}
</Accordion>
);
};
export default ShippingTree;

View File

@@ -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>[]>(
() => [
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,
},
},
});
};
return [
{
header: "ID",
accessorKey: "id",
Cell: ({ row }) => `K${row.original.id}`,
title: "Название",
accessor: "product.name",
render: shippingProduct => shippingProduct.product?.name ?? "-",
},
{
header: "Название",
accessorKey: "product.name",
Cell: ({ row }) => row.original.product?.name ?? "-",
title: "Артикул",
accessor: "product.article",
render: shippingProduct => shippingProduct.product?.article ?? "-",
},
{
header: "Артикул",
accessorKey: "product.article",
Cell: ({ row }) => row.original.product?.article ?? "-",
title: "Размер",
accessor: "product.size",
render: shippingProduct => shippingProduct.product?.size ?? "-",
},
{
header: "Размер",
accessorKey: "product.size",
Cell: ({ row }) => row.original.product?.size ?? "-",
title: "Количество",
accessor: "quantity",
},
{
header: "Количество",
accessorKey: "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>
),
},
],
[],
).filter(
columnDef => isBox || !hideBoxColumns.includes(columnDef.accessorKey || ""),
);
] as DataTableColumn<ShippingProductSchema>[];
};
export default useShippingTableColumns;

View File

@@ -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,
};
};

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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>();