crap
This commit is contained in:
@@ -30,16 +30,20 @@
|
||||
"clsx": "^2.1.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"dot-object": "^2.1.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mantine-form-zod-resolver": "^1.1.0",
|
||||
"mantine-react-table": "^2.0.0-beta.0",
|
||||
"react": "^18.2.0",
|
||||
"react-barcode": "^1.5.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-to-print": "^2.15.1",
|
||||
"reactflow": "^11.10.4",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dot-object": "^2",
|
||||
"@types/lodash": "^4",
|
||||
"@types/react": "^18.2.56",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"@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 { ClientUpdateRequest } from './models/ClientUpdateRequest';
|
||||
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 { DealAddServiceResponse } from './models/DealAddServiceResponse';
|
||||
export type { DealAddServicesRequest } from './models/DealAddServicesRequest';
|
||||
@@ -26,26 +28,41 @@ export type { DealAddServicesResponse } from './models/DealAddServicesResponse';
|
||||
export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest';
|
||||
export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse';
|
||||
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 { DealDeleteServiceResponse } from './models/DealDeleteServiceResponse';
|
||||
export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest';
|
||||
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
|
||||
export type { DealGeneralInfoSchema } from './models/DealGeneralInfoSchema';
|
||||
export type { DealGetAllResponse } from './models/DealGetAllResponse';
|
||||
export type { DealProductSchema } from './models/DealProductSchema';
|
||||
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
||||
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
||||
export type { DealSchema } from './models/DealSchema';
|
||||
export type { DealServiceSchema } from './models/DealServiceSchema';
|
||||
export type { DealStatusHistorySchema } from './models/DealStatusHistorySchema';
|
||||
export type { DealSummary } from './models/DealSummary';
|
||||
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 { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||
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 { ProductCreateResponse } from './models/ProductCreateResponse';
|
||||
export type { ProductDeleteRequest } from './models/ProductDeleteRequest';
|
||||
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 { ProductSchema } from './models/ProductSchema';
|
||||
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
|
||||
@@ -58,6 +75,7 @@ export type { ServiceCreateResponse } from './models/ServiceCreateResponse';
|
||||
export type { ServiceGetAllCategoriesResponse } from './models/ServiceGetAllCategoriesResponse';
|
||||
export type { ServiceGetAllResponse } from './models/ServiceGetAllResponse';
|
||||
export type { ServiceSchema } from './models/ServiceSchema';
|
||||
export type { UserSchema } from './models/UserSchema';
|
||||
export type { ValidationError } from './models/ValidationError';
|
||||
|
||||
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 */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { ClientSchema } from './ClientSchema';
|
||||
import type { DealProductSchema } from './DealProductSchema';
|
||||
import type { DealServiceSchema } from './DealServiceSchema';
|
||||
import type { DealStatusHistorySchema } from './DealStatusHistorySchema';
|
||||
export type DealSchema = {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -12,5 +14,9 @@ export type DealSchema = {
|
||||
currentStatus: number;
|
||||
services: Array<DealServiceSchema>;
|
||||
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 */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { DealAddProductRequest } from '../models/DealAddProductRequest';
|
||||
import type { DealAddProductResponse } from '../models/DealAddProductResponse';
|
||||
import type { DealAddServiceRequest } from '../models/DealAddServiceRequest';
|
||||
import type { DealAddServiceResponse } from '../models/DealAddServiceResponse';
|
||||
import type { DealAddServicesRequest } from '../models/DealAddServicesRequest';
|
||||
@@ -9,6 +11,10 @@ import type { DealAddServicesResponse } from '../models/DealAddServicesResponse'
|
||||
import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest';
|
||||
import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse';
|
||||
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 { DealDeleteServiceResponse } from '../models/DealDeleteServiceResponse';
|
||||
import type { DealDeleteServicesRequest } from '../models/DealDeleteServicesRequest';
|
||||
@@ -18,6 +24,10 @@ import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
|
||||
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
||||
import type { DealSchema } from '../models/DealSchema';
|
||||
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 { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
@@ -95,6 +105,58 @@ export class DealService {
|
||||
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
|
||||
* @returns DealAddServicesResponse Successful Response
|
||||
@@ -196,32 +258,80 @@ export class DealService {
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get All
|
||||
* @returns DealGetAllResponse Successful Response
|
||||
* Products Update
|
||||
* @returns DealUpdateProductQuantityResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getAllDeals(): CancelablePromise<DealGetAllResponse> {
|
||||
public static updateDealProductQuantity({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: DealUpdateProductQuantityRequest,
|
||||
}): CancelablePromise<DealUpdateProductQuantityResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/deal/get-all',
|
||||
method: 'POST',
|
||||
url: '/deal/products/update-quantity',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get Deal By Id
|
||||
* @returns DealSchema Successful Response
|
||||
* Products Add
|
||||
* @returns DealAddProductResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getDealById({
|
||||
dealId,
|
||||
public static addDealProduct({
|
||||
requestBody,
|
||||
}: {
|
||||
dealId: number,
|
||||
}): CancelablePromise<DealSchema> {
|
||||
requestBody: DealAddProductRequest,
|
||||
}): CancelablePromise<DealAddProductResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/deal/get/{deal_id}',
|
||||
path: {
|
||||
'deal_id': dealId,
|
||||
method: 'POST',
|
||||
url: '/deal/products/add',
|
||||
body: requestBody,
|
||||
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: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { ProductAddBarcodeRequest } from '../models/ProductAddBarcodeRequest';
|
||||
import type { ProductAddBarcodeResponse } from '../models/ProductAddBarcodeResponse';
|
||||
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
||||
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
||||
import type { ProductDeleteRequest } from '../models/ProductDeleteRequest';
|
||||
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 { ProductSchema } from '../models/ProductSchema';
|
||||
import type { ProductUpdateRequest } from '../models/ProductUpdateRequest';
|
||||
import type { ProductUpdateResponse } from '../models/ProductUpdateResponse';
|
||||
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 AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.tsx";
|
||||
import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
|
||||
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
|
||||
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
|
||||
|
||||
export const modals = {
|
||||
enterDeadline: EnterDeadlineModal,
|
||||
@@ -13,5 +15,7 @@ export const modals = {
|
||||
createProduct: createProductModal,
|
||||
productFormModal: ProductFormModal,
|
||||
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 useClientsList from "./hooks/useClientsList.tsx";
|
||||
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
|
||||
@@ -7,7 +7,6 @@ import {Button} from "@mantine/core";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {ClientSchema, ClientService} from "../../client";
|
||||
import {notifications} from "../../shared/lib/notifications.ts";
|
||||
import PlusMinusInput from "../../components/PlusMinusInput/PlusMinusInput.tsx";
|
||||
|
||||
const ClientsPage: FC = () => {
|
||||
const {clients, refetch} = useClientsList();
|
||||
@@ -60,7 +59,6 @@ const ClientsPage: FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const [a, setA] = useState(5);
|
||||
return (
|
||||
<div className={styles['container']}>
|
||||
<PageBlock>
|
||||
@@ -71,12 +69,6 @@ const ClientsPage: FC = () => {
|
||||
>
|
||||
Создать клиента
|
||||
</Button>
|
||||
<PlusMinusInput onChange={event=>{
|
||||
console.log(event);
|
||||
setA(event)
|
||||
}}
|
||||
value={a}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</PageBlock>
|
||||
|
||||
@@ -25,8 +25,6 @@ const BaseFormModal = <T, >(props: Props<T>) => {
|
||||
const isEditing = 'onChange' in props;
|
||||
|
||||
const onSubmit = (values: T) => {
|
||||
console.log('----values');
|
||||
console.log(values)
|
||||
if (isEditing) {
|
||||
props.onChange(values);
|
||||
} else {
|
||||
|
||||
@@ -3,26 +3,53 @@ import useDealProductsTableColumns from "./columns.tsx";
|
||||
import {FC} from "react";
|
||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||
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 {modals} from "@mantine/modals";
|
||||
import {IconBarcode, IconTrash} from "@tabler/icons-react";
|
||||
|
||||
type RestProps = {
|
||||
clientId: number;
|
||||
onMultipleDelete?: (items: DealProductSchema[]) => void;
|
||||
}
|
||||
type Props = CRUDTableProps<DealProductSchema> & RestProps;
|
||||
const DealProductsTable: FC<Props> = ({items, clientId}) => {
|
||||
const columns = useDealProductsTableColumns();
|
||||
const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
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 = () => {
|
||||
if (!onCreate) return;
|
||||
modals.openContextModal({
|
||||
modal: "addDealProduct",
|
||||
title: "Добавление товара",
|
||||
innerProps: {
|
||||
onCreate: event => console.log(event),
|
||||
onCreate: (product) => onCreate(product as DealProductSchema),
|
||||
clientId
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "printBarcode",
|
||||
title: 'Печать штрихкода',
|
||||
withCloseButton: true,
|
||||
innerProps: {
|
||||
productId: product.product.id,
|
||||
defaultQuantity: product.quantity
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -30,23 +57,39 @@ const DealProductsTable: FC<Props> = ({items, clientId}) => {
|
||||
data={items}
|
||||
columns={columns}
|
||||
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)}>
|
||||
{/*{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (*/}
|
||||
{/* <Button*/}
|
||||
{/* onClick={() => {*/}
|
||||
{/* onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))*/}
|
||||
{/* }}*/}
|
||||
{/* variant={"filled"}*/}
|
||||
{/* color={"red"}*/}
|
||||
{/* >*/}
|
||||
{/* Удалить выбранные*/}
|
||||
{/* </Button>*/}
|
||||
{/*)}*/}
|
||||
{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))
|
||||
}}
|
||||
variant={"filled"}
|
||||
color={"red"}
|
||||
>
|
||||
Удалить выбранные
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onCreateClick} variant={"default"}>
|
||||
Добавить услугу
|
||||
Добавить товар
|
||||
</Button>
|
||||
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealProductSchema>}
|
||||
|
||||
@@ -1,22 +1,65 @@
|
||||
import {useMemo} from "react";
|
||||
import {MRT_ColumnDef} from "mantine-react-table";
|
||||
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>[]>(() => [
|
||||
{
|
||||
accessorKey: "products.name",
|
||||
header: "Название"
|
||||
accessorKey: "product.article",
|
||||
header: "Артикул",
|
||||
enableSorting: false,
|
||||
enableColumnActions: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "products.article",
|
||||
header: "Артикул"
|
||||
accessorKey: "product.name",
|
||||
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",
|
||||
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;
|
||||
@@ -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 DealServicesTable from "../../components/DealServicesTable/DealServicesTable.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 {modals} from "@mantine/modals";
|
||||
import {BaseTableRef} from "../../../../components/BaseTable/BaseTable.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 {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
@@ -30,7 +33,6 @@ const useDealServicesTableState = () => {
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
|
||||
const onServiceDelete = (service: DealServiceSchema) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -68,7 +70,6 @@ const useDealServicesTableState = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onServiceCreate = (service: DealServiceSchema) => {
|
||||
console.log('-------Drawer')
|
||||
console.log(service);
|
||||
@@ -88,7 +89,6 @@ const useDealServicesTableState = () => {
|
||||
.then(setSelectedDeal)
|
||||
})
|
||||
}
|
||||
|
||||
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
|
||||
if (!selectedDeal) return;
|
||||
modals.openConfirmModal({
|
||||
@@ -152,27 +152,158 @@ const DealEditDrawerServicesTable = () => {
|
||||
}
|
||||
|
||||
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 {
|
||||
clientId: selectedDeal?.clientId || -1,
|
||||
products: selectedDeal?.products || []
|
||||
products: selectedDeal?.products || [],
|
||||
onProductUpdate,
|
||||
onProductDelete,
|
||||
onProductCreate,
|
||||
onProductMultipleDelete
|
||||
}
|
||||
}
|
||||
|
||||
const DealEditDrawerProductsTable = () => {
|
||||
const {
|
||||
products,
|
||||
clientId
|
||||
clientId,
|
||||
onProductUpdate,
|
||||
onProductDelete,
|
||||
onProductCreate,
|
||||
onProductMultipleDelete,
|
||||
} = useDealProductTableState();
|
||||
return (
|
||||
<DealProductsTable
|
||||
clientId={clientId}
|
||||
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 {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||
return {
|
||||
@@ -189,9 +320,55 @@ const DealEditDrawer: FC = () => {
|
||||
size={"95%"}
|
||||
position={"right"}
|
||||
onClose={onClose}
|
||||
opened={isVisible}>
|
||||
<DealEditDrawerServicesTable/>
|
||||
<DealEditDrawerProductsTable/>
|
||||
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/>
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"products"}>
|
||||
<Box p={rem(10)}>
|
||||
|
||||
<DealEditDrawerProductsTable/>
|
||||
</Box>
|
||||
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</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);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
@@ -39,10 +38,10 @@ const AddDealProductModal = ({
|
||||
<BaseFormModal.Body>
|
||||
<>
|
||||
<ProductSelect
|
||||
placeholder={"Выберите услугу"}
|
||||
label={"Услуга"}
|
||||
placeholder={"Выберите товар"}
|
||||
label={"Товар"}
|
||||
clientId={innerProps.clientId}
|
||||
{...form.getInputProps('service')}
|
||||
{...form.getInputProps('product')}
|
||||
/>
|
||||
<NumberInput
|
||||
placeholder={"Введите количество"}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import {MRT_TableOptions} from "mantine-react-table";
|
||||
import {useProductsTableColumns} from "./columns.tsx";
|
||||
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 {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 (
|
||||
<BaseTable
|
||||
ref={tableRef}
|
||||
@@ -33,6 +43,13 @@ const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({items, onDelete, onCh
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({row}) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Печать штрихкода">
|
||||
<ActionIcon
|
||||
onClick={() => onPrintBarcodeClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconBarcode/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {notifications} from "../../../shared/lib/notifications.ts";
|
||||
import {CreateProductRequest} from "../types.ts";
|
||||
import {ProductSchema, ProductService} from "../../../client";
|
||||
import useProductsList from "../hooks/useProductsList.tsx";
|
||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||
|
||||
export const ProductsPage: FC = () => {
|
||||
const [clientId, setClientId] = useState(-1);
|
||||
@@ -90,11 +89,7 @@ export const ProductsPage: FC = () => {
|
||||
onClick={() => onCreateProductClick()}
|
||||
variant={"default"}
|
||||
>Создать</Button>
|
||||
<ProductSelect
|
||||
onChange={event => console.log(event)}
|
||||
clientId={8}
|
||||
limit={10}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</PageBlock>
|
||||
<PageBlock>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {createLazyFileRoute} from "@tanstack/react-router";
|
||||
import {Button} from "@mantine/core";
|
||||
import {notifications} from "@mantine/notifications";
|
||||
import {Button, Text} from "@mantine/core";
|
||||
import {modals} from "@mantine/modals";
|
||||
|
||||
export const Route = createLazyFileRoute('/test')({
|
||||
component: TestPage
|
||||
@@ -8,11 +8,28 @@ export const Route = createLazyFileRoute('/test')({
|
||||
|
||||
function TestPage() {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => {
|
||||
notifications.show({title: "test", message: "хуй"})
|
||||
}}>TEST</Button>
|
||||
|
||||
</>
|
||||
)
|
||||
<Button
|
||||
onClick={() =>
|
||||
modals.openConfirmModal({
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -10,4 +10,13 @@ export enum DealStatus {
|
||||
|
||||
export const getDealStatusByName = (name: string): 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