feat: products and services tabs for mobile
This commit is contained in:
@ -97,6 +97,7 @@ const DealEditorBody: FC<Props> = props => {
|
||||
orientation={isMobile ? "horizontal" : "vertical"}
|
||||
h={isMobile ? "min-content" : "97vh"}
|
||||
mih={isMobile ? "min-content" : "97vh"}
|
||||
keepMounted={false}
|
||||
classNames={{ tab: styles.tab }}>
|
||||
<TabsList>
|
||||
{getTab("general", "Общая информация", <IconEdit />)}
|
||||
|
||||
@ -4,9 +4,31 @@
|
||||
border-radius: var(--mantine-radius-lg);
|
||||
}
|
||||
|
||||
.shadow {
|
||||
@mixin light {
|
||||
box-shadow: var(--light-shadow);
|
||||
}
|
||||
@mixin dark {
|
||||
box-shadow: var(--dark-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.image-container {
|
||||
display: flex;
|
||||
max-height: rem(250);
|
||||
max-width: rem(250);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.products-swiper :global(.swiper-pagination) {
|
||||
@mixin light {
|
||||
background-color: var(--mantine-color-gray-2);
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-6);
|
||||
}
|
||||
|
||||
border: 1px solid var(--mantine-color-default-border);
|
||||
border-radius: var(--mantine-radius-lg);
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import { isNull } from "lodash";
|
||||
import type { Swiper as SwiperClass } from "swiper/types";
|
||||
import { DealSchema } from "@/lib/client";
|
||||
import ProductsTabSlider from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductsTabSlider/ProductsTabSlider";
|
||||
import ProductsTabTable from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductsTabTable/ProductsTabTable";
|
||||
import ProductsTabViewAffix, {
|
||||
ProductsTabView,
|
||||
} from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductsTabViewAffix/ProductsTabViewAffix";
|
||||
import { FulfillmentBaseContextProvider } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = {
|
||||
@ -7,9 +14,36 @@ type Props = {
|
||||
};
|
||||
|
||||
const ProductsTab: FC<Props> = ({ value }) => {
|
||||
const [view, setView] = useState<ProductsTabView>(ProductsTabView.SLIDER);
|
||||
const swiperRef = useRef<SwiperClass | null>(null);
|
||||
const targetSlideIndex = useRef<number>(null);
|
||||
|
||||
const onProductRowSelect = async (productIndex: number) => {
|
||||
targetSlideIndex.current = productIndex;
|
||||
setView(ProductsTabView.SLIDER);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
view === ProductsTabView.SLIDER &&
|
||||
!isNull(targetSlideIndex.current)
|
||||
) {
|
||||
swiperRef.current?.slideTo(targetSlideIndex.current);
|
||||
targetSlideIndex.current = null;
|
||||
}
|
||||
}, [view]);
|
||||
|
||||
return (
|
||||
<FulfillmentBaseContextProvider deal={value}>
|
||||
<></>
|
||||
{view === ProductsTabView.SLIDER ? (
|
||||
<ProductsTabSlider swiperRef={swiperRef} />
|
||||
) : (
|
||||
<ProductsTabTable onSelect={onProductRowSelect} />
|
||||
)}
|
||||
<ProductsTabViewAffix
|
||||
value={view}
|
||||
onChange={value => setView(value as ProductsTabView)}
|
||||
/>
|
||||
</FulfillmentBaseContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
import { FC } from "react";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { ButtonProps, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import InlineButton from "@/components/ui/InlineButton/InlineButton";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = ButtonProps;
|
||||
|
||||
const AddDealProductButton: FC<Props> = props => {
|
||||
const { dealProductsList, dealProductsCrud, deal } =
|
||||
useFulfillmentBaseContext();
|
||||
|
||||
const onCreateClick = () => {
|
||||
const productIdsToExclude = dealProductsList.dealProducts.map(
|
||||
product => product.product.id
|
||||
);
|
||||
|
||||
modals.openContextModal({
|
||||
modal: "dealProductEditorModal",
|
||||
title: "Добавление товара",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
onCreate: values =>
|
||||
dealProductsCrud.onCreate({ ...values, dealId: deal.id }),
|
||||
productIdsToExclude,
|
||||
isEditing: false,
|
||||
clientId: 0, // TODO add clients
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineButton
|
||||
{...props}
|
||||
onClick={onCreateClick}>
|
||||
<IconPlus />
|
||||
<Text>Добавить товар</Text>
|
||||
</InlineButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDealProductButton;
|
||||
@ -0,0 +1,94 @@
|
||||
import { FC } from "react";
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
Flex,
|
||||
Group,
|
||||
Image,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { DealProductSchema } from "@/lib/client";
|
||||
import ProductFieldsList from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductView/components/ProductFieldsList";
|
||||
import ProductMenu from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu";
|
||||
import ProductServicesTable from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductServicesTable/ProductServicesTable";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
import styles from "../../../FulfillmentBase.module.css";
|
||||
|
||||
type Props = {
|
||||
dealProduct: DealProductSchema;
|
||||
};
|
||||
|
||||
const DealProductView: FC<Props> = ({ dealProduct }) => {
|
||||
const { dealProductsCrud } = useFulfillmentBaseContext();
|
||||
|
||||
const onChangeDealProductClick = () => {
|
||||
modals.openContextModal({
|
||||
modal: "dealProductEditorModal",
|
||||
title: "Добавление товара",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
onChange: values =>
|
||||
dealProductsCrud.onUpdate(
|
||||
dealProduct.dealId,
|
||||
dealProduct.productId,
|
||||
values
|
||||
),
|
||||
entity: dealProduct,
|
||||
isEditing: true,
|
||||
clientId: 0, // TODO add clients
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
p={"md"}
|
||||
h={"100%"}
|
||||
style={{ display: "flex", flexDirection: "column" }}>
|
||||
<Card
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "var(--mantine-spacing-sm)",
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
}}
|
||||
className={styles.shadow}
|
||||
withBorder>
|
||||
<Flex
|
||||
gap={"sm"}
|
||||
wrap={"nowrap"}
|
||||
align={"start"}>
|
||||
<Image
|
||||
src="https://placehold.co/400x500?text=Placeholder"
|
||||
alt={dealProduct.product.name}
|
||||
w={"40%"}
|
||||
bdrs={"md"}
|
||||
/>
|
||||
<Stack
|
||||
gap={0}
|
||||
w={"100%"}>
|
||||
<Group
|
||||
wrap={"nowrap"}
|
||||
justify={"space-between"}>
|
||||
<Title order={3}>{dealProduct.product.name}</Title>
|
||||
<ProductMenu
|
||||
value={dealProduct}
|
||||
onChange={onChangeDealProductClick}
|
||||
onDelete={dealProductsCrud.onDelete}
|
||||
/>
|
||||
</Group>
|
||||
<Text>Количество: {dealProduct.quantity}</Text>
|
||||
<ProductFieldsList product={dealProduct.product} />
|
||||
</Stack>
|
||||
</Flex>
|
||||
<ProductServicesTable dealProduct={dealProduct} />
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DealProductView;
|
||||
@ -0,0 +1,35 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { Title, TitleProps } from "@mantine/core";
|
||||
import { DealProductSchema } from "@/lib/client";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = TitleProps;
|
||||
|
||||
const DealProductsTotalLabel: FC<Props> = ({ order = 4, ...props }) => {
|
||||
const { dealProductsList } = useFulfillmentBaseContext();
|
||||
|
||||
const getProductTotal = (dealProduct: DealProductSchema) =>
|
||||
dealProduct.productServices.reduce(
|
||||
(acc, service) => acc + dealProduct.quantity * service.price,
|
||||
0
|
||||
);
|
||||
|
||||
const total = useMemo(
|
||||
() =>
|
||||
dealProductsList.dealProducts.reduce(
|
||||
(acc, product) => acc + getProductTotal(product),
|
||||
0
|
||||
),
|
||||
[dealProductsList.dealProducts]
|
||||
);
|
||||
|
||||
return (
|
||||
<Title
|
||||
order={order}
|
||||
{...props}>
|
||||
Итог: {total.toLocaleString("ru")}₽
|
||||
</Title>
|
||||
);
|
||||
};
|
||||
|
||||
export default DealProductsTotalLabel;
|
||||
@ -0,0 +1,54 @@
|
||||
import React, { FC } from "react";
|
||||
import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { Box, Group, Menu, Text } from "@mantine/core";
|
||||
import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon";
|
||||
import { DealProductSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
value: DealProductSchema;
|
||||
onChange: (dealProduct: DealProductSchema) => void;
|
||||
onDelete: (dealProduct: DealProductSchema) => void;
|
||||
};
|
||||
|
||||
const ProductMenu: FC<Props> = ({ value, onChange, onDelete }) => {
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Target>
|
||||
<Box
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={e => e.stopPropagation()}>
|
||||
<ThemeIcon
|
||||
bd={0}
|
||||
variant={"default"}
|
||||
size={"sm"}>
|
||||
<IconDotsVertical />
|
||||
</ThemeIcon>
|
||||
</Box>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onChange(value);
|
||||
}}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<IconEdit />
|
||||
<Text>Редактировать</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onDelete(value);
|
||||
}}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<IconTrash />
|
||||
<Text>Удалить</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductMenu;
|
||||
@ -0,0 +1,115 @@
|
||||
import { FC } from "react";
|
||||
import { IconMoodSad } from "@tabler/icons-react";
|
||||
import { Button, Flex, Group, ScrollArea, Text } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||
import { DealProductSchema, ProductServiceSchema } from "@/lib/client";
|
||||
import useProductServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductServicesTable/useProductServicesTableColumns";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = {
|
||||
dealProduct: DealProductSchema;
|
||||
};
|
||||
|
||||
const ProductServicesTable: FC<Props> = ({ dealProduct }) => {
|
||||
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 columns = useProductServicesTableColumns({
|
||||
data: dealProduct.productServices,
|
||||
quantity: dealProduct.quantity,
|
||||
onDelete: productServiceCrud.onDelete,
|
||||
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 (
|
||||
<Flex
|
||||
flex={1}
|
||||
gap={"xs"}
|
||||
direction={"column"}
|
||||
justify={"space-between"}
|
||||
style={{ minHeight: 0 }}>
|
||||
<ScrollArea
|
||||
flex={10}
|
||||
scrollbarSize={10}
|
||||
onTouchStart={e => e.stopPropagation()}
|
||||
onTouchMove={e => e.stopPropagation()}>
|
||||
<BaseTable
|
||||
records={dealProduct.productServices}
|
||||
columns={columns}
|
||||
groups={undefined}
|
||||
idAccessor={"serviceId"}
|
||||
verticalSpacing={"md"}
|
||||
withTableBorder
|
||||
style={{
|
||||
height: isEmptyTable ? "8rem" : "auto",
|
||||
}}
|
||||
emptyState={
|
||||
<Group
|
||||
gap={"xs"}
|
||||
mt={isEmptyTable ? "xl" : 0}>
|
||||
<Text>Нет услуг</Text>
|
||||
<IconMoodSad />
|
||||
</Group>
|
||||
}
|
||||
/>
|
||||
</ScrollArea>
|
||||
<Button
|
||||
flex={1}
|
||||
py={"xs"}
|
||||
onClick={onCreateClick}
|
||||
variant={"default"}>
|
||||
Добавить услугу
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
export default ProductServicesTable;
|
||||
@ -0,0 +1,71 @@
|
||||
import { useMemo } from "react";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { DataTableColumn } from "mantine-datatable";
|
||||
import { ActionIcon, Flex, Text } from "@mantine/core";
|
||||
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 => (
|
||||
<Flex gap="md">
|
||||
<ActionIcon
|
||||
variant={"subtle"}
|
||||
color={"red"}
|
||||
onClick={() => onDelete(dealProductService)}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
variant={"subtle"}
|
||||
onClick={() => onChange(dealProductService)}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
{
|
||||
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;
|
||||
@ -0,0 +1,46 @@
|
||||
import React, { FC, RefObject } from "react";
|
||||
import { Pagination } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import type { Swiper as SwiperClass } from "swiper/types";
|
||||
import { Box } from "@mantine/core";
|
||||
import AddDealProductButton from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/AddDealProductButton/AddDealProductButton";
|
||||
import DealProductView from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/DealProductView/DealProductView";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
import classes from "../../../FulfillmentBase.module.css";
|
||||
|
||||
type Props = {
|
||||
swiperRef: RefObject<SwiperClass | null>;
|
||||
};
|
||||
|
||||
const ProductsTabSlider: FC<Props> = ({ swiperRef }) => {
|
||||
const { dealProductsList } = useFulfillmentBaseContext();
|
||||
|
||||
return (
|
||||
<Swiper
|
||||
onSwiper={swiper => (swiperRef.current = swiper)}
|
||||
spaceBetween={15}
|
||||
modules={[Pagination]}
|
||||
direction={"vertical"}
|
||||
style={{ maxHeight: "calc(100vh - 110px)" }}
|
||||
className={classes["products-swiper"]}
|
||||
pagination={{ enabled: true, clickable: true }}>
|
||||
{dealProductsList.dealProducts.map(dealProduct => (
|
||||
<SwiperSlide>
|
||||
<DealProductView dealProduct={dealProduct} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
<SwiperSlide>
|
||||
<Box
|
||||
p={"md"}
|
||||
h={"100%"}>
|
||||
<AddDealProductButton
|
||||
fullWidth
|
||||
h={"100%"}
|
||||
/>
|
||||
</Box>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductsTabSlider;
|
||||
@ -0,0 +1,41 @@
|
||||
import { FC } from "react";
|
||||
import { Flex, Stack } from "@mantine/core";
|
||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||
import AddDealProductButton from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/AddDealProductButton/AddDealProductButton";
|
||||
import DealProductsTotalLabel from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/DealProductsTotalLabel/DealProductsTotalLabel";
|
||||
import useDealServicesTableColumns from "@/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductsTabTable/hooks/useDealProductsTableColumns";
|
||||
import { useFulfillmentBaseContext } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/contexts/FulfillmentBaseContext";
|
||||
|
||||
type Props = {
|
||||
onSelect: (index: number) => void;
|
||||
};
|
||||
|
||||
const ProductsTabTable: FC<Props> = ({ onSelect }) => {
|
||||
const { dealProductsList } = useFulfillmentBaseContext();
|
||||
const columns = useDealServicesTableColumns();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={"xs"}
|
||||
m={"xs"}>
|
||||
<Flex
|
||||
p={"sm"}
|
||||
bd={"1px solid var(--mantine-color-default-border)"}
|
||||
bdrs={"lg"}>
|
||||
<DealProductsTotalLabel />
|
||||
</Flex>
|
||||
<BaseTable
|
||||
records={dealProductsList.dealProducts}
|
||||
columns={columns}
|
||||
groups={undefined}
|
||||
idAccessor={"productId"}
|
||||
verticalSpacing={"md"}
|
||||
withTableBorder
|
||||
onRowClick={event => onSelect(event.index)}
|
||||
/>
|
||||
<AddDealProductButton size={"lg"} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductsTabTable;
|
||||
@ -0,0 +1,29 @@
|
||||
import { useMemo } from "react";
|
||||
import { DataTableColumn } from "mantine-datatable";
|
||||
import { DealProductSchema } from "@/lib/client";
|
||||
|
||||
const useDealServicesTableColumns = () => {
|
||||
return useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
accessor: "product.name",
|
||||
title: "Название",
|
||||
width: "60%",
|
||||
},
|
||||
{
|
||||
accessor: "quantity",
|
||||
title: "Кол-во",
|
||||
width: "20%",
|
||||
},
|
||||
{
|
||||
accessor: "product.size",
|
||||
title: "Размер",
|
||||
width: "20%",
|
||||
},
|
||||
] as DataTableColumn<DealProductSchema>[],
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
export default useDealServicesTableColumns;
|
||||
@ -0,0 +1,46 @@
|
||||
import { FC } from "react";
|
||||
import { IconLayoutDistributeVertical, IconTable } from "@tabler/icons-react";
|
||||
import {
|
||||
Affix,
|
||||
Center,
|
||||
SegmentedControl,
|
||||
SegmentedControlProps,
|
||||
} from "@mantine/core";
|
||||
import styles from "../../../FulfillmentBase.module.css";
|
||||
|
||||
export enum ProductsTabView {
|
||||
TABLE = "table",
|
||||
SLIDER = "slider",
|
||||
}
|
||||
|
||||
type Props = Omit<SegmentedControlProps, "data">;
|
||||
|
||||
const ProductsTabViewAffix: FC<Props> = props => {
|
||||
return (
|
||||
<Affix
|
||||
bdrs={"xl"}
|
||||
bd={"1px solid var(--mantine-color-default-border"}
|
||||
className={styles.shadow}
|
||||
position={{ bottom: 10, right: 10 }}>
|
||||
<Center>
|
||||
<SegmentedControl
|
||||
bdrs={"xl"}
|
||||
radius={"lg"}
|
||||
data={[
|
||||
{
|
||||
value: ProductsTabView.SLIDER,
|
||||
label: <IconLayoutDistributeVertical />,
|
||||
},
|
||||
{
|
||||
value: ProductsTabView.TABLE,
|
||||
label: <IconTable />,
|
||||
},
|
||||
]}
|
||||
{...props}
|
||||
/>
|
||||
</Center>
|
||||
</Affix>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductsTabViewAffix;
|
||||
@ -12,7 +12,7 @@ import BaseFormModal, {
|
||||
CreateEditFormProps,
|
||||
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||
import ProductSelect
|
||||
from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ProductSelect/ProductSelect";
|
||||
from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/components/ProductSelect/ProductSelect";
|
||||
|
||||
type RestProps = {
|
||||
clientId: number;
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
} from "@mantine/core";
|
||||
import { ObjectSelectProps } from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||
import { ServiceSchema } from "@/lib/client";
|
||||
import ServiceSelect from "@/modules/dealModularEditorTabs/FulfillmentBase/desktop/components/ServiceSelect/ServiceSelect";
|
||||
import ServiceSelect from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/components/ServiceSelect/ServiceSelect";
|
||||
import { ServiceType } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/types/service";
|
||||
|
||||
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, "data">;
|
||||
|
||||
Reference in New Issue
Block a user