refactor: refactored product services table

This commit is contained in:
2025-10-30 13:52:57 +04:00
parent 4cc6360bb4
commit da769fa2c0
5 changed files with 108 additions and 178 deletions

View File

@ -1,12 +1,11 @@
import { FC } from "react"; import { FC } from "react";
import { IconCopy, IconMoodSad } from "@tabler/icons-react"; import { IconCopy, IconMoodSad } from "@tabler/icons-react";
import { Button, Flex, Group, Stack, Text } from "@mantine/core"; import { Button, Flex, Group, Stack, Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip"; import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
import BaseTable from "@/components/ui/BaseTable/BaseTable"; import BaseTable from "@/components/ui/BaseTable/BaseTable";
import { DealProductSchema, ProductServiceSchema } from "@/lib/client"; import { DealProductSchema } from "@/lib/client";
import useProductServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductView/hooks/useProductServicesTableColumns"; import useProductServicesTable from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/utils/useProductServicesTable";
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext"; import useProductServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/utils/useProductServicesTableColumns";
type Props = { type Props = {
dealProduct: DealProductSchema; dealProduct: DealProductSchema;
@ -19,64 +18,23 @@ const ProductServicesTable: FC<Props> = ({
onDuplicateServices, onDuplicateServices,
onKitAdd, onKitAdd,
}) => { }) => {
const { productServiceCrud, dealProductsList } = const {
useFulfillmentBaseContext(); onChange,
onCreate,
const onChange = (item: ProductServiceSchema) => { onDelete,
const excludeServiceIds = dealProduct.productServices.map( isEmptyTable,
productService => productService.service.id isDuplicateServicesDisabled,
); } = useProductServicesTable({
const totalQuantity = dealProductsList.dealProducts.reduce( dealProduct,
(sum, prod) => prod.quantity + sum,
0
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
entity: item,
onChange: values =>
productServiceCrud.onUpdate(
item.dealId,
item.productId,
item.serviceId,
values
),
excludeServiceIds,
quantity: totalQuantity,
isEditing: true,
},
withCloseButton: false,
}); });
};
const columns = useProductServicesTableColumns({ const columns = useProductServicesTableColumns({
data: dealProduct.productServices, data: dealProduct.productServices,
quantity: dealProduct.quantity, quantity: dealProduct.quantity,
onDelete: productServiceCrud.onDelete, onDelete,
onChange, onChange,
}); });
const onCreateClick = () => {
const excludeServiceIds = dealProduct.productServices.map(
productService => productService.service.id
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
onCreate: values =>
productServiceCrud.onCreate({ ...dealProduct, ...values }),
excludeServiceIds,
quantity: dealProduct.quantity,
isEditing: false,
},
withCloseButton: false,
});
};
const isEmptyTable = dealProduct.productServices.length === 0;
return ( return (
<Stack gap={0}> <Stack gap={0}>
<BaseTable <BaseTable
@ -104,7 +62,7 @@ const ProductServicesTable: FC<Props> = ({
{onDuplicateServices && ( {onDuplicateServices && (
<ActionIconWithTip <ActionIconWithTip
tipLabel={"Продублировать услуги"} tipLabel={"Продублировать услуги"}
disabled={dealProductsList.dealProducts.length <= 1} disabled={isDuplicateServicesDisabled}
onClick={onDuplicateServices}> onClick={onDuplicateServices}>
<IconCopy /> <IconCopy />
</ActionIconWithTip> </ActionIconWithTip>
@ -117,7 +75,7 @@ const ProductServicesTable: FC<Props> = ({
</Button> </Button>
)} )}
<Button <Button
onClick={onCreateClick} onClick={onCreate}
variant={"default"}> variant={"default"}>
Добавить услугу Добавить услугу
</Button> </Button>

View File

@ -1,75 +1,26 @@
import { FC } from "react"; import { FC } from "react";
import { IconMoodSad } from "@tabler/icons-react"; import { IconMoodSad } from "@tabler/icons-react";
import { Button, Flex, Group, ScrollArea, Text } from "@mantine/core"; import { Button, Flex, Group, ScrollArea, Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import BaseTable from "@/components/ui/BaseTable/BaseTable"; import BaseTable from "@/components/ui/BaseTable/BaseTable";
import { DealProductSchema, ProductServiceSchema } from "@/lib/client"; import { DealProductSchema } from "@/lib/client";
import useProductServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductServicesTable/useProductServicesTableColumns"; import useProductServicesTable from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/utils/useProductServicesTable";
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext"; import useProductServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/utils/useProductServicesTableColumns";
type Props = { type Props = {
dealProduct: DealProductSchema; dealProduct: DealProductSchema;
}; };
const ProductServicesTable: FC<Props> = ({ dealProduct }) => { const ProductServicesTable: FC<Props> = ({ dealProduct }) => {
const { productServiceCrud, dealProductsList } = const { onChange, onCreate, onDelete, isEmptyTable } =
useFulfillmentBaseContext(); useProductServicesTable({ dealProduct });
const onChange = (item: ProductServiceSchema) => {
const excludeServiceIds = dealProduct.productServices.map(
productService => productService.service.id
);
const totalQuantity = dealProductsList.dealProducts.reduce(
(sum, prod) => prod.quantity + sum,
0
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
entity: item,
onChange: values =>
productServiceCrud.onUpdate(
item.dealId,
item.productId,
item.serviceId,
values
),
excludeServiceIds,
quantity: totalQuantity,
isEditing: true,
},
withCloseButton: false,
});
};
const columns = useProductServicesTableColumns({ const columns = useProductServicesTableColumns({
data: dealProduct.productServices, data: dealProduct.productServices,
quantity: dealProduct.quantity, quantity: dealProduct.quantity,
onDelete: productServiceCrud.onDelete, onDelete,
onChange, onChange,
}); });
const onCreateClick = () => {
const excludeServiceIds = dealProduct.productServices.map(
productService => productService.service.id
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
onCreate: values =>
productServiceCrud.onCreate({ ...dealProduct, ...values }),
excludeServiceIds,
quantity: dealProduct.quantity,
isEditing: false,
},
withCloseButton: false,
});
};
const isEmptyTable = dealProduct.productServices.length === 0;
return ( return (
<Flex <Flex
flex={1} flex={1}
@ -105,7 +56,7 @@ const ProductServicesTable: FC<Props> = ({ dealProduct }) => {
<Button <Button
flex={1} flex={1}
py={"xs"} py={"xs"}
onClick={onCreateClick} onClick={onCreate}
variant={"default"}> variant={"default"}>
Добавить услугу Добавить услугу
</Button> </Button>

View File

@ -1,62 +0,0 @@
import { useMemo } from "react";
import { DataTableColumn } from "mantine-datatable";
import { Text } from "@mantine/core";
import UpdateDeleteTableActions from "@/components/ui/BaseTable/components/UpdateDeleteTableActions";
import { ProductServiceSchema } from "@/lib/client";
type Props = {
data: ProductServiceSchema[];
quantity: number;
onChange: (dealProductService: ProductServiceSchema) => void;
onDelete: (dealProductService: ProductServiceSchema) => void;
};
const useProductServicesTableColumns = ({
data,
quantity,
onChange,
onDelete,
}: Props) => {
const totalPrice = useMemo(
() => data.reduce((acc, row) => acc + row.price * quantity, 0),
[data, quantity]
);
return useMemo(
() =>
[
{
accessor: "actions",
title: "Действия",
textAlign: "center",
width: "0%",
render: dealProductService => (
<UpdateDeleteTableActions
onDelete={() => onDelete(dealProductService)}
onChange={() => onChange(dealProductService)}
/>
),
},
{
accessor: "service.name",
title: "Услуга",
width: "70%",
},
{
accessor: "price",
title: "Цена",
width: "30%",
render: productService =>
productService.price.toLocaleString("ru"),
footer: data.length > 0 && (
<Text fw={700}>
Итог: {totalPrice.toLocaleString("ru")}
</Text>
),
},
] as DataTableColumn<ProductServiceSchema>[],
[totalPrice]
);
};
export default useProductServicesTableColumns;

View File

@ -0,0 +1,74 @@
import { modals } from "@mantine/modals";
import { DealProductSchema, ProductServiceSchema } from "@/lib/client";
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
type Props = {
dealProduct: DealProductSchema;
};
const useProductServicesTable = ({ dealProduct }: Props) => {
const { productServiceCrud, dealProductsList } =
useFulfillmentBaseContext();
const onChange = (item: ProductServiceSchema) => {
const excludeServiceIds = dealProduct.productServices.map(
productService => productService.service.id
);
const totalQuantity = dealProductsList.dealProducts.reduce(
(sum, prod) => prod.quantity + sum,
0
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
entity: item,
onChange: values =>
productServiceCrud.onUpdate(
item.dealId,
item.productId,
item.serviceId,
values
),
excludeServiceIds,
quantity: totalQuantity,
isEditing: true,
},
withCloseButton: false,
});
};
const onCreate = () => {
const excludeServiceIds = dealProduct.productServices.map(
productService => productService.service.id
);
modals.openContextModal({
modal: "productServiceEditorModal",
innerProps: {
onCreate: values =>
productServiceCrud.onCreate({ ...dealProduct, ...values }),
excludeServiceIds,
quantity: dealProduct.quantity,
isEditing: false,
},
withCloseButton: false,
});
};
const onDelete = productServiceCrud.onDelete;
const isEmptyTable = dealProduct.productServices.length === 0;
const isDuplicateServicesDisabled =
dealProductsList.dealProducts.length <= 1;
return {
onChange,
onCreate,
onDelete,
isEmptyTable,
isDuplicateServicesDisabled,
};
};
export default useProductServicesTable;

View File

@ -2,6 +2,7 @@ import { useMemo } from "react";
import { DataTableColumn } from "mantine-datatable"; import { DataTableColumn } from "mantine-datatable";
import { Box, Text } from "@mantine/core"; import { Box, Text } from "@mantine/core";
import UpdateDeleteTableActions from "@/components/ui/BaseTable/components/UpdateDeleteTableActions"; import UpdateDeleteTableActions from "@/components/ui/BaseTable/components/UpdateDeleteTableActions";
import useIsMobile from "@/hooks/utils/useIsMobile";
import { ProductServiceSchema } from "@/lib/client"; import { ProductServiceSchema } from "@/lib/client";
type Props = { type Props = {
@ -17,6 +18,8 @@ const useProductServicesTableColumns = ({
onChange, onChange,
onDelete, onDelete,
}: Props) => { }: Props) => {
const isMobile = useIsMobile();
const totalPrice = useMemo( const totalPrice = useMemo(
() => data.reduce((acc, row) => acc + row.price * quantity, 0), () => data.reduce((acc, row) => acc + row.price * quantity, 0),
[data, quantity] [data, quantity]
@ -34,21 +37,27 @@ const useProductServicesTableColumns = ({
<UpdateDeleteTableActions <UpdateDeleteTableActions
onDelete={() => onDelete(dealProductService)} onDelete={() => onDelete(dealProductService)}
onChange={() => onChange(dealProductService)} onChange={() => onChange(dealProductService)}
style={{ padding: "var(--mantine-spacing-xs)" }} style={{
padding: isMobile
? 0
: "var(--mantine-spacing-xs)",
}}
/> />
), ),
}, },
{ {
accessor: "service.name", accessor: "service.name",
title: "Услуга", title: "Услуга",
width: "70%",
}, },
{ {
accessor: "price", accessor: "price",
title: "Цена", title: "Цена",
width: "30%",
render: productService => render: productService =>
productService.price.toLocaleString("ru"), productService.price.toLocaleString("ru"),
footer: data.length > 0 && ( footer: data.length > 0 && (
<Box my={"sm"}> <Box my={isMobile ? 0 : "sm"}>
<Text fw={700}> <Text fw={700}>
Итог: {totalPrice.toLocaleString("ru")} Итог: {totalPrice.toLocaleString("ru")}
</Text> </Text>
@ -56,7 +65,7 @@ const useProductServicesTableColumns = ({
), ),
}, },
] as DataTableColumn<ProductServiceSchema>[], ] as DataTableColumn<ProductServiceSchema>[],
[totalPrice] [totalPrice, isMobile]
); );
}; };