feat: additional stuff

This commit is contained in:
2024-08-08 09:07:39 +03:00
parent 1ad96e3a69
commit 75860395b2
6 changed files with 104 additions and 47 deletions

View File

@@ -6,22 +6,22 @@
} }
.container-disabled { .container-disabled {
pointer-events: none;
opacity: 0.4;
} }
.products-list { .products-list {
width: 60%; width: 60%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: rem(10); gap: rem(10);
flex: 2;
} }
.deal-container { .deal-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%;
gap: rem(10); gap: rem(10);
flex: 1;
} }
.deal-container-wrapper { .deal-container-wrapper {

View File

@@ -19,7 +19,7 @@ import classNames from "classnames";
const ProductAndServiceTab: FC = () => { const ProductAndServiceTab: FC = () => {
const {dealState, dealServicesState, dealProductsState} = useDealProductAndServiceTabState(); const {dealState, dealServicesState, dealProductsState} = useDealProductAndServiceTabState();
const isLocked = Boolean(dealState.deal?.billRequest);
const onAddProductClick = () => { const onAddProductClick = () => {
if (!dealProductsState.onCreate || !dealState.deal) return; if (!dealProductsState.onCreate || !dealState.deal) return;
const productIds = dealState.deal.products.map(product => product.product.id); const productIds = dealState.deal.products.map(product => product.product.id);
@@ -130,7 +130,7 @@ const ProductAndServiceTab: FC = () => {
if (!dealState.deal) return; if (!dealState.deal) return;
const dealId = dealState.deal.id; const dealId = dealState.deal.id;
modals.openConfirmModal({ modals.openConfirmModal({
title: "Выставление счета", withCloseButton: false,
size: "xl", size: "xl",
children: children:
<Text style={{textAlign: "justify"}}> <Text style={{textAlign: "justify"}}>
@@ -181,35 +181,47 @@ const ProductAndServiceTab: FC = () => {
</ScrollArea> </ScrollArea>
</div> </div>
<div className={styles['deal-container']}> <div className={styles['deal-container']}>
<Flex direction={"column"} className={styles['deal-container-wrapper']}> <ScrollArea offsetScrollbars>
<DealServicesTable
onKitAdd={onDealKitAdd} <Flex direction={"column"} className={styles['deal-container-wrapper']}>
{...dealServicesState} <DealServicesTable
/> onKitAdd={onDealKitAdd}
<Divider my={rem(15)}/> {...dealServicesState}
<div className={styles['deal-container-buttons']}> />
<Button
variant={"default"} <Divider my={rem(15)}/>
fullWidth <div className={styles['deal-container-buttons']}>
onClick={onCreateProductClick} <Button
>Создать товар</Button> disabled={isLocked}
<Button
onClick={onAddProductClick} variant={"default"}
variant={"default"} fullWidth
fullWidth>Добавить товар</Button> onClick={onCreateProductClick}
</div> >Создать товар</Button>
<Divider my={rem(15)}/> <Button
<div className={styles['deal-container-buttons']}> disabled={isLocked}
<Button
onClick={onCreateBillClick} onClick={onAddProductClick}
variant={"default"} variant={"default"}
fullWidth>Выставить счет</Button> fullWidth>Добавить товар</Button>
</div> </div>
</Flex> <Divider my={rem(15)}/>
<Flex direction={"column"} className={styles['deal-container-wrapper']}> <div className={styles['deal-container-buttons']}>
<Title order={3}>Общая стоимость всех услуг: {getTotalPrice().toLocaleString("ru")}</Title> <Button
</Flex> disabled={isLocked}
onClick={onCreateBillClick}
variant={"default"}
fullWidth>Выставить счет</Button>
</div>
</Flex>
<Flex direction={"column"} className={styles['deal-container-wrapper']}>
<Title order={3}>Общая стоимость всех услуг: {getTotalPrice().toLocaleString("ru")}</Title>
</Flex>
</ScrollArea>
</div> </div>
</div> </div>

View File

@@ -9,6 +9,7 @@ import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUser
import {ServiceType} from "../../../../../../shared/enums/ServiceType.ts"; import {ServiceType} from "../../../../../../shared/enums/ServiceType.ts";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import {RootState} from "../../../../../../redux/store.ts"; import {RootState} from "../../../../../../redux/store.ts";
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
type RestProps = { type RestProps = {
onKitAdd?: (kit: GetServiceKitSchema) => void onKitAdd?: (kit: GetServiceKitSchema) => void
@@ -17,6 +18,9 @@ type Props = CRUDTableProps<DealServiceSchema> & RestProps;
const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKitAdd}) => { const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKitAdd}) => {
const authState = useSelector((state: RootState) => state.auth); const authState = useSelector((state: RootState) => state.auth);
const {dealState} = useDealProductAndServiceTabState();
const isLocked = Boolean(dealState.deal?.billRequest);
const [currentService, setCurrentService] = useState<DealServiceSchema | undefined>(); const [currentService, setCurrentService] = useState<DealServiceSchema | undefined>();
const [employeesModalVisible, setEmployeesModalVisible] = useState(false); const [employeesModalVisible, setEmployeesModalVisible] = useState(false);
@@ -81,7 +85,8 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
onSelect: onKitAdd, onSelect: onKitAdd,
serviceType: ServiceType.DEAL_SERVICE serviceType: ServiceType.DEAL_SERVICE
}, },
title: 'Печать штрихкода', withCloseButton: false
}) })
} }
return ( return (
@@ -101,11 +106,11 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
style={{textAlign: "center"}} style={{textAlign: "center"}}
mb={rem(10)} mb={rem(10)}
>Общие услуги</Title> >Общие услуги</Title>
<Flex <Flex
direction={"column"} direction={"column"}
gap={rem(10)} gap={rem(10)}
> >
{items.map(service => ( {items.map(service => (
<Flex <Flex
key={service.service.id} key={service.service.id}
@@ -114,9 +119,11 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
align={"center"} align={"center"}
> >
<Tooltip <Tooltip
onClick={() => onDeleteClick(service)} onClick={() => onDeleteClick(service)}
label="Удалить услугу"> label="Удалить услугу">
<ActionIcon <ActionIcon
disabled={isLocked}
variant={"default"}> variant={"default"}>
<IconTrash/> <IconTrash/>
</ActionIcon> </ActionIcon>
@@ -132,15 +139,18 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
flex={1} flex={1}
>{service.service.name}</Text> >{service.service.name}</Text>
<NumberInput <NumberInput
disabled={isLocked}
flex={1}
suffix={" шт."} suffix={" шт."}
onChange={event => isNumber(event) && onQuantityChange(service, event)} onChange={event => isNumber(event) && onQuantityChange(service, event)}
value={service.quantity} value={service.quantity}
/> />
<NumberInput <NumberInput
flex={1}
onChange={event => isNumber(event) && onPriceChange(service, event)} onChange={event => isNumber(event) && onPriceChange(service, event)}
suffix={"₽"} suffix={"₽"}
value={service.price} value={service.price}
disabled={authState.isGuest} disabled={authState.isGuest || isLocked}
/> />
</Flex> </Flex>
))} ))}
@@ -152,13 +162,15 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
order={3} order={3}
>Итог: {items.reduce((acc, item) => acc + (item.price * item.quantity), 0)}</Title> >Итог: {items.reduce((acc, item) => acc + (item.price * item.quantity), 0)}</Title>
</Flex> </Flex>
<Flex direction={"column"} gap={rem(10)} mt={"auto"}> <Flex direction={"column"} gap={rem(10)} mt={"auto"}>
<Button <Button
disabled={isLocked}
onClick={onCreateClick} onClick={onCreateClick}
fullWidth fullWidth
variant={"default"} variant={"default"}
>Добавить услугу</Button> >Добавить услугу</Button>
<Button <Button
disabled={isLocked}
onClick={onAddKitClick} onClick={onAddKitClick}
fullWidth fullWidth
variant={"default"} variant={"default"}

View File

@@ -10,6 +10,7 @@ import {modals} from "@mantine/modals";
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx"; import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import {RootState} from "../../../../../../redux/store.ts"; import {RootState} from "../../../../../../redux/store.ts";
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
type RestProps = { type RestProps = {
quantity: number; quantity: number;
@@ -27,6 +28,8 @@ const ProductServicesTable: FC<Props> = ({
onCopyServices, onCopyServices,
onKitAdd onKitAdd
}) => { }) => {
const {dealState} = useDealProductAndServiceTabState();
const isLocked = Boolean(dealState.deal?.billRequest);
const authState = useSelector((state: RootState) => state.auth); const authState = useSelector((state: RootState) => state.auth);
const columns = useProductServicesTableColumns({data: items, quantity}); const columns = useProductServicesTableColumns({data: items, quantity});
@@ -99,14 +102,26 @@ const ProductServicesTable: FC<Props> = ({
enableBottomToolbar: true, enableBottomToolbar: true,
renderBottomToolbar: ( renderBottomToolbar: (
<Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}> <Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}>
<Button onClick={() => onKitAdd && onKitAdd()} variant={"default"}> <Button
disabled={isLocked}
onClick={() => onKitAdd && onKitAdd()}
variant={"default"}
>
Добавить набор услуг Добавить набор услуг
</Button> </Button>
<Button onClick={() => onCopyServices && onCopyServices()} variant={"default"}> <Button
disabled={isLocked}
onClick={() => onCopyServices && onCopyServices()}
variant={"default"}
>
Продублировать услуги Продублировать услуги
</Button> </Button>
<Button onClick={onCreateClick} variant={"default"}> <Button
disabled={isLocked}
onClick={onCreateClick}
variant={"default"}
>
Добавить услугу Добавить услугу
</Button> </Button>
@@ -115,14 +130,19 @@ const ProductServicesTable: FC<Props> = ({
renderRowActions: ({row}) => ( renderRowActions: ({row}) => (
<Flex gap="md"> <Flex gap="md">
<Tooltip label="Удалить"> <Tooltip label="Удалить">
<ActionIcon onClick={() => { <ActionIcon
if (onDelete) onDelete(row.original);
}} variant={"default"}> onClick={() => {
if (onDelete) onDelete(row.original);
}} variant={"default"}>
<IconTrash/> <IconTrash/>
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label="Редактировать"> <Tooltip
label="Редактировать">
<ActionIcon <ActionIcon
onClick={() => onChangeClick(row.original)} onClick={() => onChangeClick(row.original)}
variant={"default"}> variant={"default"}>
<IconEdit/> <IconEdit/>

View File

@@ -5,6 +5,7 @@
gap: rem(20); gap: rem(20);
padding: rem(10); padding: rem(10);
margin-bottom: rem(10); margin-bottom: rem(10);
flex: 1;
} }
.image-container { .image-container {
@@ -26,6 +27,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: rem(10); gap: rem(10);
flex: 1;
} }
.attributes-container { .attributes-container {

View File

@@ -6,12 +6,13 @@ import {
ProductSchema ProductSchema
} from "../../../../../../client"; } from "../../../../../../client";
import styles from './ProductView.module.css'; import styles from './ProductView.module.css';
import {ActionIcon, Flex, Image, NumberInput, rem, Spoiler, Text, Title, Tooltip} from '@mantine/core'; import {ActionIcon, Box, Flex, Image, NumberInput, rem, Spoiler, Text, Title, Tooltip} from '@mantine/core';
import ProductServicesTable from "../ProductServicesTable/ProductServicesTable.tsx"; import ProductServicesTable from "../ProductServicesTable/ProductServicesTable.tsx";
import {isNil, isNumber} from "lodash"; import {isNil, isNumber} from "lodash";
import {IconBarcode, IconEdit, IconTrash} from "@tabler/icons-react"; import {IconBarcode, IconEdit, IconTrash} from "@tabler/icons-react";
import {modals} from "@mantine/modals"; import {modals} from "@mantine/modals";
import {ServiceType} from "../../../../../../shared/enums/ServiceType.ts"; import {ServiceType} from "../../../../../../shared/enums/ServiceType.ts";
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
type Props = { type Props = {
product: DealProductSchema; product: DealProductSchema;
@@ -40,6 +41,8 @@ const ProductView: FC<Props> = ({
onKitAdd, onKitAdd,
onProductEdit onProductEdit
}) => { }) => {
const {dealState} = useDealProductAndServiceTabState();
const isLocked = Boolean(dealState.deal?.billRequest);
const onDeleteClick = () => { const onDeleteClick = () => {
if (!onDelete) return; if (!onDelete) return;
onDelete(product); onDelete(product);
@@ -95,7 +98,7 @@ const ProductView: FC<Props> = ({
onSelect: (kit) => onKitAdd(product, kit), onSelect: (kit) => onKitAdd(product, kit),
serviceType: ServiceType.PRODUCT_SERVICE serviceType: ServiceType.PRODUCT_SERVICE
}, },
title: 'Печать штрихкода', withCloseButton: false
}) })
} }
@@ -134,7 +137,10 @@ const ProductView: FC<Props> = ({
</Spoiler> </Spoiler>
</div> </div>
<Box/>
<NumberInput <NumberInput
mt={rem(10)}
disabled={isLocked}
suffix={" шт."} suffix={" шт."}
value={product.quantity} value={product.quantity}
onChange={event => isNumber(event) && onQuantityChange(event)} onChange={event => isNumber(event) && onQuantityChange(event)}
@@ -158,9 +164,11 @@ const ProductView: FC<Props> = ({
gap={rem(10)} gap={rem(10)}
> >
<Tooltip <Tooltip
onClick={onPrintBarcodeClick} onClick={onPrintBarcodeClick}
label="Печать штрихкода"> label="Печать штрихкода">
<ActionIcon <ActionIcon
variant={"default"}> variant={"default"}>
<IconBarcode/> <IconBarcode/>
</ActionIcon> </ActionIcon>
@@ -169,13 +177,16 @@ const ProductView: FC<Props> = ({
onClick={onProductEditClick} onClick={onProductEditClick}
label="Редактировать товар"> label="Редактировать товар">
<ActionIcon <ActionIcon
variant={"default"}> variant={"default"}>
<IconEdit/> <IconEdit/>
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip onClick={onDeleteClick} label="Удалить товар"> <Tooltip onClick={onDeleteClick} label="Удалить товар">
<ActionIcon <ActionIcon
variant={"default"}> disabled={isLocked}
variant={"default"}
>
<IconTrash/> <IconTrash/>
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>