feat: price by category
This commit is contained in:
@@ -52,6 +52,8 @@ export type { CreatePayRateRequest } from './models/CreatePayRateRequest';
|
|||||||
export type { CreatePayRateResponse } from './models/CreatePayRateResponse';
|
export type { CreatePayRateResponse } from './models/CreatePayRateResponse';
|
||||||
export type { CreatePositionRequest } from './models/CreatePositionRequest';
|
export type { CreatePositionRequest } from './models/CreatePositionRequest';
|
||||||
export type { CreatePositionResponse } from './models/CreatePositionResponse';
|
export type { CreatePositionResponse } from './models/CreatePositionResponse';
|
||||||
|
export type { CreatePriceCategoryRequest } from './models/CreatePriceCategoryRequest';
|
||||||
|
export type { CreatePriceCategoryResponse } from './models/CreatePriceCategoryResponse';
|
||||||
export type { CreateServiceKitSchema } from './models/CreateServiceKitSchema';
|
export type { CreateServiceKitSchema } from './models/CreateServiceKitSchema';
|
||||||
export type { CreateServicesKitRequest } from './models/CreateServicesKitRequest';
|
export type { CreateServicesKitRequest } from './models/CreateServicesKitRequest';
|
||||||
export type { CreateServicesKitResponse } from './models/CreateServicesKitResponse';
|
export type { CreateServicesKitResponse } from './models/CreateServicesKitResponse';
|
||||||
@@ -120,6 +122,8 @@ export type { DeletePayRateRequest } from './models/DeletePayRateRequest';
|
|||||||
export type { DeletePayRateResponse } from './models/DeletePayRateResponse';
|
export type { DeletePayRateResponse } from './models/DeletePayRateResponse';
|
||||||
export type { DeletePositionRequest } from './models/DeletePositionRequest';
|
export type { DeletePositionRequest } from './models/DeletePositionRequest';
|
||||||
export type { DeletePositionResponse } from './models/DeletePositionResponse';
|
export type { DeletePositionResponse } from './models/DeletePositionResponse';
|
||||||
|
export type { DeletePriceCategoryRequest } from './models/DeletePriceCategoryRequest';
|
||||||
|
export type { DeletePriceCategoryResponse } from './models/DeletePriceCategoryResponse';
|
||||||
export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest';
|
export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest';
|
||||||
export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse';
|
export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse';
|
||||||
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
|
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
|
||||||
@@ -129,6 +133,7 @@ export type { GetAllBaseMarketplacesResponse } from './models/GetAllBaseMarketpl
|
|||||||
export type { GetAllPayRatesResponse } from './models/GetAllPayRatesResponse';
|
export type { GetAllPayRatesResponse } from './models/GetAllPayRatesResponse';
|
||||||
export type { GetAllPayrollSchemeResponse } from './models/GetAllPayrollSchemeResponse';
|
export type { GetAllPayrollSchemeResponse } from './models/GetAllPayrollSchemeResponse';
|
||||||
export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse';
|
export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse';
|
||||||
|
export type { GetAllPriceCategoriesResponse } from './models/GetAllPriceCategoriesResponse';
|
||||||
export type { GetAllRolesResponse } from './models/GetAllRolesResponse';
|
export type { GetAllRolesResponse } from './models/GetAllRolesResponse';
|
||||||
export type { GetAllServicesKitsResponse } from './models/GetAllServicesKitsResponse';
|
export type { GetAllServicesKitsResponse } from './models/GetAllServicesKitsResponse';
|
||||||
export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWarehousesResponse';
|
export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWarehousesResponse';
|
||||||
@@ -174,6 +179,7 @@ export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
|
|||||||
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
|
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
|
||||||
export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
|
export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
|
||||||
export type { RoleSchema } from './models/RoleSchema';
|
export type { RoleSchema } from './models/RoleSchema';
|
||||||
|
export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema';
|
||||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
||||||
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
||||||
@@ -183,6 +189,7 @@ export type { ServiceDeleteRequest } from './models/ServiceDeleteRequest';
|
|||||||
export type { ServiceDeleteResponse } from './models/ServiceDeleteResponse';
|
export type { ServiceDeleteResponse } from './models/ServiceDeleteResponse';
|
||||||
export type { ServiceGetAllCategoriesResponse } from './models/ServiceGetAllCategoriesResponse';
|
export type { ServiceGetAllCategoriesResponse } from './models/ServiceGetAllCategoriesResponse';
|
||||||
export type { ServiceGetAllResponse } from './models/ServiceGetAllResponse';
|
export type { ServiceGetAllResponse } from './models/ServiceGetAllResponse';
|
||||||
|
export type { ServicePriceCategorySchema } from './models/ServicePriceCategorySchema';
|
||||||
export type { ServicePriceRangeSchema } from './models/ServicePriceRangeSchema';
|
export type { ServicePriceRangeSchema } from './models/ServicePriceRangeSchema';
|
||||||
export type { ServiceSchema } from './models/ServiceSchema';
|
export type { ServiceSchema } from './models/ServiceSchema';
|
||||||
export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest';
|
export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest';
|
||||||
@@ -196,6 +203,8 @@ export type { UpdateMarketplaceRequest } from './models/UpdateMarketplaceRequest
|
|||||||
export type { UpdateMarketplaceResponse } from './models/UpdateMarketplaceResponse';
|
export type { UpdateMarketplaceResponse } from './models/UpdateMarketplaceResponse';
|
||||||
export type { UpdatePayRateRequest } from './models/UpdatePayRateRequest';
|
export type { UpdatePayRateRequest } from './models/UpdatePayRateRequest';
|
||||||
export type { UpdatePayRateResponse } from './models/UpdatePayRateResponse';
|
export type { UpdatePayRateResponse } from './models/UpdatePayRateResponse';
|
||||||
|
export type { UpdatePriceCategoryRequest } from './models/UpdatePriceCategoryRequest';
|
||||||
|
export type { UpdatePriceCategoryResponse } from './models/UpdatePriceCategoryResponse';
|
||||||
export type { UpdateServiceKitSchema } from './models/UpdateServiceKitSchema';
|
export type { UpdateServiceKitSchema } from './models/UpdateServiceKitSchema';
|
||||||
export type { UpdateServicesKitRequest } from './models/UpdateServicesKitRequest';
|
export type { UpdateServicesKitRequest } from './models/UpdateServicesKitRequest';
|
||||||
export type { UpdateServicesKitResponse } from './models/UpdateServicesKitResponse';
|
export type { UpdateServicesKitResponse } from './models/UpdateServicesKitResponse';
|
||||||
|
|||||||
8
src/client/models/CreatePriceCategoryRequest.ts
Normal file
8
src/client/models/CreatePriceCategoryRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type CreatePriceCategoryRequest = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/CreatePriceCategoryResponse.ts
Normal file
9
src/client/models/CreatePriceCategoryResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type CreatePriceCategoryResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BaseMarketplaceSchema } from './BaseMarketplaceSchema';
|
import type { BaseMarketplaceSchema } from './BaseMarketplaceSchema';
|
||||||
|
import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
|
||||||
export type DealQuickCreateRequest = {
|
export type DealQuickCreateRequest = {
|
||||||
name: string;
|
name: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
@@ -10,5 +11,6 @@ export type DealQuickCreateRequest = {
|
|||||||
acceptanceDate: string;
|
acceptanceDate: string;
|
||||||
shippingWarehouse: string;
|
shippingWarehouse: string;
|
||||||
baseMarketplace: BaseMarketplaceSchema;
|
baseMarketplace: BaseMarketplaceSchema;
|
||||||
|
category?: (ServicePriceCategorySchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type { DealBillRequestSchema } from './DealBillRequestSchema';
|
|||||||
import type { DealProductSchema } from './DealProductSchema';
|
import type { DealProductSchema } from './DealProductSchema';
|
||||||
import type { DealServiceSchema } from './DealServiceSchema';
|
import type { DealServiceSchema } from './DealServiceSchema';
|
||||||
import type { DealStatusHistorySchema } from './DealStatusHistorySchema';
|
import type { DealStatusHistorySchema } from './DealStatusHistorySchema';
|
||||||
|
import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
|
||||||
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
|
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
|
||||||
export type DealSchema = {
|
export type DealSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -24,5 +25,6 @@ export type DealSchema = {
|
|||||||
comment: string;
|
comment: string;
|
||||||
shippingWarehouse?: (ShippingWarehouseSchema | string | null);
|
shippingWarehouse?: (ShippingWarehouseSchema | string | null);
|
||||||
billRequest?: (DealBillRequestSchema | null);
|
billRequest?: (DealBillRequestSchema | null);
|
||||||
|
category?: (ServicePriceCategorySchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
8
src/client/models/DeletePriceCategoryRequest.ts
Normal file
8
src/client/models/DeletePriceCategoryRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DeletePriceCategoryRequest = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DeletePriceCategoryResponse.ts
Normal file
9
src/client/models/DeletePriceCategoryResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DeletePriceCategoryResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/GetAllPriceCategoriesResponse.ts
Normal file
9
src/client/models/GetAllPriceCategoriesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
|
||||||
|
export type GetAllPriceCategoriesResponse = {
|
||||||
|
priceCategories: Array<ServicePriceCategorySchema>;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/ServiceCategoryPriceSchema.ts
Normal file
10
src/client/models/ServiceCategoryPriceSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
|
||||||
|
export type ServiceCategoryPriceSchema = {
|
||||||
|
category: ServicePriceCategorySchema;
|
||||||
|
price: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ServicePriceCategorySchema.ts
Normal file
9
src/client/models/ServicePriceCategorySchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ServicePriceCategorySchema = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { ServiceCategoryPriceSchema } from './ServiceCategoryPriceSchema';
|
||||||
import type { ServiceCategorySchema } from './ServiceCategorySchema';
|
import type { ServiceCategorySchema } from './ServiceCategorySchema';
|
||||||
import type { ServicePriceRangeSchema } from './ServicePriceRangeSchema';
|
import type { ServicePriceRangeSchema } from './ServicePriceRangeSchema';
|
||||||
export type ServiceSchema = {
|
export type ServiceSchema = {
|
||||||
@@ -11,6 +12,7 @@ export type ServiceSchema = {
|
|||||||
price: number;
|
price: number;
|
||||||
serviceType: number;
|
serviceType: number;
|
||||||
priceRanges: Array<ServicePriceRangeSchema>;
|
priceRanges: Array<ServicePriceRangeSchema>;
|
||||||
|
categoryPrices: Array<ServiceCategoryPriceSchema>;
|
||||||
cost: (number | null);
|
cost: (number | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
9
src/client/models/UpdatePriceCategoryRequest.ts
Normal file
9
src/client/models/UpdatePriceCategoryRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type UpdatePriceCategoryRequest = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/UpdatePriceCategoryResponse.ts
Normal file
9
src/client/models/UpdatePriceCategoryResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type UpdatePriceCategoryResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -305,6 +305,27 @@ export class DealService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get Detailed Deal Document
|
||||||
|
* @returns any Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getDealDocumentDetailed({
|
||||||
|
dealId,
|
||||||
|
}: {
|
||||||
|
dealId: number,
|
||||||
|
}): CancelablePromise<any> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/deal/detailedDocument/{deal_id}',
|
||||||
|
path: {
|
||||||
|
'deal_id': dealId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Services Add
|
* Services Add
|
||||||
* @returns DealAddServicesResponse Successful Response
|
* @returns DealAddServicesResponse Successful Response
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BaseEnumListSchema } from '../models/BaseEnumListSchema';
|
import type { BaseEnumListSchema } from '../models/BaseEnumListSchema';
|
||||||
|
import type { CreatePriceCategoryRequest } from '../models/CreatePriceCategoryRequest';
|
||||||
|
import type { CreatePriceCategoryResponse } from '../models/CreatePriceCategoryResponse';
|
||||||
import type { CreateServicesKitRequest } from '../models/CreateServicesKitRequest';
|
import type { CreateServicesKitRequest } from '../models/CreateServicesKitRequest';
|
||||||
import type { CreateServicesKitResponse } from '../models/CreateServicesKitResponse';
|
import type { CreateServicesKitResponse } from '../models/CreateServicesKitResponse';
|
||||||
|
import type { DeletePriceCategoryRequest } from '../models/DeletePriceCategoryRequest';
|
||||||
|
import type { DeletePriceCategoryResponse } from '../models/DeletePriceCategoryResponse';
|
||||||
|
import type { GetAllPriceCategoriesResponse } from '../models/GetAllPriceCategoriesResponse';
|
||||||
import type { GetAllServicesKitsResponse } from '../models/GetAllServicesKitsResponse';
|
import type { GetAllServicesKitsResponse } from '../models/GetAllServicesKitsResponse';
|
||||||
import type { ServiceCreateCategoryRequest } from '../models/ServiceCreateCategoryRequest';
|
import type { ServiceCreateCategoryRequest } from '../models/ServiceCreateCategoryRequest';
|
||||||
import type { ServiceCreateCategoryResponse } from '../models/ServiceCreateCategoryResponse';
|
import type { ServiceCreateCategoryResponse } from '../models/ServiceCreateCategoryResponse';
|
||||||
@@ -16,6 +21,8 @@ import type { ServiceGetAllCategoriesResponse } from '../models/ServiceGetAllCat
|
|||||||
import type { ServiceGetAllResponse } from '../models/ServiceGetAllResponse';
|
import type { ServiceGetAllResponse } from '../models/ServiceGetAllResponse';
|
||||||
import type { ServiceUpdateRequest } from '../models/ServiceUpdateRequest';
|
import type { ServiceUpdateRequest } from '../models/ServiceUpdateRequest';
|
||||||
import type { ServiceUpdateResponse } from '../models/ServiceUpdateResponse';
|
import type { ServiceUpdateResponse } from '../models/ServiceUpdateResponse';
|
||||||
|
import type { UpdatePriceCategoryRequest } from '../models/UpdatePriceCategoryRequest';
|
||||||
|
import type { UpdatePriceCategoryResponse } from '../models/UpdatePriceCategoryResponse';
|
||||||
import type { UpdateServicesKitRequest } from '../models/UpdateServicesKitRequest';
|
import type { UpdateServicesKitRequest } from '../models/UpdateServicesKitRequest';
|
||||||
import type { UpdateServicesKitResponse } from '../models/UpdateServicesKitResponse';
|
import type { UpdateServicesKitResponse } from '../models/UpdateServicesKitResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
@@ -186,4 +193,75 @@ export class ServiceService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get All Price Categories
|
||||||
|
* @returns GetAllPriceCategoriesResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getAllPriceCategories(): CancelablePromise<GetAllPriceCategoriesResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/service/price-categories/get-all',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create Price Category
|
||||||
|
* @returns CreatePriceCategoryResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static createPriceCategory({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: CreatePriceCategoryRequest,
|
||||||
|
}): CancelablePromise<CreatePriceCategoryResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/service/price-categories/create',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update Price Category
|
||||||
|
* @returns UpdatePriceCategoryResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static updatePriceCategory({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: UpdatePriceCategoryRequest,
|
||||||
|
}): CancelablePromise<UpdatePriceCategoryResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/service/price-categories/update',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delete Price Category
|
||||||
|
* @returns DeletePriceCategoryResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static deletePriceCategory({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DeletePriceCategoryRequest,
|
||||||
|
}): CancelablePromise<DeletePriceCategoryResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/service/price-categories/delete',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +1,108 @@
|
|||||||
import {Button, rem, Textarea, TextInput} from "@mantine/core";
|
import { Button, rem, Textarea, TextInput } from "@mantine/core";
|
||||||
import {QuickDeal} from "../../../types/QuickDeal.ts";
|
import { QuickDeal } from "../../../types/QuickDeal.ts";
|
||||||
import {FC} from "react";
|
import { FC } from "react";
|
||||||
import {useForm} from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import styles from './CreateDealForm.module.css';
|
import styles from "./CreateDealForm.module.css";
|
||||||
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
|
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
|
||||||
import {DateTimePicker} from "@mantine/dates";
|
import { DateTimePicker } from "@mantine/dates";
|
||||||
import ShippingWarehouseAutocomplete
|
import ShippingWarehouseAutocomplete
|
||||||
from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||||
import BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
import BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||||
|
import ServicePriceCategorySelect from "../../Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: (quickDeal: QuickDeal) => void
|
onSubmit: (quickDeal: QuickDeal) => void
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||||
const form = useForm<QuickDeal>({
|
const form = useForm<QuickDeal>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: '',
|
name: "",
|
||||||
clientName: '',
|
clientName: "",
|
||||||
clientAddress: '',
|
clientAddress: "",
|
||||||
comment: '',
|
comment: "",
|
||||||
acceptanceDate: new Date(),
|
acceptanceDate: new Date(),
|
||||||
shippingWarehouse: '',
|
shippingWarehouse: "",
|
||||||
baseMarketplace: {
|
baseMarketplace: {
|
||||||
key: "",
|
key: "",
|
||||||
iconUrl: "",
|
iconUrl: "",
|
||||||
name: ""
|
name: "",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
style={{width: '100%'}}
|
style={{ width: "100%" }}
|
||||||
onSubmit={form.onSubmit((values) => onSubmit(values))}
|
onSubmit={form.onSubmit((values) => onSubmit(values))}
|
||||||
>
|
>
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
width: '100%',
|
width: "100%",
|
||||||
gap: rem(10),
|
gap: rem(10),
|
||||||
padding: rem(10)
|
padding: rem(10),
|
||||||
}}>
|
}}>
|
||||||
<div className={styles['inputs']}>
|
<div className={styles["inputs"]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={'Название сделки'}
|
placeholder={"Название сделки"}
|
||||||
{...form.getInputProps('name')}
|
{...form.getInputProps("name")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['inputs']}>
|
<div className={styles["inputs"]}>
|
||||||
<ClientAutocomplete
|
<ClientAutocomplete
|
||||||
nameRestProps={form.getInputProps('clientName')}
|
nameRestProps={form.getInputProps("clientName")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['inputs']}>
|
<div className={styles["inputs"]}>
|
||||||
<BaseMarketplaceSelect
|
<BaseMarketplaceSelect
|
||||||
rightSection={<></>}
|
rightSection={<></>}
|
||||||
placeholder={"Базовый маркетплейс"}
|
placeholder={"Базовый маркетплейс"}
|
||||||
{...form.getInputProps("baseMarketplace")}
|
{...form.getInputProps("baseMarketplace")}
|
||||||
/>
|
/>
|
||||||
<ShippingWarehouseAutocomplete
|
<ShippingWarehouseAutocomplete
|
||||||
{...form.getInputProps('shippingWarehouse')}
|
{...form.getInputProps("shippingWarehouse")}
|
||||||
placeholder={'Склад отгрузки'}
|
placeholder={"Склад отгрузки"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className={styles["inputs"]}>
|
||||||
|
<ServicePriceCategorySelect
|
||||||
|
rightSection={<></>}
|
||||||
|
placeholder={"Выберите категорию"}
|
||||||
|
{...form.getInputProps("category")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles['inputs']}>
|
<div className={styles["inputs"]}>
|
||||||
<Textarea
|
<Textarea
|
||||||
autosize
|
autosize
|
||||||
placeholder={'Комментарий'}
|
placeholder={"Комментарий"}
|
||||||
minRows={2}
|
minRows={2}
|
||||||
maxRows={4}
|
maxRows={4}
|
||||||
{...form.getInputProps('comment')}
|
{...form.getInputProps("comment")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['inputs']}>
|
<div className={styles["inputs"]}>
|
||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
placeholder={'Дата приемки'}
|
placeholder={"Дата приемки"}
|
||||||
{...form.getInputProps('acceptanceDate')}
|
{...form.getInputProps("acceptanceDate")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className={styles['buttons']}>
|
<div className={styles["buttons"]}>
|
||||||
<Button
|
<Button
|
||||||
type={'submit'}
|
type={"submit"}
|
||||||
>Добавить</Button>
|
>Добавить</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={'outline'}
|
variant={"outline"}
|
||||||
onClick={() => onCancel()}
|
onClick={() => onCancel()}
|
||||||
>Отменить</Button>
|
>Отменить</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateDealFrom;
|
export default CreateDealFrom;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import ObjectSelect, { ObjectSelectProps } from "../../ObjectSelect/ObjectSelect.tsx";
|
||||||
|
import { ServicePriceCategorySchema } from "../../../client";
|
||||||
|
import useServicePriceCategoriesList from "../../../pages/ServicesPage/hooks/useServicePriceCategoriesList.tsx";
|
||||||
|
|
||||||
|
type Props = Omit<ObjectSelectProps<ServicePriceCategorySchema>, "data">
|
||||||
|
|
||||||
|
const ServicePriceCategorySelect = (props: Props) => {
|
||||||
|
const { objects } = useServicePriceCategoriesList();
|
||||||
|
return (
|
||||||
|
<ObjectSelect
|
||||||
|
{...props}
|
||||||
|
data={objects}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicePriceCategorySelect;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
|
import { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx";
|
||||||
import {ServiceSchema} from "../../client";
|
import { ServicePriceCategorySchema, ServiceSchema } from "../../client";
|
||||||
import {Flex, FlexProps, NumberInput, NumberInputProps, rem} from "@mantine/core";
|
import { Flex, FlexProps, NumberInput, NumberInputProps, rem } from "@mantine/core";
|
||||||
import {FC, useEffect, useRef, 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 { ServiceType } from "../../shared/enums/ServiceType.ts";
|
||||||
|
|
||||||
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, 'data'>;
|
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, "data">;
|
||||||
type PriceProps = NumberInputProps;
|
type PriceProps = NumberInputProps;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -15,6 +15,7 @@ type Props = {
|
|||||||
containerProps: FlexProps,
|
containerProps: FlexProps,
|
||||||
filterType?: ServiceType,
|
filterType?: ServiceType,
|
||||||
lockOnEdit?: boolean
|
lockOnEdit?: boolean
|
||||||
|
category?: ServicePriceCategorySchema
|
||||||
}
|
}
|
||||||
const ServiceWithPriceInput: FC<Props> = ({
|
const ServiceWithPriceInput: FC<Props> = ({
|
||||||
serviceProps,
|
serviceProps,
|
||||||
@@ -22,10 +23,11 @@ const ServiceWithPriceInput: FC<Props> = ({
|
|||||||
quantity,
|
quantity,
|
||||||
containerProps,
|
containerProps,
|
||||||
filterType = ServiceType.PRODUCT_SERVICE,
|
filterType = ServiceType.PRODUCT_SERVICE,
|
||||||
lockOnEdit = true
|
lockOnEdit = true,
|
||||||
|
category,
|
||||||
}) => {
|
}) => {
|
||||||
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 isFirstRender = useRef(true);
|
||||||
const setPriceBasedOnQuantity = (): boolean => {
|
const setPriceBasedOnQuantity = (): boolean => {
|
||||||
@@ -35,29 +37,40 @@ const ServiceWithPriceInput: FC<Props> = ({
|
|||||||
|
|
||||||
setPrice(range.price);
|
setPrice(range.price);
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
const setPriceBasedOnCategory = () => {
|
||||||
|
if (!category || !service) return false;
|
||||||
|
const categoryPrice = service.categoryPrices.find(categoryPrice => categoryPrice.category.id === category.id);
|
||||||
|
if (!categoryPrice) return false;
|
||||||
|
setPrice(categoryPrice.price);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
const setPriceBasedOnService = () => {
|
const setPriceBasedOnService = () => {
|
||||||
if (!service) return;
|
if (!service) return;
|
||||||
|
// if category is set, we should not set price based on service
|
||||||
|
if (setPriceBasedOnCategory()) return;
|
||||||
if (setPriceBasedOnQuantity()) return;
|
if (setPriceBasedOnQuantity()) return;
|
||||||
setPrice(service.price);
|
setPrice(service.price);
|
||||||
}
|
};
|
||||||
const onServiceManualChange = (service: ServiceSchema) => {
|
const onServiceManualChange = (service: ServiceSchema) => {
|
||||||
setService(service);
|
setService(service);
|
||||||
}
|
};
|
||||||
const onPriceManualChange = (value: number | string) => {
|
const onPriceManualChange = (value: number | string) => {
|
||||||
if (typeof value !== 'number') return;
|
if (typeof value !== "number") return;
|
||||||
setPrice(value);
|
setPrice(value);
|
||||||
}
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFirstRender.current && lockOnEdit) return;
|
if (isFirstRender.current && lockOnEdit) return;
|
||||||
|
|
||||||
|
// we need to set price based on quantity only if category is not set, because category has higher priority
|
||||||
|
if (category) return;
|
||||||
setPriceBasedOnQuantity();
|
setPriceBasedOnQuantity();
|
||||||
}, [quantity]);
|
}, [quantity]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFirstRender.current && lockOnEdit) return;
|
if (isFirstRender.current && lockOnEdit) return;
|
||||||
|
|
||||||
if (!priceProps.onChange || typeof price === 'undefined') return;
|
if (!priceProps.onChange || typeof price === "undefined") return;
|
||||||
priceProps.onChange(price);
|
priceProps.onChange(price);
|
||||||
}, [price]);
|
}, [price]);
|
||||||
|
|
||||||
@@ -97,7 +110,7 @@ const ServiceWithPriceInput: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ServiceWithPriceInput;
|
export default ServiceWithPriceInput;
|
||||||
62
src/main.tsx
62
src/main.tsx
@@ -1,35 +1,35 @@
|
|||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from "react-dom/client";
|
||||||
import {RouterProvider, createRouter} from '@tanstack/react-router'
|
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||||
import {routeTree} from './routeTree.gen'
|
import { routeTree } from "./routeTree.gen";
|
||||||
import {MantineProvider} from "@mantine/core";
|
import { MantineProvider } from "@mantine/core";
|
||||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import {Provider} from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import {store} from "./redux/store.ts";
|
import { store } from "./redux/store.ts";
|
||||||
|
|
||||||
import '@mantine/core/styles.css';
|
import "@mantine/core/styles.css";
|
||||||
import '@mantine/notifications/styles.css';
|
import "@mantine/notifications/styles.css";
|
||||||
import '@mantine/dates/styles.css';
|
import "@mantine/dates/styles.css";
|
||||||
import 'mantine-react-table/styles.css';
|
import "mantine-react-table/styles.css";
|
||||||
|
|
||||||
import 'dayjs/locale/ru';
|
import "dayjs/locale/ru";
|
||||||
|
|
||||||
import './main.css';
|
import "./main.css";
|
||||||
import {Notifications} from "@mantine/notifications";
|
import { Notifications } from "@mantine/notifications";
|
||||||
import {ModalsProvider} from "@mantine/modals";
|
import { ModalsProvider } from "@mantine/modals";
|
||||||
import {OpenAPI} from "./client";
|
import { OpenAPI } from "./client";
|
||||||
import {DatesProvider} from "@mantine/dates";
|
import { DatesProvider } from "@mantine/dates";
|
||||||
import {modals} from "./modals/modals.ts";
|
import { modals } from "./modals/modals.ts";
|
||||||
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
|
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
|
||||||
|
|
||||||
// Configuring router
|
// Configuring router
|
||||||
const router = createRouter({routeTree})
|
const router = createRouter({ routeTree });
|
||||||
declare module '@tanstack/react-router' {
|
declare module "@tanstack/react-router" {
|
||||||
interface Register {
|
interface Register {
|
||||||
router: typeof router
|
router: typeof router;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@mantine/modals' {
|
declare module "@mantine/modals" {
|
||||||
export interface MantineModalsOverride {
|
export interface MantineModalsOverride {
|
||||||
modals: typeof modals;
|
modals: typeof modals;
|
||||||
}
|
}
|
||||||
@@ -39,23 +39,23 @@ declare module '@mantine/modals' {
|
|||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
// Configuring OpenAPI
|
// Configuring OpenAPI
|
||||||
OpenAPI.BASE = import.meta.env.VITE_API_URL
|
OpenAPI.BASE = import.meta.env.VITE_API_URL;
|
||||||
OpenAPI.TOKEN = JSON.parse(localStorage.getItem('authState') || "{}")['accessToken'];
|
OpenAPI.TOKEN = JSON.parse(localStorage.getItem("authState") || "{}")["accessToken"];
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<MantineProvider defaultColorScheme={"dark"}>
|
<MantineProvider defaultColorScheme={"dark"}>
|
||||||
<ModalsProvider modals={modals}>
|
<ModalsProvider labels={{ confirm: "Да", cancel: "Нет" }} modals={modals}>
|
||||||
<DatesProvider settings={{locale: 'ru'}}>
|
<DatesProvider settings={{ locale: "ru" }}>
|
||||||
<TasksProvider>
|
<TasksProvider>
|
||||||
|
|
||||||
<RouterProvider router={router}/>
|
<RouterProvider router={router} />
|
||||||
<Notifications/>
|
<Notifications />
|
||||||
</TasksProvider>
|
</TasksProvider>
|
||||||
|
|
||||||
</DatesProvider>
|
</DatesProvider>
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</Provider>
|
</Provider>,
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import ServicesKitSelectModal from "./ServicesKitSelectModal/ServicesKitSelectMo
|
|||||||
import SelectDealProductsModal from "../pages/LeadsPage/modals/SelectDealProductsModal.tsx";
|
import SelectDealProductsModal from "../pages/LeadsPage/modals/SelectDealProductsModal.tsx";
|
||||||
import ShippingWarehouseForm from "../pages/ShippingWarehousesPage/modals/ShippingWarehouseForm.tsx";
|
import ShippingWarehouseForm from "../pages/ShippingWarehousesPage/modals/ShippingWarehouseForm.tsx";
|
||||||
import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFormModal/MarketplaceFormModal.tsx";
|
import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFormModal/MarketplaceFormModal.tsx";
|
||||||
|
import ServicePriceCategoryForm from "../pages/ServicesPage/modals/ServicePriceCategoryForm.tsx";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterDeadline: EnterDeadlineModal,
|
enterDeadline: EnterDeadlineModal,
|
||||||
@@ -44,5 +45,6 @@ export const modals = {
|
|||||||
servicesKitSelectModal: ServicesKitSelectModal,
|
servicesKitSelectModal: ServicesKitSelectModal,
|
||||||
selectDealProductsModal: SelectDealProductsModal,
|
selectDealProductsModal: SelectDealProductsModal,
|
||||||
shippingWarehouseForm: ShippingWarehouseForm,
|
shippingWarehouseForm: ShippingWarehouseForm,
|
||||||
marketplaceFormModal: MarketplaceFormModal
|
marketplaceFormModal: MarketplaceFormModal,
|
||||||
}
|
servicePriceCategoryForm: ServicePriceCategoryForm,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import {Box, Drawer, rem, Tabs, Text} from "@mantine/core";
|
import { Box, Drawer, rem, Tabs, Text } from "@mantine/core";
|
||||||
import {FC, useEffect, useRef} from "react";
|
import { FC, useEffect, useRef } from "react";
|
||||||
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
|
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
|
||||||
import {useDealPageContext} from "../../contexts/DealPageContext.tsx";
|
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
|
||||||
import {DealProductSchema, DealService, DealServiceSchema} from "../../../../client";
|
import { DealProductSchema, DealService, DealServiceSchema } 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 {BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
import { BaseTableRef } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
import DealProductsTable from "../../components/DealProductsTable/DealProductsTable.tsx";
|
import DealProductsTable from "../../components/DealProductsTable/DealProductsTable.tsx";
|
||||||
import {IconBox, IconCalendarUser, IconSettings} from "@tabler/icons-react";
|
import { IconBox, IconCalendarUser, IconSettings } from "@tabler/icons-react";
|
||||||
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 ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
||||||
import {motion} from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
// import styles from './DealEditDrawer.module.css';
|
// import styles from './DealEditDrawer.module.css';
|
||||||
|
|
||||||
const useDealServicesTableState = () => {
|
const useDealServicesTableState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||||
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
|
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
|
||||||
|
|
||||||
const onServiceUpdate = (service: DealServiceSchema) => {
|
const onServiceUpdate = (service: DealServiceSchema) => {
|
||||||
@@ -24,18 +24,18 @@ const useDealServicesTableState = () => {
|
|||||||
DealService.updateDealService({
|
DealService.updateDealService({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
service
|
service,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onServiceDelete = (service: DealServiceSchema) => {
|
const onServiceDelete = (service: DealServiceSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -56,23 +56,23 @@ const useDealServicesTableState = () => {
|
|||||||
DealService.deleteDealService({
|
DealService.deleteDealService({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
serviceId: service.service.id
|
serviceId: service.service.id,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
cancel: "Отмена",
|
cancel: "Отмена",
|
||||||
confirm: "Удалить"
|
confirm: "Удалить",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onServiceCreate = (service: DealServiceSchema) => {
|
const onServiceCreate = (service: DealServiceSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.addDealService({
|
DealService.addDealService({
|
||||||
@@ -80,17 +80,17 @@ const useDealServicesTableState = () => {
|
|||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
serviceId: service.service.id,
|
serviceId: service.service.id,
|
||||||
quantity: service.quantity,
|
quantity: service.quantity,
|
||||||
price: service.price
|
price: service.price,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -106,23 +106,23 @@ const useDealServicesTableState = () => {
|
|||||||
DealService.deleteMultipleDealServices({
|
DealService.deleteMultipleDealServices({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
serviceIds: items.map(item => item.service.id)
|
serviceIds: items.map(item => item.service.id),
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
cancel: "Отмена",
|
cancel: "Отмена",
|
||||||
confirm: "Удалить"
|
confirm: "Удалить",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onServiceUpdate,
|
onServiceUpdate,
|
||||||
@@ -130,9 +130,9 @@ const useDealServicesTableState = () => {
|
|||||||
onServiceCreate,
|
onServiceCreate,
|
||||||
onsServiceMultipleDelete,
|
onsServiceMultipleDelete,
|
||||||
tableRef,
|
tableRef,
|
||||||
services: selectedDeal?.services || []
|
services: selectedDeal?.services || [],
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
const DealEditDrawerServicesTable = () => {
|
const DealEditDrawerServicesTable = () => {
|
||||||
const {
|
const {
|
||||||
services,
|
services,
|
||||||
@@ -140,7 +140,7 @@ const DealEditDrawerServicesTable = () => {
|
|||||||
onServiceCreate,
|
onServiceCreate,
|
||||||
onServiceUpdate,
|
onServiceUpdate,
|
||||||
onServiceDelete,
|
onServiceDelete,
|
||||||
onsServiceMultipleDelete
|
onsServiceMultipleDelete,
|
||||||
} = useDealServicesTableState();
|
} = useDealServicesTableState();
|
||||||
|
|
||||||
return (<DealServicesTable
|
return (<DealServicesTable
|
||||||
@@ -150,26 +150,26 @@ const DealEditDrawerServicesTable = () => {
|
|||||||
onDelete={onServiceDelete}
|
onDelete={onServiceDelete}
|
||||||
onCreate={onServiceCreate}
|
onCreate={onServiceCreate}
|
||||||
onMultipleDelete={onsServiceMultipleDelete}
|
onMultipleDelete={onsServiceMultipleDelete}
|
||||||
/>)
|
/>);
|
||||||
}
|
};
|
||||||
|
|
||||||
const useDealProductTableState = () => {
|
const useDealProductTableState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||||
|
|
||||||
const onProductUpdate = (product: DealProductSchema) => {
|
const onProductUpdate = (product: DealProductSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.updateDealProduct({
|
DealService.updateDealProduct({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
product: product
|
product: product,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onProductDelete = (product: DealProductSchema) => {
|
const onProductDelete = (product: DealProductSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -190,39 +190,39 @@ const useDealProductTableState = () => {
|
|||||||
DealService.deleteDealProduct({
|
DealService.deleteDealProduct({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
productId: product.product.id
|
productId: product.product.id,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
cancel: "Отмена",
|
cancel: "Отмена",
|
||||||
confirm: "Удалить"
|
confirm: "Удалить",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onProductCreate = (product: DealProductSchema) => {
|
const onProductCreate = (product: DealProductSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
DealService.addDealProduct({
|
DealService.addDealProduct({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
product: product
|
product: product,
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onProductMultipleDelete = (items: DealProductSchema[]) => {
|
const onProductMultipleDelete = (items: DealProductSchema[]) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -238,32 +238,32 @@ const useDealProductTableState = () => {
|
|||||||
DealService.deleteMultipleDealProducts({
|
DealService.deleteMultipleDealProducts({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
dealId: selectedDeal.id,
|
dealId: selectedDeal.id,
|
||||||
productIds: items.map(item => item.product.id)
|
productIds: items.map(item => item.product.id),
|
||||||
}
|
},
|
||||||
}).then(async ({ok, message}) => {
|
}).then(async ({ ok, message }) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DealService.getDealById({dealId: selectedDeal.id})
|
await DealService.getDealById({ dealId: selectedDeal.id })
|
||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
cancel: "Отмена",
|
cancel: "Отмена",
|
||||||
confirm: "Удалить"
|
confirm: "Удалить",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
return {
|
return {
|
||||||
clientId: selectedDeal?.clientId || -1,
|
clientId: selectedDeal?.clientId || -1,
|
||||||
products: selectedDeal?.products || [],
|
products: selectedDeal?.products || [],
|
||||||
onProductUpdate,
|
onProductUpdate,
|
||||||
onProductDelete,
|
onProductDelete,
|
||||||
onProductCreate,
|
onProductCreate,
|
||||||
onProductMultipleDelete
|
onProductMultipleDelete,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
const DealEditDrawerProductsTable = () => {
|
const DealEditDrawerProductsTable = () => {
|
||||||
const {
|
const {
|
||||||
products,
|
products,
|
||||||
@@ -283,79 +283,86 @@ const DealEditDrawerProductsTable = () => {
|
|||||||
onCreate={onProductCreate}
|
onCreate={onProductCreate}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const useDealStatusChangeState = () => {
|
const useDealStatusChangeState = () => {
|
||||||
const {selectedDeal} = useDealPageContext();
|
const { selectedDeal } = useDealPageContext();
|
||||||
return {
|
return {
|
||||||
statusHistory: selectedDeal?.statusHistory || []
|
statusHistory: selectedDeal?.statusHistory || [],
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
const DealEditDrawerStatusChangeTable = () => {
|
const DealEditDrawerStatusChangeTable = () => {
|
||||||
const {statusHistory} = useDealStatusChangeState();
|
const { statusHistory } = useDealStatusChangeState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DealStatusChangeTable
|
<DealStatusChangeTable
|
||||||
items={statusHistory}
|
items={statusHistory}
|
||||||
/>)
|
/>);
|
||||||
}
|
};
|
||||||
|
|
||||||
const useDealEditDrawerState = () => {
|
const useDealEditDrawerState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||||
return {
|
return {
|
||||||
isVisible: selectedDeal !== undefined,
|
isVisible: selectedDeal !== undefined,
|
||||||
onClose: () => setSelectedDeal(undefined)
|
onClose: () => setSelectedDeal(undefined),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const DealEditDrawer: FC = () => {
|
const DealEditDrawer: FC = () => {
|
||||||
const {isVisible, onClose} = useDealEditDrawerState();
|
const { isVisible, onClose } = useDealEditDrawerState();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible) return;
|
if (isVisible) return;
|
||||||
queryClient.invalidateQueries({queryKey: ["getDealSummaries"]})
|
queryClient.invalidateQueries({ queryKey: ["getDealSummaries"] });
|
||||||
}, [isVisible]);
|
}, [isVisible]);
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
size={"calc(100vw - 150px)"}
|
size={"calc(100vw - 150px)"}
|
||||||
position={"right"}
|
position={"right"}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
removeScrollProps={{allowPinchZoom: true}}
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
withCloseButton={false}
|
withCloseButton={false}
|
||||||
opened={isVisible}
|
opened={isVisible}
|
||||||
styles={{body: {height: '100%'}}}
|
styles={{
|
||||||
|
body: {
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column", gap: rem(10),
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={"general"}
|
defaultValue={"general"}
|
||||||
h={'100%'}
|
flex={1}
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<Tabs.List
|
<Tabs.List
|
||||||
>
|
>
|
||||||
<Tabs.Tab value={"general"} leftSection={<IconSettings/>}>
|
<Tabs.Tab value={"general"} leftSection={<IconSettings />}>
|
||||||
Общее
|
Общее
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab value={"history"} leftSection={<IconCalendarUser/>}>
|
<Tabs.Tab value={"history"} leftSection={<IconCalendarUser />}>
|
||||||
История
|
История
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab value={"servicesAndProducts"} leftSection={<IconBox/>}>
|
<Tabs.Tab value={"servicesAndProducts"} leftSection={<IconBox />}>
|
||||||
Товары и услуги
|
Товары и услуги
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Panel value={"general"}>
|
<Tabs.Panel value={"general"}>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Box h={"100%"} w={"100%"} p={rem(10)}>
|
<Box h={"100%"} w={"100%"} p={rem(10)}>
|
||||||
<DealEditDrawerGeneralTab/>
|
<DealEditDrawerGeneralTab />
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -364,40 +371,40 @@ const DealEditDrawer: FC = () => {
|
|||||||
<Tabs.Panel value={"history"}>
|
<Tabs.Panel value={"history"}>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<Box p={rem(10)}>
|
<Box p={rem(10)}>
|
||||||
<DealEditDrawerStatusChangeTable/>
|
<DealEditDrawerStatusChangeTable />
|
||||||
</Box>
|
</Box>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value={"servicesAndProducts"}>
|
<Tabs.Panel value={"servicesAndProducts"}>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<Box p={rem(10)}>
|
<Box p={rem(10)}>
|
||||||
<ProductAndServiceTab/>
|
<ProductAndServiceTab />
|
||||||
</Box>
|
</Box>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</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>
|
||||||
<Tabs.Panel value={"products"}>
|
<Tabs.Panel value={"products"}>
|
||||||
<Box p={rem(10)}>
|
<Box p={rem(10)}>
|
||||||
<DealEditDrawerProductsTable/>
|
<DealEditDrawerProductsTable />
|
||||||
</Box>
|
</Box>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default DealEditDrawer;
|
export default DealEditDrawer;
|
||||||
@@ -103,7 +103,7 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||||||
<Flex direction={"column"}>
|
<Flex direction={"column"} justify={"space-between"} h={"100%"}>
|
||||||
<Fieldset legend={`Общие параметры [ID: ${deal.id}]`}>
|
<Fieldset legend={`Общие параметры [ID: ${deal.id}]`}>
|
||||||
<Flex direction={"column"} gap={rem(10)}>
|
<Flex direction={"column"} gap={rem(10)}>
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -122,6 +122,15 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
placeholder={"Текущий статус"}
|
placeholder={"Текущий статус"}
|
||||||
label={"Текущий статус"}
|
label={"Текущий статус"}
|
||||||
value={DealStatusDictionary[deal.currentStatus as DealStatus]} />
|
value={DealStatusDictionary[deal.currentStatus as DealStatus]} />
|
||||||
|
{deal.category && (
|
||||||
|
<TextInput
|
||||||
|
disabled
|
||||||
|
placeholder={"Категория"}
|
||||||
|
label={"Категория"}
|
||||||
|
value={deal.category.name}
|
||||||
|
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Textarea
|
<Textarea
|
||||||
label={"Коментарий к сделке"}
|
label={"Коментарий к сделке"}
|
||||||
placeholder={"Введите коментарий к сделке"}
|
placeholder={"Введите коментарий к сделке"}
|
||||||
@@ -141,34 +150,6 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
<Fieldset legend={"Клиент"}>
|
|
||||||
<TextInput
|
|
||||||
disabled
|
|
||||||
placeholder={"Название"}
|
|
||||||
label={"Название"}
|
|
||||||
value={deal.client.name}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
placeholder={"Введите телефон"}
|
|
||||||
label={"Телефон клиента"}
|
|
||||||
{...form.getInputProps("client.details.phoneNumber")}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
placeholder={"Введите email"}
|
|
||||||
label={"Email"}
|
|
||||||
{...form.getInputProps("client.details.email")}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
placeholder={"Введите телеграм"}
|
|
||||||
label={"Телеграм"}
|
|
||||||
{...form.getInputProps("client.details.telegram")}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
placeholder={"Введите ИНН"}
|
|
||||||
label={"ИНН"}
|
|
||||||
{...form.getInputProps("client.details.inn")}
|
|
||||||
/>
|
|
||||||
</Fieldset>
|
|
||||||
<Flex mt={"md"} gap={rem(10)} align={"center"} justify={"flex-end"}>
|
<Flex mt={"md"} gap={rem(10)} align={"center"} justify={"flex-end"}>
|
||||||
<Flex align={"center"} gap={rem(10)} justify={"center"}>
|
<Flex align={"center"} gap={rem(10)} justify={"center"}>
|
||||||
|
|
||||||
@@ -199,10 +180,7 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
:
|
:
|
||||||
<ButtonCopyControlled
|
<ButtonCopyControlled
|
||||||
onCopyClick={() => {
|
onCopyClick={() => {
|
||||||
// get current datetime for filename, replaced dots with _
|
|
||||||
const date = getCurrentDateTimeForFilename();
|
const date = getCurrentDateTimeForFilename();
|
||||||
|
|
||||||
|
|
||||||
FileSaver.saveAs(`${import.meta.env.VITE_API_URL}/deal/document/${deal.id}`,
|
FileSaver.saveAs(`${import.meta.env.VITE_API_URL}/deal/document/${deal.id}`,
|
||||||
`bill_${deal.id}_${date}.pdf`);
|
`bill_${deal.id}_${date}.pdf`);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import {ContextModalProps} from "@mantine/modals";
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
import {DealProductSchema, DealProductServiceSchema} from "../../../client";
|
import { DealProductSchema, DealProductServiceSchema } from "../../../client";
|
||||||
import {useForm} from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import {Fieldset, NumberInput} from "@mantine/core";
|
import { NumberInput } from "@mantine/core";
|
||||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||||
import DealProductServiceTable from "../components/DealProductsTable/DealProductServiceTable.tsx";
|
import { omit } from "lodash";
|
||||||
import {omit} from "lodash";
|
|
||||||
import {BaseFormInputProps} from "../../../types/utils.ts";
|
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
clientId: number;
|
clientId: number;
|
||||||
@@ -65,13 +63,13 @@ const AddDealProductModal = ({
|
|||||||
min={1}
|
min={1}
|
||||||
{...form.getInputProps('quantity')}
|
{...form.getInputProps('quantity')}
|
||||||
/>
|
/>
|
||||||
<Fieldset legend={'Услуги'}>
|
{/*<Fieldset legend={'Услуги'}>*/}
|
||||||
<DealProductServiceTable
|
{/* <DealProductServiceTable*/}
|
||||||
quantity={form.values.quantity || 1}
|
{/* quantity={form.values.quantity || 1}*/}
|
||||||
{...form.getInputProps('services') as
|
{/* {...form.getInputProps('services') as*/}
|
||||||
BaseFormInputProps<DealProductServiceSchema[]>}
|
{/* BaseFormInputProps<DealProductServiceSchema[]>}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</Fieldset>
|
{/*</Fieldset>*/}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {ContextModalProps} from "@mantine/modals";
|
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, ServicePriceCategorySchema } from "../../../client";
|
||||||
import {useForm} from "@mantine/form";
|
import {useForm} from "@mantine/form";
|
||||||
import {ComboboxItem, ComboboxItemGroup, NumberInput, OptionsFilter} from "@mantine/core";
|
import {ComboboxItem, ComboboxItemGroup, NumberInput, OptionsFilter} from "@mantine/core";
|
||||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||||
@@ -10,6 +10,7 @@ import {RootState} from "../../../redux/store.ts";
|
|||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
serviceIds?: number[];
|
serviceIds?: number[];
|
||||||
|
category?: ServicePriceCategorySchema;
|
||||||
}
|
}
|
||||||
type Props = CreateEditFormProps<Partial<DealServiceSchema>> & RestProps;
|
type Props = CreateEditFormProps<Partial<DealServiceSchema>> & RestProps;
|
||||||
const AddDealServiceModal = ({
|
const AddDealServiceModal = ({
|
||||||
@@ -18,7 +19,7 @@ const AddDealServiceModal = ({
|
|||||||
innerProps
|
innerProps
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
const authState = useSelector((state: RootState) => state.auth);
|
const authState = useSelector((state: RootState) => state.auth);
|
||||||
|
console.log(innerProps.category)
|
||||||
const isEditing = 'element' in innerProps;
|
const isEditing = 'element' in innerProps;
|
||||||
const form = useForm<Partial<DealServiceSchema>>({
|
const form = useForm<Partial<DealServiceSchema>>({
|
||||||
initialValues: isEditing ? innerProps.element : {
|
initialValues: isEditing ? innerProps.element : {
|
||||||
@@ -54,6 +55,7 @@ const AddDealServiceModal = ({
|
|||||||
<BaseFormModal.Body>
|
<BaseFormModal.Body>
|
||||||
<>
|
<>
|
||||||
<ServiceWithPriceInput
|
<ServiceWithPriceInput
|
||||||
|
category={innerProps.category}
|
||||||
serviceProps={{
|
serviceProps={{
|
||||||
...form.getInputProps('service'),
|
...form.getInputProps('service'),
|
||||||
label: "Услуга",
|
label: "Услуга",
|
||||||
|
|||||||
@@ -1,42 +1,43 @@
|
|||||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
import {DealProductServiceSchema, ServiceSchema} from "../../../client";
|
import { DealProductServiceSchema, ServicePriceCategorySchema, ServiceSchema } from "../../../client";
|
||||||
import {ContextModalProps} from "@mantine/modals";
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
import {useForm, UseFormReturnType} from "@mantine/form";
|
import { useForm, UseFormReturnType } from "@mantine/form";
|
||||||
import {isNil, isNumber} from "lodash";
|
import { isNil, isNumber } from "lodash";
|
||||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||||
import {Flex} from "@mantine/core";
|
import { Flex } from "@mantine/core";
|
||||||
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";
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
quantity: number;
|
quantity: number;
|
||||||
serviceIds: number[];
|
serviceIds: number[];
|
||||||
|
category?: ServicePriceCategorySchema;
|
||||||
}
|
}
|
||||||
type Props = CreateEditFormProps<DealProductServiceSchema> & RestProps;
|
type Props = CreateEditFormProps<DealProductServiceSchema> & RestProps;
|
||||||
|
|
||||||
const ProductServiceFormModal = ({
|
const ProductServiceFormModal = ({
|
||||||
context,
|
context,
|
||||||
id, innerProps
|
id, innerProps,
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
const authState = useSelector((state: RootState) => state.auth);
|
const authState = useSelector((state: RootState) => state.auth);
|
||||||
|
|
||||||
const isEditing = 'onChange' in innerProps;
|
const isEditing = "onChange" in innerProps;
|
||||||
const initialValues: Partial<DealProductServiceSchema> = isEditing ? innerProps.element : {
|
const initialValues: Partial<DealProductServiceSchema> = isEditing ? innerProps.element : {
|
||||||
service: undefined,
|
service: undefined,
|
||||||
price: undefined,
|
price: undefined,
|
||||||
employees: []
|
employees: [],
|
||||||
}
|
};
|
||||||
const form = useForm<Partial<DealProductServiceSchema>>({
|
const form = useForm<Partial<DealProductServiceSchema>>({
|
||||||
initialValues,
|
initialValues,
|
||||||
validate: {
|
validate: {
|
||||||
service: (service?: ServiceSchema) => isNil(service) || service.id < 0 ? 'Укажите услугу' : null,
|
service: (service?: ServiceSchema) => isNil(service) || service.id < 0 ? "Укажите услугу" : null,
|
||||||
price: (price?: number) => !isNumber(price) || price < 0 ? 'Укажите цену' : null
|
price: (price?: number) => !isNumber(price) || price < 0 ? "Укажите цену" : null,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
{...innerProps}
|
{...innerProps}
|
||||||
@@ -49,25 +50,26 @@ const ProductServiceFormModal = ({
|
|||||||
<Flex w={"100%"}>
|
<Flex w={"100%"}>
|
||||||
|
|
||||||
<ServiceWithPriceInput
|
<ServiceWithPriceInput
|
||||||
|
category={innerProps.category}
|
||||||
serviceProps={{
|
serviceProps={{
|
||||||
...form.getInputProps('service'),
|
...form.getInputProps("service"),
|
||||||
label: "Услуга",
|
label: "Услуга",
|
||||||
placeholder: "Выберите услугу",
|
placeholder: "Выберите услугу",
|
||||||
disabled: isEditing,
|
disabled: isEditing,
|
||||||
filterBy: (item) => !innerProps.serviceIds.includes(item.id) || isEditing,
|
filterBy: (item) => !innerProps.serviceIds.includes(item.id) || isEditing,
|
||||||
style: {width: "100%"}
|
style: { width: "100%" },
|
||||||
}}
|
}}
|
||||||
priceProps={{
|
priceProps={{
|
||||||
...form.getInputProps('price'),
|
...form.getInputProps("price"),
|
||||||
label: "Цена",
|
label: "Цена",
|
||||||
placeholder: "Введите цену",
|
placeholder: "Введите цену",
|
||||||
style: {width: "100%"},
|
style: { width: "100%" },
|
||||||
disabled: authState.isGuest
|
disabled: authState.isGuest,
|
||||||
}}
|
}}
|
||||||
filterType={ServiceType.PRODUCT_SERVICE}
|
filterType={ServiceType.PRODUCT_SERVICE}
|
||||||
containerProps={{
|
containerProps={{
|
||||||
direction: "column",
|
direction: "column",
|
||||||
style: {width: "100%"}
|
style: { width: "100%" },
|
||||||
|
|
||||||
}}
|
}}
|
||||||
lockOnEdit={isEditing}
|
lockOnEdit={isEditing}
|
||||||
@@ -79,6 +81,6 @@ const ProductServiceFormModal = ({
|
|||||||
</BaseFormModal.Body>
|
</BaseFormModal.Body>
|
||||||
</BaseFormModal>
|
</BaseFormModal>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
export default ProductServiceFormModal;
|
export default ProductServiceFormModal;
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
import {CRUDTableProps} from "../../../../../../types/CRUDTable.tsx";
|
import { CRUDTableProps } from "../../../../../../types/CRUDTable.tsx";
|
||||||
import {DealServiceSchema, GetServiceKitSchema, UserSchema} from "../../../../../../client";
|
import { DealServiceSchema, GetServiceKitSchema, UserSchema } from "../../../../../../client";
|
||||||
import {FC, useState} from "react";
|
import { FC, useState } from "react";
|
||||||
import {ActionIcon, Button, Flex, Modal, NumberInput, rem, Text, Title, Tooltip} from "@mantine/core";
|
import { ActionIcon, Button, Flex, Modal, NumberInput, rem, Text, Title, Tooltip } from "@mantine/core";
|
||||||
import {IconTrash, IconUsersGroup} from "@tabler/icons-react";
|
import { IconTrash, IconUsersGroup } from "@tabler/icons-react";
|
||||||
import {modals} from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
import {isNumber} from "lodash";
|
import { isNumber } from "lodash";
|
||||||
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
|
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
|
||||||
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";
|
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
onKitAdd?: (kit: GetServiceKitSchema) => void
|
onKitAdd?: (kit: GetServiceKitSchema) => void
|
||||||
};
|
};
|
||||||
type Props = CRUDTableProps<DealServiceSchema> & RestProps;
|
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 { dealState } = useDealProductAndServiceTabState();
|
||||||
const isLocked = Boolean(dealState.deal?.billRequest);
|
const isLocked = Boolean(dealState.deal?.billRequest);
|
||||||
|
|
||||||
const [currentService, setCurrentService] = useState<DealServiceSchema | undefined>();
|
const [currentService, setCurrentService] = useState<DealServiceSchema | undefined>();
|
||||||
@@ -27,68 +27,70 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
|
|||||||
const onDeleteClick = (item: DealServiceSchema) => {
|
const onDeleteClick = (item: DealServiceSchema) => {
|
||||||
if (!onDelete) return;
|
if (!onDelete) return;
|
||||||
onDelete(item);
|
onDelete(item);
|
||||||
}
|
};
|
||||||
const onCreateClick = () => {
|
const onCreateClick = () => {
|
||||||
if (!onCreate) return;
|
if (!onCreate) return;
|
||||||
|
console.log("228")
|
||||||
const serviceIds = items.map(service => service.service.id);
|
const serviceIds = items.map(service => service.service.id);
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "addDealService",
|
modal: "addDealService",
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onCreate: onCreate,
|
onCreate: onCreate,
|
||||||
serviceIds
|
serviceIds,
|
||||||
|
category: dealState.deal?.category || undefined,
|
||||||
},
|
},
|
||||||
withCloseButton: false
|
withCloseButton: false,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onQuantityChange = (item: DealServiceSchema, quantity: number) => {
|
const onQuantityChange = (item: DealServiceSchema, quantity: number) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
onChange({
|
onChange({
|
||||||
...item,
|
...item,
|
||||||
quantity
|
quantity,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onPriceChange = (item: DealServiceSchema, price: number) => {
|
const onPriceChange = (item: DealServiceSchema, price: number) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
onChange({
|
onChange({
|
||||||
...item,
|
...item,
|
||||||
price
|
price,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const onEmployeeClick = (item: DealServiceSchema) => {
|
const onEmployeeClick = (item: DealServiceSchema) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
setCurrentService(item);
|
setCurrentService(item);
|
||||||
setEmployeesModalVisible(true);
|
setEmployeesModalVisible(true);
|
||||||
}
|
};
|
||||||
const onEmployeeModalClose = () => {
|
const onEmployeeModalClose = () => {
|
||||||
setEmployeesModalVisible(false);
|
setEmployeesModalVisible(false);
|
||||||
setCurrentService(undefined);
|
setCurrentService(undefined);
|
||||||
}
|
};
|
||||||
const getCurrentEmployees = (): UserSchema[] => {
|
const getCurrentEmployees = (): UserSchema[] => {
|
||||||
if (!currentService) return [];
|
if (!currentService) return [];
|
||||||
const item = items.find(i => i.service.id === currentService.service.id)
|
const item = items.find(i => i.service.id === currentService.service.id);
|
||||||
if (!item) return [];
|
if (!item) return [];
|
||||||
return item.employees;
|
return item.employees;
|
||||||
}
|
};
|
||||||
const onEmployeesChange = (items: UserSchema[]) => {
|
const onEmployeesChange = (items: UserSchema[]) => {
|
||||||
if (!currentService || !onChange) return;
|
if (!currentService || !onChange) return;
|
||||||
onChange({
|
onChange({
|
||||||
...currentService,
|
...currentService,
|
||||||
employees: items
|
employees: items,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
const onAddKitClick = () => {
|
const onAddKitClick = () => {
|
||||||
if (!onKitAdd) return;
|
if (!onKitAdd) return;
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "servicesKitSelectModal",
|
modal: "servicesKitSelectModal",
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onSelect: onKitAdd,
|
onSelect: onKitAdd,
|
||||||
serviceType: ServiceType.DEAL_SERVICE
|
serviceType: ServiceType.DEAL_SERVICE,
|
||||||
},
|
},
|
||||||
withCloseButton: false
|
withCloseButton: false,
|
||||||
|
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -103,7 +105,7 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
|
|||||||
<Title
|
<Title
|
||||||
order={3}
|
order={3}
|
||||||
w={"100%"}
|
w={"100%"}
|
||||||
style={{textAlign: "center"}}
|
style={{ textAlign: "center" }}
|
||||||
mb={rem(10)}
|
mb={rem(10)}
|
||||||
>Общие услуги</Title>
|
>Общие услуги</Title>
|
||||||
|
|
||||||
@@ -125,13 +127,13 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
|
|||||||
<ActionIcon
|
<ActionIcon
|
||||||
disabled={isLocked}
|
disabled={isLocked}
|
||||||
variant={"default"}>
|
variant={"default"}>
|
||||||
<IconTrash/>
|
<IconTrash />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{!authState.isGuest &&
|
{!authState.isGuest &&
|
||||||
<Tooltip label="Сотрудники">
|
<Tooltip label="Сотрудники">
|
||||||
<ActionIcon onClick={() => onEmployeeClick(service)} variant={"default"}>
|
<ActionIcon onClick={() => onEmployeeClick(service)} variant={"default"}>
|
||||||
<IconUsersGroup/>
|
<IconUsersGroup />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
@@ -157,7 +159,7 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Title
|
<Title
|
||||||
style={{textAlign: "end"}}
|
style={{ textAlign: "end" }}
|
||||||
mt={rem(10)}
|
mt={rem(10)}
|
||||||
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>
|
||||||
@@ -196,6 +198,6 @@ const DealServicesTable: FC<Props> = ({items, onDelete, onCreate, onChange, onKi
|
|||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
export default DealServicesTable;
|
export default DealServicesTable;
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import {CRUDTableProps} from "../../../../../../types/CRUDTable.tsx";
|
import { CRUDTableProps } from "../../../../../../types/CRUDTable.tsx";
|
||||||
import {DealProductServiceSchema, UserSchema} from "../../../../../../client";
|
import { DealProductServiceSchema, UserSchema } from "../../../../../../client";
|
||||||
import {FC, useState} from "react";
|
import { FC, useState } from "react";
|
||||||
import useProductServicesTableColumns from "./columns.tsx";
|
import useProductServicesTableColumns from "./columns.tsx";
|
||||||
import {BaseTable} from "../../../../../../components/BaseTable/BaseTable.tsx";
|
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
|
||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import { MRT_TableOptions } from "mantine-react-table";
|
||||||
import {ActionIcon, Button, Flex, Modal, rem, Tooltip} from "@mantine/core";
|
import { ActionIcon, Button, Flex, Modal, rem, Tooltip } from "@mantine/core";
|
||||||
import {IconEdit, IconTrash, IconUsersGroup} from "@tabler/icons-react";
|
import { IconEdit, IconTrash, IconUsersGroup } from "@tabler/icons-react";
|
||||||
import {modals} from "@mantine/modals";
|
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";
|
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
@@ -26,13 +26,13 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
onDelete,
|
onDelete,
|
||||||
onChange,
|
onChange,
|
||||||
onCopyServices,
|
onCopyServices,
|
||||||
onKitAdd
|
onKitAdd,
|
||||||
}) => {
|
}) => {
|
||||||
const {dealState} = useDealProductAndServiceTabState();
|
const { dealState } = useDealProductAndServiceTabState();
|
||||||
const isLocked = Boolean(dealState.deal?.billRequest);
|
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 });
|
||||||
const serviceIds = items.map(service => service.service.id);
|
const serviceIds = items.map(service => service.service.id);
|
||||||
|
|
||||||
const [currentService, setCurrentService] = useState<DealProductServiceSchema | undefined>();
|
const [currentService, setCurrentService] = useState<DealProductServiceSchema | undefined>();
|
||||||
@@ -45,11 +45,12 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
innerProps: {
|
innerProps: {
|
||||||
onCreate: onCreate,
|
onCreate: onCreate,
|
||||||
serviceIds,
|
serviceIds,
|
||||||
quantity
|
quantity,
|
||||||
|
category: dealState.deal?.category || undefined,
|
||||||
},
|
},
|
||||||
withCloseButton: false
|
withCloseButton: false,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const onChangeClick = (item: DealProductServiceSchema) => {
|
const onChangeClick = (item: DealProductServiceSchema) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
@@ -59,33 +60,35 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
element: item,
|
element: item,
|
||||||
onChange,
|
onChange,
|
||||||
serviceIds,
|
serviceIds,
|
||||||
quantity
|
quantity,
|
||||||
|
category: dealState.deal?.category || undefined,
|
||||||
|
|
||||||
},
|
},
|
||||||
withCloseButton: false
|
withCloseButton: false,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
const onEmployeeClick = (item: DealProductServiceSchema) => {
|
const onEmployeeClick = (item: DealProductServiceSchema) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
setCurrentService(item);
|
setCurrentService(item);
|
||||||
setEmployeesModalVisible(true);
|
setEmployeesModalVisible(true);
|
||||||
}
|
};
|
||||||
const onEmployeeModalClose = () => {
|
const onEmployeeModalClose = () => {
|
||||||
setEmployeesModalVisible(false);
|
setEmployeesModalVisible(false);
|
||||||
setCurrentService(undefined);
|
setCurrentService(undefined);
|
||||||
}
|
};
|
||||||
const getCurrentEmployees = (): UserSchema[] => {
|
const getCurrentEmployees = (): UserSchema[] => {
|
||||||
if (!currentService) return [];
|
if (!currentService) return [];
|
||||||
const item = items.find(i => i.service.id === currentService.service.id)
|
const item = items.find(i => i.service.id === currentService.service.id);
|
||||||
if (!item) return [];
|
if (!item) return [];
|
||||||
return item.employees;
|
return item.employees;
|
||||||
}
|
};
|
||||||
const onEmployeesChange = (items: UserSchema[]) => {
|
const onEmployeesChange = (items: UserSchema[]) => {
|
||||||
if (!currentService || !onChange) return;
|
if (!currentService || !onChange) return;
|
||||||
onChange({
|
onChange({
|
||||||
...currentService,
|
...currentService,
|
||||||
employees: items
|
employees: items,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -127,7 +130,7 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
renderRowActions: ({row}) => (
|
renderRowActions: ({ row }) => (
|
||||||
<Flex gap="md">
|
<Flex gap="md">
|
||||||
<Tooltip label="Удалить">
|
<Tooltip label="Удалить">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
@@ -135,7 +138,7 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (onDelete) onDelete(row.original);
|
if (onDelete) onDelete(row.original);
|
||||||
}} variant={"default"}>
|
}} variant={"default"}>
|
||||||
<IconTrash/>
|
<IconTrash />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -145,13 +148,13 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
|
|
||||||
onClick={() => onChangeClick(row.original)}
|
onClick={() => onChangeClick(row.original)}
|
||||||
variant={"default"}>
|
variant={"default"}>
|
||||||
<IconEdit/>
|
<IconEdit />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{!authState.isGuest &&
|
{!authState.isGuest &&
|
||||||
<Tooltip label="Сотрудники">
|
<Tooltip label="Сотрудники">
|
||||||
<ActionIcon onClick={() => onEmployeeClick(row.original)} variant={"default"}>
|
<ActionIcon onClick={() => onEmployeeClick(row.original)} variant={"default"}>
|
||||||
<IconUsersGroup/>
|
<IconUsersGroup />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
@@ -177,6 +180,6 @@ const ProductServicesTable: FC<Props> = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
export default ProductServicesTable;
|
export default ProductServicesTable;
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
import {FC, useEffect, useState} from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import styles from './LeadsPage.module.css';
|
import styles from "./LeadsPage.module.css";
|
||||||
import Board from "../../../components/Dnd/Board/Board.tsx";
|
import Board from "../../../components/Dnd/Board/Board.tsx";
|
||||||
import {DragDropContext, Droppable, DropResult} from "@hello-pangea/dnd";
|
import { DragDropContext, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||||
import {useDealSummaries} from "../hooks/useDealSummaries.tsx";
|
import { useDealSummaries } from "../hooks/useDealSummaries.tsx";
|
||||||
import {DealStatus, getDealStatusByName} from "../../../shared/enums/DealStatus.ts";
|
import { DealStatus, getDealStatusByName } from "../../../shared/enums/DealStatus.ts";
|
||||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
||||||
import {DealPageContextProvider} from "../contexts/DealPageContext.tsx";
|
import { DealPageContextProvider } from "../contexts/DealPageContext.tsx";
|
||||||
import {modals} from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
import {DealService, DealSummaryReorderRequest} from "../../../client";
|
import { DealService, DealSummaryReorderRequest } from "../../../client";
|
||||||
import {ActionIcon, Flex, rem, Text} from "@mantine/core";
|
import { ActionIcon, Flex, rem, Text } from "@mantine/core";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
import {IconMenu2, IconMenuDeep} from "@tabler/icons-react";
|
import { IconMenu2, IconMenuDeep } from "@tabler/icons-react";
|
||||||
import useDealsPageState from "../../DealsPage/hooks/useDealsPageState.tsx";
|
import useDealsPageState from "../../DealsPage/hooks/useDealsPageState.tsx";
|
||||||
import DealStatusSelect from "../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx";
|
import DealStatusSelect from "../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx";
|
||||||
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||||
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
||||||
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
|
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
|
||||||
import {motion} from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
enum DisplayMode {
|
enum DisplayMode {
|
||||||
BOARD,
|
BOARD,
|
||||||
@@ -26,9 +26,9 @@ enum DisplayMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const LeadsPage: FC = () => {
|
export const LeadsPage: FC = () => {
|
||||||
const {data, form} = useDealsPageState();
|
const { data, form } = useDealsPageState();
|
||||||
|
|
||||||
const {summariesRaw, refetch} = useDealSummaries();
|
const { summariesRaw, refetch } = useDealSummaries();
|
||||||
const [summaries, setSummaries] = useState(summariesRaw);
|
const [summaries, setSummaries] = useState(summariesRaw);
|
||||||
const [displayMode, setDisplayMode] = useState<DisplayMode>(DisplayMode.BOARD);
|
const [displayMode, setDisplayMode] = useState<DisplayMode>(DisplayMode.BOARD);
|
||||||
const [isDragEnded, setIsDragEnded] = useState(true);
|
const [isDragEnded, setIsDragEnded] = useState(true);
|
||||||
@@ -46,19 +46,19 @@ export const LeadsPage: FC = () => {
|
|||||||
Вы действительно хотите удалить сделку {summary.name}?
|
Вы действительно хотите удалить сделку {summary.name}?
|
||||||
</Flex>,
|
</Flex>,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
DealService.deleteDeal({requestBody: {dealId: dealId}})
|
DealService.deleteDeal({ requestBody: { dealId: dealId } })
|
||||||
.then(async ({ok, message}) => {
|
.then(async ({ ok, message }) => {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
await refetch();
|
await refetch();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
confirm: "Удалить",
|
confirm: "Удалить",
|
||||||
cancel: "Отмена"
|
cancel: "Отмена",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
const onSuccess = (dealId: number) => {
|
const onSuccess = (dealId: number) => {
|
||||||
const summary = summaries.find(summary => summary.id == dealId);
|
const summary = summaries.find(summary => summary.id == dealId);
|
||||||
if (!summary) return;
|
if (!summary) return;
|
||||||
@@ -69,19 +69,19 @@ export const LeadsPage: FC = () => {
|
|||||||
Вы действительно хотите завершить сделку {summary.name}?
|
Вы действительно хотите завершить сделку {summary.name}?
|
||||||
</Flex>,
|
</Flex>,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
DealService.completeDeal({requestBody: {dealId: dealId}})
|
DealService.completeDeal({ requestBody: { dealId: dealId } })
|
||||||
.then(async ({ok, message}) => {
|
.then(async ({ ok, message }) => {
|
||||||
notifications.guess(ok, {message});
|
notifications.guess(ok, { message });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
await refetch();
|
await refetch();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
confirm: "Завершить",
|
confirm: "Завершить",
|
||||||
cancel: "Отмена"
|
cancel: "Отмена",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
const onDragEnd = async (result: DropResult) => {
|
const onDragEnd = async (result: DropResult) => {
|
||||||
setIsDragEnded(true);
|
setIsDragEnded(true);
|
||||||
// If there is no changes
|
// If there is no changes
|
||||||
@@ -97,11 +97,11 @@ export const LeadsPage: FC = () => {
|
|||||||
|
|
||||||
// Checking if it is custom actions
|
// Checking if it is custom actions
|
||||||
const droppableId = result.destination.droppableId;
|
const droppableId = result.destination.droppableId;
|
||||||
if (droppableId === 'DELETE') {
|
if (droppableId === "DELETE") {
|
||||||
onDelete(dealId);
|
onDelete(dealId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (droppableId === 'SUCCESS') {
|
if (droppableId === "SUCCESS") {
|
||||||
onSuccess(dealId);
|
onSuccess(dealId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -109,10 +109,10 @@ export const LeadsPage: FC = () => {
|
|||||||
const request: Partial<DealSummaryReorderRequest> = {
|
const request: Partial<DealSummaryReorderRequest> = {
|
||||||
dealId: dealId,
|
dealId: dealId,
|
||||||
index: result.destination.index,
|
index: result.destination.index,
|
||||||
status: status
|
status: status,
|
||||||
}
|
};
|
||||||
if (status == summary.status) {
|
if (status == summary.status) {
|
||||||
DealService.reorderDealSummaries({requestBody: request as DealSummaryReorderRequest})
|
DealService.reorderDealSummaries({ requestBody: request as DealSummaryReorderRequest })
|
||||||
.then(async response => {
|
.then(async response => {
|
||||||
setSummaries(response.summaries);
|
setSummaries(response.summaries);
|
||||||
await refetch();
|
await refetch();
|
||||||
@@ -120,33 +120,33 @@ export const LeadsPage: FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: 'enterDeadline',
|
modal: "enterDeadline",
|
||||||
title: "Необходимо указать дедлайн",
|
title: "Необходимо указать дедлайн",
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onSubmit: (event) => DealService.reorderDealSummaries({requestBody: event})
|
onSubmit: (event) => DealService.reorderDealSummaries({ requestBody: event })
|
||||||
.then(async response => {
|
.then(async response => {
|
||||||
setSummaries(response.summaries);
|
setSummaries(response.summaries);
|
||||||
await refetch();
|
await refetch();
|
||||||
}),
|
}),
|
||||||
request: request
|
request: request,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
};
|
||||||
const getTableBody = () => {
|
const getTableBody = () => {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={displayMode}
|
key={displayMode}
|
||||||
|
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
|
|
||||||
<DealsTable items={data}/>
|
<DealsTable items={data} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
const getBoardBody = () => {
|
const getBoardBody = () => {
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@@ -154,12 +154,12 @@ export const LeadsPage: FC = () => {
|
|||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
flex: 1
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
key={displayMode}
|
key={displayMode}
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
|
|
||||||
<DragDropContext
|
<DragDropContext
|
||||||
@@ -170,24 +170,24 @@ export const LeadsPage: FC = () => {
|
|||||||
<Flex
|
<Flex
|
||||||
justify={"space-between"}
|
justify={"space-between"}
|
||||||
direction={"column"}
|
direction={"column"}
|
||||||
style={{flex: 1}}
|
style={{ flex: 1 }}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className={styles['boards']}>
|
<div className={styles["boards"]}>
|
||||||
<Board
|
<Board
|
||||||
withCreateButton
|
withCreateButton
|
||||||
summaries={summaries
|
summaries={summaries
|
||||||
.filter(summary => summary.status == DealStatus.AWAITING_ACCEPTANCE)}
|
.filter(summary => summary.status == DealStatus.AWAITING_ACCEPTANCE)}
|
||||||
title={"Ожидает приемки"}
|
title={"Ожидает приемки"}
|
||||||
droppableId={"AWAITING_ACCEPTANCE"}
|
droppableId={"AWAITING_ACCEPTANCE"}
|
||||||
color={'#4A90E2'}
|
color={"#4A90E2"}
|
||||||
/>
|
/>
|
||||||
<Board
|
<Board
|
||||||
summaries={summaries
|
summaries={summaries
|
||||||
.filter(summary => summary.status == DealStatus.PACKAGING)}
|
.filter(summary => summary.status == DealStatus.PACKAGING)}
|
||||||
title={"Упаковка"}
|
title={"Упаковка"}
|
||||||
droppableId={"PACKAGING"}
|
droppableId={"PACKAGING"}
|
||||||
color={'#F5A623'}
|
color={"#F5A623"}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<Board
|
<Board
|
||||||
@@ -195,7 +195,7 @@ export const LeadsPage: FC = () => {
|
|||||||
.filter(summary => summary.status == DealStatus.AWAITING_SHIPMENT)}
|
.filter(summary => summary.status == DealStatus.AWAITING_SHIPMENT)}
|
||||||
title={"Ожидает отгрузки"}
|
title={"Ожидает отгрузки"}
|
||||||
droppableId={"AWAITING_SHIPMENT"}
|
droppableId={"AWAITING_SHIPMENT"}
|
||||||
color={'#7ED321'}
|
color={"#7ED321"}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<Board
|
<Board
|
||||||
@@ -203,7 +203,7 @@ export const LeadsPage: FC = () => {
|
|||||||
.filter(summary => summary.status == DealStatus.AWAITING_PAYMENT)}
|
.filter(summary => summary.status == DealStatus.AWAITING_PAYMENT)}
|
||||||
title={"Ожидает оплаты"}
|
title={"Ожидает оплаты"}
|
||||||
droppableId={"AWAITING_PAYMENT"}
|
droppableId={"AWAITING_PAYMENT"}
|
||||||
color={'#D0021B'}
|
color={"#D0021B"}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<Board
|
<Board
|
||||||
@@ -211,15 +211,15 @@ export const LeadsPage: FC = () => {
|
|||||||
.filter(summary => summary.status == DealStatus.COMPLETED)}
|
.filter(summary => summary.status == DealStatus.COMPLETED)}
|
||||||
title={"Завершена"}
|
title={"Завершена"}
|
||||||
droppableId={"COMPLETED"}
|
droppableId={"COMPLETED"}
|
||||||
color={'#417505'}
|
color={"#417505"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Flex justify={"space-between"} gap={rem(10)}>
|
<Flex justify={"space-between"} gap={rem(10)}>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
classNames(
|
classNames(
|
||||||
styles['delete'],
|
styles["delete"],
|
||||||
isDragEnded && styles['delete-hidden']
|
isDragEnded && styles["delete-hidden"],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -250,8 +250,8 @@ export const LeadsPage: FC = () => {
|
|||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
classNames(
|
classNames(
|
||||||
styles['delete'],
|
styles["delete"],
|
||||||
isDragEnded && styles['delete-hidden']
|
isDragEnded && styles["delete-hidden"],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -284,11 +284,11 @@ export const LeadsPage: FC = () => {
|
|||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
const getBody = () => {
|
const getBody = () => {
|
||||||
return displayMode === DisplayMode.TABLE ? getTableBody() : getBoardBody();
|
return displayMode === DisplayMode.TABLE ? getTableBody() : getBoardBody();
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<PageBlock
|
<PageBlock
|
||||||
fullHeight
|
fullHeight
|
||||||
@@ -302,7 +302,7 @@ export const LeadsPage: FC = () => {
|
|||||||
>
|
>
|
||||||
<DealPageContextProvider>
|
<DealPageContextProvider>
|
||||||
<PageBlock
|
<PageBlock
|
||||||
style={{flex: 0}}
|
style={{ flex: 0 }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
align={"center"}
|
align={"center"}
|
||||||
@@ -326,7 +326,7 @@ export const LeadsPage: FC = () => {
|
|||||||
|
|
||||||
}>
|
}>
|
||||||
<IconMenuDeep
|
<IconMenuDeep
|
||||||
style={{rotate: "-90deg"}}
|
style={{ rotate: "-90deg" }}
|
||||||
/>
|
/>
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
@@ -346,14 +346,14 @@ export const LeadsPage: FC = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<motion.div
|
<motion.div
|
||||||
key={displayMode}
|
key={displayMode}
|
||||||
initial={{opacity: 0}}
|
initial={{ opacity: 0 }}
|
||||||
animate={{opacity: 1}}
|
animate={{ opacity: 1 }}
|
||||||
transition={{duration: 0.2}}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
||||||
className={styles['top-panel']}
|
className={styles["top-panel"]}
|
||||||
style={{display: displayMode === DisplayMode.TABLE ? "flex" : "none"}}
|
style={{ display: displayMode === DisplayMode.TABLE ? "flex" : "none" }}
|
||||||
>
|
>
|
||||||
<DealStatusSelect
|
<DealStatusSelect
|
||||||
onClear={() => form.setFieldValue("dealStatus", null)}
|
onClear={() => form.setFieldValue("dealStatus", null)}
|
||||||
@@ -384,7 +384,7 @@ export const LeadsPage: FC = () => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: "100%"
|
height: "100%",
|
||||||
}}>
|
}}>
|
||||||
{getBody()}
|
{getBody()}
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
@@ -394,5 +394,5 @@ export const LeadsPage: FC = () => {
|
|||||||
|
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
|
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||||
|
import { ServicePriceCategorySchema } from "../../../../client";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import useServicePriceCategoryTableColumns from "./columns.tsx";
|
||||||
|
import { MRT_TableOptions } from "mantine-react-table";
|
||||||
|
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||||
|
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
type Props = CRUDTableProps<ServicePriceCategorySchema>
|
||||||
|
|
||||||
|
const ServicePriceCategoryTable: FC<Props> = ({ items, onChange, onDelete }) => {
|
||||||
|
const columns = useServicePriceCategoryTableColumns();
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
data={items}
|
||||||
|
columns={columns}
|
||||||
|
restProps={{
|
||||||
|
enableRowActions: true,
|
||||||
|
renderRowActions: ({ row }) => (
|
||||||
|
<Flex gap="md">
|
||||||
|
<Tooltip label="Редактировать">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => onChange && onChange(row.original)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Удалить">
|
||||||
|
<ActionIcon onClick={() => onDelete && onDelete(row.original)} variant={"default"}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
} as MRT_TableOptions<ServicePriceCategorySchema>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicePriceCategoryTable;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { MRT_ColumnDef } from "mantine-react-table";
|
||||||
|
import { ServicePriceCategorySchema } from "../../../../client";
|
||||||
|
|
||||||
|
const useServicePriceCategoryTableColumns = () => {
|
||||||
|
|
||||||
|
return useMemo<MRT_ColumnDef<ServicePriceCategorySchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Название",
|
||||||
|
enableColumnActions: false,
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
], []);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServicePriceCategoryTableColumns;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { BaseFormInputProps } from "../../../../types/utils.ts";
|
||||||
|
import type { ServiceCategoryPriceSchema, ServicePriceCategorySchema } from "../../../../client";
|
||||||
|
import { FC, useEffect, useState } from "react";
|
||||||
|
import { Flex, NumberInput, rem } from "@mantine/core";
|
||||||
|
import ServicePriceCategorySelect
|
||||||
|
from "../../../../components/Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.tsx";
|
||||||
|
|
||||||
|
export type PriceCategoryInputProps = BaseFormInputProps<ServiceCategoryPriceSchema[]>
|
||||||
|
|
||||||
|
const PriceCategoryInput: FC<PriceCategoryInputProps> = (props: PriceCategoryInputProps) => {
|
||||||
|
const [innerState, setInnerState] = useState<ServiceCategoryPriceSchema[]>(props.value || []);
|
||||||
|
const [category, setCategory] = useState<ServicePriceCategorySchema | undefined>(undefined);
|
||||||
|
|
||||||
|
const getValue = (): number | undefined | string => {
|
||||||
|
if (category === undefined) return undefined;
|
||||||
|
const value = innerState.find(item => item.category.id === category.id);
|
||||||
|
|
||||||
|
return value?.price || "";
|
||||||
|
};
|
||||||
|
const handleChange = (value: number | string) => {
|
||||||
|
// remove value if is string
|
||||||
|
if (typeof value === "string") {
|
||||||
|
setInnerState(innerState.filter(item => item.category.id !== category?.id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newValue = {
|
||||||
|
category: category as ServicePriceCategorySchema,
|
||||||
|
price: value,
|
||||||
|
};
|
||||||
|
const newInnerState = innerState.filter(item => item.category.id !== category?.id);
|
||||||
|
setInnerState([...newInnerState, newValue]);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value === innerState) return;
|
||||||
|
props.onChange(innerState);
|
||||||
|
}, [innerState]);
|
||||||
|
return (
|
||||||
|
<Flex direction={"column"} gap={rem(10)}>
|
||||||
|
<ServicePriceCategorySelect
|
||||||
|
label={"Категория цены"}
|
||||||
|
placeholder={"Выберите категорию цены"}
|
||||||
|
onChange={setCategory}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
label={"Цена"}
|
||||||
|
placeholder={"Введите цену"}
|
||||||
|
value={getValue()}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PriceCategoryInput;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { omit } from "lodash";
|
||||||
|
|
||||||
|
export enum ServicePriceType {
|
||||||
|
DEFAULT,
|
||||||
|
BY_RANGE,
|
||||||
|
BY_CATEGORY,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
onChange: (value: ServicePriceType) => void;
|
||||||
|
value?: ServicePriceType;
|
||||||
|
}
|
||||||
|
type Props = Omit<SegmentedControlProps, "data" | "value" | "onChange"> & RestProps;
|
||||||
|
const ServicePriceTypeSegmentedControl: FC<Props> = (props) => {
|
||||||
|
const { onChange, value } = props;
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
label: "По умолчанию",
|
||||||
|
value: ServicePriceType.DEFAULT.toString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "По диапазону",
|
||||||
|
value: ServicePriceType.BY_RANGE.toString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "По категории",
|
||||||
|
value: ServicePriceType.BY_CATEGORY.toString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const handleChange = (value: string) => {
|
||||||
|
onChange(Number(value));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const restProps = omit(props, ["onChange", "value"]);
|
||||||
|
return (
|
||||||
|
<SegmentedControl
|
||||||
|
data={data}
|
||||||
|
value={value?.toString()}
|
||||||
|
onChange={handleChange}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicePriceTypeSegmentedControl;
|
||||||
@@ -1,27 +1,33 @@
|
|||||||
import {FC} from "react";
|
import { FC } from "react";
|
||||||
import {SegmentedControl, SegmentedControlProps} from "@mantine/core";
|
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||||
|
|
||||||
export enum ServicesTab {
|
export enum ServicesTab {
|
||||||
DEAL_SERVICE,
|
DEAL_SERVICE,
|
||||||
PRODUCT_SERVICE,
|
PRODUCT_SERVICE,
|
||||||
SERVICES_KITS
|
SERVICES_KITS,
|
||||||
|
SERVICES_PRICE_CATEGORIES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Props = Omit<SegmentedControlProps, 'data'>;
|
type Props = Omit<SegmentedControlProps, "data">;
|
||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
label: 'Для товара',
|
label: "Для товара",
|
||||||
value: ServicesTab.PRODUCT_SERVICE.toString()
|
value: ServicesTab.PRODUCT_SERVICE.toString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Для сделки',
|
label: "Для сделки",
|
||||||
value: ServicesTab.DEAL_SERVICE.toString()
|
value: ServicesTab.DEAL_SERVICE.toString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Наборы услуг',
|
label: "Наборы услуг",
|
||||||
value: ServicesTab.SERVICES_KITS.toString()
|
value: ServicesTab.SERVICES_KITS.toString(),
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
label: "Категории цен",
|
||||||
|
value: ServicesTab.SERVICES_PRICE_CATEGORIES.toString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
const ServiceTypeSegmentedControl: FC<Props> = (props) => {
|
const ServiceTypeSegmentedControl: FC<Props> = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -29,6 +35,6 @@ const ServiceTypeSegmentedControl: FC<Props> = (props) => {
|
|||||||
data={data}
|
data={data}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
export default ServiceTypeSegmentedControl
|
export default ServiceTypeSegmentedControl;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import ObjectList from "../../../hooks/objectList.tsx";
|
||||||
|
import { ServiceService } from "../../../client";
|
||||||
|
|
||||||
|
const useServicePriceCategoriesList = () => ObjectList({
|
||||||
|
queryFn: ServiceService.getAllPriceCategories,
|
||||||
|
getObjectsFn: (response) => response.priceCategories,
|
||||||
|
queryKey: "getAllPriceCategories",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useServicePriceCategoriesList;
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import UseObjectState from "../../../types/UseObjectState.ts";
|
||||||
|
import { type ServicePriceCategorySchema, ServiceService } from "../../../client";
|
||||||
|
import useServicePriceCategoriesList from "./useServicePriceCategoriesList.tsx";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
const useServicePriceCategoryState = (): UseObjectState<ServicePriceCategorySchema> => {
|
||||||
|
const { objects, refetch } = useServicePriceCategoriesList();
|
||||||
|
const onCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "servicePriceCategoryForm",
|
||||||
|
title: "Создание категории цен",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onCreate = (values: ServicePriceCategorySchema) => {
|
||||||
|
console.log(ServiceService);
|
||||||
|
ServiceService.createPriceCategory({
|
||||||
|
requestBody: {
|
||||||
|
name: values.name,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onDelete = (item: ServicePriceCategorySchema) => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление категории",
|
||||||
|
children: "Вы уверены, что хотите удалить категорию?",
|
||||||
|
onConfirm: () => {
|
||||||
|
|
||||||
|
ServiceService.deletePriceCategory({
|
||||||
|
requestBody: {
|
||||||
|
id: item.id,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onChange = (item: ServicePriceCategorySchema) => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "servicePriceCategoryForm",
|
||||||
|
title: "Изменение категории цен",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onChange: (values: ServicePriceCategorySchema) => {
|
||||||
|
ServiceService.updatePriceCategory({
|
||||||
|
requestBody: {
|
||||||
|
id: item.id,
|
||||||
|
name: values.name,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
element: item,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onCreateClick,
|
||||||
|
onCreate,
|
||||||
|
onDelete,
|
||||||
|
onChange,
|
||||||
|
objects,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default useServicePriceCategoryState;
|
||||||
57
src/pages/ServicesPage/hooks/useServicesKitsState.tsx
Normal file
57
src/pages/ServicesPage/hooks/useServicesKitsState.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { GetServiceKitSchema, ServiceService } from "../../../client";
|
||||||
|
import { omit } from "lodash";
|
||||||
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import useServicesKitsList from "./useServicesKitsList.tsx";
|
||||||
|
|
||||||
|
const useServicesKitsState = () => {
|
||||||
|
const { objects: servicesKits, refetch: refetchKits } = useServicesKitsList();
|
||||||
|
|
||||||
|
const onKitCreate = (kit: GetServiceKitSchema) => {
|
||||||
|
ServiceService.createServicesKit({
|
||||||
|
requestBody: {
|
||||||
|
data: {
|
||||||
|
...omit(kit, ["services", "id"]),
|
||||||
|
servicesIds: kit.services.map(service => service.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetchKits();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKitCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "serviceKitModalForm",
|
||||||
|
title: "Создание набора услуг",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate: onKitCreate,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onKitUpdate = (kit: GetServiceKitSchema) => {
|
||||||
|
ServiceService.updateServicesKit({
|
||||||
|
requestBody: {
|
||||||
|
data: {
|
||||||
|
...omit(kit, ["services"]),
|
||||||
|
servicesIds: kit.services.map(service => service.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetchKits();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onKitCreateClick,
|
||||||
|
onKitUpdate,
|
||||||
|
servicesKits,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServicesKitsState;
|
||||||
89
src/pages/ServicesPage/hooks/useServicesState.tsx
Normal file
89
src/pages/ServicesPage/hooks/useServicesState.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { ServiceCategorySchema, ServiceSchema, ServiceService } from "../../../client";
|
||||||
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
|
import useServicesList from "./useServicesList.tsx";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
|
||||||
|
const useServicesState = () => {
|
||||||
|
const { services, refetch } = useServicesList();
|
||||||
|
|
||||||
|
const onCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "createService",
|
||||||
|
title: "Создание услуги",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onCreate = (values: ServiceSchema) => {
|
||||||
|
ServiceService.createService({ requestBody: { service: values } })
|
||||||
|
.then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCreateCategoryClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "createServiceCategory",
|
||||||
|
title: "Создание категории",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate: onCategoryCreate,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onCategoryCreate = (category: ServiceCategorySchema) => {
|
||||||
|
ServiceService.createServiceCategory({ requestBody: { category: category } })
|
||||||
|
.then(({ ok, message }) =>
|
||||||
|
notifications.guess(ok, { message: message }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onServiceDelete = (service: ServiceSchema) => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление услуги",
|
||||||
|
children: (<Text>
|
||||||
|
Вы уверены, что хотите удалить услугу "{service.name}"?
|
||||||
|
</Text>),
|
||||||
|
onConfirm: () => {
|
||||||
|
ServiceService.deleteService({ requestBody: { serviceId: service.id } })
|
||||||
|
.then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
confirm: "Удалить",
|
||||||
|
cancel: "Отмена",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onServiceUpdate = (service: ServiceSchema) => {
|
||||||
|
ServiceService
|
||||||
|
.updateService({
|
||||||
|
requestBody: {
|
||||||
|
data: service,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async ({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message: message });
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
services,
|
||||||
|
onCreateClick,
|
||||||
|
onServiceDelete,
|
||||||
|
onServiceUpdate,
|
||||||
|
onCreateCategoryClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServicesState;
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
import {ServicePriceRangeSchema, ServiceSchema} from "../../../client";
|
import { ServicePriceRangeSchema, ServiceSchema } from "../../../client";
|
||||||
import {useForm} from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import {ContextModalProps} from "@mantine/modals";
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
import {NumberInput, TextInput} from "@mantine/core";
|
import { Fieldset, Flex, rem, TextInput } from "@mantine/core";
|
||||||
import ServiceCategorySelect from "../components/ServiceCategorySelect/ServiceCategorySelect.tsx";
|
import ServiceCategorySelect from "../components/ServiceCategorySelect/ServiceCategorySelect.tsx";
|
||||||
import ServiceTypeSelect from "../components/ServiceTypeSelect/ServiceTypeSelect.tsx";
|
import ServiceTypeSelect from "../components/ServiceTypeSelect/ServiceTypeSelect.tsx";
|
||||||
import ServicePriceInput from "../components/ServicePriceInput/ServicePriceInput.tsx";
|
import ServicePriceTypeSegmentedControl, {
|
||||||
import {PriceRangeInputType} from "../components/ServicePriceInput/RangePriceInput.tsx";
|
ServicePriceType,
|
||||||
|
} from "../components/ServicePriceTypeSegmentedControl/ServicePriceTypeSegmentedControl.tsx";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import RangePriceInput, { PriceRangeInputType } from "../components/ServicePriceInput/RangePriceInput.tsx";
|
||||||
|
import SinglePriceInput from "../components/ServicePriceInput/SinglePriceInput.tsx";
|
||||||
|
import PriceCategoryInput, { PriceCategoryInputProps } from "../components/ServicePriceInput/PriceCategoryInput.tsx";
|
||||||
|
|
||||||
type Props = CreateEditFormProps<ServiceSchema>
|
type Props = CreateEditFormProps<ServiceSchema>
|
||||||
const CreateServiceModal = ({
|
const CreateServiceModal = ({
|
||||||
@@ -14,40 +19,61 @@ const CreateServiceModal = ({
|
|||||||
id,
|
id,
|
||||||
innerProps,
|
innerProps,
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
|
const [priceType, setPriceType] = useState<ServicePriceType>(ServicePriceType.DEFAULT);
|
||||||
const isEditing = 'onChange' in innerProps;
|
const isEditing = "onChange" in innerProps;
|
||||||
const initialValues: ServiceSchema = isEditing ? innerProps.element : {
|
const initialValues: ServiceSchema = isEditing ? innerProps.element : {
|
||||||
id: -1,
|
id: -1,
|
||||||
name: '',
|
name: "",
|
||||||
price: 0,
|
price: 0,
|
||||||
category: {
|
category: {
|
||||||
id: -1,
|
id: -1,
|
||||||
name: ''
|
name: "",
|
||||||
},
|
},
|
||||||
serviceType: -1,
|
serviceType: -1,
|
||||||
priceRanges: [] as ServicePriceRangeSchema[],
|
priceRanges: [] as ServicePriceRangeSchema[],
|
||||||
cost: null
|
cost: null,
|
||||||
}
|
categoryPrices: [],
|
||||||
|
};
|
||||||
|
|
||||||
const form = useForm<ServiceSchema>({
|
const form = useForm<ServiceSchema>({
|
||||||
initialValues: initialValues,
|
initialValues: initialValues,
|
||||||
validate: {
|
validate: {
|
||||||
name: (name: string) => name.trim() !== '' ? null : "Необходимо ввести название услуги",
|
name: (name: string) => name.trim() !== "" ? null : "Необходимо ввести название услуги",
|
||||||
category: (category: {
|
category: (category: {
|
||||||
id: number,
|
id: number,
|
||||||
name: string
|
name: string
|
||||||
}) => category.id !== -1 ? null : "Необходимо выбрать категорию",
|
}) => category.id !== -1 ? null : "Необходимо выбрать категорию",
|
||||||
serviceType: (serviceType: number) => serviceType !== -1 ? null : "Необходимо выбрать тип услуги",
|
serviceType: (serviceType: number) => serviceType !== -1 ? null : "Необходимо выбрать тип услуги",
|
||||||
priceRanges: (value, values) => value.length > 0 || values.price > 0 ? null : "Необходимо добавить хотя бы один диапазон цен или указать цену за единицу услуги",
|
priceRanges: (value, values) => value.length > 0 || values.price > 0 ? null : "Необходимо добавить хотя бы один диапазон цен или указать цену за единицу услуги",
|
||||||
price: (value, values) => value > 0 || values.priceRanges.length > 0 ? null : "Необходимо добавить хотя бы один диапазон цен или указать цену за единицу услуги"
|
price: (value, values) => value > 0 || values.priceRanges.length > 0 ? null : "Необходимо добавить хотя бы один диапазон цен или указать цену за единицу услуги",
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(form.values.categoryPrices);
|
||||||
|
}, [form.values]);
|
||||||
|
const getPriceBody = () => {
|
||||||
|
switch (priceType) {
|
||||||
|
case ServicePriceType.DEFAULT:
|
||||||
|
return <SinglePriceInput
|
||||||
|
placeholder={"Введите стоимость услуги"}
|
||||||
|
label={"Cтоимость услуги"}
|
||||||
|
hideControls
|
||||||
|
{...form.getInputProps("cost")}
|
||||||
|
/>;
|
||||||
|
case ServicePriceType.BY_RANGE:
|
||||||
|
return <RangePriceInput
|
||||||
|
{...form.getInputProps("priceRanges") as PriceRangeInputType}
|
||||||
|
/>;
|
||||||
|
case ServicePriceType.BY_CATEGORY:
|
||||||
|
return <PriceCategoryInput
|
||||||
|
{...form.getInputProps("categoryPrices") as PriceCategoryInputProps}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
|
||||||
|
|
||||||
const onCancelClick = () => {
|
const onCancelClick = () => {
|
||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
{...innerProps}
|
{...innerProps}
|
||||||
@@ -57,43 +83,39 @@ const CreateServiceModal = ({
|
|||||||
>
|
>
|
||||||
<BaseFormModal.Body>
|
<BaseFormModal.Body>
|
||||||
<>
|
<>
|
||||||
<ServiceCategorySelect
|
<Fieldset legend={"Общие параметры"}>
|
||||||
placeholder={"Выберите категорию"}
|
<ServiceCategorySelect
|
||||||
label={"Категория услуги"}
|
placeholder={"Выберите категорию"}
|
||||||
{...form.getInputProps('category')}
|
label={"Категория услуги"}
|
||||||
/>
|
{...form.getInputProps("category")}
|
||||||
<TextInput
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите название услуги"}
|
||||||
|
label={"Название услуги"}
|
||||||
|
{...form.getInputProps("name")}
|
||||||
|
/>
|
||||||
|
<ServiceTypeSelect
|
||||||
|
placeholder={"Выберите тип услуги"}
|
||||||
|
label={"Тип услуги"}
|
||||||
|
{...form.getInputProps("serviceType")}
|
||||||
|
/>
|
||||||
|
</Fieldset>
|
||||||
|
<Fieldset legend={"Стоимость"}>
|
||||||
|
<Flex direction={"column"} gap={rem(10)} justify={"center"}>
|
||||||
|
<ServicePriceTypeSegmentedControl
|
||||||
|
value={priceType}
|
||||||
|
onChange={setPriceType}
|
||||||
|
/>
|
||||||
|
{getPriceBody()}
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
</Fieldset>
|
||||||
|
|
||||||
placeholder={"Введите название услуги"}
|
|
||||||
label={"Название услуги"}
|
|
||||||
{...form.getInputProps('name')}
|
|
||||||
/>
|
|
||||||
<ServiceTypeSelect
|
|
||||||
placeholder={"Выберите тип услуги"}
|
|
||||||
label={"Тип услуги"}
|
|
||||||
{...form.getInputProps('serviceType')}
|
|
||||||
/>
|
|
||||||
<NumberInput
|
|
||||||
placeholder={"Введите себестоимость услуги"}
|
|
||||||
label={"Себестоимость услуги"}
|
|
||||||
hideControls
|
|
||||||
{...form.getInputProps('cost')}
|
|
||||||
/>
|
|
||||||
<ServicePriceInput
|
|
||||||
singlePriceInputProps={{
|
|
||||||
hideControls: true,
|
|
||||||
label: "Цена за единицу услуги",
|
|
||||||
placeholder: "Введите цену за одну услугу",
|
|
||||||
...form.getInputProps('price'),
|
|
||||||
}}
|
|
||||||
priceRangeInputProps={{
|
|
||||||
...form.getInputProps('priceRanges')
|
|
||||||
} as PriceRangeInputType}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
</BaseFormModal.Body>
|
</BaseFormModal.Body>
|
||||||
</BaseFormModal>
|
</BaseFormModal>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateServiceModal;
|
export default CreateServiceModal;
|
||||||
38
src/pages/ServicesPage/modals/ServicePriceCategoryForm.tsx
Normal file
38
src/pages/ServicesPage/modals/ServicePriceCategoryForm.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { ServicePriceCategorySchema } from "../../../client";
|
||||||
|
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import { TextInput } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<ServicePriceCategorySchema>;
|
||||||
|
|
||||||
|
const ServicePriceCategoryForm = ({ context, id, innerProps }: ContextModalProps<Props>) => {
|
||||||
|
const isEditing = "element" in innerProps;
|
||||||
|
const initialValues: Partial<ServicePriceCategorySchema> = isEditing ? innerProps.element : {
|
||||||
|
name: "",
|
||||||
|
};
|
||||||
|
const form = useForm<Partial<ServicePriceCategorySchema>>({
|
||||||
|
initialValues,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
onClose={() => context.closeContextModal(id)}
|
||||||
|
>
|
||||||
|
<BaseFormModal.Body>
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
label={"Название"}
|
||||||
|
placeholder={"Введите название категории"}
|
||||||
|
{...form.getInputProps("name")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
</BaseFormModal.Body>
|
||||||
|
</BaseFormModal>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicePriceCategoryForm;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
import {GetServiceKitSchema} from "../../../client";
|
import { GetServiceKitSchema } from "../../../client";
|
||||||
import {ContextModalProps} from "@mantine/modals";
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
import {useForm} from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import {ServiceType} from "../../../shared/enums/ServiceType.ts";
|
import { ServiceType } from "../../../shared/enums/ServiceType.ts";
|
||||||
import {TextInput} from "@mantine/core";
|
import { TextInput } from "@mantine/core";
|
||||||
import ServiceTypeSelect from "../components/ServiceTypeSelect/ServiceTypeSelect.tsx";
|
import ServiceTypeSelect from "../components/ServiceTypeSelect/ServiceTypeSelect.tsx";
|
||||||
import ServicesMultiselect from "../../../components/Selects/ServicesMultiselect/ServicesMultiselect.tsx";
|
import ServicesMultiselect from "../../../components/Selects/ServicesMultiselect/ServicesMultiselect.tsx";
|
||||||
|
|
||||||
@@ -13,17 +13,17 @@ const ServiceKitModalForm = ({
|
|||||||
id,
|
id,
|
||||||
innerProps,
|
innerProps,
|
||||||
}: ContextModalProps<Props>) => {
|
}: ContextModalProps<Props>) => {
|
||||||
const isEditing = 'element' in innerProps;
|
const isEditing = "element" in innerProps;
|
||||||
const initialValues: Partial<GetServiceKitSchema> = isEditing ? innerProps.element : {
|
const initialValues: Partial<GetServiceKitSchema> = isEditing ? innerProps.element : {
|
||||||
name: "",
|
name: "",
|
||||||
serviceType: ServiceType.DEAL_SERVICE,
|
serviceType: ServiceType.DEAL_SERVICE,
|
||||||
services: []
|
services: [],
|
||||||
}
|
};
|
||||||
|
|
||||||
const form = useForm<Partial<GetServiceKitSchema>>(
|
const form = useForm<Partial<GetServiceKitSchema>>(
|
||||||
{
|
{
|
||||||
initialValues
|
initialValues,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
@@ -54,7 +54,7 @@ const ServiceKitModalForm = ({
|
|||||||
</>
|
</>
|
||||||
</BaseFormModal.Body>
|
</BaseFormModal.Body>
|
||||||
</BaseFormModal>
|
</BaseFormModal>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ServiceKitModalForm;
|
export default ServiceKitModalForm;
|
||||||
@@ -1,173 +1,90 @@
|
|||||||
import {FC, useState} 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 PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
import styles from './ServicesPage.module.css';
|
import styles from "./ServicesPage.module.css";
|
||||||
import {Button, Text} from "@mantine/core";
|
import { Button } from "@mantine/core";
|
||||||
import {GetServiceKitSchema, ServiceCategorySchema, ServiceSchema, ServiceService} from "../../../client";
|
|
||||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
|
||||||
import {modals} from "@mantine/modals";
|
|
||||||
import ServiceTypeSegmentedControl, {
|
import ServiceTypeSegmentedControl, {
|
||||||
ServicesTab
|
ServicesTab,
|
||||||
} from "../components/ServiceTypeSegmentedControl/ServiceTypeSegmentedControl.tsx";
|
} from "../components/ServiceTypeSegmentedControl/ServiceTypeSegmentedControl.tsx";
|
||||||
import useServicesKitsList from "../hooks/useServicesKitsList.tsx";
|
|
||||||
import ServicesKitsTable from "../components/ServicesKitsTable/ServicesKitsTable.tsx";
|
import ServicesKitsTable from "../components/ServicesKitsTable/ServicesKitsTable.tsx";
|
||||||
import {omit} from "lodash";
|
import ServicePriceCategoryTable from "../components/ServicePriceCategoryTable/ServicePriceCategoryTable.tsx";
|
||||||
|
import useServicesState from "../hooks/useServicesState.tsx";
|
||||||
|
import useServicesKitsState from "../hooks/useServicesKitsState.tsx";
|
||||||
|
import useServicePriceCategoryState from "../hooks/useServicePriceCategoryState.tsx";
|
||||||
|
import { ObjectStateToTableProps } from "../../../types/utils.ts";
|
||||||
|
|
||||||
|
|
||||||
export const ServicesPage: FC = () => {
|
export const ServicesPage: FC = () => {
|
||||||
const {services, refetch} = useServicesList();
|
const [serviceType, setServiceType] = useState(ServicesTab.DEAL_SERVICE);
|
||||||
const {objects: servicesKits, refetch: refetchKits} = useServicesKitsList();
|
const { services, onServiceDelete, onServiceUpdate, onCreateClick, onCreateCategoryClick } = useServicesState();
|
||||||
const [serviceType, setServiceType] = useState(ServicesTab.DEAL_SERVICE)
|
const { servicesKits, onKitUpdate, onKitCreateClick } = useServicesKitsState();
|
||||||
// region Service create
|
const { onCreateClick: onCreatePriceCategoryClick, ...priceCategoryRestProps } = useServicePriceCategoryState();
|
||||||
const onCreateClick = () => {
|
const getBody = () => {
|
||||||
modals.openContextModal({
|
switch (serviceType) {
|
||||||
modal: 'createService',
|
case ServicesTab.SERVICES_KITS:
|
||||||
title: 'Создание услуги',
|
return (
|
||||||
withCloseButton: false,
|
<ServicesKitsTable
|
||||||
innerProps: {
|
items={servicesKits}
|
||||||
onCreate
|
onChange={onKitUpdate}
|
||||||
}
|
/>
|
||||||
})
|
);
|
||||||
}
|
case ServicesTab.DEAL_SERVICE:
|
||||||
const onCreate = (values: ServiceSchema) => {
|
case ServicesTab.PRODUCT_SERVICE:
|
||||||
ServiceService.createService({requestBody: {service: values}})
|
return (
|
||||||
.then(async ({ok, message}) => {
|
<ServicesTable
|
||||||
notifications.guess(ok, {message: message});
|
onDelete={onServiceDelete}
|
||||||
if (!ok) return;
|
onChange={onServiceUpdate}
|
||||||
await refetch();
|
items={services.filter(service => service.serviceType == serviceType)}
|
||||||
})
|
/>
|
||||||
}
|
);
|
||||||
// endregion
|
case ServicesTab.SERVICES_PRICE_CATEGORIES:
|
||||||
|
return (
|
||||||
|
<ServicePriceCategoryTable
|
||||||
|
{...ObjectStateToTableProps(priceCategoryRestProps)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// region Category create
|
const getControls = () => {
|
||||||
const onCreateCategoryClick = () => {
|
switch (serviceType) {
|
||||||
modals.openContextModal({
|
case ServicesTab.SERVICES_KITS:
|
||||||
modal: "createServiceCategory",
|
return (
|
||||||
title: 'Создание категории',
|
<Button onClick={onKitCreateClick} variant={"default"}>Создать набор</Button>
|
||||||
withCloseButton: false,
|
);
|
||||||
innerProps: {
|
case ServicesTab.DEAL_SERVICE:
|
||||||
onCreate: onCategoryCreate
|
case ServicesTab.PRODUCT_SERVICE:
|
||||||
}
|
return (
|
||||||
})
|
<>
|
||||||
}
|
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
|
||||||
const onCategoryCreate = (category: ServiceCategorySchema) => {
|
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать категорию</Button>
|
||||||
ServiceService.createServiceCategory({requestBody: {category: category}})
|
</>
|
||||||
.then(({ok, message}) =>
|
);
|
||||||
notifications.guess(ok, {message: message}))
|
case ServicesTab.SERVICES_PRICE_CATEGORIES:
|
||||||
}
|
return (
|
||||||
// endregion
|
<Button variant={"default"} onClick={() => {
|
||||||
|
if (onCreatePriceCategoryClick) {
|
||||||
const onServiceDelete = (service: ServiceSchema) => {
|
onCreatePriceCategoryClick();
|
||||||
modals.openConfirmModal({
|
}
|
||||||
title: 'Удаление услуги',
|
}}>Создать категорию</Button>
|
||||||
children: (<Text>
|
);
|
||||||
Вы уверены, что хотите удалить услугу "{service.name}"?
|
}
|
||||||
</Text>),
|
};
|
||||||
onConfirm: () => {
|
|
||||||
ServiceService.deleteService({requestBody: {serviceId: service.id}})
|
|
||||||
.then(async ({ok, message}) => {
|
|
||||||
notifications.guess(ok, {message: message});
|
|
||||||
if (!ok) return;
|
|
||||||
await refetch();
|
|
||||||
})
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
confirm: 'Удалить',
|
|
||||||
cancel: 'Отмена'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const onServiceUpdate = (service: ServiceSchema) => {
|
|
||||||
ServiceService
|
|
||||||
.updateService({
|
|
||||||
requestBody: {
|
|
||||||
data: service
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(async ({ok, message}) => {
|
|
||||||
notifications.guess(ok, {message: message});
|
|
||||||
if (!ok) return;
|
|
||||||
await refetch();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const onKitCreate = (kit: GetServiceKitSchema) => {
|
|
||||||
ServiceService.createServicesKit({
|
|
||||||
requestBody: {
|
|
||||||
data: {
|
|
||||||
...omit(kit, ["services", "id"]),
|
|
||||||
servicesIds: kit.services.map(service => service.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(async ({ok, message}) => {
|
|
||||||
notifications.guess(ok, {message: message});
|
|
||||||
if (!ok) return;
|
|
||||||
await refetchKits();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const onKitCreateClick = () => {
|
|
||||||
modals.openContextModal({
|
|
||||||
modal: 'serviceKitModalForm',
|
|
||||||
title: 'Создание набора услуг',
|
|
||||||
withCloseButton: false,
|
|
||||||
innerProps: {
|
|
||||||
onCreate: onKitCreate
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const onKitUpdate = (kit: GetServiceKitSchema) => {
|
|
||||||
ServiceService.updateServicesKit({
|
|
||||||
requestBody: {
|
|
||||||
data: {
|
|
||||||
...omit(kit, ["services"]),
|
|
||||||
servicesIds: kit.services.map(service => service.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(async ({ok, message}) => {
|
|
||||||
notifications.guess(ok, {message: message});
|
|
||||||
if (!ok) return;
|
|
||||||
await refetchKits();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className={styles['container']}>
|
<div className={styles["container"]}>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
<div className={styles['top-panel']}>
|
<div className={styles["top-panel"]}>
|
||||||
{
|
{getControls()}
|
||||||
serviceType === ServicesTab.SERVICES_KITS ?
|
|
||||||
<Button onClick={onKitCreateClick} variant={"default"}>Создать набор</Button> :
|
|
||||||
<>
|
|
||||||
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
|
|
||||||
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать
|
|
||||||
категорию</Button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
<ServiceTypeSegmentedControl
|
<ServiceTypeSegmentedControl
|
||||||
className={styles['top-panel-last-item']}
|
className={styles["top-panel-last-item"]}
|
||||||
value={serviceType.toString()}
|
value={serviceType.toString()}
|
||||||
onChange={(event) => setServiceType(parseInt(event))}
|
onChange={(event) => setServiceType(parseInt(event))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
{
|
{getBody()}
|
||||||
serviceType === ServicesTab.SERVICES_KITS ?
|
|
||||||
<ServicesKitsTable
|
|
||||||
items={servicesKits}
|
|
||||||
onChange={onKitUpdate}
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
<ServicesTable
|
|
||||||
onDelete={onServiceDelete}
|
|
||||||
onChange={onServiceUpdate}
|
|
||||||
items={services.filter(service => service.serviceType == serviceType)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {BaseMarketplaceSchema} from "../client";
|
import { BaseMarketplaceSchema, ServicePriceCategorySchema } from "../client";
|
||||||
|
|
||||||
export type QuickDeal = {
|
export type QuickDeal = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -7,5 +7,6 @@ export type QuickDeal = {
|
|||||||
comment: string;
|
comment: string;
|
||||||
acceptanceDate: Date;
|
acceptanceDate: Date;
|
||||||
shippingWarehouse: string;
|
shippingWarehouse: string;
|
||||||
baseMarketplace: BaseMarketplaceSchema
|
baseMarketplace: BaseMarketplaceSchema;
|
||||||
|
category?: ServicePriceCategorySchema;
|
||||||
}
|
}
|
||||||
14
src/types/UseObjectState.ts
Normal file
14
src/types/UseObjectState.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
type UseObjectState<T> = {
|
||||||
|
onCreateClick?: () => void;
|
||||||
|
onCreate: (values: T) => void;
|
||||||
|
|
||||||
|
onDeleteClick?: (item: T) => void;
|
||||||
|
onDelete: (item: T) => void;
|
||||||
|
|
||||||
|
onChangeClick?: (item: T) => void;
|
||||||
|
onChange: (item: T) => void;
|
||||||
|
|
||||||
|
objects: T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UseObjectState;
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import {ProductSchema} from "../client";
|
import { ProductSchema } from "../client";
|
||||||
import {isNil} from "lodash";
|
import { isNil } from "lodash";
|
||||||
import {ProductFieldNames} from "../pages/LeadsPage/tabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
|
import { ProductFieldNames } from "../pages/LeadsPage/tabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
|
||||||
|
import UseObjectState from "./UseObjectState.ts";
|
||||||
|
import { CRUDTableProps } from "./CRUDTable.tsx";
|
||||||
|
import { MRT_RowData } from "mantine-react-table";
|
||||||
|
|
||||||
export type ObjectWithNameAndId = {
|
export type ObjectWithNameAndId = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -14,12 +17,22 @@ export type BaseFormInputProps<T> = {
|
|||||||
}
|
}
|
||||||
export const formatDate = (date: string) => {
|
export const formatDate = (date: string) => {
|
||||||
return new Date(date).toLocaleDateString("ru-RU");
|
return new Date(date).toLocaleDateString("ru-RU");
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getProductFields = (product: ProductSchema) => {
|
export const getProductFields = (product: ProductSchema) => {
|
||||||
return Object.entries(product).map(([key, value]) => {
|
return Object.entries(product).map(([key, value]) => {
|
||||||
const fieldName = ProductFieldNames[key as keyof ProductSchema];
|
const fieldName = ProductFieldNames[key as keyof ProductSchema];
|
||||||
if (!fieldName || isNil(value) || value === '' || !value) return;
|
if (!fieldName || isNil(value) || value === "" || !value) return;
|
||||||
return [fieldName.toString(), value.toString()];
|
return [fieldName.toString(), value.toString()];
|
||||||
}).filter(obj => obj !== undefined) || [];
|
}).filter(obj => obj !== undefined) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function ObjectStateToTableProps<T extends MRT_RowData>(state: UseObjectState<T>): CRUDTableProps<T> {
|
||||||
|
return {
|
||||||
|
items: state.objects,
|
||||||
|
onDelete: state.onDelete,
|
||||||
|
onChange: state.onChange,
|
||||||
|
onCreate: state.onCreate,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user