feat: projects and boards
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
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 { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import useUpdateDeal from "../hooks/useUpdateDeal.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
type Props = {
|
||||
items: BoxSchema[];
|
||||
}
|
||||
|
||||
const BoxesTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<BoxSchema>({ isBox: true });
|
||||
const { update } = useUpdateDeal();
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
|
||||
const onDeleteClick = (box: BoxSchema) => {
|
||||
ShippingService.deleteBox({
|
||||
boxId: box.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (ok) update();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onEditClick = (box: BoxSchema) => {
|
||||
if (!deal) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование короба",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
updateOnSubmit: update,
|
||||
isBox: true,
|
||||
shippingData: {
|
||||
boxId: box.id,
|
||||
productId: box.product?.id,
|
||||
quantity: box.quantity,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
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>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoxesTable;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ProductSchema } from "../../../../../client";
|
||||
import { FC, useState } from "react";
|
||||
import ObjectSelect, { ObjectSelectProps } from "../../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
import getRenderOptions from "../../../../../components/ProductSelect/utils/getRenderOptions.tsx";
|
||||
|
||||
|
||||
type Props = Omit<ObjectSelectProps<ProductSchema>, "searchValue" | "onSearchChange">;
|
||||
|
||||
const ShippingProductSelect: FC<Props> = (props: Props) => {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const getFilteredData = () => {
|
||||
const searchValue: string = search.toLowerCase();
|
||||
|
||||
const data: ProductSchema[] = props.data.filter(product => {
|
||||
return (
|
||||
product.name.toLowerCase().includes(searchValue) ||
|
||||
product.article?.toLowerCase().includes(searchValue) ||
|
||||
product.barcodes && product.barcodes[0].toLowerCase().includes(searchValue)
|
||||
);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
return (
|
||||
<ObjectSelect
|
||||
label={"Товар"}
|
||||
placeholder={"Выберите товар"}
|
||||
searchable
|
||||
{...props}
|
||||
data={getFilteredData()}
|
||||
searchValue={search}
|
||||
onSearchChange={setSearch}
|
||||
renderOption={getRenderOptions(props.data)}
|
||||
filter={({ options }) => options}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingProductSelect;
|
||||
@@ -0,0 +1,87 @@
|
||||
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 useUpdateDeal from "../hooks/useUpdateDeal.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
items: ShippingProductSchema[];
|
||||
}
|
||||
|
||||
const ShippingProductsTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<ShippingProductSchema>({ isBox: false });
|
||||
const { update } = useUpdateDeal();
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
|
||||
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 (!deal) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование товара на паллете",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
updateOnSubmit: update,
|
||||
isBox: false,
|
||||
shippingData: {
|
||||
shippingProductId: shippingProduct.id,
|
||||
productId: shippingProduct.product.id,
|
||||
quantity: shippingProduct.quantity,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
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>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingProductsTable;
|
||||
151
src/pages/DealsPage/tabs/ShippingTab/components/ShippingTree.tsx
Normal file
151
src/pages/DealsPage/tabs/ShippingTab/components/ShippingTree.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { Accordion, ActionIcon, Button, Center, Group, rem, Stack, Title, Tooltip } 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 useShipping from "../hooks/useShipping.tsx";
|
||||
|
||||
const ShippingTree = () => {
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
|
||||
const {
|
||||
onCreateBoxInPallet,
|
||||
onCreateShippingProduct,
|
||||
onDeletePalletClick,
|
||||
palletIds,
|
||||
} = useShipping();
|
||||
|
||||
const sortById = (data?: PalletSchema[] | BoxSchema[] | ShippingProductSchema[]) => {
|
||||
return data?.sort((a, b) => a.id - b.id);
|
||||
};
|
||||
|
||||
const getPallets = () => {
|
||||
const sortedPallets = sortById(deal?.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 (deal?.boxes && deal?.boxes.length > 0) {
|
||||
const boxes = deal?.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"}
|
||||
>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const createBoxOrShippingProductButton = (palletId: number, isBox: boolean) => {
|
||||
const createButtonLabel = isBox ? "Добавить короб" : "Добавить товар";
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant={"default"}
|
||||
onClick={() => {
|
||||
if (isBox) {
|
||||
onCreateBoxInPallet(palletId);
|
||||
} else {
|
||||
onCreateShippingProduct(palletId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Group gap={rem(5)}>
|
||||
<IconPlus />
|
||||
{createButtonLabel}
|
||||
</Group>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const getPalletContent = (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) {
|
||||
palletButtons = [
|
||||
createBoxOrShippingProductButton(pallet.id, true),
|
||||
createBoxOrShippingProductButton(pallet.id, false),
|
||||
];
|
||||
} else {
|
||||
palletButtons = [
|
||||
createBoxOrShippingProductButton(pallet.id, isBox),
|
||||
];
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
multiple={true}
|
||||
defaultValue={palletIds}
|
||||
bd={"solid 1px gray"}
|
||||
>
|
||||
{getPallets()}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingTree;
|
||||
Reference in New Issue
Block a user