feat: deal product services
This commit is contained in:
@@ -1,30 +1,73 @@
|
||||
import {Checkbox, Flex, NumberInput} from "@mantine/core";
|
||||
import ServiceSelectNew from "../../../../components/Selects/ServiceSelectNew/ServiceSelectNew.tsx";
|
||||
import {ServiceType} from "../../../../shared/enums/ServiceType.ts";
|
||||
import {useForm} from "@mantine/form";
|
||||
import {DealProductServiceSchema} from "../../../../client";
|
||||
import {ActionIcon, Button, Flex, Input, rem} from "@mantine/core";
|
||||
import {BaseFormInputProps} from "../../../../types/utils.ts";
|
||||
import {DealProductServiceSchema, ServiceSchema} from "../../../../client";
|
||||
import {FC, useEffect, useState} from "react";
|
||||
import ServiceWithPriceInput from "../../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||
import {isNumber} from "lodash";
|
||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
||||
import {IconTrash} from "@tabler/icons-react";
|
||||
|
||||
const DealProductServiceTable = () => {
|
||||
const initialValues: Partial<DealProductServiceSchema> = {}
|
||||
const form = useForm<Partial<DealProductServiceSchema>>(
|
||||
);
|
||||
type RestProps = {
|
||||
quantity: number;
|
||||
}
|
||||
type Props = BaseFormInputProps<DealProductServiceSchema[]> & RestProps;
|
||||
const DealProductServiceTable: FC<Props> = (props: Props) => {
|
||||
const {value, onChange, quantity, error} = props;
|
||||
const [innerValue, setInnerValue] = useState<Partial<DealProductServiceSchema>[]>(value || []);
|
||||
const onServiceChange = (idx: number, value: ServiceSchema) => {
|
||||
setInnerValue(oldValue => oldValue.map((item, i) => i === idx ? {...item, service: value} : item));
|
||||
}
|
||||
const onQuantityChange = (idx: number, value: string | number) => {
|
||||
if (!isNumber(value)) return;
|
||||
setInnerValue(oldValue => oldValue.map((item, i) => i === idx ? {...item, price: value} : item));
|
||||
}
|
||||
const onCreate = () => {
|
||||
if (innerValue.length > 0 && !innerValue.at(-1)?.service) {
|
||||
notifications.error({message: "Заполните последнюю услугу"})
|
||||
return;
|
||||
}
|
||||
setInnerValue(prevState => [...prevState, {service: undefined, quantity: 1}])
|
||||
}
|
||||
const onDelete = (idx: number) => {
|
||||
setInnerValue(oldValue => oldValue.filter((_, i) => i !== idx));
|
||||
|
||||
}
|
||||
useEffect(() => {
|
||||
onChange(innerValue as DealProductServiceSchema[]);
|
||||
}, [innerValue]);
|
||||
return (
|
||||
<Flex direction={"column"}>
|
||||
<ServiceSelectNew
|
||||
filterType={ServiceType.PRODUCT_SERVICE}
|
||||
onChange={() => {
|
||||
}}/>
|
||||
<NumberInput
|
||||
label={"Количество"}
|
||||
/>
|
||||
<Checkbox
|
||||
label={"Привязать количество услуг к количеству товара"}
|
||||
/>
|
||||
<NumberInput
|
||||
label={"Цена за единицу"}
|
||||
/>
|
||||
<Input.Wrapper error={error}>
|
||||
<Flex direction={"column"} gap={rem(10)}>
|
||||
{innerValue.map((service, idx) => (
|
||||
<Flex key={idx} direction={"row"} gap={rem(10)} align={"center"} justify={"stretch"}>
|
||||
<ActionIcon onClick={() => onDelete(idx)} variant={"default"}>
|
||||
<IconTrash/>
|
||||
</ActionIcon>
|
||||
<ServiceWithPriceInput
|
||||
serviceProps={{
|
||||
onChange: (event) => onServiceChange(idx, event),
|
||||
value: service.service,
|
||||
placeholder: "Выберите услугу",
|
||||
style: {width: "100%"}
|
||||
}}
|
||||
priceProps={{
|
||||
onChange: (event) => onQuantityChange(idx, event),
|
||||
value: service.price,
|
||||
placeholder: "Введите стоимость",
|
||||
hideControls: true,
|
||||
style: {width: "100%"},
|
||||
suffix: "₽"
|
||||
|
||||
</Flex>
|
||||
}}
|
||||
containerProps={{w: "100%"}}
|
||||
quantity={quantity}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
))}
|
||||
<Button onClick={onCreate} variant={"default"}>Добавить услугу</Button>
|
||||
</Flex>
|
||||
</Input.Wrapper>
|
||||
)
|
||||
}
|
||||
export default DealProductServiceTable;
|
||||
@@ -6,7 +6,7 @@ import {DealProductSchema, ProductService} from "../../../../client";
|
||||
import {ActionIcon, Button, Flex, rem, Tooltip} from "@mantine/core";
|
||||
import {MRT_TableOptions} from "mantine-react-table";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {IconBarcode, IconTrash} from "@tabler/icons-react";
|
||||
import {IconBarcode, IconEdit, IconTrash} from "@tabler/icons-react";
|
||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
||||
import {CreateProductRequest} from "../../../ProductsPage/types.ts";
|
||||
|
||||
@@ -39,7 +39,8 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
innerProps: {
|
||||
onCreate: (product) => onCreate(product as DealProductSchema),
|
||||
clientId
|
||||
}
|
||||
},
|
||||
size:"lg"
|
||||
})
|
||||
}
|
||||
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
||||
@@ -71,6 +72,21 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
const onEditClick = (product: DealProductSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "addDealProduct",
|
||||
title: 'Создание товара',
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
clientId: clientId,
|
||||
element: product,
|
||||
onChange: () => {
|
||||
}
|
||||
},
|
||||
size:"lg"
|
||||
|
||||
})
|
||||
}
|
||||
return (
|
||||
<BaseTable
|
||||
data={items}
|
||||
@@ -91,6 +107,11 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
<IconBarcode/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon onClick={() => onEditClick(row.original)} variant={"default"}>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
renderBottomToolbar: ({table}) => (
|
||||
|
||||
@@ -214,8 +214,7 @@ const useDealProductTableState = () => {
|
||||
DealService.addDealProduct({
|
||||
requestBody: {
|
||||
dealId: selectedDeal.id,
|
||||
productId: product.product.id,
|
||||
quantity: product.quantity
|
||||
product: product
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
if (!ok) {
|
||||
|
||||
@@ -1,37 +1,53 @@
|
||||
import {ContextModalProps} from "@mantine/modals";
|
||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import {DealProductSchema} from "../../../client";
|
||||
import {DealProductSchema, DealProductServiceSchema} from "../../../client";
|
||||
import {useForm} from "@mantine/form";
|
||||
import {NumberInput} from "@mantine/core";
|
||||
import {Fieldset, NumberInput} from "@mantine/core";
|
||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||
import DealProductServiceTable from "../components/DealProductsTable/DealProductServiceTable.tsx";
|
||||
import {omit} from "lodash";
|
||||
import {BaseFormInputProps} from "../../../types/utils.ts";
|
||||
|
||||
type RestProps = {
|
||||
clientId: number
|
||||
}
|
||||
|
||||
type Props = CreateEditFormProps<Partial<DealProductSchema>> & RestProps;
|
||||
type Props = CreateEditFormProps<DealProductSchema> & RestProps;
|
||||
const AddDealProductModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps
|
||||
}: ContextModalProps<Props>) => {
|
||||
const isEditing = 'element' in innerProps;
|
||||
const restProps = omit(innerProps, ['clientId']);
|
||||
|
||||
const validateServices = (services?: DealProductServiceSchema[]) => {
|
||||
if (!services || services.length == 0) return null;
|
||||
console.log("validating...");
|
||||
console.log( services.filter(service => service.service === undefined))
|
||||
return services.find(service => service.service === undefined) ? "Удалите пустые услуги" : null;
|
||||
}
|
||||
const form = useForm<Partial<DealProductSchema>>({
|
||||
initialValues: {
|
||||
initialValues: isEditing ? innerProps.element : {
|
||||
product: undefined,
|
||||
quantity: 0
|
||||
services: [],
|
||||
quantity: 1
|
||||
},
|
||||
validate: {
|
||||
product: (product?: DealProductSchema['product']) => product !== undefined ? null : "Необходимо выбрать товар",
|
||||
quantity: (quantity?: number) => (quantity && quantity > 0) ? null : "Количество должно быть больше 0"
|
||||
quantity: (quantity?: number) => (quantity && quantity > 0) ? null : "Количество должно быть больше 0",
|
||||
services: validateServices
|
||||
}
|
||||
});
|
||||
|
||||
const onClose = () => {
|
||||
context.closeContextModal(id);
|
||||
}
|
||||
|
||||
console.log(form.values)
|
||||
return (
|
||||
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
{...restProps as CreateEditFormProps<DealProductSchema>}
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={onClose}>
|
||||
@@ -49,10 +65,19 @@ const AddDealProductModal = ({
|
||||
min={1}
|
||||
{...form.getInputProps('quantity')}
|
||||
/>
|
||||
<Fieldset legend={'Услуги'}>
|
||||
<DealProductServiceTable
|
||||
quantity={form.values.quantity || 1}
|
||||
{...form.getInputProps('services') as
|
||||
BaseFormInputProps<DealProductServiceSchema[]>}
|
||||
/>
|
||||
</Fieldset>
|
||||
|
||||
</>
|
||||
|
||||
</BaseFormModal.Body>
|
||||
</BaseFormModal>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user