k
This commit is contained in:
		
							
								
								
									
										86
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								package.json
									
									
									
									
									
								
							@@ -11,54 +11,58 @@
 | 
			
		||||
    "generate-client": "openapi --input http://127.0.0.1:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@hello-pangea/dnd": "^16.5.0",
 | 
			
		||||
    "@mantine/core": "^7.6.1",
 | 
			
		||||
    "@mantine/dates": "^7.6.1",
 | 
			
		||||
    "@mantine/dropzone": "^7.9.2",
 | 
			
		||||
    "@mantine/form": "^7.6.1",
 | 
			
		||||
    "@mantine/hooks": "^7.6.1",
 | 
			
		||||
    "@mantine/modals": "^7.6.1",
 | 
			
		||||
    "@mantine/notifications": "^7.6.1",
 | 
			
		||||
    "@reduxjs/toolkit": "^2.2.1",
 | 
			
		||||
    "@tabler/icons-react": "^2.47.0",
 | 
			
		||||
    "@tanstack/react-query": "^5.22.2",
 | 
			
		||||
    "@tanstack/react-router": "^1.16.6",
 | 
			
		||||
    "@tanstack/router-devtools": "^1.16.6",
 | 
			
		||||
    "@tanstack/router-vite-plugin": "^1.16.5",
 | 
			
		||||
    "axios": "^1.6.7",
 | 
			
		||||
    "@hello-pangea/dnd": "^16.6.0",
 | 
			
		||||
    "@mantine/core": "^7.11.2",
 | 
			
		||||
    "@mantine/dates": "^7.11.2",
 | 
			
		||||
    "@mantine/dropzone": "^7.11.2",
 | 
			
		||||
    "@mantine/form": "^7.11.2",
 | 
			
		||||
    "@mantine/hooks": "^7.11.2",
 | 
			
		||||
    "@mantine/modals": "^7.11.2",
 | 
			
		||||
    "@mantine/notifications": "^7.11.2",
 | 
			
		||||
    "@reduxjs/toolkit": "^2.2.6",
 | 
			
		||||
    "@tabler/icons-react": "^3.11.0",
 | 
			
		||||
    "@tanstack/react-query": "^5.51.9",
 | 
			
		||||
    "@tanstack/react-router": "^1.45.6",
 | 
			
		||||
    "@tanstack/router-devtools": "^1.45.6",
 | 
			
		||||
    "@tanstack/router-vite-plugin": "^1.45.3",
 | 
			
		||||
    "axios": "^1.7.2",
 | 
			
		||||
    "classnames": "^2.5.1",
 | 
			
		||||
    "clsx": "^2.1.0",
 | 
			
		||||
    "dayjs": "^1.11.10",
 | 
			
		||||
    "dot-object": "^2.1.4",
 | 
			
		||||
    "clsx": "^2.1.1",
 | 
			
		||||
    "dayjs": "^1.11.12",
 | 
			
		||||
    "dot-object": "^2.1.5",
 | 
			
		||||
    "framer-motion": "^11.3.8",
 | 
			
		||||
    "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",
 | 
			
		||||
    "mantine-react-table": "^2.0.0-beta.5",
 | 
			
		||||
    "phone": "^3.1.49",
 | 
			
		||||
    "react": "^18.3.1",
 | 
			
		||||
    "react-barcode": "^1.5.3",
 | 
			
		||||
    "react-dom": "^18.3.1",
 | 
			
		||||
    "react-imask": "^7.6.1",
 | 
			
		||||
    "react-redux": "^9.1.2",
 | 
			
		||||
    "react-to-print": "^2.15.1",
 | 
			
		||||
    "reactflow": "^11.10.4",
 | 
			
		||||
    "zod": "^3.22.4"
 | 
			
		||||
    "reactflow": "^11.11.4",
 | 
			
		||||
    "zod": "^3.23.8"
 | 
			
		||||
  },
 | 
			
		||||
  "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",
 | 
			
		||||
    "@typescript-eslint/parser": "^7.0.2",
 | 
			
		||||
    "@vitejs/plugin-react-swc": "^3.5.0",
 | 
			
		||||
    "eslint": "^8.56.0",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^4.6.0",
 | 
			
		||||
    "eslint-plugin-react-refresh": "^0.4.5",
 | 
			
		||||
    "openapi-typescript-codegen": "^0.27.0",
 | 
			
		||||
    "postcss": "^8.4.35",
 | 
			
		||||
    "postcss-preset-mantine": "^1.13.0",
 | 
			
		||||
    "@types/dot-object": "^2.1.6",
 | 
			
		||||
    "@types/lodash": "^4.17.7",
 | 
			
		||||
    "@types/react": "^18.3.3",
 | 
			
		||||
    "@types/react-dom": "^18.3.0",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^7.16.1",
 | 
			
		||||
    "@typescript-eslint/parser": "^7.16.1",
 | 
			
		||||
    "@vitejs/plugin-react-swc": "^3.7.0",
 | 
			
		||||
    "eslint": "^9.7.0",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^4.6.2",
 | 
			
		||||
    "eslint-plugin-react-refresh": "^0.4.8",
 | 
			
		||||
    "openapi-typescript-codegen": "^0.29.0",
 | 
			
		||||
    "postcss": "^8.4.39",
 | 
			
		||||
    "postcss-preset-mantine": "^1.16.0",
 | 
			
		||||
    "postcss-simple-vars": "^7.0.1",
 | 
			
		||||
    "sass": "^1.71.1",
 | 
			
		||||
    "typescript": "^5.2.2",
 | 
			
		||||
    "vite": "^5.1.4"
 | 
			
		||||
    "sass": "^1.77.8",
 | 
			
		||||
    "typescript": "^5.5.3",
 | 
			
		||||
    "vite": "^5.3.4",
 | 
			
		||||
    "yarn-upgrade-all": "^0.7.2"
 | 
			
		||||
  },
 | 
			
		||||
  "packageManager": "yarn@4.1.0"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,8 @@ export type { ClientUpdateRequest } from './models/ClientUpdateRequest';
 | 
			
		||||
export type { ClientUpdateResponse } from './models/ClientUpdateResponse';
 | 
			
		||||
export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarcodeTemplateAttributeRequest';
 | 
			
		||||
export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse';
 | 
			
		||||
export type { CreatePositionRequest } from './models/CreatePositionRequest';
 | 
			
		||||
export type { CreatePositionResponse } from './models/CreatePositionResponse';
 | 
			
		||||
export type { DealAddProductRequest } from './models/DealAddProductRequest';
 | 
			
		||||
export type { DealAddProductResponse } from './models/DealAddProductResponse';
 | 
			
		||||
export type { DealAddServiceRequest } from './models/DealAddServiceRequest';
 | 
			
		||||
@@ -80,7 +82,10 @@ export type { DealUpdateServiceResponse } from './models/DealUpdateServiceRespon
 | 
			
		||||
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
 | 
			
		||||
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
 | 
			
		||||
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
 | 
			
		||||
export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse';
 | 
			
		||||
export type { GetAllRolesResponse } from './models/GetAllRolesResponse';
 | 
			
		||||
export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWarehousesResponse';
 | 
			
		||||
export type { GetAllUsersResponse } from './models/GetAllUsersResponse';
 | 
			
		||||
export type { GetBarcodeTemplateByIdRequest } from './models/GetBarcodeTemplateByIdRequest';
 | 
			
		||||
export type { GetBarcodeTemplateByIdResponse } from './models/GetBarcodeTemplateByIdResponse';
 | 
			
		||||
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
 | 
			
		||||
@@ -89,6 +94,8 @@ export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest
 | 
			
		||||
export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse';
 | 
			
		||||
export type { HTTPValidationError } from './models/HTTPValidationError';
 | 
			
		||||
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
 | 
			
		||||
export type { PermissionSchema } from './models/PermissionSchema';
 | 
			
		||||
export type { PositionSchema } from './models/PositionSchema';
 | 
			
		||||
export type { ProductAddBarcodeRequest } from './models/ProductAddBarcodeRequest';
 | 
			
		||||
export type { ProductAddBarcodeResponse } from './models/ProductAddBarcodeResponse';
 | 
			
		||||
export type { ProductCreateRequest } from './models/ProductCreateRequest';
 | 
			
		||||
@@ -104,6 +111,7 @@ export type { ProductSchema } from './models/ProductSchema';
 | 
			
		||||
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
 | 
			
		||||
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
 | 
			
		||||
export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
 | 
			
		||||
export type { RoleSchema } from './models/RoleSchema';
 | 
			
		||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
 | 
			
		||||
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
 | 
			
		||||
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
 | 
			
		||||
@@ -118,13 +126,19 @@ export type { ServiceSchema } from './models/ServiceSchema';
 | 
			
		||||
export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest';
 | 
			
		||||
export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse';
 | 
			
		||||
export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema';
 | 
			
		||||
export type { UpdateUserRequest } from './models/UpdateUserRequest';
 | 
			
		||||
export type { UpdateUserResponse } from './models/UpdateUserResponse';
 | 
			
		||||
export type { UserSchema } from './models/UserSchema';
 | 
			
		||||
export type { UserUpdate } from './models/UserUpdate';
 | 
			
		||||
export type { ValidationError } from './models/ValidationError';
 | 
			
		||||
 | 
			
		||||
export { AuthService } from './services/AuthService';
 | 
			
		||||
export { BarcodeService } from './services/BarcodeService';
 | 
			
		||||
export { ClientService } from './services/ClientService';
 | 
			
		||||
export { DealService } from './services/DealService';
 | 
			
		||||
export { PositionService } from './services/PositionService';
 | 
			
		||||
export { ProductService } from './services/ProductService';
 | 
			
		||||
export { RoleService } from './services/RoleService';
 | 
			
		||||
export { ServiceService } from './services/ServiceService';
 | 
			
		||||
export { ShippingWarehouseService } from './services/ShippingWarehouseService';
 | 
			
		||||
export { UserService } from './services/UserService';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/CreatePositionRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/CreatePositionRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { PositionSchema } from './PositionSchema';
 | 
			
		||||
export type CreatePositionRequest = {
 | 
			
		||||
    data: PositionSchema;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/CreatePositionResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/CreatePositionResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type CreatePositionResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -7,5 +7,6 @@ export type DealGeneralInfoSchema = {
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
    isCompleted: boolean;
 | 
			
		||||
    comment: string;
 | 
			
		||||
    shippingWarehouse?: (string | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,6 @@ export type DealSchema = {
 | 
			
		||||
    isCompleted: boolean;
 | 
			
		||||
    client: ClientSchema;
 | 
			
		||||
    comment: string;
 | 
			
		||||
    shippingWarehouse?: (ShippingWarehouseSchema | null);
 | 
			
		||||
    shippingWarehouse?: (ShippingWarehouseSchema | string | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/GetAllPositionsResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetAllPositionsResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { PositionSchema } from './PositionSchema';
 | 
			
		||||
export type GetAllPositionsResponse = {
 | 
			
		||||
    positions: Array<PositionSchema>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/GetAllRolesResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetAllRolesResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { RoleSchema } from './RoleSchema';
 | 
			
		||||
export type GetAllRolesResponse = {
 | 
			
		||||
    roles: Array<RoleSchema>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/GetAllUsersResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetAllUsersResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { UserSchema } from './UserSchema';
 | 
			
		||||
export type GetAllUsersResponse = {
 | 
			
		||||
    users: Array<UserSchema>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/PermissionSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/PermissionSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type PermissionSchema = {
 | 
			
		||||
    key: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/PositionSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/PositionSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type PositionSchema = {
 | 
			
		||||
    name: string;
 | 
			
		||||
    key: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								src/client/models/RoleSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/RoleSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { PermissionSchema } from './PermissionSchema';
 | 
			
		||||
export type RoleSchema = {
 | 
			
		||||
    key: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
    permissions: Array<PermissionSchema>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateUserRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateUserRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { UserUpdate } from './UserUpdate';
 | 
			
		||||
export type UpdateUserRequest = {
 | 
			
		||||
    data: UserUpdate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateUserResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateUserResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UpdateUserResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -2,10 +2,20 @@
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { PositionSchema } from './PositionSchema';
 | 
			
		||||
import type { RoleSchema } from './RoleSchema';
 | 
			
		||||
export type UserSchema = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    telegramId: number;
 | 
			
		||||
    phoneNumber?: (string | null);
 | 
			
		||||
    firstName: string;
 | 
			
		||||
    secondName: string;
 | 
			
		||||
    comment: string;
 | 
			
		||||
    isAdmin: boolean;
 | 
			
		||||
    isBlocked: boolean;
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
    roleKey: string;
 | 
			
		||||
    role: RoleSchema;
 | 
			
		||||
    position?: (PositionSchema | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								src/client/models/UserUpdate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/client/models/UserUpdate.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UserUpdate = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    telegramId: number;
 | 
			
		||||
    phoneNumber?: (string | null);
 | 
			
		||||
    firstName: string;
 | 
			
		||||
    secondName: string;
 | 
			
		||||
    comment: string;
 | 
			
		||||
    isAdmin: boolean;
 | 
			
		||||
    isBlocked: boolean;
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
    positionKey?: (string | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								src/client/services/PositionService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/client/services/PositionService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { CreatePositionRequest } from '../models/CreatePositionRequest';
 | 
			
		||||
import type { CreatePositionResponse } from '../models/CreatePositionResponse';
 | 
			
		||||
import type { GetAllPositionsResponse } from '../models/GetAllPositionsResponse';
 | 
			
		||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
			
		||||
import { OpenAPI } from '../core/OpenAPI';
 | 
			
		||||
import { request as __request } from '../core/request';
 | 
			
		||||
export class PositionService {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get All
 | 
			
		||||
     * @returns GetAllPositionsResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static getAllPositions(): CancelablePromise<GetAllPositionsResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: '/position/get-all',
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Create
 | 
			
		||||
     * @returns CreatePositionResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static createPosition({
 | 
			
		||||
        requestBody,
 | 
			
		||||
    }: {
 | 
			
		||||
        requestBody: CreatePositionRequest,
 | 
			
		||||
    }): CancelablePromise<CreatePositionResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/position/create',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/client/services/RoleService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/client/services/RoleService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { GetAllRolesResponse } from '../models/GetAllRolesResponse';
 | 
			
		||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
			
		||||
import { OpenAPI } from '../core/OpenAPI';
 | 
			
		||||
import { request as __request } from '../core/request';
 | 
			
		||||
export class RoleService {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get All
 | 
			
		||||
     * @returns GetAllRolesResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static getAllRoles(): CancelablePromise<GetAllRolesResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: '/role/get-all',
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/client/services/UserService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/client/services/UserService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do no edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { GetAllUsersResponse } from '../models/GetAllUsersResponse';
 | 
			
		||||
import type { UpdateUserRequest } from '../models/UpdateUserRequest';
 | 
			
		||||
import type { UpdateUserResponse } from '../models/UpdateUserResponse';
 | 
			
		||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
			
		||||
import { OpenAPI } from '../core/OpenAPI';
 | 
			
		||||
import { request as __request } from '../core/request';
 | 
			
		||||
export class UserService {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get All
 | 
			
		||||
     * @returns GetAllUsersResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static getAllUsers(): CancelablePromise<GetAllUsersResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: '/user/get-all',
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Update
 | 
			
		||||
     * @returns UpdateUserResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static updateUser({
 | 
			
		||||
        requestBody,
 | 
			
		||||
    }: {
 | 
			
		||||
        requestBody: UpdateUserRequest,
 | 
			
		||||
    }): CancelablePromise<UpdateUserResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/user/update',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								src/components/AnimatedOutlet/au.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/components/AnimatedOutlet/au.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import {forwardRef, RefObject, useContext, useRef} from "react";
 | 
			
		||||
import {getRouterContext, Outlet} from "@tanstack/react-router";
 | 
			
		||||
import {motion, useIsPresent} from "framer-motion";
 | 
			
		||||
import {cloneDeep} from "lodash";
 | 
			
		||||
 | 
			
		||||
const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
 | 
			
		||||
    const RouterContext = getRouterContext();
 | 
			
		||||
 | 
			
		||||
    const routerContext = useContext(RouterContext);
 | 
			
		||||
 | 
			
		||||
    const renderedContext = useRef(routerContext);
 | 
			
		||||
 | 
			
		||||
    const isPresent = useIsPresent();
 | 
			
		||||
 | 
			
		||||
    if (isPresent) {
 | 
			
		||||
        renderedContext.current = cloneDeep(routerContext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <motion.div ref={ref}
 | 
			
		||||
                    initial={{x: "-100%"}}
 | 
			
		||||
                    animate={{x: 0}}
 | 
			
		||||
                    transition={{duration: 0.3}}
 | 
			
		||||
                    onAnimationComplete={()=>{
 | 
			
		||||
                        (ref as RefObject<HTMLDivElement>).current?.style.removeProperty("transform")
 | 
			
		||||
                    }}
 | 
			
		||||
        >
 | 
			
		||||
            <RouterContext.Provider value={renderedContext.current}>
 | 
			
		||||
                <Outlet/>
 | 
			
		||||
            </RouterContext.Provider>
 | 
			
		||||
        </motion.div>
 | 
			
		||||
    );
 | 
			
		||||
});
 | 
			
		||||
export default AnimatedOutlet
 | 
			
		||||
@@ -26,7 +26,7 @@ export type BaseTableRef<T extends MRT_RowData> = {
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
			
		||||
// @ts-expect-error
 | 
			
		||||
export const BaseTable = forwardRef<BaseTableRef<never>, Props<never>>((props, ref) => {
 | 
			
		||||
    const {data, columns, restProps, striped, onSelectionChange} = props;
 | 
			
		||||
    const {data, columns, restProps, striped = true, onSelectionChange} = props;
 | 
			
		||||
 | 
			
		||||
    const table = useMantineReactTable({
 | 
			
		||||
        localization: MRT_Localization_RU,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorSchem
 | 
			
		||||
import {
 | 
			
		||||
    IconBarcode,
 | 
			
		||||
    IconBox,
 | 
			
		||||
    IconCash,
 | 
			
		||||
    IconCash, IconDashboard,
 | 
			
		||||
    IconFileBarcode,
 | 
			
		||||
    IconHome2,
 | 
			
		||||
    IconLogout,
 | 
			
		||||
@@ -76,7 +76,7 @@ export function Navbar() {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const navigate = useNavigate();
 | 
			
		||||
    const router = useRouterState();
 | 
			
		||||
    const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: false});
 | 
			
		||||
    const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: true});
 | 
			
		||||
    const onLogoutClick = () => {
 | 
			
		||||
        dispatch(logout());
 | 
			
		||||
        navigate({to: '/login'});
 | 
			
		||||
@@ -110,6 +110,12 @@ export function Navbar() {
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <Stack w={"100%"} justify="center" gap={0}>
 | 
			
		||||
                <NavbarLink icon={IconDashboard}
 | 
			
		||||
                            href={"/admin"}
 | 
			
		||||
                            index={-1}
 | 
			
		||||
                            label={"Панель администратора"}
 | 
			
		||||
                            onClick={() => onNavlinkClick({href: "/admin", index: -1, icon: IconDashboard})}
 | 
			
		||||
                />
 | 
			
		||||
                <NavbarLink label={"Сменить тему"} onClick={toggleColorScheme}
 | 
			
		||||
                            icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
 | 
			
		||||
                <NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,35 +1,55 @@
 | 
			
		||||
import {Select, SelectProps} from "@mantine/core";
 | 
			
		||||
import {useEffect, useMemo, useState} from "react";
 | 
			
		||||
import {ObjectWithNameAndId} from "../../types/utils.ts";
 | 
			
		||||
import {groupBy, omit} from "lodash";
 | 
			
		||||
 | 
			
		||||
interface ObjectWithIdAndName {
 | 
			
		||||
    id: number,
 | 
			
		||||
    name: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SelectObjectType<T extends ObjectWithNameAndId> = T;
 | 
			
		||||
export type SelectObjectType<T> = T;
 | 
			
		||||
 | 
			
		||||
type ControlledValueProps<T extends ObjectWithNameAndId> = {
 | 
			
		||||
type ControlledValueProps<T> = {
 | 
			
		||||
    value: SelectObjectType<T>,
 | 
			
		||||
    onChange: (value: SelectObjectType<T>) => void;
 | 
			
		||||
}
 | 
			
		||||
type CustomLabelAndKeyProps<T> = {
 | 
			
		||||
    getLabelFn: (item: SelectObjectType<T>) => string;
 | 
			
		||||
    getValueFn: (item: SelectObjectType<T>) => string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RestProps<T extends ObjectWithNameAndId> = {
 | 
			
		||||
type RestProps<T> = {
 | 
			
		||||
    defaultValue?: SelectObjectType<T>
 | 
			
		||||
    onChange: (value: SelectObjectType<T>) => void;
 | 
			
		||||
    data: SelectObjectType<T>[];
 | 
			
		||||
    groupBy?: (item: SelectObjectType<T>) => string;
 | 
			
		||||
    filterBy?: (item: SelectObjectType<T>) => boolean;
 | 
			
		||||
};
 | 
			
		||||
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
 | 
			
		||||
    return item.name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type ObjectSelectProps<T extends ObjectWithNameAndId> =
 | 
			
		||||
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
 | 
			
		||||
    return item.id.toString();
 | 
			
		||||
}
 | 
			
		||||
export type ObjectSelectProps<T> =
 | 
			
		||||
    (RestProps<T> & Partial<ControlledValueProps<T>>)
 | 
			
		||||
    & Omit<SelectProps, 'value' | 'onChange' | 'data'>;
 | 
			
		||||
    & Omit<SelectProps, 'value' | 'onChange' | 'data'>
 | 
			
		||||
    & (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>)
 | 
			
		||||
 | 
			
		||||
const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<T>) => {
 | 
			
		||||
const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
 | 
			
		||||
 | 
			
		||||
    const isControlled = 'value' in props;
 | 
			
		||||
    const haveGetValueFn = 'getValueFn' in props;
 | 
			
		||||
    const haveGetLabelFn = 'getLabelFn' in props;
 | 
			
		||||
    const [internalValue, setInternalValue] = useState<SelectObjectType<T> | undefined>(props.defaultValue);
 | 
			
		||||
 | 
			
		||||
    const value = isControlled ? props.value : internalValue;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
 | 
			
		||||
    const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
 | 
			
		||||
 | 
			
		||||
    const data = useMemo(() => {
 | 
			
		||||
        const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
 | 
			
		||||
        if (props.groupBy) {
 | 
			
		||||
@@ -38,21 +58,21 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
 | 
			
		||||
            return Object.entries(groupedData).map(([group, items]) => ({
 | 
			
		||||
                group,
 | 
			
		||||
                items: items.map(item => ({
 | 
			
		||||
                    label: item.name,
 | 
			
		||||
                    value: item.id.toString()
 | 
			
		||||
                    label: getLabelFn(item),
 | 
			
		||||
                    value: getValueFn(item)
 | 
			
		||||
                }))
 | 
			
		||||
            }));
 | 
			
		||||
        } else {
 | 
			
		||||
            return propsData.map(item => ({
 | 
			
		||||
                label: item.name,
 | 
			
		||||
                value: item.id.toString()
 | 
			
		||||
                label: getLabelFn(item),
 | 
			
		||||
                value: getValueFn(item)
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    }, [props.data, props.groupBy]);
 | 
			
		||||
 | 
			
		||||
    const handleOnChange = (event: string | null) => {
 | 
			
		||||
        if (!event) return;
 | 
			
		||||
        const object = props.data.find(item => parseInt(event) == item.id);
 | 
			
		||||
        const object = props.data.find(item => event == getValueFn(item));
 | 
			
		||||
        if (!object) return;
 | 
			
		||||
        if (isControlled) {
 | 
			
		||||
            props.onChange(object);
 | 
			
		||||
@@ -65,11 +85,11 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
 | 
			
		||||
        if (isControlled || !internalValue) return;
 | 
			
		||||
        props.onChange(internalValue);
 | 
			
		||||
    }, [internalValue]);
 | 
			
		||||
    const restProps = omit(props, ['filterBy', 'groupBy']);
 | 
			
		||||
    const restProps = omit(props, ['filterBy', 'groupBy', 'getValueFn', 'getLabelFn']);
 | 
			
		||||
    return (
 | 
			
		||||
        <Select
 | 
			
		||||
            {...restProps}
 | 
			
		||||
            value={value?.id.toString()}
 | 
			
		||||
            value={value && getValueFn(value)}
 | 
			
		||||
            onChange={handleOnChange}
 | 
			
		||||
            data={data}
 | 
			
		||||
        />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								src/hooks/objectList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/hooks/objectList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import {QueryObserverResult, RefetchOptions, useQuery} from "@tanstack/react-query";
 | 
			
		||||
import {CancelablePromise} from "../client";
 | 
			
		||||
 | 
			
		||||
type Props<T, K> = {
 | 
			
		||||
    queryFn: () => CancelablePromise<T>,
 | 
			
		||||
    getObjectsFn: (response: T) => K[],
 | 
			
		||||
    queryKey: string
 | 
			
		||||
}
 | 
			
		||||
type Response<T, K> = {
 | 
			
		||||
    objects: K[],
 | 
			
		||||
    refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<T, Error>>
 | 
			
		||||
}
 | 
			
		||||
const ObjectList = <T, K, >(props: Props<T, K>): Response<T, K> => {
 | 
			
		||||
    const {isPending, error, data, refetch} = useQuery({
 | 
			
		||||
        queryKey: [props.queryKey],
 | 
			
		||||
        queryFn: props.queryFn
 | 
			
		||||
    });
 | 
			
		||||
    const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
 | 
			
		||||
    return {objects, refetch}
 | 
			
		||||
}
 | 
			
		||||
export default ObjectList
 | 
			
		||||
@@ -10,6 +10,7 @@ import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
			
		||||
import BarcodeTemplateFormModal
 | 
			
		||||
    from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
			
		||||
import ProductServiceFormModal from "../pages/LeadsPage/modals/ProductServiceFormModal.tsx";
 | 
			
		||||
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
			
		||||
 | 
			
		||||
export const modals = {
 | 
			
		||||
    enterDeadline: EnterDeadlineModal,
 | 
			
		||||
@@ -22,5 +23,6 @@ export const modals = {
 | 
			
		||||
    productServiceForm: ProductServiceFormModal,
 | 
			
		||||
    printBarcode: PrintBarcodeModal,
 | 
			
		||||
    addBarcode: AddBarcodeModal,
 | 
			
		||||
    barcodeTemplateFormModal: BarcodeTemplateFormModal
 | 
			
		||||
    barcodeTemplateFormModal: BarcodeTemplateFormModal,
 | 
			
		||||
    userFormModal: UserFormModal
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								src/pages/AdminPage/AdminPage.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/pages/AdminPage/AdminPage.module.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
.container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								src/pages/AdminPage/AdminPage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/pages/AdminPage/AdminPage.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
import styles from './AdminPage.module.css';
 | 
			
		||||
import {Tabs} from "@mantine/core";
 | 
			
		||||
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
 | 
			
		||||
import {IconBriefcase, IconUser, IconUsersGroup} from "@tabler/icons-react";
 | 
			
		||||
import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
 | 
			
		||||
import UsersTab from "./tabs/Users/UsersTab.tsx";
 | 
			
		||||
import {motion} from "framer-motion";
 | 
			
		||||
 | 
			
		||||
const AdminPage = () => {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className={styles['container']}>
 | 
			
		||||
            <PageBlock fullHeight>
 | 
			
		||||
                <Tabs variant={"outline"} keepMounted={false} defaultValue={"users"}>
 | 
			
		||||
                    <Tabs.List>
 | 
			
		||||
                        <Tabs.Tab value={"users"} leftSection={<IconUser/>}>
 | 
			
		||||
                            Пользователи
 | 
			
		||||
                        </Tabs.Tab>
 | 
			
		||||
                        <Tabs.Tab value={"rolesAndPositions"} leftSection={<IconBriefcase/>}>
 | 
			
		||||
                            Роли и должности
 | 
			
		||||
                        </Tabs.Tab>
 | 
			
		||||
                        <Tabs.Tab value={"employees"} leftSection={<IconUsersGroup/>}>
 | 
			
		||||
                            Сотрудники
 | 
			
		||||
                        </Tabs.Tab>
 | 
			
		||||
                    </Tabs.List>
 | 
			
		||||
                    <Tabs.Panel value={"users"}>
 | 
			
		||||
                        <motion.div
 | 
			
		||||
 | 
			
		||||
                            initial={{scaleY: 0}}
 | 
			
		||||
                            animate={{scaleY: 1}}
 | 
			
		||||
                            transition={{duration: 0.1}}>
 | 
			
		||||
                            <UsersTab/>
 | 
			
		||||
 | 
			
		||||
                        </motion.div>
 | 
			
		||||
                    </Tabs.Panel>
 | 
			
		||||
                    <Tabs.Panel value={"rolesAndPositions"}>
 | 
			
		||||
                        <motion.div
 | 
			
		||||
                            initial={{scaleY: 0}}
 | 
			
		||||
                            animate={{scaleY: 1}}
 | 
			
		||||
                            transition={{duration: 0.1}}
 | 
			
		||||
                        >
 | 
			
		||||
                            <RolesAndPositionsTab/>
 | 
			
		||||
                        </motion.div>
 | 
			
		||||
                    </Tabs.Panel>
 | 
			
		||||
 | 
			
		||||
                </Tabs>
 | 
			
		||||
            </PageBlock>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default AdminPage;
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
import ObjectSelect, {ObjectSelectProps} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
			
		||||
import {PositionSchema} from "../../../../client";
 | 
			
		||||
import {FC} from "react";
 | 
			
		||||
import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = Omit<ObjectSelectProps<PositionSchema>, 'data' | 'getLabelFn' | 'getValueFn'>;
 | 
			
		||||
 | 
			
		||||
const PositionSelect: FC<Props> = (props) => {
 | 
			
		||||
    const {objects: positions} = usePositionsList();
 | 
			
		||||
    return (
 | 
			
		||||
        <ObjectSelect
 | 
			
		||||
            getLabelFn={(position) => position.name}
 | 
			
		||||
            getValueFn={(position) => position.key}
 | 
			
		||||
            data={positions}
 | 
			
		||||
            {...props}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
export default PositionSelect;
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
			
		||||
import {PositionSchema} from "../../../../client";
 | 
			
		||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import {usePositionsTableColumns} from "./columns.tsx";
 | 
			
		||||
import {FC} from "react";
 | 
			
		||||
import {Button, Flex, rem} from "@mantine/core";
 | 
			
		||||
 | 
			
		||||
type Props = CRUDTableProps<PositionSchema>;
 | 
			
		||||
 | 
			
		||||
const PositionsTable: FC<Props> = ({items}) => {
 | 
			
		||||
    const columns = usePositionsTableColumns();
 | 
			
		||||
    return (
 | 
			
		||||
 | 
			
		||||
        <BaseTable
 | 
			
		||||
            restProps={{
 | 
			
		||||
                enableTopToolbar: true,
 | 
			
		||||
                enableSorting: false,
 | 
			
		||||
                enableColumnActions: false,
 | 
			
		||||
                renderTopToolbar: () => (
 | 
			
		||||
                    <Flex p={rem(10)}>
 | 
			
		||||
 | 
			
		||||
                        <Button
 | 
			
		||||
                            variant={"default"}
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                            Создать должность
 | 
			
		||||
                        </Button>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
 | 
			
		||||
                )
 | 
			
		||||
            }}
 | 
			
		||||
            data={items}
 | 
			
		||||
            columns={columns}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default PositionsTable;
 | 
			
		||||
							
								
								
									
										16
									
								
								src/pages/AdminPage/components/PositionsTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/pages/AdminPage/components/PositionsTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import {useMemo} from "react";
 | 
			
		||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
			
		||||
import {PositionSchema} from "../../../../client";
 | 
			
		||||
 | 
			
		||||
export const usePositionsTableColumns = () => {
 | 
			
		||||
    return useMemo<MRT_ColumnDef<PositionSchema>[]>(() => [
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "key",
 | 
			
		||||
            header: "Ключ"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "name",
 | 
			
		||||
            header: "Название должности"
 | 
			
		||||
        }
 | 
			
		||||
    ], []);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/pages/AdminPage/components/RoleSelect/RoleSelect.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/pages/AdminPage/components/RoleSelect/RoleSelect.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
import ObjectSelect, {ObjectSelectProps} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
			
		||||
import {RoleSchema} from "../../../../client";
 | 
			
		||||
import {FC} from "react";
 | 
			
		||||
import useRolesList from "../../hooks/useRolesList.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = Omit<ObjectSelectProps<RoleSchema>, 'data' | 'getLabelFn' | 'getValueFn'>;
 | 
			
		||||
 | 
			
		||||
const RolesSelect: FC<Props> = (props) => {
 | 
			
		||||
    const {objects: roles} = useRolesList();
 | 
			
		||||
    return (
 | 
			
		||||
        <ObjectSelect
 | 
			
		||||
            getLabelFn={(position) => position.name}
 | 
			
		||||
            getValueFn={(position) => position.key}
 | 
			
		||||
            data={roles}
 | 
			
		||||
            {...props}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
export default RolesSelect;
 | 
			
		||||
							
								
								
									
										81
									
								
								src/pages/AdminPage/components/UsersTable/UsersTable.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/pages/AdminPage/components/UsersTable/UsersTable.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
			
		||||
import {UserSchema} from "../../../../client";
 | 
			
		||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import {FC} from "react";
 | 
			
		||||
import {ActionIcon, Flex, Text, Tooltip} from "@mantine/core";
 | 
			
		||||
import {useUsersTableColumns} from "./columns.tsx";
 | 
			
		||||
import {IconEdit, IconTrash} from "@tabler/icons-react";
 | 
			
		||||
import {modals} from "@mantine/modals";
 | 
			
		||||
import {MRT_TableOptions} from "mantine-react-table";
 | 
			
		||||
 | 
			
		||||
type Props = CRUDTableProps<UserSchema>;
 | 
			
		||||
 | 
			
		||||
const UsersTable: FC<Props> = ({items, onChange, onDelete}) => {
 | 
			
		||||
    const columns = useUsersTableColumns();
 | 
			
		||||
    const onEditClick = (user: UserSchema) => {
 | 
			
		||||
        if (!onChange) return;
 | 
			
		||||
        modals.openContextModal({
 | 
			
		||||
            modal: "userFormModal",
 | 
			
		||||
            title: 'Редактирование пользователя',
 | 
			
		||||
            withCloseButton: false,
 | 
			
		||||
            innerProps: {
 | 
			
		||||
                onChange: onChange,
 | 
			
		||||
                element: user,
 | 
			
		||||
            },
 | 
			
		||||
            size: "md"
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const onDeleteClick = (user: UserSchema) => {
 | 
			
		||||
        if (!onDelete) return;
 | 
			
		||||
        modals.openConfirmModal({
 | 
			
		||||
            title: 'Удаление пользователя',
 | 
			
		||||
            centered: true,
 | 
			
		||||
            children: (
 | 
			
		||||
                <Text size="sm">
 | 
			
		||||
                    Вы уверены что хотите удалить пользователя {user.firstName} {user.secondName}
 | 
			
		||||
                </Text>
 | 
			
		||||
            ),
 | 
			
		||||
            labels: {confirm: 'Да', cancel: "Нет"},
 | 
			
		||||
            confirmProps: {color: 'red'},
 | 
			
		||||
            onConfirm: () => onDelete(user)
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <BaseTable
 | 
			
		||||
            data={items}
 | 
			
		||||
            columns={columns}
 | 
			
		||||
            restProps={{
 | 
			
		||||
                enableTopToolbar: false,
 | 
			
		||||
                enableSorting: false,
 | 
			
		||||
                enableColumnActions: false,
 | 
			
		||||
                enableRowActions: true,
 | 
			
		||||
                renderRowActions: ({row}) => (
 | 
			
		||||
                    <Flex gap="md">
 | 
			
		||||
                        <Tooltip onClick={() => {
 | 
			
		||||
                            onDeleteClick(row.original);
 | 
			
		||||
                        }} label="Удалить">
 | 
			
		||||
                            <ActionIcon variant={"default"}>
 | 
			
		||||
                                <IconTrash/>
 | 
			
		||||
                            </ActionIcon>
 | 
			
		||||
                        </Tooltip>
 | 
			
		||||
                        <Tooltip
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
                                onEditClick(row.original)
 | 
			
		||||
                            }}
 | 
			
		||||
                            label="Редактировать">
 | 
			
		||||
                            <ActionIcon
 | 
			
		||||
                                variant={"default"}>
 | 
			
		||||
                                <IconEdit/>
 | 
			
		||||
                            </ActionIcon>
 | 
			
		||||
                        </Tooltip>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                ),
 | 
			
		||||
            } as MRT_TableOptions<UserSchema>}
 | 
			
		||||
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default UsersTable;
 | 
			
		||||
							
								
								
									
										40
									
								
								src/pages/AdminPage/components/UsersTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/AdminPage/components/UsersTable/columns.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import {useMemo} from "react";
 | 
			
		||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
			
		||||
import {UserSchema} from "../../../../client";
 | 
			
		||||
import {IconCheck, IconX} from "@tabler/icons-react";
 | 
			
		||||
 | 
			
		||||
export const useUsersTableColumns = () => {
 | 
			
		||||
    return useMemo<MRT_ColumnDef<UserSchema>[]>(() => [
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "id",
 | 
			
		||||
            header: "ID"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "telegramId",
 | 
			
		||||
            header: "ID Телеграм"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "phoneNumber",
 | 
			
		||||
            header: "Номер телефона"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "role.name",
 | 
			
		||||
            header: "Роль"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "comment",
 | 
			
		||||
            header: "Дополнительная информация"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "isAdmin",
 | 
			
		||||
            header: "Администратор",
 | 
			
		||||
            Cell: ({row}) => row.original.isAdmin ? <IconCheck/> : <IconX/>
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            accessorKey: "isBlocked",
 | 
			
		||||
            header: "Заблокирован",
 | 
			
		||||
            Cell: ({row}) => row.original.isBlocked ? <IconCheck/> : <IconX/>
 | 
			
		||||
        },
 | 
			
		||||
    ], []);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/pages/AdminPage/hooks/usePositionsList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/pages/AdminPage/hooks/usePositionsList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import {PositionService} from "../../../client";
 | 
			
		||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
			
		||||
 | 
			
		||||
const usePositionsList = () => ObjectList({
 | 
			
		||||
    queryFn: PositionService.getAllPositions,
 | 
			
		||||
    getObjectsFn: response => response.positions,
 | 
			
		||||
    queryKey: "getAllPositions"
 | 
			
		||||
})
 | 
			
		||||
export default usePositionsList;
 | 
			
		||||
							
								
								
									
										9
									
								
								src/pages/AdminPage/hooks/useRolesList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/pages/AdminPage/hooks/useRolesList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
			
		||||
import {RoleService} from "../../../client";
 | 
			
		||||
 | 
			
		||||
const useRolesList = () => ObjectList({
 | 
			
		||||
    queryFn: RoleService.getAllRoles,
 | 
			
		||||
    getObjectsFn: response => response.roles,
 | 
			
		||||
    queryKey: "getAllRoles"
 | 
			
		||||
})
 | 
			
		||||
export default useRolesList;
 | 
			
		||||
							
								
								
									
										11
									
								
								src/pages/AdminPage/hooks/useUsersList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/pages/AdminPage/hooks/useUsersList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
			
		||||
import {UserService} from "../../../client";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const useUsersList = () => ObjectList({
 | 
			
		||||
    queryFn: UserService.getAllUsers,
 | 
			
		||||
    getObjectsFn: (response) => response.users,
 | 
			
		||||
    queryKey: "getAllUsers"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default useUsersList;
 | 
			
		||||
							
								
								
									
										108
									
								
								src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
import {ContextModalProps} from "@mantine/modals";
 | 
			
		||||
import BaseFormModal, {EditProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import {UserSchema} from "../../../../client";
 | 
			
		||||
import {useForm} from "@mantine/form";
 | 
			
		||||
import {Checkbox, Fieldset, Input, Stack, Textarea, TextInput} from "@mantine/core";
 | 
			
		||||
import RoleSelect from "../../components/RoleSelect/RoleSelect.tsx";
 | 
			
		||||
import PositionSelect from "../../components/PositionSelect/PositionSelect.tsx";
 | 
			
		||||
import {UserRoleEnum} from "../../../../shared/enums/UserRole.ts";
 | 
			
		||||
import {capitalize} from "lodash";
 | 
			
		||||
import {IMaskInput} from "react-imask";
 | 
			
		||||
import phone from "phone";
 | 
			
		||||
 | 
			
		||||
type Props = EditProps<UserSchema>;
 | 
			
		||||
const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
 | 
			
		||||
    const initialValues = innerProps.element;
 | 
			
		||||
 | 
			
		||||
    const form = useForm<UserSchema>({
 | 
			
		||||
        initialValues: initialValues,
 | 
			
		||||
        validate: {
 | 
			
		||||
            firstName: value => !value.trim() && "Укажите имя пользователя",
 | 
			
		||||
            secondName: value => !value.trim() && "Укажите фамилию",
 | 
			
		||||
            position: (value, values) => ((values.role.key === UserRoleEnum.EMPLOYEE) && (!value)) && 'Необходимо указать должность сотрудника',
 | 
			
		||||
            phoneNumber: value => !phone(value || '', {
 | 
			
		||||
                country: "",
 | 
			
		||||
                strictDetection: false,
 | 
			
		||||
                validateMobilePrefix: false
 | 
			
		||||
            }).isValid && 'Неверно указан номер телефона',
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    console.log(form.getInputProps('isAdmin'))
 | 
			
		||||
    return (<BaseFormModal
 | 
			
		||||
            form={form}
 | 
			
		||||
            closeOnSubmit
 | 
			
		||||
            onClose={() => context.closeContextModal(id)}
 | 
			
		||||
            {...innerProps}
 | 
			
		||||
        >
 | 
			
		||||
            <BaseFormModal.Body>
 | 
			
		||||
                <>
 | 
			
		||||
                    <Fieldset legend={"Общая информация"}>
 | 
			
		||||
                        <Stack>
 | 
			
		||||
                            <TextInput
 | 
			
		||||
                                label={"Имя"}
 | 
			
		||||
                                placeholder={"Введите имя пользователя"}
 | 
			
		||||
                                {...form.getInputProps("firstName")}
 | 
			
		||||
                                onChange={event => form.getInputProps('firstName').onChange(capitalize(event.target.value).trim())}
 | 
			
		||||
 | 
			
		||||
                            />
 | 
			
		||||
                            <TextInput
 | 
			
		||||
                                {...form.getInputProps("secondName")}
 | 
			
		||||
                                label={"Фамилия"}
 | 
			
		||||
                                placeholder={"Введите фамилию пользователя"}
 | 
			
		||||
                                onChange={event => form.getInputProps('secondName').onChange(capitalize(event.target.value).trim())}
 | 
			
		||||
                            />
 | 
			
		||||
                            <Input.Wrapper
 | 
			
		||||
                                label={"Номер телефона"}
 | 
			
		||||
                                error={form.getInputProps("phoneNumber").error}
 | 
			
		||||
                            >
 | 
			
		||||
                                <Input
 | 
			
		||||
                                    component={IMaskInput}
 | 
			
		||||
                                    mask="+7 000 000-00-00"
 | 
			
		||||
                                    placeholder={"Введите номер телефона"}
 | 
			
		||||
                                    {...form.getInputProps("phoneNumber")}
 | 
			
		||||
                                />
 | 
			
		||||
                            </Input.Wrapper>
 | 
			
		||||
                        </Stack>
 | 
			
		||||
                    </Fieldset>
 | 
			
		||||
                    <Fieldset legend={"Роль и должность"}>
 | 
			
		||||
                        <Stack>
 | 
			
		||||
 | 
			
		||||
                            <RoleSelect
 | 
			
		||||
                                label={"Роль пользователя"}
 | 
			
		||||
                                placeholder={"Выберите роль пользователя"}
 | 
			
		||||
                                {...form.getInputProps('role')}
 | 
			
		||||
                            />
 | 
			
		||||
                            {form.values.role.key === UserRoleEnum.EMPLOYEE &&
 | 
			
		||||
                                <PositionSelect
 | 
			
		||||
                                    label={"Должность сотрудника"}
 | 
			
		||||
                                    placeholder={"Выберите должность сотрудника"}
 | 
			
		||||
                                    {...form.getInputProps('position')}
 | 
			
		||||
                                />
 | 
			
		||||
                            }
 | 
			
		||||
                        </Stack>
 | 
			
		||||
 | 
			
		||||
                    </Fieldset>
 | 
			
		||||
                    <Fieldset legend={"Дополнительные параметры"}>
 | 
			
		||||
                        <Stack>
 | 
			
		||||
                            <Checkbox
 | 
			
		||||
                                label={"Права администратора"}
 | 
			
		||||
 | 
			
		||||
                                {...form.getInputProps('isAdmin', {type: "checkbox"})}
 | 
			
		||||
                            />
 | 
			
		||||
                            <Checkbox
 | 
			
		||||
                                label={"Заблокирован"}
 | 
			
		||||
                                {...form.getInputProps('isBlocked', {type: "checkbox"})}
 | 
			
		||||
                            />
 | 
			
		||||
                            <Textarea
 | 
			
		||||
                                label={"Дополнительная информация"}
 | 
			
		||||
                                {...form.getInputProps('comment')}
 | 
			
		||||
                            />
 | 
			
		||||
                        </Stack>
 | 
			
		||||
                    </Fieldset>
 | 
			
		||||
                </>
 | 
			
		||||
            </BaseFormModal.Body>
 | 
			
		||||
        </BaseFormModal>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default UserFormModal;
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
import {Tabs} from "@mantine/core";
 | 
			
		||||
import PositionsTable from "../../components/PositionsTable/PositionsTable.tsx";
 | 
			
		||||
import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
			
		||||
import {motion} from "framer-motion";
 | 
			
		||||
 | 
			
		||||
const RolesAndPositionsTab = () => {
 | 
			
		||||
    const {objects: positions} = usePositionsList();
 | 
			
		||||
    return (
 | 
			
		||||
        <Tabs w={"100%"}
 | 
			
		||||
              variant={"default"}
 | 
			
		||||
              keepMounted={false}
 | 
			
		||||
              defaultValue={"roles"}
 | 
			
		||||
        >
 | 
			
		||||
            <Tabs.List grow>
 | 
			
		||||
                <Tabs.Tab value={"roles"}>Роли</Tabs.Tab>
 | 
			
		||||
                <Tabs.Tab value={"positions"}>Должности</Tabs.Tab>
 | 
			
		||||
            </Tabs.List>
 | 
			
		||||
            <Tabs.Panel value={"roles"}>
 | 
			
		||||
                <motion.div
 | 
			
		||||
                    initial={{scaleY: 0}}
 | 
			
		||||
                    animate={{scaleY: 1}}
 | 
			
		||||
                    transition={{duration: 0.1}}
 | 
			
		||||
                >
 | 
			
		||||
                    <PositionsTable items={positions}/>
 | 
			
		||||
                </motion.div>
 | 
			
		||||
            </Tabs.Panel>
 | 
			
		||||
            <Tabs.Panel value={"positions"}>
 | 
			
		||||
                <motion.div
 | 
			
		||||
                    initial={{scaleY: 0}}
 | 
			
		||||
                    animate={{scaleY: 1}}
 | 
			
		||||
                    transition={{duration: 0.1}}
 | 
			
		||||
                >
 | 
			
		||||
                    <PositionsTable items={positions}/>
 | 
			
		||||
                </motion.div>
 | 
			
		||||
            </Tabs.Panel>
 | 
			
		||||
        </Tabs>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
export default RolesAndPositionsTab;
 | 
			
		||||
							
								
								
									
										35
									
								
								src/pages/AdminPage/tabs/Users/UsersTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/pages/AdminPage/tabs/Users/UsersTab.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import useUsersList from "../../hooks/useUsersList.tsx";
 | 
			
		||||
import UsersTable from "../../components/UsersTable/UsersTable.tsx";
 | 
			
		||||
import {UserSchema, UserService} from "../../../../client";
 | 
			
		||||
import {notifications} from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
 | 
			
		||||
const UsersTab = () => {
 | 
			
		||||
    const {objects: users, refetch} = useUsersList();
 | 
			
		||||
 | 
			
		||||
    const onChange = (user: UserSchema) => {
 | 
			
		||||
        UserService.updateUser({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                data: {
 | 
			
		||||
                    ...user,
 | 
			
		||||
                    positionKey: user.position?.key,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }).then(async ({ok, message}) => {
 | 
			
		||||
            notifications.guess(ok, {message});
 | 
			
		||||
            if (!ok) return;
 | 
			
		||||
            await refetch();
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    const onDelete = async (user: UserSchema) => {
 | 
			
		||||
        onChange({...user, isDeleted: true});
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
        <UsersTable
 | 
			
		||||
            items={users}
 | 
			
		||||
            onChange={onChange}
 | 
			
		||||
            onDelete={onDelete}
 | 
			
		||||
        />
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default UsersTab;
 | 
			
		||||
@@ -2,23 +2,22 @@ import {UseFormReturnType} from "@mantine/form";
 | 
			
		||||
import {Button, Flex, rem} from "@mantine/core";
 | 
			
		||||
import {FC} from "react";
 | 
			
		||||
 | 
			
		||||
type CreateProps<T> = {
 | 
			
		||||
export type CreateProps<T> = {
 | 
			
		||||
    onCreate(values: T): void;
 | 
			
		||||
}
 | 
			
		||||
type EditProps<T> = {
 | 
			
		||||
export type EditProps<T> = {
 | 
			
		||||
    onChange(values: T): void;
 | 
			
		||||
    element: T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CreateEditFormProps<T> = CreateProps<T> | EditProps<T>;
 | 
			
		||||
 | 
			
		||||
type BaseProps<T> = {
 | 
			
		||||
export type BaseFormProps<T> = {
 | 
			
		||||
    form: UseFormReturnType<T>
 | 
			
		||||
    onClose: () => void;
 | 
			
		||||
    closeOnSubmit?: boolean;
 | 
			
		||||
    children: React.JSX.Element;
 | 
			
		||||
}
 | 
			
		||||
type Props<T> = BaseProps<T> & (CreateProps<T> | EditProps<T>);
 | 
			
		||||
type Props<T> = BaseFormProps<T> & (CreateProps<T> | EditProps<T>);
 | 
			
		||||
 | 
			
		||||
const BaseFormModal = <T, >(props: Props<T>) => {
 | 
			
		||||
    const {closeOnSubmit = false} = props;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,18 @@ import {FC} from "react";
 | 
			
		||||
import {useDealPageContext} from "../../../contexts/DealPageContext.tsx";
 | 
			
		||||
import {Button, Checkbox, Divider, Fieldset, Flex, Group, rem, Textarea, TextInput} from "@mantine/core";
 | 
			
		||||
import {useForm} from "@mantine/form";
 | 
			
		||||
import {ClientService, DealSchema, DealService} from "../../../../../client";
 | 
			
		||||
import {ClientService, DealSchema, DealService, ShippingWarehouseSchema} 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";
 | 
			
		||||
import ShippingWarehouseAutocomplete
 | 
			
		||||
    from "../../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    deal: DealSchema
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FormType = Omit<DealSchema, 'statusHistory' | 'services' | 'products'>
 | 
			
		||||
 | 
			
		||||
const Content: FC<Props> = ({deal}) => {
 | 
			
		||||
@@ -30,7 +33,7 @@ const Content: FC<Props> = ({deal}) => {
 | 
			
		||||
        return DealService.updateDealGeneralInfo({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                dealId: deal.id,
 | 
			
		||||
                data: values
 | 
			
		||||
                data: {...values, shippingWarehouse: values.shippingWarehouse?.toString()}
 | 
			
		||||
            }
 | 
			
		||||
        }).then(({ok, message}) => {
 | 
			
		||||
            notifications.guess(ok, {message});
 | 
			
		||||
@@ -61,6 +64,10 @@ const Content: FC<Props> = ({deal}) => {
 | 
			
		||||
        // updating deal info
 | 
			
		||||
        await updateDealInfo(values);
 | 
			
		||||
    }
 | 
			
		||||
    const isShippingWarehouse = (value: (ShippingWarehouseSchema | string | null | undefined)): value is ShippingWarehouseSchema => {
 | 
			
		||||
        return !["string", "null", "undefined"].includes((typeof value));
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
        <form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
 | 
			
		||||
            <Flex direction={'column'}>
 | 
			
		||||
@@ -87,11 +94,17 @@ const Content: FC<Props> = ({deal}) => {
 | 
			
		||||
                            placeholder={'Введите коментарий к сделке'}
 | 
			
		||||
                            {...form.getInputProps('comment')}
 | 
			
		||||
                        />
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            disabled
 | 
			
		||||
                        <ShippingWarehouseAutocomplete
 | 
			
		||||
                            placeholder={"Введите склад отгрузки"}
 | 
			
		||||
                            label={"Склад отгрузки"}
 | 
			
		||||
                            value={form.values.shippingWarehouse?.name}
 | 
			
		||||
                            value={isShippingWarehouse(form.values.shippingWarehouse) ? form.values.shippingWarehouse : undefined}
 | 
			
		||||
                            onChange={event => {
 | 
			
		||||
                                if (isShippingWarehouse(event)) {
 | 
			
		||||
                                    form.getInputProps('shippingWarehouse').onChange(event.name)
 | 
			
		||||
                                    return
 | 
			
		||||
                                }
 | 
			
		||||
                                form.getInputProps('shippingWarehouse').onChange(event)
 | 
			
		||||
                            }}
 | 
			
		||||
                        />
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                </Fieldset>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,17 +5,27 @@ import {AuthService} from "../../client";
 | 
			
		||||
import TelegramLoginButton, {TelegramUser} from "../../components/TelegramAuthButton/TelegramAuthButton.tsx";
 | 
			
		||||
import {notifications} from "../../shared/lib/notifications.ts";
 | 
			
		||||
import {login} from "../../features/authSlice.ts";
 | 
			
		||||
import {useNavigate} from "@tanstack/react-router";
 | 
			
		||||
import {Navigate, useNavigate} from "@tanstack/react-router";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {useEffect} from "react";
 | 
			
		||||
 | 
			
		||||
const LoginPage = () => {
 | 
			
		||||
    const dispatch = useAppDispatch();
 | 
			
		||||
    const authState = useSelector((state: RootState) => state.auth);
 | 
			
		||||
    const navigate = useNavigate();
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (authState.isAuthorized)
 | 
			
		||||
            // ???????????
 | 
			
		||||
            navigate({to: "/leads"}).then(() => {
 | 
			
		||||
                navigate({to: "/leads"}).then(() => {
 | 
			
		||||
                    notifications.success({message: "Вы успешно вошли!"})
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
    }, [authState.isAuthorized])
 | 
			
		||||
    if (authState.isAuthorized) {
 | 
			
		||||
        navigate({to: "/leads"})
 | 
			
		||||
        return (<></>)
 | 
			
		||||
        return (<Navigate to={"/leads"}/>)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
 | 
			
		||||
        <Container size={420} my={40}>
 | 
			
		||||
@@ -41,9 +51,6 @@ const LoginPage = () => {
 | 
			
		||||
                            AuthService.loginAuthLoginPost({requestBody: data})
 | 
			
		||||
                                .then(({accessToken}) => {
 | 
			
		||||
                                    dispatch(login({accessToken: accessToken}));
 | 
			
		||||
                                    navigate({to: "/"}).then(() => {
 | 
			
		||||
                                        notifications.success({message: "Вы успешно вошли!"})
 | 
			
		||||
                                    })
 | 
			
		||||
                                }).catch(() => {
 | 
			
		||||
                                notifications.error({message: "Неудалось войти!"})
 | 
			
		||||
                            })
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,19 @@
 | 
			
		||||
import {Outlet} from "@tanstack/react-router";
 | 
			
		||||
import {useMatch, useMatches} from "@tanstack/react-router";
 | 
			
		||||
import {useEffect} from "react";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
import {RootState} from "../../redux/store.ts";
 | 
			
		||||
import {OpenAPI} from "../../client";
 | 
			
		||||
import PageWrapper from "../PageWrapper/PageWrapper.tsx";
 | 
			
		||||
import {LoadingOverlay} from "@mantine/core";
 | 
			
		||||
import {AnimatePresence} from "framer-motion";
 | 
			
		||||
import AnimatedOutlet from "../../components/AnimatedOutlet/au.tsx";
 | 
			
		||||
 | 
			
		||||
const RootPage = () => {
 | 
			
		||||
    const matches = useMatches();
 | 
			
		||||
    const match = useMatch({strict: false});
 | 
			
		||||
    const nextMatchIndex = matches.findIndex((d) => d.id === match.id) + 1;
 | 
			
		||||
    const nextMatch = matches[nextMatchIndex];
 | 
			
		||||
 | 
			
		||||
    const authState = useSelector((state: RootState) => state.auth);
 | 
			
		||||
    const uiState = useSelector((state: RootState) => state.ui);
 | 
			
		||||
    const rewriteLocalStorage = () => {
 | 
			
		||||
@@ -20,12 +27,13 @@ const RootPage = () => {
 | 
			
		||||
        rewriteLocalStorage();
 | 
			
		||||
        setOpenApiToken();
 | 
			
		||||
    }, [authState]);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <LoadingOverlay visible={uiState.isLoading}/>
 | 
			
		||||
            <PageWrapper>
 | 
			
		||||
                <Outlet/>
 | 
			
		||||
                <AnimatePresence mode="popLayout">
 | 
			
		||||
                    <AnimatedOutlet key={nextMatch.id}/>
 | 
			
		||||
                </AnimatePresence>
 | 
			
		||||
            </PageWrapper>
 | 
			
		||||
 | 
			
		||||
        </>
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,9 @@ export const ServicesPage: FC = () => {
 | 
			
		||||
                await refetch();
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className={styles['container']}>
 | 
			
		||||
            <PageBlock>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ const LoginLazyImport = createFileRoute('/login')()
 | 
			
		||||
const LeadsLazyImport = createFileRoute('/leads')()
 | 
			
		||||
const ClientsLazyImport = createFileRoute('/clients')()
 | 
			
		||||
const BarcodeLazyImport = createFileRoute('/barcode')()
 | 
			
		||||
const AdminLazyImport = createFileRoute('/admin')()
 | 
			
		||||
const IndexLazyImport = createFileRoute('/')()
 | 
			
		||||
 | 
			
		||||
// Create/Update Routes
 | 
			
		||||
@@ -62,6 +63,11 @@ const BarcodeLazyRoute = BarcodeLazyImport.update({
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/barcode.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const AdminLazyRoute = AdminLazyImport.update({
 | 
			
		||||
  path: '/admin',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/admin.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const IndexLazyRoute = IndexLazyImport.update({
 | 
			
		||||
  path: '/',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
@@ -72,34 +78,65 @@ const IndexLazyRoute = IndexLazyImport.update({
 | 
			
		||||
declare module '@tanstack/react-router' {
 | 
			
		||||
  interface FileRoutesByPath {
 | 
			
		||||
    '/': {
 | 
			
		||||
      id: '/'
 | 
			
		||||
      path: '/'
 | 
			
		||||
      fullPath: '/'
 | 
			
		||||
      preLoaderRoute: typeof IndexLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/admin': {
 | 
			
		||||
      id: '/admin'
 | 
			
		||||
      path: '/admin'
 | 
			
		||||
      fullPath: '/admin'
 | 
			
		||||
      preLoaderRoute: typeof AdminLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/barcode': {
 | 
			
		||||
      id: '/barcode'
 | 
			
		||||
      path: '/barcode'
 | 
			
		||||
      fullPath: '/barcode'
 | 
			
		||||
      preLoaderRoute: typeof BarcodeLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/clients': {
 | 
			
		||||
      id: '/clients'
 | 
			
		||||
      path: '/clients'
 | 
			
		||||
      fullPath: '/clients'
 | 
			
		||||
      preLoaderRoute: typeof ClientsLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/leads': {
 | 
			
		||||
      id: '/leads'
 | 
			
		||||
      path: '/leads'
 | 
			
		||||
      fullPath: '/leads'
 | 
			
		||||
      preLoaderRoute: typeof LeadsLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/login': {
 | 
			
		||||
      id: '/login'
 | 
			
		||||
      path: '/login'
 | 
			
		||||
      fullPath: '/login'
 | 
			
		||||
      preLoaderRoute: typeof LoginLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/products': {
 | 
			
		||||
      id: '/products'
 | 
			
		||||
      path: '/products'
 | 
			
		||||
      fullPath: '/products'
 | 
			
		||||
      preLoaderRoute: typeof ProductsLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/services': {
 | 
			
		||||
      id: '/services'
 | 
			
		||||
      path: '/services'
 | 
			
		||||
      fullPath: '/services'
 | 
			
		||||
      preLoaderRoute: typeof ServicesLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
    '/test': {
 | 
			
		||||
      id: '/test'
 | 
			
		||||
      path: '/test'
 | 
			
		||||
      fullPath: '/test'
 | 
			
		||||
      preLoaderRoute: typeof TestLazyImport
 | 
			
		||||
      parentRoute: typeof rootRoute
 | 
			
		||||
    }
 | 
			
		||||
@@ -108,8 +145,9 @@ declare module '@tanstack/react-router' {
 | 
			
		||||
 | 
			
		||||
// Create and export the route tree
 | 
			
		||||
 | 
			
		||||
export const routeTree = rootRoute.addChildren([
 | 
			
		||||
export const routeTree = rootRoute.addChildren({
 | 
			
		||||
  IndexLazyRoute,
 | 
			
		||||
  AdminLazyRoute,
 | 
			
		||||
  BarcodeLazyRoute,
 | 
			
		||||
  ClientsLazyRoute,
 | 
			
		||||
  LeadsLazyRoute,
 | 
			
		||||
@@ -117,6 +155,54 @@ export const routeTree = rootRoute.addChildren([
 | 
			
		||||
  ProductsLazyRoute,
 | 
			
		||||
  ServicesLazyRoute,
 | 
			
		||||
  TestLazyRoute,
 | 
			
		||||
])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/* prettier-ignore-end */
 | 
			
		||||
 | 
			
		||||
/* ROUTE_MANIFEST_START
 | 
			
		||||
{
 | 
			
		||||
  "routes": {
 | 
			
		||||
    "__root__": {
 | 
			
		||||
      "filePath": "__root.tsx",
 | 
			
		||||
      "children": [
 | 
			
		||||
        "/",
 | 
			
		||||
        "/admin",
 | 
			
		||||
        "/barcode",
 | 
			
		||||
        "/clients",
 | 
			
		||||
        "/leads",
 | 
			
		||||
        "/login",
 | 
			
		||||
        "/products",
 | 
			
		||||
        "/services",
 | 
			
		||||
        "/test"
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "/": {
 | 
			
		||||
      "filePath": "index.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/admin": {
 | 
			
		||||
      "filePath": "admin.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/barcode": {
 | 
			
		||||
      "filePath": "barcode.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/clients": {
 | 
			
		||||
      "filePath": "clients.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/leads": {
 | 
			
		||||
      "filePath": "leads.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/login": {
 | 
			
		||||
      "filePath": "login.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/products": {
 | 
			
		||||
      "filePath": "products.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/services": {
 | 
			
		||||
      "filePath": "services.lazy.tsx"
 | 
			
		||||
    },
 | 
			
		||||
    "/test": {
 | 
			
		||||
      "filePath": "test.lazy.tsx"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
ROUTE_MANIFEST_END */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								src/routes/admin.lazy.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/routes/admin.lazy.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
import {createLazyFileRoute} from "@tanstack/react-router";
 | 
			
		||||
import AdminPage from "../pages/AdminPage/AdminPage.tsx";
 | 
			
		||||
 | 
			
		||||
export const Route = createLazyFileRoute('/admin')({
 | 
			
		||||
    component: AdminPage
 | 
			
		||||
})
 | 
			
		||||
@@ -1,14 +1,43 @@
 | 
			
		||||
import {createLazyFileRoute} from "@tanstack/react-router";
 | 
			
		||||
import {Flex, Tabs} from '@mantine/core';
 | 
			
		||||
import {motion} from 'framer-motion';
 | 
			
		||||
 | 
			
		||||
export const Route = createLazyFileRoute('/test')({
 | 
			
		||||
    component: TestPage
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const tabs = [
 | 
			
		||||
    ['tab111', 'content111'],
 | 
			
		||||
    ['tab222', 'content222'],
 | 
			
		||||
    ['tab333', 'content333'],
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function Demo() {
 | 
			
		||||
    return (
 | 
			
		||||
        <Tabs>
 | 
			
		||||
            {tabs.map((el, i) => (
 | 
			
		||||
                <Tabs.Tab key={i} value={el[0]}>
 | 
			
		||||
                    <motion.div
 | 
			
		||||
                        initial={{opacity: 0}}
 | 
			
		||||
                        animate={{opacity: 1}}
 | 
			
		||||
                        transition={{duration: 0.5}}
 | 
			
		||||
                    >
 | 
			
		||||
                        <div>{el[1]}</div>
 | 
			
		||||
                    </motion.div>
 | 
			
		||||
                </Tabs.Tab>
 | 
			
		||||
            ))}
 | 
			
		||||
        </Tabs>
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function TestPage() {
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            {/*<ShippingWarehouseAutocomplete/>*/}
 | 
			
		||||
            <Flex w={"80vw"}>
 | 
			
		||||
 | 
			
		||||
                <Demo/>
 | 
			
		||||
            </Flex>
 | 
			
		||||
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								src/shared/enums/UserRole.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/shared/enums/UserRole.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
export enum UserRoleEnum {
 | 
			
		||||
    USER = 'user',
 | 
			
		||||
    EMPLOYEE = 'employee',
 | 
			
		||||
    MANAGER = 'manager',
 | 
			
		||||
    ADMIN = 'admin'
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user