feat: deal product services
This commit is contained in:
@@ -70,8 +70,12 @@ export type { DealUpdateGeneralInfoRequest } from './models/DealUpdateGeneralInf
|
|||||||
export type { DealUpdateGeneralInfoResponse } from './models/DealUpdateGeneralInfoResponse';
|
export type { DealUpdateGeneralInfoResponse } from './models/DealUpdateGeneralInfoResponse';
|
||||||
export type { DealUpdateProductQuantityRequest } from './models/DealUpdateProductQuantityRequest';
|
export type { DealUpdateProductQuantityRequest } from './models/DealUpdateProductQuantityRequest';
|
||||||
export type { DealUpdateProductQuantityResponse } from './models/DealUpdateProductQuantityResponse';
|
export type { DealUpdateProductQuantityResponse } from './models/DealUpdateProductQuantityResponse';
|
||||||
|
export type { DealUpdateProductRequest } from './models/DealUpdateProductRequest';
|
||||||
|
export type { DealUpdateProductResponse } from './models/DealUpdateProductResponse';
|
||||||
export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServiceQuantityRequest';
|
export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServiceQuantityRequest';
|
||||||
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
||||||
|
export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest';
|
||||||
|
export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse';
|
||||||
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
|
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
|
||||||
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
|
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
|
||||||
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
|
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export type DealAddServiceRequest = {
|
|||||||
dealId: number;
|
dealId: number;
|
||||||
serviceId: number;
|
serviceId: number;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
|
price: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
10
src/client/models/DealUpdateProductRequest.ts
Normal file
10
src/client/models/DealUpdateProductRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { DealProductSchema } from './DealProductSchema';
|
||||||
|
export type DealUpdateProductRequest = {
|
||||||
|
dealId: number;
|
||||||
|
product: DealProductSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealUpdateProductResponse.ts
Normal file
9
src/client/models/DealUpdateProductResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealUpdateProductResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/DealUpdateServiceRequest.ts
Normal file
10
src/client/models/DealUpdateServiceRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { DealServiceSchema } from './DealServiceSchema';
|
||||||
|
export type DealUpdateServiceRequest = {
|
||||||
|
dealId: number;
|
||||||
|
service: DealServiceSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealUpdateServiceResponse.ts
Normal file
9
src/client/models/DealUpdateServiceResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealUpdateServiceResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -31,8 +31,12 @@ import type { DealUpdateGeneralInfoRequest } from '../models/DealUpdateGeneralIn
|
|||||||
import type { DealUpdateGeneralInfoResponse } from '../models/DealUpdateGeneralInfoResponse';
|
import type { DealUpdateGeneralInfoResponse } from '../models/DealUpdateGeneralInfoResponse';
|
||||||
import type { DealUpdateProductQuantityRequest } from '../models/DealUpdateProductQuantityRequest';
|
import type { DealUpdateProductQuantityRequest } from '../models/DealUpdateProductQuantityRequest';
|
||||||
import type { DealUpdateProductQuantityResponse } from '../models/DealUpdateProductQuantityResponse';
|
import type { DealUpdateProductQuantityResponse } from '../models/DealUpdateProductQuantityResponse';
|
||||||
|
import type { DealUpdateProductRequest } from '../models/DealUpdateProductRequest';
|
||||||
|
import type { DealUpdateProductResponse } from '../models/DealUpdateProductResponse';
|
||||||
import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServiceQuantityRequest';
|
import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServiceQuantityRequest';
|
||||||
import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse';
|
import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse';
|
||||||
|
import type { DealUpdateServiceRequest } from '../models/DealUpdateServiceRequest';
|
||||||
|
import type { DealUpdateServiceResponse } from '../models/DealUpdateServiceResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
import { OpenAPI } from '../core/OpenAPI';
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
import { request as __request } from '../core/request';
|
import { request as __request } from '../core/request';
|
||||||
@@ -241,7 +245,7 @@ export class DealService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Services Update
|
* Services Update Quantity
|
||||||
* @returns DealUpdateServiceQuantityResponse Successful Response
|
* @returns DealUpdateServiceQuantityResponse Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
@@ -260,6 +264,26 @@ export class DealService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Services Update
|
||||||
|
* @returns DealUpdateServiceResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static updateDealService({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealUpdateServiceRequest,
|
||||||
|
}): CancelablePromise<DealUpdateServiceResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/services/update',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Services Delete
|
* Services Delete
|
||||||
* @returns DealDeleteServiceResponse Successful Response
|
* @returns DealDeleteServiceResponse Successful Response
|
||||||
@@ -380,4 +404,24 @@ export class DealService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Products Update
|
||||||
|
* @returns DealUpdateProductResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static updateDealProduct({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealUpdateProductRequest,
|
||||||
|
}): CancelablePromise<DealUpdateProductResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/product/update',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ const DealSummaryCard: FC<Props> = ({dealSummary}) => {
|
|||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
const diff = deadlineDate.getTime() - currentDate.getTime();
|
const diff = deadlineDate.getTime() - currentDate.getTime();
|
||||||
const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
|
const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
|
||||||
|
if (diffDays < 0)
|
||||||
|
return 'grey.8'; // for past deadlines
|
||||||
if (diffDays === 1) {
|
if (diffDays === 1) {
|
||||||
return 'yellow.8';
|
return 'yellow.8';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Select, SelectProps} from "@mantine/core";
|
import {Select, SelectProps} from "@mantine/core";
|
||||||
import {useEffect, useMemo, useState} from "react";
|
import {useEffect, useMemo, useState} from "react";
|
||||||
import {ObjectWithNameAndId} from "../../types/utils.ts";
|
import {ObjectWithNameAndId} from "../../types/utils.ts";
|
||||||
import {groupBy} from "lodash";
|
import {groupBy, omit} from "lodash";
|
||||||
|
|
||||||
|
|
||||||
export type SelectObjectType<T extends ObjectWithNameAndId> = T;
|
export type SelectObjectType<T extends ObjectWithNameAndId> = T;
|
||||||
@@ -16,6 +16,7 @@ type RestProps<T extends ObjectWithNameAndId> = {
|
|||||||
onChange: (value: SelectObjectType<T>) => void;
|
onChange: (value: SelectObjectType<T>) => void;
|
||||||
data: SelectObjectType<T>[];
|
data: SelectObjectType<T>[];
|
||||||
groupBy?: (item: SelectObjectType<T>) => string;
|
groupBy?: (item: SelectObjectType<T>) => string;
|
||||||
|
filterBy?: (item: SelectObjectType<T>) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ObjectSelectProps<T extends ObjectWithNameAndId> =
|
export type ObjectSelectProps<T extends ObjectWithNameAndId> =
|
||||||
@@ -30,8 +31,10 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
|
|||||||
const value = isControlled ? props.value : internalValue;
|
const value = isControlled ? props.value : internalValue;
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
|
const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
|
||||||
if (props.groupBy) {
|
if (props.groupBy) {
|
||||||
const groupedData = groupBy(props.data, props.groupBy);
|
|
||||||
|
const groupedData = groupBy(propsData, props.groupBy);
|
||||||
return Object.entries(groupedData).map(([group, items]) => ({
|
return Object.entries(groupedData).map(([group, items]) => ({
|
||||||
group,
|
group,
|
||||||
items: items.map(item => ({
|
items: items.map(item => ({
|
||||||
@@ -40,7 +43,7 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
|
|||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
return props.data.map(item => ({
|
return propsData.map(item => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
value: item.id.toString()
|
value: item.id.toString()
|
||||||
}));
|
}));
|
||||||
@@ -62,10 +65,10 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
|
|||||||
if (isControlled || !internalValue) return;
|
if (isControlled || !internalValue) return;
|
||||||
props.onChange(internalValue);
|
props.onChange(internalValue);
|
||||||
}, [internalValue]);
|
}, [internalValue]);
|
||||||
|
const restProps = omit(props, ['filterBy', 'groupBy']);
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
{...props}
|
{...restProps}
|
||||||
value={value?.id.toString()}
|
value={value?.id.toString()}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
data={data}
|
data={data}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {ProductSchema} from "../../client";
|
import {ProductSchema} from "../../client";
|
||||||
import {Select, SelectProps} from "@mantine/core";
|
import {FC} from "react";
|
||||||
import {FC, useEffect, useMemo, useState} from "react";
|
|
||||||
import useProductsList from "../../pages/ProductsPage/hooks/useProductsList.tsx";
|
import useProductsList from "../../pages/ProductsPage/hooks/useProductsList.tsx";
|
||||||
import {omit} from "lodash";
|
import {omit} from "lodash";
|
||||||
import ObjectSelect, {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
|
import ObjectSelect, {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
import {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
|
import {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
|
||||||
import {ServiceSchema} from "../../client";
|
import {ServiceSchema} from "../../client";
|
||||||
import {Flex, FlexProps, NumberInput, NumberInputProps, rem} from "@mantine/core";
|
import {ActionIcon, Flex, FlexProps, NumberInput, NumberInputProps, rem} from "@mantine/core";
|
||||||
import {FC, useEffect, useState} from "react";
|
import {FC, useEffect, useRef, useState} from "react";
|
||||||
import ServiceSelectNew from "../Selects/ServiceSelectNew/ServiceSelectNew.tsx";
|
import ServiceSelectNew from "../Selects/ServiceSelectNew/ServiceSelectNew.tsx";
|
||||||
|
import {ServiceType} from "../../shared/enums/ServiceType.ts";
|
||||||
|
import {IconReload, IconTrash, IconUpload} from "@tabler/icons-react";
|
||||||
|
|
||||||
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, 'data'>;
|
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, 'data'>;
|
||||||
type PriceProps = NumberInputProps;
|
type PriceProps = NumberInputProps;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
serviceProps: ServiceProps,
|
serviceProps: ServiceProps,
|
||||||
priceProps: PriceProps
|
priceProps: PriceProps,
|
||||||
quantity: number;
|
quantity: number,
|
||||||
containerProps: FlexProps
|
containerProps: FlexProps,
|
||||||
|
filterType?: ServiceType
|
||||||
}
|
}
|
||||||
const ServiceWithPriceInput: FC<Props> = ({serviceProps, priceProps, quantity, containerProps}) => {
|
const ServiceWithPriceInput: FC<Props> = ({
|
||||||
|
serviceProps,
|
||||||
|
priceProps,
|
||||||
|
quantity,
|
||||||
|
containerProps,
|
||||||
|
filterType = ServiceType.PRODUCT_SERVICE
|
||||||
|
}) => {
|
||||||
const [price, setPrice] = useState<number | undefined>(
|
const [price, setPrice] = useState<number | undefined>(
|
||||||
typeof priceProps.value === 'number' ? priceProps.value : undefined);
|
typeof priceProps.value === 'number' ? priceProps.value : undefined);
|
||||||
const [service, setService] = useState<ServiceSchema | undefined>(serviceProps.value);
|
const [service, setService] = useState<ServiceSchema | undefined>(serviceProps.value);
|
||||||
|
const isFirstRender = useRef(true);
|
||||||
const setPriceBasedOnQuantity = (): boolean => {
|
const setPriceBasedOnQuantity = (): boolean => {
|
||||||
if (!service || !service.priceRanges.length) return false;
|
if (!service || !service.priceRanges.length) return false;
|
||||||
const range = service.priceRanges.find(priceRange =>
|
const range = service.priceRanges.find(priceRange =>
|
||||||
quantity >= priceRange.fromQuantity && quantity <= priceRange.toQuantity) || service.priceRanges[0];
|
quantity >= priceRange.fromQuantity && quantity <= priceRange.toQuantity) || service.priceRanges[0];
|
||||||
|
|
||||||
setPrice(range.price);
|
setPrice(range.price);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -38,34 +48,56 @@ const ServiceWithPriceInput: FC<Props> = ({serviceProps, priceProps, quantity, c
|
|||||||
setPrice(value);
|
setPrice(value);
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isFirstRender.current) return;
|
||||||
|
|
||||||
setPriceBasedOnQuantity();
|
setPriceBasedOnQuantity();
|
||||||
}, [quantity]);
|
}, [quantity]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!priceProps.onChange || !price) return;
|
if (isFirstRender.current) return;
|
||||||
|
|
||||||
|
if (!priceProps.onChange || typeof price === 'undefined') return;
|
||||||
priceProps.onChange(price);
|
priceProps.onChange(price);
|
||||||
}, [price]);
|
}, [price]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// if (!isFirstRender) setPrice(0);
|
||||||
|
// if (isFirstRender.current && price) return;
|
||||||
if (!serviceProps.onChange || !service) return;
|
if (!serviceProps.onChange || !service) return;
|
||||||
|
if (price && isFirstRender.current) return;
|
||||||
setPriceBasedOnService();
|
setPriceBasedOnService();
|
||||||
serviceProps.onChange(service);
|
serviceProps.onChange(service);
|
||||||
}, [service]);
|
}, [service]);
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('first render')
|
||||||
|
isFirstRender.current = false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onReload = () => {
|
||||||
|
setPriceBasedOnService();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
{...containerProps}
|
align={"center"}
|
||||||
gap={rem(10)}
|
gap={rem(10)}
|
||||||
|
{...containerProps}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<ActionIcon variant={"default"}>
|
||||||
|
<IconReload onClick={() => onReload()}/>
|
||||||
|
</ActionIcon>
|
||||||
<ServiceSelectNew
|
<ServiceSelectNew
|
||||||
{...serviceProps}
|
{...serviceProps}
|
||||||
value={service}
|
value={service}
|
||||||
onChange={onServiceManualChange}
|
onChange={onServiceManualChange}
|
||||||
|
filterType={filterType}
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
{...priceProps}
|
{...priceProps}
|
||||||
onChange={onPriceManualChange}
|
onChange={onPriceManualChange}
|
||||||
value={price}
|
value={price}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ type Props = {
|
|||||||
const BarcodeTemplateAdditionalFieldTable: FC<Props> = (props: Props) => {
|
const BarcodeTemplateAdditionalFieldTable: FC<Props> = (props: Props) => {
|
||||||
const {value, onChange} = props;
|
const {value, onChange} = props;
|
||||||
const [innerValue, setInnerValue] = useState<FieldType[]>(props.value || []);
|
const [innerValue, setInnerValue] = useState<FieldType[]>(props.value || []);
|
||||||
console.log(innerValue);
|
|
||||||
const onNameChange = (field: FieldType, newName: string) => {
|
const onNameChange = (field: FieldType, newName: string) => {
|
||||||
const newField = {...field, name: newName};
|
const newField = {...field, name: newName};
|
||||||
const newFields = innerValue.map(f => f === field ? newField : f);
|
const newFields = innerValue.map(f => f === field ? newField : f);
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ const BarcodeTemplateFormModal = ({
|
|||||||
name: (name: string | undefined) => name && name.trim() !== '' ? null : "Необходимо ввести название шаблона",
|
name: (name: string | undefined) => name && name.trim() !== '' ? null : "Необходимо ввести название шаблона",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(form.values.additionalAttributes);
|
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
{...innerProps}
|
{...innerProps}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {ActionIcon, Button, Flex, Input, rem} from "@mantine/core";
|
import {ActionIcon, Button, ComboboxItem, ComboboxItemGroup, Flex, Input, OptionsFilter, rem} from "@mantine/core";
|
||||||
import {BaseFormInputProps} from "../../../../types/utils.ts";
|
import {BaseFormInputProps} from "../../../../types/utils.ts";
|
||||||
import {DealProductServiceSchema, ServiceSchema} from "../../../../client";
|
import {DealProductServiceSchema, ServiceSchema} from "../../../../client";
|
||||||
import {FC, useEffect, useState} from "react";
|
import {FC, useEffect, useState} from "react";
|
||||||
@@ -32,6 +32,16 @@ const DealProductServiceTable: FC<Props> = (props: Props) => {
|
|||||||
setInnerValue(oldValue => oldValue.filter((_, i) => i !== idx));
|
setInnerValue(oldValue => oldValue.filter((_, i) => i !== idx));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serviceOptionsFilter = ({options}: { options: ComboboxItemGroup[] }) => {
|
||||||
|
const productServiceIds = innerValue.map(service => service.service?.id);
|
||||||
|
return (options as ComboboxItemGroup[]).map(({items, group}) => {
|
||||||
|
return {
|
||||||
|
group,
|
||||||
|
items: items.filter(item => !productServiceIds.includes(parseInt((item as ComboboxItem).value)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange(innerValue as DealProductServiceSchema[]);
|
onChange(innerValue as DealProductServiceSchema[]);
|
||||||
}, [innerValue]);
|
}, [innerValue]);
|
||||||
@@ -39,7 +49,8 @@ const DealProductServiceTable: FC<Props> = (props: Props) => {
|
|||||||
<Input.Wrapper error={error}>
|
<Input.Wrapper error={error}>
|
||||||
<Flex direction={"column"} gap={rem(10)}>
|
<Flex direction={"column"} gap={rem(10)}>
|
||||||
{innerValue.map((service, idx) => (
|
{innerValue.map((service, idx) => (
|
||||||
<Flex key={idx} direction={"row"} gap={rem(10)} align={"center"} justify={"stretch"}>
|
<Flex key={service.service?.name || idx} direction={"row"} gap={rem(10)} align={"center"}
|
||||||
|
justify={"stretch"}>
|
||||||
<ActionIcon onClick={() => onDelete(idx)} variant={"default"}>
|
<ActionIcon onClick={() => onDelete(idx)} variant={"default"}>
|
||||||
<IconTrash/>
|
<IconTrash/>
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
@@ -48,7 +59,8 @@ const DealProductServiceTable: FC<Props> = (props: Props) => {
|
|||||||
onChange: (event) => onServiceChange(idx, event),
|
onChange: (event) => onServiceChange(idx, event),
|
||||||
value: service.service,
|
value: service.service,
|
||||||
placeholder: "Выберите услугу",
|
placeholder: "Выберите услугу",
|
||||||
style: {width: "100%"}
|
style: {width: "100%"},
|
||||||
|
filter: serviceOptionsFilter as OptionsFilter
|
||||||
}}
|
}}
|
||||||
priceProps={{
|
priceProps={{
|
||||||
onChange: (event) => onQuantityChange(idx, event),
|
onChange: (event) => onQuantityChange(idx, event),
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
|||||||
onCreate: (product) => onCreate(product as DealProductSchema),
|
onCreate: (product) => onCreate(product as DealProductSchema),
|
||||||
clientId
|
clientId
|
||||||
},
|
},
|
||||||
size:"lg"
|
size: "lg"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
||||||
@@ -73,6 +73,7 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
const onEditClick = (product: DealProductSchema) => {
|
const onEditClick = (product: DealProductSchema) => {
|
||||||
|
if (!onChange) return;
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "addDealProduct",
|
modal: "addDealProduct",
|
||||||
title: 'Создание товара',
|
title: 'Создание товара',
|
||||||
@@ -80,10 +81,9 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
|||||||
innerProps: {
|
innerProps: {
|
||||||
clientId: clientId,
|
clientId: clientId,
|
||||||
element: product,
|
element: product,
|
||||||
onChange: () => {
|
onChange: onChange
|
||||||
}
|
|
||||||
},
|
},
|
||||||
size:"lg"
|
size: "lg"
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {MRT_ColumnDef} from "mantine-react-table";
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
import {DealProductSchema} from "../../../../client";
|
import {DealProductSchema} from "../../../../client";
|
||||||
import PlusMinusInput from "../../../../components/PlusMinusInput/PlusMinusInput.tsx";
|
|
||||||
import {List} from "@mantine/core";
|
import {List} from "@mantine/core";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -11,7 +10,7 @@ type Props = {
|
|||||||
const useDealProductsTableColumns = (props: Props) => {
|
const useDealProductsTableColumns = (props: Props) => {
|
||||||
const {onChange, data} = props;
|
const {onChange, data} = props;
|
||||||
const totalQuantity = useMemo(() => data.reduce((acc, row) => acc + row.quantity, 0), [data]);
|
const totalQuantity = useMemo(() => data.reduce((acc, row) => acc + row.quantity, 0), [data]);
|
||||||
|
const totalPrice = useMemo(() => data.reduce((totalAcc, row) => totalAcc + row.services.reduce((singleAcc, service) => singleAcc + service.price * row.quantity, 0), 0), [data]);
|
||||||
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(() => [
|
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(() => [
|
||||||
{
|
{
|
||||||
accessorKey: "product.article",
|
accessorKey: "product.article",
|
||||||
@@ -50,14 +49,24 @@ const useDealProductsTableColumns = (props: Props) => {
|
|||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
Footer: <>Всего товаров: {totalQuantity} </>,
|
Footer: <>Всего товаров: {totalQuantity} </>,
|
||||||
Cell: ({row}) => {
|
},
|
||||||
return (
|
{
|
||||||
<PlusMinusInput
|
header: "Услуги",
|
||||||
value={row.original.quantity}
|
Cell: ({row}) => <List size={"sm"}>{
|
||||||
onChange={(value) => onChange(row.original, value)}
|
row.original.services.map(service => `${service.service.name} (${service.price}₽ за шт)`)
|
||||||
/>
|
.map(serviceText => <List.Item key={serviceText}>
|
||||||
)
|
{serviceText}
|
||||||
}
|
</List.Item>
|
||||||
|
)}
|
||||||
|
</List>,
|
||||||
|
enableColumnActions: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Итоговая стоимость услуг",
|
||||||
|
Cell: ({row}) => <>
|
||||||
|
{row.original.services.reduce((acc, service) => acc + row.original.quantity * service.price, 0)}</>,
|
||||||
|
enableColumnActions: false,
|
||||||
|
Footer: <>Всего стоимость услуг: {totalPrice}</>
|
||||||
}
|
}
|
||||||
], [onChange, data])
|
], [onChange, data])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
|||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
import {ActionIcon, Button, Flex, rem, Tooltip} from "@mantine/core";
|
import {ActionIcon, Button, Flex, rem, Tooltip} from "@mantine/core";
|
||||||
import {openContextModal} from "@mantine/modals";
|
import {openContextModal} from "@mantine/modals";
|
||||||
import {IconTrash} from "@tabler/icons-react";
|
import {IconEdit, IconTrash} from "@tabler/icons-react";
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
onMultipleDelete?: (items: DealServiceSchema[]) => void;
|
onMultipleDelete?: (items: DealServiceSchema[]) => void;
|
||||||
@@ -22,13 +22,15 @@ const DealServicesTable: FC<Props> = (
|
|||||||
onMultipleDelete,
|
onMultipleDelete,
|
||||||
tableRef
|
tableRef
|
||||||
}) => {
|
}) => {
|
||||||
|
const serviceIds = items.map(item => item.service.id);
|
||||||
const onQuantityChange = (service: DealServiceSchema, quantity: number) => {
|
const onQuantityChange = (service: DealServiceSchema, quantity: number) => {
|
||||||
if (!onChange) return;
|
return;
|
||||||
if (quantity <= 0 && onDelete) {
|
// if (!onChange) return;
|
||||||
onDelete(service);
|
// if (quantity <= 0 && onDelete) {
|
||||||
return;
|
// onDelete(service);
|
||||||
}
|
// return;
|
||||||
onChange({...service, quantity});
|
// }
|
||||||
|
// onChange({...service, quantity});
|
||||||
}
|
}
|
||||||
const columns = useDealServicesTableColumns({
|
const columns = useDealServicesTableColumns({
|
||||||
onChange: onQuantityChange,
|
onChange: onQuantityChange,
|
||||||
@@ -40,11 +42,24 @@ const DealServicesTable: FC<Props> = (
|
|||||||
title: "Добавление услуги",
|
title: "Добавление услуги",
|
||||||
modal: "addDealService",
|
modal: "addDealService",
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onCreate: (event) => onCreate(event as DealServiceSchema)
|
onCreate: (event) => onCreate(event as DealServiceSchema),
|
||||||
|
serviceIds
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const onEditClick = (service: DealServiceSchema) => {
|
||||||
|
if (!onChange) return;
|
||||||
|
openContextModal({
|
||||||
|
title: "Добавление услуги",
|
||||||
|
modal: "addDealService",
|
||||||
|
innerProps: {
|
||||||
|
element: service,
|
||||||
|
onChange,
|
||||||
|
serviceIds
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BaseTable
|
<BaseTable
|
||||||
ref={tableRef}
|
ref={tableRef}
|
||||||
@@ -80,7 +95,6 @@ const DealServicesTable: FC<Props> = (
|
|||||||
),
|
),
|
||||||
renderRowActions: ({row}) => (
|
renderRowActions: ({row}) => (
|
||||||
<Flex gap="md">
|
<Flex gap="md">
|
||||||
|
|
||||||
<Tooltip label="Удалить">
|
<Tooltip label="Удалить">
|
||||||
<ActionIcon onClick={() => {
|
<ActionIcon onClick={() => {
|
||||||
if (onDelete) onDelete(row.original);
|
if (onDelete) onDelete(row.original);
|
||||||
@@ -88,6 +102,13 @@ const DealServicesTable: FC<Props> = (
|
|||||||
<IconTrash/>
|
<IconTrash/>
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip label="Редактировать">
|
||||||
|
<ActionIcon onClick={() => {
|
||||||
|
onEditClick(row.original);
|
||||||
|
}} variant={"default"}>
|
||||||
|
<IconEdit/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
} as MRT_TableOptions<DealServiceSchema>}
|
} as MRT_TableOptions<DealServiceSchema>}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {MRT_ColumnDef} from "mantine-react-table";
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {DealServiceSchema} from "../../../../client";
|
import {DealServiceSchema} from "../../../../client";
|
||||||
import PlusMinusInput from "../../../../components/PlusMinusInput/PlusMinusInput.tsx";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onChange: (service: DealServiceSchema, quantity: number) => void;
|
onChange: (service: DealServiceSchema, quantity: number) => void;
|
||||||
@@ -11,7 +10,7 @@ type Props = {
|
|||||||
export const useDealServicesTableColumns = (props: Props) => {
|
export const useDealServicesTableColumns = (props: Props) => {
|
||||||
const {onChange, data} = props;
|
const {onChange, data} = props;
|
||||||
const totalPrice = useMemo(() =>
|
const totalPrice = useMemo(() =>
|
||||||
data.reduce((acc, row) => acc + row.quantity * row.service.price, 0)
|
data.reduce((acc, row) => acc + row.quantity * row.price, 0)
|
||||||
,
|
,
|
||||||
[data]);
|
[data]);
|
||||||
|
|
||||||
@@ -28,34 +27,34 @@ export const useDealServicesTableColumns = (props: Props) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableGrouping: false,
|
enableGrouping: false,
|
||||||
accessorKey: "service.price",
|
accessorKey: "price",
|
||||||
header: "Цена",
|
header: "Цена",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableGrouping: false,
|
enableGrouping: false,
|
||||||
accessorKey: "quantity",
|
accessorKey: "quantity",
|
||||||
header: "Количество",
|
header: "Количество",
|
||||||
Cell: ({row}) => {
|
// Cell: ({row}) => {
|
||||||
return (
|
// return (
|
||||||
<PlusMinusInput
|
// <PlusMinusInput
|
||||||
value={row.original.quantity}
|
// value={row.original.quantity}
|
||||||
onChange={(value) => onChange(row.original, value)}
|
// onChange={(value) => onChange(row.original, value)}
|
||||||
/>
|
// />
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableGrouping: false,
|
enableGrouping: false,
|
||||||
header: "Сумма",
|
header: "Сумма",
|
||||||
Cell: ({row}) => {
|
Cell: ({row}) => {
|
||||||
return row.original.quantity * row.original.service.price;
|
return row.original.quantity * row.original.price;
|
||||||
},
|
},
|
||||||
aggregationFn: "sum",
|
aggregationFn: "sum",
|
||||||
AggregatedCell: ({cell}) => {
|
AggregatedCell: ({cell}) => {
|
||||||
return <>Итоговая сумма по категории: {" "}
|
return <>Итоговая сумма по категории: {" "}
|
||||||
{
|
{
|
||||||
cell.row.subRows?.reduce((acc, row) =>
|
cell.row.subRows?.reduce((acc, row) =>
|
||||||
acc + row.original.quantity * row.original.service.price, 0)
|
acc + row.original.quantity * row.original.price, 0)
|
||||||
}
|
}
|
||||||
</>;
|
</>;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const useDealStatusChangeTableColumns = () => {
|
|||||||
accessorKey: "comment",
|
accessorKey: "comment",
|
||||||
header: "Комментарий",
|
header: "Комментарий",
|
||||||
Cell: ({row}) =>
|
Cell: ({row}) =>
|
||||||
<Spoiler onDoubleClick={()=>{console.log("double click")}} maxHeight={80} showLabel={"Показать весь"} hideLabel={"Скрыть"}>
|
<Spoiler maxHeight={80} showLabel={"Показать весь"} hideLabel={"Скрыть"}>
|
||||||
<Text style={{wordWrap: "break-word", wordBreak: "break-all", whiteSpace: "normal"}} span>
|
<Text style={{wordWrap: "break-word", wordBreak: "break-all", whiteSpace: "normal"}} span>
|
||||||
{row.original.comment}<br/>
|
{row.original.comment}<br/>
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.bottom-panel {
|
||||||
|
padding: rem(10);
|
||||||
|
border-radius: rem(5);
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-1);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {IconBarcode, IconBox, IconCalendarUser, IconSettings} from "@tabler/icon
|
|||||||
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
|
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
|
||||||
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
|
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
|
||||||
import {useQueryClient} from "@tanstack/react-query";
|
import {useQueryClient} from "@tanstack/react-query";
|
||||||
|
// import styles from './DealEditDrawer.module.css';
|
||||||
|
|
||||||
const useDealServicesTableState = () => {
|
const useDealServicesTableState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
@@ -18,11 +19,10 @@ const useDealServicesTableState = () => {
|
|||||||
|
|
||||||
const onServiceUpdate = (service: DealServiceSchema) => {
|
const onServiceUpdate = (service: DealServiceSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.updateDealServiceQuantity({
|
DealService.updateDealService({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
serviceId: service.service.id,
|
service
|
||||||
quantity: service.quantity
|
|
||||||
}
|
}
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ok, message}) => {
|
||||||
|
|
||||||
@@ -72,14 +72,13 @@ const useDealServicesTableState = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
const onServiceCreate = (service: DealServiceSchema) => {
|
const onServiceCreate = (service: DealServiceSchema) => {
|
||||||
console.log('-------Drawer')
|
|
||||||
console.log(service);
|
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.addDealService({
|
DealService.addDealService({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
serviceId: service.service.id,
|
serviceId: service.service.id,
|
||||||
quantity: service.quantity
|
quantity: service.quantity,
|
||||||
|
price: service.price
|
||||||
}
|
}
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ok, message}) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -157,17 +156,14 @@ const useDealProductTableState = () => {
|
|||||||
|
|
||||||
const onProductUpdate = (product: DealProductSchema) => {
|
const onProductUpdate = (product: DealProductSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.updateDealProductQuantity({
|
DealService.updateDealProduct({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
productId: product.product.id,
|
product: product
|
||||||
quantity: product.quantity
|
|
||||||
}
|
}
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ok, message}) => {
|
||||||
if (!ok) {
|
notifications.guess(ok, {message});
|
||||||
notifications.guess(ok, {message});
|
if (!ok) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({dealId: selectedDeal.id})
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal)
|
||||||
})
|
})
|
||||||
@@ -361,7 +357,6 @@ const DealEditDrawer: FC = () => {
|
|||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value={"services"}>
|
<Tabs.Panel value={"services"}>
|
||||||
<Box p={rem(10)}>
|
<Box p={rem(10)}>
|
||||||
|
|
||||||
<DealEditDrawerServicesTable/>
|
<DealEditDrawerServicesTable/>
|
||||||
</Box>
|
</Box>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
@@ -370,9 +365,15 @@ const DealEditDrawer: FC = () => {
|
|||||||
|
|
||||||
<DealEditDrawerProductsTable/>
|
<DealEditDrawerProductsTable/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
{/*<Flex*/}
|
||||||
|
{/* h={"10%"}*/}
|
||||||
|
{/* align={'flex-end'}*/}
|
||||||
|
{/* justify={"flex-end"}*/}
|
||||||
|
{/*>*/}
|
||||||
|
|
||||||
|
{/*</Flex>*/}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ const AddDealProductModal = ({
|
|||||||
|
|
||||||
const validateServices = (services?: DealProductServiceSchema[]) => {
|
const validateServices = (services?: DealProductServiceSchema[]) => {
|
||||||
if (!services || services.length == 0) return null;
|
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;
|
return services.find(service => service.service === undefined) ? "Удалите пустые услуги" : null;
|
||||||
}
|
}
|
||||||
const form = useForm<Partial<DealProductSchema>>({
|
const form = useForm<Partial<DealProductSchema>>({
|
||||||
@@ -39,11 +37,11 @@ const AddDealProductModal = ({
|
|||||||
services: validateServices
|
services: validateServices
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log(form.values);
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
}
|
||||||
console.log(form.values)
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
|
|||||||
@@ -2,19 +2,24 @@ import {ContextModalProps} from "@mantine/modals";
|
|||||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
import {DealServiceSchema} from "../../../client";
|
import {DealServiceSchema} from "../../../client";
|
||||||
import {useForm} from "@mantine/form";
|
import {useForm} from "@mantine/form";
|
||||||
import {NumberInput} from "@mantine/core";
|
import {ComboboxItem, ComboboxItemGroup, NumberInput, OptionsFilter} from "@mantine/core";
|
||||||
import ServiceSelect from "../../../components/ServiceSelect/ServiceSelect.tsx";
|
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||||
|
import {ServiceType} from "../../../shared/enums/ServiceType.ts";
|
||||||
|
|
||||||
type Props = CreateEditFormProps<Partial<DealServiceSchema>>;
|
type RestProps = {
|
||||||
|
serviceIds?: number[];
|
||||||
|
}
|
||||||
|
type Props = CreateEditFormProps<Partial<DealServiceSchema>> & RestProps;
|
||||||
const AddDealServiceModal = ({
|
const AddDealServiceModal = ({
|
||||||
context,
|
context,
|
||||||
id,
|
id,
|
||||||
innerProps
|
innerProps
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
|
const isEditing = 'element' in innerProps;
|
||||||
const form = useForm<Partial<DealServiceSchema>>({
|
const form = useForm<Partial<DealServiceSchema>>({
|
||||||
initialValues: {
|
initialValues: isEditing ? innerProps.element : {
|
||||||
service: undefined,
|
service: undefined,
|
||||||
quantity: 0,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
validate: {
|
validate: {
|
||||||
service: (service?: DealServiceSchema['service']) => service !== undefined ? null : "Необходимо выбрать услугу",
|
service: (service?: DealServiceSchema['service']) => service !== undefined ? null : "Необходимо выбрать услугу",
|
||||||
@@ -25,6 +30,16 @@ const AddDealServiceModal = ({
|
|||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serviceOptionsFilter = ({options}: { options: ComboboxItemGroup[] }) => {
|
||||||
|
if (!innerProps.serviceIds) return options;
|
||||||
|
const productServiceIds = innerProps.serviceIds;
|
||||||
|
return (options as ComboboxItemGroup[]).map(({items, group}) => {
|
||||||
|
return {
|
||||||
|
group,
|
||||||
|
items: items.filter(item => !productServiceIds.includes(parseInt((item as ComboboxItem).value)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
@@ -34,10 +49,27 @@ const AddDealServiceModal = ({
|
|||||||
onClose={onClose}>
|
onClose={onClose}>
|
||||||
<BaseFormModal.Body>
|
<BaseFormModal.Body>
|
||||||
<>
|
<>
|
||||||
<ServiceSelect
|
<ServiceWithPriceInput
|
||||||
placeholder={"Выберите услугу"}
|
serviceProps={{
|
||||||
label={"Услуга"}
|
...form.getInputProps('service'),
|
||||||
{...form.getInputProps('service')}
|
label: "Услуга",
|
||||||
|
placeholder: "Выберите услугу",
|
||||||
|
style: {width: '100%'},
|
||||||
|
disabled: isEditing,
|
||||||
|
filter: serviceOptionsFilter as OptionsFilter
|
||||||
|
}}
|
||||||
|
priceProps={{
|
||||||
|
...form.getInputProps('price'),
|
||||||
|
label: "Цена",
|
||||||
|
placeholder: "Введите цену",
|
||||||
|
style: {width: '100%'}
|
||||||
|
}}
|
||||||
|
quantity={form.values.quantity || 1}
|
||||||
|
containerProps={{
|
||||||
|
direction: "column",
|
||||||
|
style: {width: "100%"}
|
||||||
|
}}
|
||||||
|
filterType={ServiceType.DEAL_SERVICE}
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
placeholder={"Введите количество"}
|
placeholder={"Введите количество"}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import {FC} from "react";
|
||||||
|
import {SegmentedControl, SegmentedControlProps} from "@mantine/core";
|
||||||
|
import {ServiceType} from "../../../../shared/enums/ServiceType.ts";
|
||||||
|
|
||||||
|
|
||||||
|
type Props = Omit<SegmentedControlProps, 'data'>;
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
label: 'Для товара',
|
||||||
|
value: ServiceType.PRODUCT_SERVICE.toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Для сделки',
|
||||||
|
value: ServiceType.DEAL_SERVICE.toString()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const ServiceTypeSegmentedControl: FC<Props> = (props) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SegmentedControl
|
||||||
|
data={data}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default ServiceTypeSegmentedControl
|
||||||
@@ -44,7 +44,6 @@ const CreateServiceModal = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
console.log(form.values)
|
|
||||||
const onCancelClick = () => {
|
const onCancelClick = () => {
|
||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,9 @@
|
|||||||
padding: rem(5);
|
padding: rem(5);
|
||||||
gap: rem(10);
|
gap: rem(10);
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-panel-last-item {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {FC} from "react";
|
import {FC, useState} from "react";
|
||||||
import ServicesTable from "../components/ServicesTable/ServicesTable.tsx";
|
import ServicesTable from "../components/ServicesTable/ServicesTable.tsx";
|
||||||
import useServicesList from "../hooks/useServicesList.tsx";
|
import useServicesList from "../hooks/useServicesList.tsx";
|
||||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
@@ -7,10 +7,12 @@ import {Button, Text} from "@mantine/core";
|
|||||||
import {ServiceCategorySchema, ServiceSchema, ServiceService} from "../../../client";
|
import {ServiceCategorySchema, ServiceSchema, ServiceService} from "../../../client";
|
||||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
import {notifications} from "../../../shared/lib/notifications.ts";
|
||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
|
import ServiceTypeSegmentedControl from "../components/ServiceTypeSegmentedControl/ServiceTypeSegmentedControl.tsx";
|
||||||
|
import {ServiceType} from "../../../shared/enums/ServiceType.ts";
|
||||||
|
|
||||||
export const ServicesPage: FC = () => {
|
export const ServicesPage: FC = () => {
|
||||||
const {services, refetch} = useServicesList();
|
const {services, refetch} = useServicesList();
|
||||||
|
const [serviceType, setServiceType] = useState(ServiceType.DEAL_SERVICE)
|
||||||
// region Service create
|
// region Service create
|
||||||
const onCreateClick = () => {
|
const onCreateClick = () => {
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
@@ -90,13 +92,18 @@ export const ServicesPage: FC = () => {
|
|||||||
<div className={styles['top-panel']}>
|
<div className={styles['top-panel']}>
|
||||||
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
|
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
|
||||||
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать категорию</Button>
|
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать категорию</Button>
|
||||||
|
<ServiceTypeSegmentedControl
|
||||||
|
className={styles['top-panel-last-item']}
|
||||||
|
value={serviceType.toString()}
|
||||||
|
onChange={(event) => setServiceType(parseInt(event))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
<ServicesTable
|
<ServicesTable
|
||||||
onDelete={onServiceDelete}
|
onDelete={onServiceDelete}
|
||||||
onChange={onServiceUpdate}
|
onChange={onServiceUpdate}
|
||||||
items={services}
|
items={services.filter(service => service.serviceType == serviceType)}
|
||||||
/>
|
/>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,73 +1,14 @@
|
|||||||
import {createLazyFileRoute} from "@tanstack/react-router";
|
import {createLazyFileRoute} from "@tanstack/react-router";
|
||||||
import ServiceSelectNew from "../components/Selects/ServiceSelectNew/ServiceSelectNew.tsx";
|
|
||||||
import {ServiceType} from "../shared/enums/ServiceType.ts";
|
|
||||||
import ServiceWithPriceInput from "../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
|
||||||
import {useEffect, useState} from "react";
|
|
||||||
import {ServiceSchema, ServiceService} from "../client";
|
|
||||||
import {NumberInput} from "@mantine/core";
|
|
||||||
import {isNumber} from "lodash";
|
|
||||||
import useServicesList from "../pages/ServicesPage/hooks/useServicesList.tsx";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute('/test')({
|
export const Route = createLazyFileRoute('/test')({
|
||||||
component: TestPage
|
component: TestPage
|
||||||
})
|
})
|
||||||
|
|
||||||
const data = [{
|
|
||||||
label: "test",
|
|
||||||
value: '0'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "test2",
|
|
||||||
value: '1'
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
function TestPage() {
|
function TestPage() {
|
||||||
const [service, setService] = useState<ServiceSchema | undefined>({
|
|
||||||
"id": 96,
|
|
||||||
"name": "123",
|
|
||||||
"category": {"id": 1, "name": "Услуги по работе с товаром с учетом суммы всех сторон в см."},
|
|
||||||
"price": 0,
|
|
||||||
"serviceType": 1,
|
|
||||||
"priceRanges": [{"id": 4, "fromQuantity": 1, "toQuantity": 200, "price": 35}, {
|
|
||||||
"id": 3,
|
|
||||||
"fromQuantity": 201,
|
|
||||||
"toQuantity": 300,
|
|
||||||
"price": 24
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
const [price, setPrice] = useState<number | string>();
|
|
||||||
const [q, setQ] = useState(1);
|
|
||||||
|
|
||||||
|
|
||||||
console.log('service:---------');
|
|
||||||
console.log(service);
|
|
||||||
console.log('price:---------');
|
|
||||||
console.log(price);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NumberInput
|
|
||||||
value={q}
|
|
||||||
onChange={event => {
|
|
||||||
if (!isNumber(event)) return;
|
|
||||||
setQ(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ServiceWithPriceInput
|
|
||||||
priceProps={{
|
|
||||||
onChange: setPrice,
|
|
||||||
value: price,
|
|
||||||
label: "Цена"
|
|
||||||
}}
|
|
||||||
quantity={q}
|
|
||||||
serviceProps={{
|
|
||||||
onChange: setService,
|
|
||||||
value: service,
|
|
||||||
label: "Услуга"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user