crap
This commit is contained in:
@@ -30,16 +30,20 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"dot-object": "^2.1.4",
|
"dot-object": "^2.1.4",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"mantine-form-zod-resolver": "^1.1.0",
|
"mantine-form-zod-resolver": "^1.1.0",
|
||||||
"mantine-react-table": "^2.0.0-beta.0",
|
"mantine-react-table": "^2.0.0-beta.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-barcode": "^1.5.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
|
"react-to-print": "^2.15.1",
|
||||||
"reactflow": "^11.10.4",
|
"reactflow": "^11.10.4",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dot-object": "^2",
|
"@types/dot-object": "^2",
|
||||||
|
"@types/lodash": "^4",
|
||||||
"@types/react": "^18.2.56",
|
"@types/react": "^18.2.56",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||||
|
|||||||
11
src/api/product/useGetProductById.tsx
Normal file
11
src/api/product/useGetProductById.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import {ProductService} from "../../client";
|
||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export const useGetProductById = (id: number) => {
|
||||||
|
const {data, error, isLoading, refetch} = useQuery({
|
||||||
|
queryKey: ['getProductById', id],
|
||||||
|
queryFn: () => ProductService.getProductById({productId: id}),
|
||||||
|
select: (data) => data
|
||||||
|
});
|
||||||
|
return {product: data, error, isLoading, refetch}
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@ export type { ClientSchema } from './models/ClientSchema';
|
|||||||
export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest';
|
export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest';
|
||||||
export type { ClientUpdateRequest } from './models/ClientUpdateRequest';
|
export type { ClientUpdateRequest } from './models/ClientUpdateRequest';
|
||||||
export type { ClientUpdateResponse } from './models/ClientUpdateResponse';
|
export type { ClientUpdateResponse } from './models/ClientUpdateResponse';
|
||||||
|
export type { DealAddProductRequest } from './models/DealAddProductRequest';
|
||||||
|
export type { DealAddProductResponse } from './models/DealAddProductResponse';
|
||||||
export type { DealAddServiceRequest } from './models/DealAddServiceRequest';
|
export type { DealAddServiceRequest } from './models/DealAddServiceRequest';
|
||||||
export type { DealAddServiceResponse } from './models/DealAddServiceResponse';
|
export type { DealAddServiceResponse } from './models/DealAddServiceResponse';
|
||||||
export type { DealAddServicesRequest } from './models/DealAddServicesRequest';
|
export type { DealAddServicesRequest } from './models/DealAddServicesRequest';
|
||||||
@@ -26,26 +28,41 @@ export type { DealAddServicesResponse } from './models/DealAddServicesResponse';
|
|||||||
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
||||||
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
|
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
|
||||||
export type { DealCreateRequest } from './models/DealCreateRequest';
|
export type { DealCreateRequest } from './models/DealCreateRequest';
|
||||||
|
export type { DealDeleteProductRequest } from './models/DealDeleteProductRequest';
|
||||||
|
export type { DealDeleteProductResponse } from './models/DealDeleteProductResponse';
|
||||||
|
export type { DealDeleteProductsRequest } from './models/DealDeleteProductsRequest';
|
||||||
|
export type { DealDeleteProductsResponse } from './models/DealDeleteProductsResponse';
|
||||||
export type { DealDeleteServiceRequest } from './models/DealDeleteServiceRequest';
|
export type { DealDeleteServiceRequest } from './models/DealDeleteServiceRequest';
|
||||||
export type { DealDeleteServiceResponse } from './models/DealDeleteServiceResponse';
|
export type { DealDeleteServiceResponse } from './models/DealDeleteServiceResponse';
|
||||||
export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest';
|
export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest';
|
||||||
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
|
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
|
||||||
|
export type { DealGeneralInfoSchema } from './models/DealGeneralInfoSchema';
|
||||||
export type { DealGetAllResponse } from './models/DealGetAllResponse';
|
export type { DealGetAllResponse } from './models/DealGetAllResponse';
|
||||||
export type { DealProductSchema } from './models/DealProductSchema';
|
export type { DealProductSchema } from './models/DealProductSchema';
|
||||||
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
||||||
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
||||||
export type { DealSchema } from './models/DealSchema';
|
export type { DealSchema } from './models/DealSchema';
|
||||||
export type { DealServiceSchema } from './models/DealServiceSchema';
|
export type { DealServiceSchema } from './models/DealServiceSchema';
|
||||||
|
export type { DealStatusHistorySchema } from './models/DealStatusHistorySchema';
|
||||||
export type { DealSummary } from './models/DealSummary';
|
export type { DealSummary } from './models/DealSummary';
|
||||||
export type { DealSummaryResponse } from './models/DealSummaryResponse';
|
export type { DealSummaryResponse } from './models/DealSummaryResponse';
|
||||||
|
export type { DealUpdateGeneralInfoRequest } from './models/DealUpdateGeneralInfoRequest';
|
||||||
|
export type { DealUpdateGeneralInfoResponse } from './models/DealUpdateGeneralInfoResponse';
|
||||||
|
export type { DealUpdateProductQuantityRequest } from './models/DealUpdateProductQuantityRequest';
|
||||||
|
export type { DealUpdateProductQuantityResponse } from './models/DealUpdateProductQuantityResponse';
|
||||||
export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServiceQuantityRequest';
|
export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServiceQuantityRequest';
|
||||||
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
||||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||||
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
|
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
|
||||||
|
export type { ProductAddBarcodeRequest } from './models/ProductAddBarcodeRequest';
|
||||||
|
export type { ProductAddBarcodeResponse } from './models/ProductAddBarcodeResponse';
|
||||||
export type { ProductCreateRequest } from './models/ProductCreateRequest';
|
export type { ProductCreateRequest } from './models/ProductCreateRequest';
|
||||||
export type { ProductCreateResponse } from './models/ProductCreateResponse';
|
export type { ProductCreateResponse } from './models/ProductCreateResponse';
|
||||||
export type { ProductDeleteRequest } from './models/ProductDeleteRequest';
|
export type { ProductDeleteRequest } from './models/ProductDeleteRequest';
|
||||||
export type { ProductDeleteResponse } from './models/ProductDeleteResponse';
|
export type { ProductDeleteResponse } from './models/ProductDeleteResponse';
|
||||||
|
export type { ProductExistsBarcodeResponse } from './models/ProductExistsBarcodeResponse';
|
||||||
|
export type { ProductGenerateBarcodeRequest } from './models/ProductGenerateBarcodeRequest';
|
||||||
|
export type { ProductGenerateBarcodeResponse } from './models/ProductGenerateBarcodeResponse';
|
||||||
export type { ProductGetResponse } from './models/ProductGetResponse';
|
export type { ProductGetResponse } from './models/ProductGetResponse';
|
||||||
export type { ProductSchema } from './models/ProductSchema';
|
export type { ProductSchema } from './models/ProductSchema';
|
||||||
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
|
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
|
||||||
@@ -58,6 +75,7 @@ export type { ServiceCreateResponse } from './models/ServiceCreateResponse';
|
|||||||
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 { ServiceSchema } from './models/ServiceSchema';
|
export type { ServiceSchema } from './models/ServiceSchema';
|
||||||
|
export type { UserSchema } from './models/UserSchema';
|
||||||
export type { ValidationError } from './models/ValidationError';
|
export type { ValidationError } from './models/ValidationError';
|
||||||
|
|
||||||
export { AuthService } from './services/AuthService';
|
export { AuthService } from './services/AuthService';
|
||||||
|
|||||||
10
src/client/models/DealAddProductRequest.ts
Normal file
10
src/client/models/DealAddProductRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealAddProductRequest = {
|
||||||
|
dealId: number;
|
||||||
|
productId: number;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealAddProductResponse.ts
Normal file
9
src/client/models/DealAddProductResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealAddProductResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealDeleteProductRequest.ts
Normal file
9
src/client/models/DealDeleteProductRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealDeleteProductRequest = {
|
||||||
|
dealId: number;
|
||||||
|
productId: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealDeleteProductResponse.ts
Normal file
9
src/client/models/DealDeleteProductResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealDeleteProductResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealDeleteProductsRequest.ts
Normal file
9
src/client/models/DealDeleteProductsRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealDeleteProductsRequest = {
|
||||||
|
dealId: number;
|
||||||
|
productIds: Array<number>;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealDeleteProductsResponse.ts
Normal file
9
src/client/models/DealDeleteProductsResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealDeleteProductsResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/DealGeneralInfoSchema.ts
Normal file
10
src/client/models/DealGeneralInfoSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealGeneralInfoSchema = {
|
||||||
|
name: string;
|
||||||
|
isDeleted: boolean;
|
||||||
|
isCompleted: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,8 +2,10 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { ClientSchema } from './ClientSchema';
|
||||||
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';
|
||||||
export type DealSchema = {
|
export type DealSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -12,5 +14,9 @@ export type DealSchema = {
|
|||||||
currentStatus: number;
|
currentStatus: number;
|
||||||
services: Array<DealServiceSchema>;
|
services: Array<DealServiceSchema>;
|
||||||
products: Array<DealProductSchema>;
|
products: Array<DealProductSchema>;
|
||||||
|
statusHistory: Array<DealStatusHistorySchema>;
|
||||||
|
isDeleted: boolean;
|
||||||
|
isCompleted: boolean;
|
||||||
|
client: ClientSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
13
src/client/models/DealStatusHistorySchema.ts
Normal file
13
src/client/models/DealStatusHistorySchema.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { UserSchema } from './UserSchema';
|
||||||
|
export type DealStatusHistorySchema = {
|
||||||
|
user: UserSchema;
|
||||||
|
changedAt: string;
|
||||||
|
fromStatus: number;
|
||||||
|
toStatus: number;
|
||||||
|
nextStatusDeadline: string;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/DealUpdateGeneralInfoRequest.ts
Normal file
10
src/client/models/DealUpdateGeneralInfoRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { DealGeneralInfoSchema } from './DealGeneralInfoSchema';
|
||||||
|
export type DealUpdateGeneralInfoRequest = {
|
||||||
|
dealId: number;
|
||||||
|
data: DealGeneralInfoSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealUpdateGeneralInfoResponse.ts
Normal file
9
src/client/models/DealUpdateGeneralInfoResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealUpdateGeneralInfoResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/DealUpdateProductQuantityRequest.ts
Normal file
10
src/client/models/DealUpdateProductQuantityRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealUpdateProductQuantityRequest = {
|
||||||
|
dealId: number;
|
||||||
|
productId: number;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DealUpdateProductQuantityResponse.ts
Normal file
9
src/client/models/DealUpdateProductQuantityResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DealUpdateProductQuantityResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ProductAddBarcodeRequest.ts
Normal file
9
src/client/models/ProductAddBarcodeRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductAddBarcodeRequest = {
|
||||||
|
productId: number;
|
||||||
|
barcode: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ProductAddBarcodeResponse.ts
Normal file
9
src/client/models/ProductAddBarcodeResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductAddBarcodeResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
8
src/client/models/ProductExistsBarcodeResponse.ts
Normal file
8
src/client/models/ProductExistsBarcodeResponse.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductExistsBarcodeResponse = {
|
||||||
|
exists: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
8
src/client/models/ProductGenerateBarcodeRequest.ts
Normal file
8
src/client/models/ProductGenerateBarcodeRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductGenerateBarcodeRequest = {
|
||||||
|
productId: number;
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/ProductGenerateBarcodeResponse.ts
Normal file
10
src/client/models/ProductGenerateBarcodeResponse.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductGenerateBarcodeResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
barcode: string;
|
||||||
|
};
|
||||||
|
|
||||||
11
src/client/models/UserSchema.ts
Normal file
11
src/client/models/UserSchema.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type UserSchema = {
|
||||||
|
id: number;
|
||||||
|
telegramId: number;
|
||||||
|
phoneNumber?: (string | null);
|
||||||
|
isAdmin: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { DealAddProductRequest } from '../models/DealAddProductRequest';
|
||||||
|
import type { DealAddProductResponse } from '../models/DealAddProductResponse';
|
||||||
import type { DealAddServiceRequest } from '../models/DealAddServiceRequest';
|
import type { DealAddServiceRequest } from '../models/DealAddServiceRequest';
|
||||||
import type { DealAddServiceResponse } from '../models/DealAddServiceResponse';
|
import type { DealAddServiceResponse } from '../models/DealAddServiceResponse';
|
||||||
import type { DealAddServicesRequest } from '../models/DealAddServicesRequest';
|
import type { DealAddServicesRequest } from '../models/DealAddServicesRequest';
|
||||||
@@ -9,6 +11,10 @@ import type { DealAddServicesResponse } from '../models/DealAddServicesResponse'
|
|||||||
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
||||||
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
||||||
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
import type { DealCreateRequest } from '../models/DealCreateRequest';
|
||||||
|
import type { DealDeleteProductRequest } from '../models/DealDeleteProductRequest';
|
||||||
|
import type { DealDeleteProductResponse } from '../models/DealDeleteProductResponse';
|
||||||
|
import type { DealDeleteProductsRequest } from '../models/DealDeleteProductsRequest';
|
||||||
|
import type { DealDeleteProductsResponse } from '../models/DealDeleteProductsResponse';
|
||||||
import type { DealDeleteServiceRequest } from '../models/DealDeleteServiceRequest';
|
import type { DealDeleteServiceRequest } from '../models/DealDeleteServiceRequest';
|
||||||
import type { DealDeleteServiceResponse } from '../models/DealDeleteServiceResponse';
|
import type { DealDeleteServiceResponse } from '../models/DealDeleteServiceResponse';
|
||||||
import type { DealDeleteServicesRequest } from '../models/DealDeleteServicesRequest';
|
import type { DealDeleteServicesRequest } from '../models/DealDeleteServicesRequest';
|
||||||
@@ -18,6 +24,10 @@ import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
|
|||||||
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
||||||
import type { DealSchema } from '../models/DealSchema';
|
import type { DealSchema } from '../models/DealSchema';
|
||||||
import type { DealSummaryResponse } from '../models/DealSummaryResponse';
|
import type { DealSummaryResponse } from '../models/DealSummaryResponse';
|
||||||
|
import type { DealUpdateGeneralInfoRequest } from '../models/DealUpdateGeneralInfoRequest';
|
||||||
|
import type { DealUpdateGeneralInfoResponse } from '../models/DealUpdateGeneralInfoResponse';
|
||||||
|
import type { DealUpdateProductQuantityRequest } from '../models/DealUpdateProductQuantityRequest';
|
||||||
|
import type { DealUpdateProductQuantityResponse } from '../models/DealUpdateProductQuantityResponse';
|
||||||
import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServiceQuantityRequest';
|
import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServiceQuantityRequest';
|
||||||
import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse';
|
import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
@@ -95,6 +105,58 @@ export class DealService {
|
|||||||
url: '/deal/summaries',
|
url: '/deal/summaries',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get All
|
||||||
|
* @returns DealGetAllResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getAllDeals(): CancelablePromise<DealGetAllResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/deal/get-all',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Deal By Id
|
||||||
|
* @returns DealSchema Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getDealById({
|
||||||
|
dealId,
|
||||||
|
}: {
|
||||||
|
dealId: number,
|
||||||
|
}): CancelablePromise<DealSchema> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/deal/get/{deal_id}',
|
||||||
|
path: {
|
||||||
|
'deal_id': dealId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update General Info
|
||||||
|
* @returns DealUpdateGeneralInfoResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static updateDealGeneralInfo({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealUpdateGeneralInfoRequest,
|
||||||
|
}): CancelablePromise<DealUpdateGeneralInfoResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/update-general-info',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Services Add
|
* Services Add
|
||||||
* @returns DealAddServicesResponse Successful Response
|
* @returns DealAddServicesResponse Successful Response
|
||||||
@@ -196,32 +258,80 @@ export class DealService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get All
|
* Products Update
|
||||||
* @returns DealGetAllResponse Successful Response
|
* @returns DealUpdateProductQuantityResponse Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static getAllDeals(): CancelablePromise<DealGetAllResponse> {
|
public static updateDealProductQuantity({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealUpdateProductQuantityRequest,
|
||||||
|
}): CancelablePromise<DealUpdateProductQuantityResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: 'POST',
|
||||||
url: '/deal/get-all',
|
url: '/deal/products/update-quantity',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get Deal By Id
|
* Products Add
|
||||||
* @returns DealSchema Successful Response
|
* @returns DealAddProductResponse Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static getDealById({
|
public static addDealProduct({
|
||||||
dealId,
|
requestBody,
|
||||||
}: {
|
}: {
|
||||||
dealId: number,
|
requestBody: DealAddProductRequest,
|
||||||
}): CancelablePromise<DealSchema> {
|
}): CancelablePromise<DealAddProductResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: 'POST',
|
||||||
url: '/deal/get/{deal_id}',
|
url: '/deal/products/add',
|
||||||
path: {
|
body: requestBody,
|
||||||
'deal_id': dealId,
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Products Delete
|
||||||
|
* @returns DealDeleteProductResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static deleteDealProduct({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealDeleteProductRequest,
|
||||||
|
}): CancelablePromise<DealDeleteProductResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/products/delete',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Products Delete
|
||||||
|
* @returns DealDeleteProductsResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static deleteMultipleDealProducts({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: DealDeleteProductsRequest,
|
||||||
|
}): CancelablePromise<DealDeleteProductsResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/deal/products/delete/multiple',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
errors: {
|
errors: {
|
||||||
422: `Validation Error`,
|
422: `Validation Error`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,11 +2,17 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { ProductAddBarcodeRequest } from '../models/ProductAddBarcodeRequest';
|
||||||
|
import type { ProductAddBarcodeResponse } from '../models/ProductAddBarcodeResponse';
|
||||||
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
||||||
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
||||||
import type { ProductDeleteRequest } from '../models/ProductDeleteRequest';
|
import type { ProductDeleteRequest } from '../models/ProductDeleteRequest';
|
||||||
import type { ProductDeleteResponse } from '../models/ProductDeleteResponse';
|
import type { ProductDeleteResponse } from '../models/ProductDeleteResponse';
|
||||||
|
import type { ProductExistsBarcodeResponse } from '../models/ProductExistsBarcodeResponse';
|
||||||
|
import type { ProductGenerateBarcodeRequest } from '../models/ProductGenerateBarcodeRequest';
|
||||||
|
import type { ProductGenerateBarcodeResponse } from '../models/ProductGenerateBarcodeResponse';
|
||||||
import type { ProductGetResponse } from '../models/ProductGetResponse';
|
import type { ProductGetResponse } from '../models/ProductGetResponse';
|
||||||
|
import type { ProductSchema } from '../models/ProductSchema';
|
||||||
import type { ProductUpdateRequest } from '../models/ProductUpdateRequest';
|
import type { ProductUpdateRequest } from '../models/ProductUpdateRequest';
|
||||||
import type { ProductUpdateResponse } from '../models/ProductUpdateResponse';
|
import type { ProductUpdateResponse } from '../models/ProductUpdateResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
@@ -100,4 +106,89 @@ export class ProductService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get Product By Id
|
||||||
|
* @returns ProductSchema Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getProductById({
|
||||||
|
productId,
|
||||||
|
}: {
|
||||||
|
productId: number,
|
||||||
|
}): CancelablePromise<ProductSchema> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/product/get-by-id',
|
||||||
|
query: {
|
||||||
|
'product_id': productId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add Product Barcode
|
||||||
|
* @returns ProductAddBarcodeResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static addProductBarcode({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ProductAddBarcodeRequest,
|
||||||
|
}): CancelablePromise<ProductAddBarcodeResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/barcode/add',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Exists Product Barcode
|
||||||
|
* @returns ProductExistsBarcodeResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static existsProductBarcode({
|
||||||
|
productId,
|
||||||
|
barcode,
|
||||||
|
}: {
|
||||||
|
productId: number,
|
||||||
|
barcode: string,
|
||||||
|
}): CancelablePromise<ProductExistsBarcodeResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/product/barcode/exists',
|
||||||
|
query: {
|
||||||
|
'product_id': productId,
|
||||||
|
'barcode': barcode,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generate Product Barcode
|
||||||
|
* @returns ProductGenerateBarcodeResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static generateProductBarcode({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ProductGenerateBarcodeRequest,
|
||||||
|
}): CancelablePromise<ProductGenerateBarcodeResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/barcode/generate',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/modals/AddBarcodeModal/AddBarcodeModal.tsx
Normal file
63
src/modals/AddBarcodeModal/AddBarcodeModal.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
import {Button, Flex, rem, TextInput} from "@mantine/core";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {ProductService} from "../../client";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
productId: number;
|
||||||
|
onSubmit: (barcode: string) => void;
|
||||||
|
}
|
||||||
|
const PrintBarcodeModal = ({
|
||||||
|
id,
|
||||||
|
context,
|
||||||
|
innerProps
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const {productId, onSubmit} = innerProps;
|
||||||
|
const [barcode, setBarcode] = useState<string | undefined>();
|
||||||
|
const [isBarcodeExist, setIsBarcodeExist] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const getErrorMessage = () => {
|
||||||
|
if (!barcode) return "Штрихкод не может быть пустым";
|
||||||
|
if (isBarcodeExist) return "Штрихкод уже существует";
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (!barcode) return;
|
||||||
|
ProductService.existsProductBarcode({
|
||||||
|
productId: innerProps.productId,
|
||||||
|
barcode: barcode.trim()
|
||||||
|
}).then((response) => {
|
||||||
|
setIsBarcodeExist(response.exists);
|
||||||
|
})
|
||||||
|
}, [productId, barcode]);
|
||||||
|
|
||||||
|
const onSubmitClick = () => {
|
||||||
|
if (!barcode || isBarcodeExist) return;
|
||||||
|
onSubmit(barcode.trim());
|
||||||
|
context.closeModal(id);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
gap={rem(10)}
|
||||||
|
direction={"column"}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
required
|
||||||
|
error={getErrorMessage()}
|
||||||
|
label={"Штрихкод"}
|
||||||
|
placeholder={"Введите или отсканируйте штрихкод"}
|
||||||
|
value={barcode}
|
||||||
|
onChange={(event) => setBarcode(event.currentTarget.value)}
|
||||||
|
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={onSubmitClick}
|
||||||
|
disabled={!barcode || isBarcodeExist}
|
||||||
|
>
|
||||||
|
Добавить
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrintBarcodeModal;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.print-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.print-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
132
src/modals/PrintBarcodeModal/PrintBarcodeModal.tsx
Normal file
132
src/modals/PrintBarcodeModal/PrintBarcodeModal.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import {ContextModalProps, modals} from "@mantine/modals";
|
||||||
|
import {Button, Divider, Flex, NumberInput, rem, Select, Spoiler} from "@mantine/core";
|
||||||
|
import Barcode from "react-barcode";
|
||||||
|
import {useEffect, useRef, useState} from "react";
|
||||||
|
import {useReactToPrint} from "react-to-print";
|
||||||
|
import {ProductService} from "../../client";
|
||||||
|
import styles from "./PrintBarcodeModal.module.css";
|
||||||
|
import {useGetProductById} from "../../api/product/useGetProductById.tsx";
|
||||||
|
import {notifications} from "../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
productId: number;
|
||||||
|
defaultQuantity?: number;
|
||||||
|
}
|
||||||
|
const PrintBarcodeModal = ({
|
||||||
|
innerProps
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const {productId, defaultQuantity = 1} = innerProps;
|
||||||
|
const [quantity, setQuantity] = useState(defaultQuantity);
|
||||||
|
const [barcode, setBarcode] = useState<string | undefined>()
|
||||||
|
|
||||||
|
const {product, refetch} = useGetProductById(productId);
|
||||||
|
|
||||||
|
const barcodeRef = useRef(null);
|
||||||
|
const handlePrint = useReactToPrint({
|
||||||
|
content: () => barcodeRef.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const onAdd = (newBarcode: string) => {
|
||||||
|
ProductService.addProductBarcode({requestBody: {productId, barcode: newBarcode}})
|
||||||
|
.then(async ({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onAddClick = () => {
|
||||||
|
if (!product) return;
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "addBarcode",
|
||||||
|
title: 'Добавление штрихкода',
|
||||||
|
withCloseButton: true,
|
||||||
|
innerProps: {
|
||||||
|
productId: product.id,
|
||||||
|
onSubmit: onAdd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onGenerateClick = () => {
|
||||||
|
if (!product) return;
|
||||||
|
ProductService.generateProductBarcode({requestBody: {productId}})
|
||||||
|
.then(async ({ok, message, barcode}) => {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
setBarcode(barcode);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (!product) return;
|
||||||
|
if (product.barcodes.length === 1)
|
||||||
|
setBarcode(product.barcodes[0]);
|
||||||
|
}, [product]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap={rem(10)}
|
||||||
|
direction={"column"}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={barcode}
|
||||||
|
onChange={(value) => setBarcode(value || undefined)}
|
||||||
|
data={product?.barcodes}
|
||||||
|
label={"Штрихкод"}
|
||||||
|
placeholder={"Выберите штрихкод"}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
label={"Количество копий"}
|
||||||
|
placeholder={"Введите количество копий"}
|
||||||
|
value={quantity}
|
||||||
|
onChange={(value) => typeof value === "number" && setQuantity(value)}
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
<Spoiler
|
||||||
|
w={"100%"}
|
||||||
|
style={{
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
styles={{control: {width: "100%"}}}
|
||||||
|
showLabel={"Показать предпросмотр"}
|
||||||
|
hideLabel={"Скрыть предпросмотр"}
|
||||||
|
maxHeight={0}>
|
||||||
|
<div>
|
||||||
|
{barcode &&
|
||||||
|
<Barcode value={barcode}/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Spoiler>
|
||||||
|
<Divider
|
||||||
|
my={rem(10)}
|
||||||
|
/>
|
||||||
|
<Flex direction={"column"} gap={rem(10)}>
|
||||||
|
<Flex gap={rem(10)}>
|
||||||
|
<Button
|
||||||
|
onClick={() => onAddClick()}
|
||||||
|
variant={"default"}
|
||||||
|
fullWidth>
|
||||||
|
Добавить вручную
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => onGenerateClick()}
|
||||||
|
variant={"default"}
|
||||||
|
fullWidth>
|
||||||
|
Сгенерировать
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<Button
|
||||||
|
size={"lg"}
|
||||||
|
disabled={!barcode}
|
||||||
|
onClick={() => handlePrint()}
|
||||||
|
>Печать</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<div className={styles['print-only']} ref={barcodeRef}>
|
||||||
|
{barcode && Array.from({length: quantity}).map((_, index) => (
|
||||||
|
<Barcode key={index} value={barcode}/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrintBarcodeModal;
|
||||||
@@ -5,6 +5,8 @@ import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/
|
|||||||
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
||||||
import AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.tsx";
|
import AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.tsx";
|
||||||
import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
|
import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
|
||||||
|
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
|
||||||
|
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterDeadline: EnterDeadlineModal,
|
enterDeadline: EnterDeadlineModal,
|
||||||
@@ -13,5 +15,7 @@ export const modals = {
|
|||||||
createProduct: createProductModal,
|
createProduct: createProductModal,
|
||||||
productFormModal: ProductFormModal,
|
productFormModal: ProductFormModal,
|
||||||
addDealService: AddDealServiceModal,
|
addDealService: AddDealServiceModal,
|
||||||
addDealProduct: AddDealProductModal
|
addDealProduct: AddDealProductModal,
|
||||||
|
printBarcode: PrintBarcodeModal,
|
||||||
|
addBarcode: AddBarcodeModal
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {FC, useState} from "react";
|
import {FC} from "react";
|
||||||
import ClientsTable from "./components/ClientsTable/ClientsTable.tsx";
|
import ClientsTable from "./components/ClientsTable/ClientsTable.tsx";
|
||||||
import useClientsList from "./hooks/useClientsList.tsx";
|
import useClientsList from "./hooks/useClientsList.tsx";
|
||||||
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
|
||||||
@@ -7,7 +7,6 @@ import {Button} from "@mantine/core";
|
|||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
import {ClientSchema, ClientService} from "../../client";
|
import {ClientSchema, ClientService} from "../../client";
|
||||||
import {notifications} from "../../shared/lib/notifications.ts";
|
import {notifications} from "../../shared/lib/notifications.ts";
|
||||||
import PlusMinusInput from "../../components/PlusMinusInput/PlusMinusInput.tsx";
|
|
||||||
|
|
||||||
const ClientsPage: FC = () => {
|
const ClientsPage: FC = () => {
|
||||||
const {clients, refetch} = useClientsList();
|
const {clients, refetch} = useClientsList();
|
||||||
@@ -60,7 +59,6 @@ const ClientsPage: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [a, setA] = useState(5);
|
|
||||||
return (
|
return (
|
||||||
<div className={styles['container']}>
|
<div className={styles['container']}>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
@@ -71,12 +69,6 @@ const ClientsPage: FC = () => {
|
|||||||
>
|
>
|
||||||
Создать клиента
|
Создать клиента
|
||||||
</Button>
|
</Button>
|
||||||
<PlusMinusInput onChange={event=>{
|
|
||||||
console.log(event);
|
|
||||||
setA(event)
|
|
||||||
}}
|
|
||||||
value={a}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ const BaseFormModal = <T, >(props: Props<T>) => {
|
|||||||
const isEditing = 'onChange' in props;
|
const isEditing = 'onChange' in props;
|
||||||
|
|
||||||
const onSubmit = (values: T) => {
|
const onSubmit = (values: T) => {
|
||||||
console.log('----values');
|
|
||||||
console.log(values)
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
props.onChange(values);
|
props.onChange(values);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,26 +3,53 @@ import useDealProductsTableColumns from "./columns.tsx";
|
|||||||
import {FC} from "react";
|
import {FC} from "react";
|
||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||||
import {DealProductSchema} from "../../../../client";
|
import {DealProductSchema} from "../../../../client";
|
||||||
import {Button, Flex, rem} from "@mantine/core";
|
import {ActionIcon, Button, Flex, rem, Tooltip} from "@mantine/core";
|
||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
|
import {IconBarcode, IconTrash} from "@tabler/icons-react";
|
||||||
|
|
||||||
type RestProps = {
|
type RestProps = {
|
||||||
clientId: number;
|
clientId: number;
|
||||||
|
onMultipleDelete?: (items: DealProductSchema[]) => void;
|
||||||
}
|
}
|
||||||
type Props = CRUDTableProps<DealProductSchema> & RestProps;
|
type Props = CRUDTableProps<DealProductSchema> & RestProps;
|
||||||
const DealProductsTable: FC<Props> = ({items, clientId}) => {
|
const DealProductsTable: FC<Props> = (props: Props) => {
|
||||||
const columns = useDealProductsTableColumns();
|
const {items, clientId, onChange, onCreate, onDelete, onMultipleDelete} = props;
|
||||||
|
|
||||||
|
const columns = useDealProductsTableColumns({
|
||||||
|
onChange: (product, quantity) => {
|
||||||
|
if (!onChange) return;
|
||||||
|
if (quantity <= 0 && onDelete) {
|
||||||
|
onDelete(product);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange({...product, quantity})
|
||||||
|
},
|
||||||
|
data: items
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
const onCreateClick = () => {
|
const onCreateClick = () => {
|
||||||
|
if (!onCreate) return;
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "addDealProduct",
|
modal: "addDealProduct",
|
||||||
title: "Добавление товара",
|
title: "Добавление товара",
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onCreate: event => console.log(event),
|
onCreate: (product) => onCreate(product as DealProductSchema),
|
||||||
clientId
|
clientId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "printBarcode",
|
||||||
|
title: 'Печать штрихкода',
|
||||||
|
withCloseButton: true,
|
||||||
|
innerProps: {
|
||||||
|
productId: product.product.id,
|
||||||
|
defaultQuantity: product.quantity
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,23 +57,39 @@ const DealProductsTable: FC<Props> = ({items, clientId}) => {
|
|||||||
data={items}
|
data={items}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
restProps={{
|
restProps={{
|
||||||
renderBottomToolbar: ({}) => (
|
enableBottomToolbar: true,
|
||||||
|
enableRowActions: true,
|
||||||
|
enableRowSelection: true,
|
||||||
|
renderRowActions: ({row}) => (
|
||||||
|
<Flex gap="md">
|
||||||
|
<Tooltip label="Удалить">
|
||||||
|
<ActionIcon onClick={() => onDelete && onDelete(row.original)} variant={"default"}>
|
||||||
|
<IconTrash/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Печать штрихкода">
|
||||||
|
<ActionIcon onClick={() => onPrintBarcodeClick(row.original)} variant={"default"}>
|
||||||
|
<IconBarcode/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
renderBottomToolbar: ({table}) => (
|
||||||
<Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}>
|
<Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}>
|
||||||
{/*{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (*/}
|
{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (
|
||||||
{/* <Button*/}
|
<Button
|
||||||
{/* onClick={() => {*/}
|
onClick={() => {
|
||||||
{/* onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))*/}
|
onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))
|
||||||
{/* }}*/}
|
}}
|
||||||
{/* variant={"filled"}*/}
|
variant={"filled"}
|
||||||
{/* color={"red"}*/}
|
color={"red"}
|
||||||
{/* >*/}
|
>
|
||||||
{/* Удалить выбранные*/}
|
Удалить выбранные
|
||||||
{/* </Button>*/}
|
</Button>
|
||||||
{/*)}*/}
|
)}
|
||||||
<Button onClick={onCreateClick} variant={"default"}>
|
<Button onClick={onCreateClick} variant={"default"}>
|
||||||
Добавить услугу
|
Добавить товар
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
} as MRT_TableOptions<DealProductSchema>}
|
} as MRT_TableOptions<DealProductSchema>}
|
||||||
|
|||||||
@@ -1,22 +1,65 @@
|
|||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {MRT_ColumnDef} from "mantine-react-table";
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
import {DealProductSchema} from "../../../../client";
|
import {DealProductSchema} from "../../../../client";
|
||||||
|
import PlusMinusInput from "../../../../components/PlusMinusInput/PlusMinusInput.tsx";
|
||||||
|
import {List} from "@mantine/core";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onChange: (product: DealProductSchema, quantity: number) => void;
|
||||||
|
data: DealProductSchema[];
|
||||||
|
}
|
||||||
|
const useDealProductsTableColumns = (props: Props) => {
|
||||||
|
const {onChange, data} = props;
|
||||||
|
const totalQuantity = useMemo(() => data.reduce((acc, row) => acc + row.quantity, 0), [data]);
|
||||||
|
|
||||||
const useDealProductsTableColumns = () => {
|
|
||||||
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(() => [
|
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(() => [
|
||||||
{
|
{
|
||||||
accessorKey: "products.name",
|
accessorKey: "product.article",
|
||||||
header: "Название"
|
header: "Артикул",
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "products.article",
|
accessorKey: "product.name",
|
||||||
header: "Артикул"
|
header: "Название",
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "product.barcodes",
|
||||||
|
header: "Штрихкоды",
|
||||||
|
Cell: ({cell}) => {
|
||||||
|
return (
|
||||||
|
<List size={"sm"}>
|
||||||
|
|
||||||
|
{cell.getValue<string[]>()?.map(barcode => (
|
||||||
|
<List.Item key={barcode}>
|
||||||
|
{barcode}
|
||||||
|
</List.Item>
|
||||||
|
))}
|
||||||
|
|
||||||
|
</List>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "quantity",
|
accessorKey: "quantity",
|
||||||
header: "Количество"
|
header: "Количество",
|
||||||
|
enableSorting: false,
|
||||||
|
enableColumnActions: false,
|
||||||
|
Footer: <>Всего товаров: {totalQuantity} </>,
|
||||||
|
Cell: ({row}) => {
|
||||||
|
return (
|
||||||
|
<PlusMinusInput
|
||||||
|
value={row.original.quantity}
|
||||||
|
onChange={(value) => onChange(row.original, value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
], [])
|
}
|
||||||
|
], [onChange, data])
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useDealProductsTableColumns;
|
export default useDealProductsTableColumns;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import {DealStatusHistorySchema} from "../../../../client";
|
||||||
|
import {useDealStatusChangeTableColumns} from "./columns.tsx";
|
||||||
|
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import {FC} from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
items: DealStatusHistorySchema[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const DealStatusChangeTable: FC<Props> = (props: Props) => {
|
||||||
|
const {items} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
data={items}
|
||||||
|
columns={useDealStatusChangeTableColumns()}
|
||||||
|
restProps={{
|
||||||
|
enableRowActions: false,
|
||||||
|
enableColumnActions: false,
|
||||||
|
enableSorting: false,
|
||||||
|
enableBottomToolbar: false,
|
||||||
|
enableColumnFilters: false,
|
||||||
|
enableColumnVisibilityToggle: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default DealStatusChangeTable;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import {useMemo} from "react";
|
||||||
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
|
import {DealStatusHistorySchema} from "../../../../client";
|
||||||
|
import {DealStatus, DealStatusDictionary} from "../../../../shared/enums/DealStatus.ts";
|
||||||
|
|
||||||
|
export const useDealStatusChangeTableColumns = () => {
|
||||||
|
return useMemo<MRT_ColumnDef<DealStatusHistorySchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "changedAt",
|
||||||
|
header: "Дата",
|
||||||
|
accessorFn: (row) => new Date(row.changedAt).toLocaleString('ru-RU'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "user.id",
|
||||||
|
header: "ID пользователя",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "fromStatus",
|
||||||
|
header: "Из статуса",
|
||||||
|
accessorFn: (row) => DealStatusDictionary[row.fromStatus as DealStatus],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "toStatus",
|
||||||
|
header: "В статус",
|
||||||
|
accessorFn: (row) => DealStatusDictionary[row.toStatus as DealStatus],
|
||||||
|
|
||||||
|
}
|
||||||
|
], []);
|
||||||
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import {Drawer, Text} from "@mantine/core";
|
import {Box, Drawer, rem, Tabs, Text} from "@mantine/core";
|
||||||
import {FC, useRef} from "react";
|
import {FC, 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 {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 {IconBarcode, IconBox, IconCalendarUser, IconSettings} from "@tabler/icons-react";
|
||||||
|
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
|
||||||
|
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
|
||||||
|
|
||||||
const useDealServicesTableState = () => {
|
const useDealServicesTableState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
@@ -30,7 +33,6 @@ const useDealServicesTableState = () => {
|
|||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onServiceDelete = (service: DealServiceSchema) => {
|
const onServiceDelete = (service: DealServiceSchema) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -68,7 +70,6 @@ const useDealServicesTableState = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onServiceCreate = (service: DealServiceSchema) => {
|
const onServiceCreate = (service: DealServiceSchema) => {
|
||||||
console.log('-------Drawer')
|
console.log('-------Drawer')
|
||||||
console.log(service);
|
console.log(service);
|
||||||
@@ -88,7 +89,6 @@ const useDealServicesTableState = () => {
|
|||||||
.then(setSelectedDeal)
|
.then(setSelectedDeal)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
||||||
if (!selectedDeal) return;
|
if (!selectedDeal) return;
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
@@ -152,27 +152,158 @@ const DealEditDrawerServicesTable = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useDealProductTableState = () => {
|
const useDealProductTableState = () => {
|
||||||
const {selectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
|
|
||||||
|
const onProductUpdate = (product: DealProductSchema) => {
|
||||||
|
if (!selectedDeal) return;
|
||||||
|
DealService.updateDealProductQuantity({
|
||||||
|
requestBody: {
|
||||||
|
dealId: selectedDeal.id,
|
||||||
|
productId: product.product.id,
|
||||||
|
quantity: product.quantity
|
||||||
|
}
|
||||||
|
}).then(async ({ok, message}) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await DealService.getDealById({dealId: selectedDeal.id})
|
||||||
|
.then(setSelectedDeal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onProductDelete = (product: DealProductSchema) => {
|
||||||
|
if (!selectedDeal) return;
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление товара",
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<Text>
|
||||||
|
Вы уверены, что хотите удалить товар:
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
{product.product.name}?
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
|
),
|
||||||
|
onConfirm: () => {
|
||||||
|
DealService.deleteDealProduct({
|
||||||
|
requestBody: {
|
||||||
|
dealId: selectedDeal.id,
|
||||||
|
productId: product.product.id
|
||||||
|
}
|
||||||
|
}).then(async ({ok, message}) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await DealService.getDealById({dealId: selectedDeal.id})
|
||||||
|
.then(setSelectedDeal)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
cancel: "Отмена",
|
||||||
|
confirm: "Удалить"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onProductCreate = (product: DealProductSchema) => {
|
||||||
|
if (!selectedDeal) return;
|
||||||
|
DealService.addDealProduct({
|
||||||
|
requestBody: {
|
||||||
|
dealId: selectedDeal.id,
|
||||||
|
productId: product.product.id,
|
||||||
|
quantity: product.quantity
|
||||||
|
}
|
||||||
|
}).then(async ({ok, message}) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await DealService.getDealById({dealId: selectedDeal.id})
|
||||||
|
.then(setSelectedDeal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const onProductMultipleDelete = (items: DealProductSchema[]) => {
|
||||||
|
if (!selectedDeal) return;
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление товаров",
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<Text>
|
||||||
|
Вы уверены, что хотите удалить выбранные товары?
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onConfirm: () => {
|
||||||
|
DealService.deleteMultipleDealProducts({
|
||||||
|
requestBody: {
|
||||||
|
dealId: selectedDeal.id,
|
||||||
|
productIds: items.map(item => item.product.id)
|
||||||
|
}
|
||||||
|
}).then(async ({ok, message}) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await DealService.getDealById({dealId: selectedDeal.id})
|
||||||
|
.then(setSelectedDeal)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
cancel: "Отмена",
|
||||||
|
confirm: "Удалить"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
clientId: selectedDeal?.clientId || -1,
|
clientId: selectedDeal?.clientId || -1,
|
||||||
products: selectedDeal?.products || []
|
products: selectedDeal?.products || [],
|
||||||
|
onProductUpdate,
|
||||||
|
onProductDelete,
|
||||||
|
onProductCreate,
|
||||||
|
onProductMultipleDelete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DealEditDrawerProductsTable = () => {
|
const DealEditDrawerProductsTable = () => {
|
||||||
const {
|
const {
|
||||||
products,
|
products,
|
||||||
clientId
|
clientId,
|
||||||
|
onProductUpdate,
|
||||||
|
onProductDelete,
|
||||||
|
onProductCreate,
|
||||||
|
onProductMultipleDelete,
|
||||||
} = useDealProductTableState();
|
} = useDealProductTableState();
|
||||||
return (
|
return (
|
||||||
<DealProductsTable
|
<DealProductsTable
|
||||||
clientId={clientId}
|
clientId={clientId}
|
||||||
items={products}
|
items={products}
|
||||||
|
onChange={onProductUpdate}
|
||||||
|
onMultipleDelete={onProductMultipleDelete}
|
||||||
|
onDelete={onProductDelete}
|
||||||
|
onCreate={onProductCreate}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useDealStatusChangeState = () => {
|
||||||
|
const {selectedDeal} = useDealPageContext();
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusHistory: selectedDeal?.statusHistory || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const DealEditDrawerStatusChangeTable = () => {
|
||||||
|
const {statusHistory} = useDealStatusChangeState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DealStatusChangeTable
|
||||||
|
items={statusHistory}
|
||||||
|
/>)
|
||||||
|
}
|
||||||
|
|
||||||
const useDealEditDrawerState = () => {
|
const useDealEditDrawerState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
return {
|
return {
|
||||||
@@ -189,9 +320,55 @@ const DealEditDrawer: FC = () => {
|
|||||||
size={"95%"}
|
size={"95%"}
|
||||||
position={"right"}
|
position={"right"}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
opened={isVisible}>
|
removeScrollProps={{allowPinchZoom: true}}
|
||||||
|
withCloseButton={false}
|
||||||
|
opened={isVisible}
|
||||||
|
styles={{body: {height: '100%'}}}
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
defaultValue={"general"}
|
||||||
|
h={'100%'}
|
||||||
|
variant={"outline"}
|
||||||
|
orientation={"vertical"}>
|
||||||
|
<Tabs.List
|
||||||
|
>
|
||||||
|
<Tabs.Tab value={"general"} leftSection={<IconSettings/>}>
|
||||||
|
Общее
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab value={"history"} leftSection={<IconCalendarUser/>}>
|
||||||
|
История
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab value={"services"} leftSection={<IconBox/>}>
|
||||||
|
Услуги
|
||||||
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab value={"products"} leftSection={<IconBarcode/>}>
|
||||||
|
Товары
|
||||||
|
</Tabs.Tab>
|
||||||
|
</Tabs.List>
|
||||||
|
<Tabs.Panel value={"general"}>
|
||||||
|
<Box h={"100%"} w={"100%"} p={rem(10)}>
|
||||||
|
<DealEditDrawerGeneralTab/>
|
||||||
|
</Box>
|
||||||
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value={"history"}>
|
||||||
|
<Box p={rem(10)}>
|
||||||
|
<DealEditDrawerStatusChangeTable/>
|
||||||
|
</Box>
|
||||||
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value={"services"}>
|
||||||
|
<Box p={rem(10)}>
|
||||||
|
|
||||||
<DealEditDrawerServicesTable/>
|
<DealEditDrawerServicesTable/>
|
||||||
|
</Box>
|
||||||
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value={"products"}>
|
||||||
|
<Box p={rem(10)}>
|
||||||
|
|
||||||
<DealEditDrawerProductsTable/>
|
<DealEditDrawerProductsTable/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
</Tabs.Panel>
|
||||||
|
</Tabs>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import {FC} from "react";
|
||||||
|
import {useDealPageContext} from "../../../contexts/DealPageContext.tsx";
|
||||||
|
import {Button, Checkbox, Fieldset, Flex, Group, rem, TextInput} from "@mantine/core";
|
||||||
|
import {useForm} from "@mantine/form";
|
||||||
|
import {ClientService, DealSchema, DealService} from "../../../../../client";
|
||||||
|
import {DealStatus, DealStatusDictionary} from "../../../../../shared/enums/DealStatus.ts";
|
||||||
|
import {isEqual} from "lodash";
|
||||||
|
import {notifications} from "../../../../../shared/lib/notifications.ts";
|
||||||
|
import {useQueryClient} from "@tanstack/react-query";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
deal: DealSchema
|
||||||
|
}
|
||||||
|
type FormType = Omit<DealSchema, 'statusHistory' | 'services' | 'products'>
|
||||||
|
|
||||||
|
const Content: FC<Props> = ({deal}) => {
|
||||||
|
const {setSelectedDeal} = useDealPageContext();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const initialValues: FormType = deal;
|
||||||
|
const form = useForm<FormType>(
|
||||||
|
{
|
||||||
|
initialValues: initialValues,
|
||||||
|
validate: {
|
||||||
|
name: (value: string) => value.length > 0 ? null : 'Название сделки не может быть пустым'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const updateDealInfo = async (values: FormType) => {
|
||||||
|
return DealService.updateDealGeneralInfo({
|
||||||
|
requestBody: {
|
||||||
|
dealId: deal.id,
|
||||||
|
data: values
|
||||||
|
}
|
||||||
|
}).then(({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
if (!ok) return;
|
||||||
|
DealService.getDealById({dealId: deal.id})
|
||||||
|
.then((data) => {
|
||||||
|
setSelectedDeal(data);
|
||||||
|
form.setInitialValues(data);
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ['getDealSummaries']
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const updateClientInfo = async (values: FormType) => {
|
||||||
|
return ClientService.updateClient({
|
||||||
|
requestBody: {
|
||||||
|
data: values.client
|
||||||
|
}
|
||||||
|
}).then(({ok, message}) =>
|
||||||
|
notifications.guess(ok, {message}));
|
||||||
|
}
|
||||||
|
const handleSubmit = async (values: FormType) => {
|
||||||
|
// Updating client info if there changes
|
||||||
|
if (!isEqual(values.client, deal.client)) {
|
||||||
|
await updateClientInfo(values);
|
||||||
|
}
|
||||||
|
// updating deal info
|
||||||
|
await updateDealInfo(values);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||||||
|
<Fieldset legend={"Общие параметры"}>
|
||||||
|
<Flex direction={"column"} gap={rem(10)}>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Название сделки"}
|
||||||
|
label={"Название сделки"}
|
||||||
|
{...form.getInputProps('name')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
disabled
|
||||||
|
placeholder={"Дата создания"}
|
||||||
|
label={"Дата создания"}
|
||||||
|
value={new Date(deal.createdAt).toLocaleString('ru-RU')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
disabled
|
||||||
|
placeholder={"Текущий статус"}
|
||||||
|
label={"Текущий статус"}
|
||||||
|
value={DealStatusDictionary[deal.currentStatus as DealStatus]}/>
|
||||||
|
<Checkbox
|
||||||
|
label={"Сделка завершена"}
|
||||||
|
{...form.getInputProps('isCompleted')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label={"Сделка удалена"}
|
||||||
|
{...form.getInputProps('isDeleted')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
</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.address')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите ИНН"}
|
||||||
|
label={"ИНН"}
|
||||||
|
{...form.getInputProps('client.details.inn')}
|
||||||
|
/>
|
||||||
|
</Fieldset>
|
||||||
|
<Group justify={"flex-end"} mt={"md"}>
|
||||||
|
<Button
|
||||||
|
color={"red"}
|
||||||
|
type={"reset"}
|
||||||
|
disabled={isEqual(initialValues, form.values)}
|
||||||
|
onClick={() => form.reset()}
|
||||||
|
>Отменить изменения</Button>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
type={"submit"}
|
||||||
|
disabled={isEqual(initialValues, form.values)}
|
||||||
|
>Сохранить изменения</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const DealEditDrawerGeneralTab: FC = () => {
|
||||||
|
const {selectedDeal} = useDealPageContext();
|
||||||
|
if (!selectedDeal) return <>No deal selected</>;
|
||||||
|
return (
|
||||||
|
<Content deal={selectedDeal}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default DealEditDrawerGeneralTab;
|
||||||
@@ -29,7 +29,6 @@ const AddDealProductModal = ({
|
|||||||
context.closeContextModal(id);
|
context.closeContextModal(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseFormModal
|
<BaseFormModal
|
||||||
{...innerProps}
|
{...innerProps}
|
||||||
@@ -39,10 +38,10 @@ const AddDealProductModal = ({
|
|||||||
<BaseFormModal.Body>
|
<BaseFormModal.Body>
|
||||||
<>
|
<>
|
||||||
<ProductSelect
|
<ProductSelect
|
||||||
placeholder={"Выберите услугу"}
|
placeholder={"Выберите товар"}
|
||||||
label={"Услуга"}
|
label={"Товар"}
|
||||||
clientId={innerProps.clientId}
|
clientId={innerProps.clientId}
|
||||||
{...form.getInputProps('service')}
|
{...form.getInputProps('product')}
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
placeholder={"Введите количество"}
|
placeholder={"Введите количество"}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
|||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
import {useProductsTableColumns} from "./columns.tsx";
|
import {useProductsTableColumns} from "./columns.tsx";
|
||||||
import {ActionIcon, Flex, Tooltip} from "@mantine/core";
|
import {ActionIcon, Flex, Tooltip} from "@mantine/core";
|
||||||
import {IconEdit, IconTrash} from "@tabler/icons-react";
|
import {IconBarcode, IconEdit, IconTrash} from "@tabler/icons-react";
|
||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
|
|
||||||
@@ -23,6 +23,16 @@ const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({items, onDelete, onCh
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const onPrintBarcodeClick = (product: ProductSchema) => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "printBarcode",
|
||||||
|
title: 'Печать штрихкода',
|
||||||
|
withCloseButton: true,
|
||||||
|
innerProps: {
|
||||||
|
productId: product.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BaseTable
|
<BaseTable
|
||||||
ref={tableRef}
|
ref={tableRef}
|
||||||
@@ -33,6 +43,13 @@ const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({items, onDelete, onCh
|
|||||||
enableRowActions: true,
|
enableRowActions: true,
|
||||||
renderRowActions: ({row}) => (
|
renderRowActions: ({row}) => (
|
||||||
<Flex gap="md">
|
<Flex gap="md">
|
||||||
|
<Tooltip label="Печать штрихкода">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => onPrintBarcodeClick(row.original)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconBarcode/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip label="Редактировать">
|
<Tooltip label="Редактировать">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={() => onEditClick(row.original)}
|
onClick={() => onEditClick(row.original)}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {notifications} from "../../../shared/lib/notifications.ts";
|
|||||||
import {CreateProductRequest} from "../types.ts";
|
import {CreateProductRequest} from "../types.ts";
|
||||||
import {ProductSchema, ProductService} from "../../../client";
|
import {ProductSchema, ProductService} from "../../../client";
|
||||||
import useProductsList from "../hooks/useProductsList.tsx";
|
import useProductsList from "../hooks/useProductsList.tsx";
|
||||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
|
||||||
|
|
||||||
export const ProductsPage: FC = () => {
|
export const ProductsPage: FC = () => {
|
||||||
const [clientId, setClientId] = useState(-1);
|
const [clientId, setClientId] = useState(-1);
|
||||||
@@ -90,11 +89,7 @@ export const ProductsPage: FC = () => {
|
|||||||
onClick={() => onCreateProductClick()}
|
onClick={() => onCreateProductClick()}
|
||||||
variant={"default"}
|
variant={"default"}
|
||||||
>Создать</Button>
|
>Создать</Button>
|
||||||
<ProductSelect
|
|
||||||
onChange={event => console.log(event)}
|
|
||||||
clientId={8}
|
|
||||||
limit={10}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {createLazyFileRoute} from "@tanstack/react-router";
|
import {createLazyFileRoute} from "@tanstack/react-router";
|
||||||
import {Button} from "@mantine/core";
|
import {Button, Text} from "@mantine/core";
|
||||||
import {notifications} from "@mantine/notifications";
|
import {modals} from "@mantine/modals";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute('/test')({
|
export const Route = createLazyFileRoute('/test')({
|
||||||
component: TestPage
|
component: TestPage
|
||||||
@@ -8,11 +8,28 @@ export const Route = createLazyFileRoute('/test')({
|
|||||||
|
|
||||||
function TestPage() {
|
function TestPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Button
|
||||||
<Button onClick={() => {
|
onClick={() =>
|
||||||
notifications.show({title: "test", message: "хуй"})
|
modals.openConfirmModal({
|
||||||
}}>TEST</Button>
|
title: 'Please confirm your action',
|
||||||
|
closeOnConfirm: false,
|
||||||
</>
|
labels: {confirm: 'Next modal', cancel: 'Close modal'},
|
||||||
)
|
onConfirm: () =>
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: 'This is modal at second layer',
|
||||||
|
labels: {confirm: 'Close modal', cancel: 'Back'},
|
||||||
|
closeOnConfirm: false,
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
When this modal is closed modals state will revert to first modal
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
onConfirm: modals.closeAll,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Open multiple steps modal
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -11,3 +11,12 @@ export enum DealStatus {
|
|||||||
export const getDealStatusByName = (name: string): DealStatus => {
|
export const getDealStatusByName = (name: string): DealStatus => {
|
||||||
return DealStatus[name as keyof typeof DealStatus];
|
return DealStatus[name as keyof typeof DealStatus];
|
||||||
}
|
}
|
||||||
|
export const DealStatusDictionary = {
|
||||||
|
[DealStatus.CREATED]: "Создан",
|
||||||
|
[DealStatus.AWAITING_ACCEPTANCE]: "Ожидает приемки",
|
||||||
|
[DealStatus.PACKAGING]: "Упаковка",
|
||||||
|
[DealStatus.AWAITING_SHIPMENT]: "Ожидает отгрузки",
|
||||||
|
[DealStatus.AWAITING_PAYMENT]: "Ожидает оплаты",
|
||||||
|
[DealStatus.COMPLETED]: "Завершена",
|
||||||
|
[DealStatus.CANCELLED]: "Отменена",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user