Merge remote-tracking branch 'origin/statistics'
# Conflicts: # src/client/models/DealSchema.ts
This commit is contained in:
		@@ -16,8 +16,9 @@
 | 
				
			|||||||
        "@fortawesome/free-solid-svg-icons": "^6.6.0",
 | 
					        "@fortawesome/free-solid-svg-icons": "^6.6.0",
 | 
				
			||||||
        "@fortawesome/react-fontawesome": "^0.2.2",
 | 
					        "@fortawesome/react-fontawesome": "^0.2.2",
 | 
				
			||||||
        "@hello-pangea/dnd": "^17.0.0",
 | 
					        "@hello-pangea/dnd": "^17.0.0",
 | 
				
			||||||
 | 
					        "@mantine/charts": "^7.13.5",
 | 
				
			||||||
        "@mantine/core": "^7.11.2",
 | 
					        "@mantine/core": "^7.11.2",
 | 
				
			||||||
        "@mantine/dates": "^7.11.2",
 | 
					        "@mantine/dates": "^7.13.5",
 | 
				
			||||||
        "@mantine/dropzone": "^7.11.2",
 | 
					        "@mantine/dropzone": "^7.11.2",
 | 
				
			||||||
        "@mantine/form": "^7.11.2",
 | 
					        "@mantine/form": "^7.11.2",
 | 
				
			||||||
        "@mantine/hooks": "^7.11.2",
 | 
					        "@mantine/hooks": "^7.11.2",
 | 
				
			||||||
@@ -51,6 +52,7 @@
 | 
				
			|||||||
        "react-redux": "^9.1.2",
 | 
					        "react-redux": "^9.1.2",
 | 
				
			||||||
        "react-to-print": "^2.15.1",
 | 
					        "react-to-print": "^2.15.1",
 | 
				
			||||||
        "reactflow": "^11.11.4",
 | 
					        "reactflow": "^11.11.4",
 | 
				
			||||||
 | 
					        "recharts": "^2.13.3",
 | 
				
			||||||
        "zod": "^3.23.8"
 | 
					        "zod": "^3.23.8"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -166,11 +166,16 @@ export type { GetClientMarketplacesResponse } from './models/GetClientMarketplac
 | 
				
			|||||||
export type { GetDealBillById } from './models/GetDealBillById';
 | 
					export type { GetDealBillById } from './models/GetDealBillById';
 | 
				
			||||||
export type { GetDealProductsBarcodesPdfRequest } from './models/GetDealProductsBarcodesPdfRequest';
 | 
					export type { GetDealProductsBarcodesPdfRequest } from './models/GetDealProductsBarcodesPdfRequest';
 | 
				
			||||||
export type { GetDealProductsBarcodesPdfResponse } from './models/GetDealProductsBarcodesPdfResponse';
 | 
					export type { GetDealProductsBarcodesPdfResponse } from './models/GetDealProductsBarcodesPdfResponse';
 | 
				
			||||||
 | 
					export type { GetManagersResponse } from './models/GetManagersResponse';
 | 
				
			||||||
export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
 | 
					export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
 | 
				
			||||||
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
 | 
					export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
 | 
				
			||||||
export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse';
 | 
					export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse';
 | 
				
			||||||
export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest';
 | 
					export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest';
 | 
				
			||||||
export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse';
 | 
					export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse';
 | 
				
			||||||
 | 
					export type { GetProfitChartDataRequest } from './models/GetProfitChartDataRequest';
 | 
				
			||||||
 | 
					export type { GetProfitChartDataResponse } from './models/GetProfitChartDataResponse';
 | 
				
			||||||
 | 
					export type { GetProfitTableDataRequest } from './models/GetProfitTableDataRequest';
 | 
				
			||||||
 | 
					export type { GetProfitTableDataResponse } from './models/GetProfitTableDataResponse';
 | 
				
			||||||
export type { GetServiceKitSchema } from './models/GetServiceKitSchema';
 | 
					export type { GetServiceKitSchema } from './models/GetServiceKitSchema';
 | 
				
			||||||
export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest';
 | 
					export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest';
 | 
				
			||||||
export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse';
 | 
					export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse';
 | 
				
			||||||
@@ -205,6 +210,9 @@ export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
 | 
				
			|||||||
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
 | 
					export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
 | 
				
			||||||
export type { ProductUploadBarcodeImageResponse } from './models/ProductUploadBarcodeImageResponse';
 | 
					export type { ProductUploadBarcodeImageResponse } from './models/ProductUploadBarcodeImageResponse';
 | 
				
			||||||
export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
 | 
					export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
 | 
				
			||||||
 | 
					export type { ProfitChartDataItem } from './models/ProfitChartDataItem';
 | 
				
			||||||
 | 
					export type { ProfitTableDataItem } from './models/ProfitTableDataItem';
 | 
				
			||||||
 | 
					export type { ProfitTableGroupBy } from './models/ProfitTableGroupBy';
 | 
				
			||||||
export type { RoleSchema } from './models/RoleSchema';
 | 
					export type { RoleSchema } from './models/RoleSchema';
 | 
				
			||||||
export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema';
 | 
					export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema';
 | 
				
			||||||
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
 | 
					export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
 | 
				
			||||||
@@ -267,6 +275,7 @@ export { ProductService } from './services/ProductService';
 | 
				
			|||||||
export { RoleService } from './services/RoleService';
 | 
					export { RoleService } from './services/RoleService';
 | 
				
			||||||
export { ServiceService } from './services/ServiceService';
 | 
					export { ServiceService } from './services/ServiceService';
 | 
				
			||||||
export { ShippingWarehouseService } from './services/ShippingWarehouseService';
 | 
					export { ShippingWarehouseService } from './services/ShippingWarehouseService';
 | 
				
			||||||
 | 
					export { StatisticsService } from './services/StatisticsService';
 | 
				
			||||||
export { TaskService } from './services/TaskService';
 | 
					export { TaskService } from './services/TaskService';
 | 
				
			||||||
export { TimeTrackingService } from './services/TimeTrackingService';
 | 
					export { TimeTrackingService } from './services/TimeTrackingService';
 | 
				
			||||||
export { UserService } from './services/UserService';
 | 
					export { UserService } from './services/UserService';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
/* istanbul ignore file */
 | 
					/* istanbul ignore file */
 | 
				
			||||||
/* tslint:disable */
 | 
					/* tslint:disable */
 | 
				
			||||||
/* eslint-disable */
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { UserSchema } from './UserSchema';
 | 
				
			||||||
export type DealGeneralInfoSchema = {
 | 
					export type DealGeneralInfoSchema = {
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    isDeleted: boolean;
 | 
					    isDeleted: boolean;
 | 
				
			||||||
@@ -10,5 +11,6 @@ export type DealGeneralInfoSchema = {
 | 
				
			|||||||
    shippingWarehouse?: (string | null);
 | 
					    shippingWarehouse?: (string | null);
 | 
				
			||||||
    deliveryDate?: (string | null);
 | 
					    deliveryDate?: (string | null);
 | 
				
			||||||
    receivingSlotDate?: (string | null);
 | 
					    receivingSlotDate?: (string | null);
 | 
				
			||||||
 | 
					    manager?: (UserSchema | null);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,14 @@
 | 
				
			|||||||
/* eslint-disable */
 | 
					/* eslint-disable */
 | 
				
			||||||
import type { ClientSchema } from './ClientSchema';
 | 
					import type { ClientSchema } from './ClientSchema';
 | 
				
			||||||
import type { DealBillRequestSchema } from './DealBillRequestSchema';
 | 
					import type { DealBillRequestSchema } from './DealBillRequestSchema';
 | 
				
			||||||
import type { DealGroupSchema } from './DealGroupSchema';
 | 
					 | 
				
			||||||
import type { DealProductSchema } from './DealProductSchema';
 | 
					import type { DealProductSchema } from './DealProductSchema';
 | 
				
			||||||
import type { DealServiceSchema } from './DealServiceSchema';
 | 
					import type { DealServiceSchema } from './DealServiceSchema';
 | 
				
			||||||
import type { DealStatusHistorySchema } from './DealStatusHistorySchema';
 | 
					import type { DealStatusHistorySchema } from './DealStatusHistorySchema';
 | 
				
			||||||
import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
 | 
					import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema';
 | 
				
			||||||
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
 | 
					import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
 | 
				
			||||||
 | 
					import type { UserSchema } from './UserSchema';
 | 
				
			||||||
 | 
					import type { DealGroupSchema } from './DealGroupSchema';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type DealSchema = {
 | 
					export type DealSchema = {
 | 
				
			||||||
    id: number;
 | 
					    id: number;
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
@@ -28,6 +30,8 @@ export type DealSchema = {
 | 
				
			|||||||
    billRequest?: (DealBillRequestSchema | null);
 | 
					    billRequest?: (DealBillRequestSchema | null);
 | 
				
			||||||
    category?: (ServicePriceCategorySchema | null);
 | 
					    category?: (ServicePriceCategorySchema | null);
 | 
				
			||||||
    group?: (DealGroupSchema | null);
 | 
					    group?: (DealGroupSchema | null);
 | 
				
			||||||
 | 
					    manager?: (UserSchema | null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    deliveryDate?: (string | null);
 | 
					    deliveryDate?: (string | null);
 | 
				
			||||||
    receivingSlotDate?: (string | null);
 | 
					    receivingSlotDate?: (string | null);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/client/models/GetProfitChartDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/GetProfitChartDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type GetProfitChartDataRequest = {
 | 
				
			||||||
 | 
					    dateRange: any[];
 | 
				
			||||||
 | 
					    clientId: number;
 | 
				
			||||||
 | 
					    baseMarketplaceKey: string;
 | 
				
			||||||
 | 
					    dealStatusId: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/GetProfitChartDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetProfitChartDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { ProfitChartDataItem } from './ProfitChartDataItem';
 | 
				
			||||||
 | 
					export type GetProfitChartDataResponse = {
 | 
				
			||||||
 | 
					    data: Array<ProfitChartDataItem>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/client/models/GetProfitTableDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/models/GetProfitTableDataRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { ProfitTableGroupBy } from './ProfitTableGroupBy';
 | 
				
			||||||
 | 
					export type GetProfitTableDataRequest = {
 | 
				
			||||||
 | 
					    dateRange: any[];
 | 
				
			||||||
 | 
					    groupTableBy: ProfitTableGroupBy;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/GetProfitTableDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/GetProfitTableDataResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { ProfitTableDataItem } from './ProfitTableDataItem';
 | 
				
			||||||
 | 
					export type GetProfitTableDataResponse = {
 | 
				
			||||||
 | 
					    data: Array<ProfitTableDataItem>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/client/models/ProfitChartDataItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/ProfitChartDataItem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type ProfitChartDataItem = {
 | 
				
			||||||
 | 
					    date: string;
 | 
				
			||||||
 | 
					    revenue: number;
 | 
				
			||||||
 | 
					    profit: number;
 | 
				
			||||||
 | 
					    dealsCount: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/client/models/ProfitTableDataItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/client/models/ProfitTableDataItem.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type ProfitTableDataItem = {
 | 
				
			||||||
 | 
					    groupedValue: (string | number);
 | 
				
			||||||
 | 
					    revenue: number;
 | 
				
			||||||
 | 
					    profit: number;
 | 
				
			||||||
 | 
					    dealsCount: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/client/models/ProfitTableGroupBy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/client/models/ProfitTableGroupBy.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type ProfitTableGroupBy = 0 | 1 | 2 | 3 | 4;
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/client/services/StatisticsService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/client/services/StatisticsService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { GetProfitChartDataRequest } from '../models/GetProfitChartDataRequest';
 | 
				
			||||||
 | 
					import type { GetProfitChartDataResponse } from '../models/GetProfitChartDataResponse';
 | 
				
			||||||
 | 
					import type { GetProfitTableDataRequest } from '../models/GetProfitTableDataRequest';
 | 
				
			||||||
 | 
					import type { GetProfitTableDataResponse } from '../models/GetProfitTableDataResponse';
 | 
				
			||||||
 | 
					import type { CancelablePromise } from '../core/CancelablePromise';
 | 
				
			||||||
 | 
					import { OpenAPI } from '../core/OpenAPI';
 | 
				
			||||||
 | 
					import { request as __request } from '../core/request';
 | 
				
			||||||
 | 
					export class StatisticsService {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get Profit Chart Data
 | 
				
			||||||
 | 
					     * @returns GetProfitChartDataResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static getProfitChartData({
 | 
				
			||||||
 | 
					        requestBody,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        requestBody: GetProfitChartDataRequest,
 | 
				
			||||||
 | 
					    }): CancelablePromise<GetProfitChartDataResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/statistics/get-profit-chart-data',
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get Profit Table Data
 | 
				
			||||||
 | 
					     * @returns GetProfitTableDataResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static getProfitTableData({
 | 
				
			||||||
 | 
					        requestBody,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        requestBody: GetProfitTableDataRequest,
 | 
				
			||||||
 | 
					    }): CancelablePromise<GetProfitTableDataResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/statistics/get-profit-table-data',
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
import type { CreateUserRequest } from '../models/CreateUserRequest';
 | 
					import type { CreateUserRequest } from '../models/CreateUserRequest';
 | 
				
			||||||
import type { CreateUserResponse } from '../models/CreateUserResponse';
 | 
					import type { CreateUserResponse } from '../models/CreateUserResponse';
 | 
				
			||||||
import type { GetAllUsersResponse } from '../models/GetAllUsersResponse';
 | 
					import type { GetAllUsersResponse } from '../models/GetAllUsersResponse';
 | 
				
			||||||
 | 
					import type { GetManagersResponse } from '../models/GetManagersResponse';
 | 
				
			||||||
import type { UpdateUserRequest } from '../models/UpdateUserRequest';
 | 
					import type { UpdateUserRequest } from '../models/UpdateUserRequest';
 | 
				
			||||||
import type { UpdateUserResponse } from '../models/UpdateUserResponse';
 | 
					import type { UpdateUserResponse } from '../models/UpdateUserResponse';
 | 
				
			||||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
					import type { CancelablePromise } from '../core/CancelablePromise';
 | 
				
			||||||
@@ -62,4 +63,15 @@ export class UserService {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get Managers
 | 
				
			||||||
 | 
					     * @returns GetManagersResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static getManagers(): CancelablePromise<GetManagersResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'GET',
 | 
				
			||||||
 | 
					            url: '/user/get-managers',
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								src/components/ManagerSelect/ManagerSelect.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/components/ManagerSelect/ManagerSelect.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
 | 
					import ObjectSelect, { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { UserSchema } from "../../client";
 | 
				
			||||||
 | 
					import useManagersList from "../../pages/LeadsPage/hooks/useManagersList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<UserSchema | null>,
 | 
				
			||||||
 | 
					    "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					const UserSelect: FC<Props> = props => {
 | 
				
			||||||
 | 
					    const { objects: managers } = useManagersList();
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <ObjectSelect
 | 
				
			||||||
 | 
					            data={managers}
 | 
				
			||||||
 | 
					            getLabelFn={(manager: UserSchema) => `${manager.firstName} ${manager.secondName}`}
 | 
				
			||||||
 | 
					            getValueFn={(manager: UserSchema) => manager.id.toString()}
 | 
				
			||||||
 | 
					            clearable
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					            onClear={() => props.onChange(null)}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default UserSelect;
 | 
				
			||||||
@@ -4,7 +4,7 @@ import {
 | 
				
			|||||||
    IconBarcode,
 | 
					    IconBarcode,
 | 
				
			||||||
    IconBox,
 | 
					    IconBox,
 | 
				
			||||||
    IconBuildingWarehouse,
 | 
					    IconBuildingWarehouse,
 | 
				
			||||||
    IconCash,
 | 
					    IconCash, IconChartDots,
 | 
				
			||||||
    IconDashboard,
 | 
					    IconDashboard,
 | 
				
			||||||
    IconFileBarcode,
 | 
					    IconFileBarcode,
 | 
				
			||||||
    IconHome2,
 | 
					    IconHome2,
 | 
				
			||||||
@@ -93,6 +93,11 @@ const mockdata = [
 | 
				
			|||||||
        label: "Маркетплейсы",
 | 
					        label: "Маркетплейсы",
 | 
				
			||||||
        href: "/marketplaces",
 | 
					        href: "/marketplaces",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        icon: IconChartDots,
 | 
				
			||||||
 | 
					        label: "Статистика",
 | 
				
			||||||
 | 
					        href: "/statistics",
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function Navbar() {
 | 
					export function Navbar() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,7 @@ import { IconBarcode, IconPrinter } from "@tabler/icons-react";
 | 
				
			|||||||
import styles from "../../../ui/LeadsPage.module.css";
 | 
					import styles from "../../../ui/LeadsPage.module.css";
 | 
				
			||||||
import { base64ToBlob } from "../../../../../shared/lib/utils.ts";
 | 
					import { base64ToBlob } from "../../../../../shared/lib/utils.ts";
 | 
				
			||||||
import { DatePickerInput } from "@mantine/dates";
 | 
					import { DatePickerInput } from "@mantine/dates";
 | 
				
			||||||
 | 
					import ManagerSelect from "../../../../../components/ManagerSelect/ManagerSelect.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    deal: DealSchema;
 | 
					    deal: DealSchema;
 | 
				
			||||||
@@ -211,6 +212,11 @@ const Content: FC<Props> = ({ deal }) => {
 | 
				
			|||||||
                            {...form.getInputProps("receivingSlotDate")}
 | 
					                            {...form.getInputProps("receivingSlotDate")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
 | 
					                        <ManagerSelect
 | 
				
			||||||
 | 
					                            placeholder={"Укажите менеджера"}
 | 
				
			||||||
 | 
					                            label={"Менеджер"}
 | 
				
			||||||
 | 
					                            {...form.getInputProps("manager")}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </Flex>
 | 
					                    </Flex>
 | 
				
			||||||
                </Fieldset>
 | 
					                </Fieldset>
 | 
				
			||||||
                <Flex
 | 
					                <Flex
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/pages/LeadsPage/hooks/useManagersList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/pages/LeadsPage/hooks/useManagersList.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					import ObjectList from "../../../hooks/objectList.tsx";
 | 
				
			||||||
 | 
					import { UserService } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useManagersList = () =>
 | 
				
			||||||
 | 
					    ObjectList({
 | 
				
			||||||
 | 
					        queryFn: UserService.getManagers,
 | 
				
			||||||
 | 
					        getObjectsFn: response => response.managers,
 | 
				
			||||||
 | 
					        queryKey: "getManagers",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useManagersList;
 | 
				
			||||||
@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum StatisticsTab {
 | 
				
			||||||
 | 
					    PROFIT,
 | 
				
			||||||
 | 
					    SALARIES,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = Omit<SegmentedControlProps, "data">;
 | 
				
			||||||
 | 
					const data = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "Выручка по сделкам",
 | 
				
			||||||
 | 
					        value: StatisticsTab.PROFIT.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "Зарплаты",
 | 
				
			||||||
 | 
					        value: StatisticsTab.SALARIES.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const StatisticsTabSegmentedControl: FC<Props> = props => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <SegmentedControl
 | 
				
			||||||
 | 
					            data={data}
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/pages/StatisticsPage/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/pages/StatisticsPage/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export { StatisticsPage } from "./ui/StatisticsPage.tsx";
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/pages/StatisticsPage/tabs/ProfitTab/ProfitTab.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import { ProfitChart } from "./components/ProfitChart/ProfitChart.tsx";
 | 
				
			||||||
 | 
					import styles from "../../ui/StatisticsPage.module.css";
 | 
				
			||||||
 | 
					import { ProfitTable } from "./components/ProfitTable/ProfitTable.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitTab = () => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <div className={styles["page-container"]}>
 | 
				
			||||||
 | 
					            <ProfitChart />
 | 
				
			||||||
 | 
					            <ProfitTable />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					import { DatePickerInput, DatePickerInputProps } from "@mantine/dates";
 | 
				
			||||||
 | 
					import { Stack, Text } from "@mantine/core";
 | 
				
			||||||
 | 
					import ClientSelectNew from "../../../../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
 | 
				
			||||||
 | 
					import { BaseMarketplaceSchema, ClientSchema } from "../../../../../../client";
 | 
				
			||||||
 | 
					import { ObjectSelectProps } from "../../../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import BaseMarketplaceSelect
 | 
				
			||||||
 | 
					    from "../../../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
				
			||||||
 | 
					import DealStatusSelect from "../../../../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx";
 | 
				
			||||||
 | 
					import { DealStatusType } from "../../../../../../shared/enums/DealStatus.ts";
 | 
				
			||||||
 | 
					import { ProfitTableSegmentedControl } from "../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FiltersProps = {
 | 
				
			||||||
 | 
					    datePickerProps?: DatePickerInputProps<"range">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clientSelectProps?: Omit<ObjectSelectProps<ClientSchema>, "data">;
 | 
				
			||||||
 | 
					    onClientClear?: () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    baseMarketplaceSelectProps?: Omit<
 | 
				
			||||||
 | 
					        ObjectSelectProps<BaseMarketplaceSchema>,
 | 
				
			||||||
 | 
					        "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
 | 
					    >;
 | 
				
			||||||
 | 
					    onBaseMarketplaceClear?: () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dealStatusSelectProps?: Omit<ObjectSelectProps<DealStatusType>, "data">;
 | 
				
			||||||
 | 
					    onDealStatusClear?: () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    groupTableByProps?: {
 | 
				
			||||||
 | 
					        value: string,
 | 
				
			||||||
 | 
					        onChange: (value: string) => void,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Filters = (props: FiltersProps) => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        datePickerProps,
 | 
				
			||||||
 | 
					        clientSelectProps,
 | 
				
			||||||
 | 
					        onClientClear,
 | 
				
			||||||
 | 
					        baseMarketplaceSelectProps,
 | 
				
			||||||
 | 
					        onBaseMarketplaceClear,
 | 
				
			||||||
 | 
					        dealStatusSelectProps,
 | 
				
			||||||
 | 
					        onDealStatusClear,
 | 
				
			||||||
 | 
					        groupTableByProps,
 | 
				
			||||||
 | 
					    } = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Stack mb={"lg"}>
 | 
				
			||||||
 | 
					            {datePickerProps &&
 | 
				
			||||||
 | 
					                <DatePickerInput
 | 
				
			||||||
 | 
					                    {...datePickerProps}
 | 
				
			||||||
 | 
					                    type="range"
 | 
				
			||||||
 | 
					                    placeholder="Выберите даты"
 | 
				
			||||||
 | 
					                    maxDate={new Date()}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {dealStatusSelectProps &&
 | 
				
			||||||
 | 
					                <DealStatusSelect
 | 
				
			||||||
 | 
					                    {...dealStatusSelectProps}
 | 
				
			||||||
 | 
					                    onClear={onDealStatusClear}
 | 
				
			||||||
 | 
					                    clearable
 | 
				
			||||||
 | 
					                    placeholder={"Выберите статус"}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {clientSelectProps &&
 | 
				
			||||||
 | 
					                <ClientSelectNew
 | 
				
			||||||
 | 
					                    {...clientSelectProps}
 | 
				
			||||||
 | 
					                    onClear={onClientClear}
 | 
				
			||||||
 | 
					                    clearable
 | 
				
			||||||
 | 
					                    searchable
 | 
				
			||||||
 | 
					                    placeholder={"Выберите клиента"}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {baseMarketplaceSelectProps &&
 | 
				
			||||||
 | 
					                <BaseMarketplaceSelect
 | 
				
			||||||
 | 
					                    {...baseMarketplaceSelectProps}
 | 
				
			||||||
 | 
					                    onClear={onBaseMarketplaceClear}
 | 
				
			||||||
 | 
					                    clearable
 | 
				
			||||||
 | 
					                    placeholder={"Выберите маркетплейс"}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            {groupTableByProps &&
 | 
				
			||||||
 | 
					                <>
 | 
				
			||||||
 | 
					                    <Text>Группировать:</Text>
 | 
				
			||||||
 | 
					                    <ProfitTableSegmentedControl
 | 
				
			||||||
 | 
					                        {...groupTableByProps}
 | 
				
			||||||
 | 
					                        orientation={"vertical"}
 | 
				
			||||||
 | 
					                        size={"md"}
 | 
				
			||||||
 | 
					                        w={"100%"}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </>
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        </Stack>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import { AreaChart } from "@mantine/charts";
 | 
				
			||||||
 | 
					import "@mantine/charts/styles.css";
 | 
				
			||||||
 | 
					import PageBlock from "../../../../../../components/PageBlock/PageBlock.tsx";
 | 
				
			||||||
 | 
					import { useProfitChart } from "./hooks/useProfitChart.tsx";
 | 
				
			||||||
 | 
					import { Skeleton, Stack } from "@mantine/core";
 | 
				
			||||||
 | 
					import { ProfitChartFiltersModal } from "../../modals/ProfitChartFiltersModal.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitChart = () => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        profitData,
 | 
				
			||||||
 | 
					        form,
 | 
				
			||||||
 | 
					        isLoading,
 | 
				
			||||||
 | 
					    } = useProfitChart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getChartsSeries = [
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            { name: "profit", label: "Прибыль", color: "indigo.6" },
 | 
				
			||||||
 | 
					            { name: "revenue", label: "Выручка", color: "teal.6" },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            { name: "dealsCount", label: "Количество сделок", color: "indigo.6" },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const units = ["₽", "шт"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <PageBlock style={{ flex: 3, minWidth: "600px", padding: "25px" }}>
 | 
				
			||||||
 | 
					            <ProfitChartFiltersModal
 | 
				
			||||||
 | 
					                form={form}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Skeleton visible={isLoading}>
 | 
				
			||||||
 | 
					                <Stack gap={"xl"}>
 | 
				
			||||||
 | 
					                    {getChartsSeries.map((series, idx) => {
 | 
				
			||||||
 | 
					                        return (
 | 
				
			||||||
 | 
					                            <AreaChart
 | 
				
			||||||
 | 
					                                my={"sm"}
 | 
				
			||||||
 | 
					                                w={"98%"}
 | 
				
			||||||
 | 
					                                h={"39vh"}
 | 
				
			||||||
 | 
					                                data={profitData}
 | 
				
			||||||
 | 
					                                dataKey="date"
 | 
				
			||||||
 | 
					                                unit={units[idx]}
 | 
				
			||||||
 | 
					                                tooltipAnimationDuration={200}
 | 
				
			||||||
 | 
					                                valueFormatter={(value) => new Intl.NumberFormat("ru-RU").format(value)}
 | 
				
			||||||
 | 
					                                series={series}
 | 
				
			||||||
 | 
					                                fillOpacity={0.5}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    })}
 | 
				
			||||||
 | 
					                </Stack>
 | 
				
			||||||
 | 
					            </Skeleton>
 | 
				
			||||||
 | 
					        </PageBlock>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					import { ChartFormFilters } from "../../../../../types/ChartFormFilters.ts";
 | 
				
			||||||
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
 | 
					import { dateToString, getDefaultDates } from "../../../../../utils/dates.ts";
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { ProfitChartDataItem, StatisticsService } from "../../../../../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useProfitChart = () => {
 | 
				
			||||||
 | 
					    const form = useForm<ChartFormFilters>({
 | 
				
			||||||
 | 
					        mode: "controlled",
 | 
				
			||||||
 | 
					        initialValues: {
 | 
				
			||||||
 | 
					            dateRange: getDefaultDates(),
 | 
				
			||||||
 | 
					            client: null,
 | 
				
			||||||
 | 
					            marketplace: null,
 | 
				
			||||||
 | 
					            dealStatus: null,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    const [profitData, setProfitData] = useState<ProfitChartDataItem[]>([]);
 | 
				
			||||||
 | 
					    const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getFilters = () => {
 | 
				
			||||||
 | 
					        const dateRange = form.values.dateRange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            dateRange: [
 | 
				
			||||||
 | 
					                dateToString(dateRange[0]),
 | 
				
			||||||
 | 
					                dateToString(dateRange[1]),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            clientId: form.values.client?.id ?? -1,
 | 
				
			||||||
 | 
					            baseMarketplaceKey: form.values.marketplace?.key ?? "all",
 | 
				
			||||||
 | 
					            dealStatusId: form.values.dealStatus?.id ?? -1,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fetchProfitData = () => {
 | 
				
			||||||
 | 
					        setIsLoading(true);
 | 
				
			||||||
 | 
					        StatisticsService.getProfitChartData({
 | 
				
			||||||
 | 
					            requestBody: getFilters(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(res => {
 | 
				
			||||||
 | 
					                setProfitData(res.data);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsLoading(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fetchProfitData();
 | 
				
			||||||
 | 
					    }, [form.values]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        profitData,
 | 
				
			||||||
 | 
					        form,
 | 
				
			||||||
 | 
					        isLoading,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import PageBlock from "../../../../../../components/PageBlock/PageBlock.tsx";
 | 
				
			||||||
 | 
					import { MantineReactTable } from "mantine-react-table";
 | 
				
			||||||
 | 
					import { useProfitTable } from "./hooks/useProfitTable.tsx";
 | 
				
			||||||
 | 
					import { Skeleton } from "@mantine/core";
 | 
				
			||||||
 | 
					import { ProfitTableFiltersModal } from "../../modals/ProfitTableFiltersModal.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitTable = () => {
 | 
				
			||||||
 | 
					    const { table, form, isLoading } = useProfitTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <PageBlock style={{ flex: 2, minWidth: "600px", padding: "25px" }}>
 | 
				
			||||||
 | 
					            <ProfitTableFiltersModal form={form} />
 | 
				
			||||||
 | 
					            <Skeleton visible={isLoading}>
 | 
				
			||||||
 | 
					                <MantineReactTable table={table} />
 | 
				
			||||||
 | 
					            </Skeleton>
 | 
				
			||||||
 | 
					        </PageBlock>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					import { useMemo } from "react";
 | 
				
			||||||
 | 
					import { MRT_ColumnDef } from "mantine-react-table";
 | 
				
			||||||
 | 
					import { ProfitTableDataItem } from "../../../../../../../client";
 | 
				
			||||||
 | 
					import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
				
			||||||
 | 
					import { DealStatus, DealStatusDictionary } from "../../../../../../../shared/enums/DealStatus.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    groupTableBy: GroupStatisticsTable;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useProfitTableColumns = ({ groupTableBy }: Props) => {
 | 
				
			||||||
 | 
					    const groupedValueHeader = {
 | 
				
			||||||
 | 
					        [GroupStatisticsTable.BY_DATES]: "Дата",
 | 
				
			||||||
 | 
					        [GroupStatisticsTable.BY_CLIENTS]: "Клиент",
 | 
				
			||||||
 | 
					        [GroupStatisticsTable.BY_STATUSES]: "Статус",
 | 
				
			||||||
 | 
					        [GroupStatisticsTable.BY_MARKETPLACES]: "Маркетплейс",
 | 
				
			||||||
 | 
					        [GroupStatisticsTable.BY_WAREHOUSES]: "Склад отгрузки",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return useMemo<MRT_ColumnDef<ProfitTableDataItem>[]>(
 | 
				
			||||||
 | 
					        () => [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                accessorKey: "groupedValue",
 | 
				
			||||||
 | 
					                header: groupedValueHeader[groupTableBy],
 | 
				
			||||||
 | 
					                enableSorting: groupTableBy === GroupStatisticsTable.BY_DATES,
 | 
				
			||||||
 | 
					                Cell: ({ row }) => {
 | 
				
			||||||
 | 
					                    if (groupTableBy === GroupStatisticsTable.BY_STATUSES) {
 | 
				
			||||||
 | 
					                        const statusIndex = row.original.groupedValue as DealStatus;
 | 
				
			||||||
 | 
					                        return DealStatusDictionary[statusIndex];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return row.original.groupedValue;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                size: 60,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                accessorKey: "dealsCount",
 | 
				
			||||||
 | 
					                header: "Кол-во",
 | 
				
			||||||
 | 
					                size: 40,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                accessorKey: "profit",
 | 
				
			||||||
 | 
					                header: "Прибыль",
 | 
				
			||||||
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
 | 
					                    row.original.profit.toLocaleString("ru-RU") + "₽",
 | 
				
			||||||
 | 
					                size: 50,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                accessorKey: "revenue",
 | 
				
			||||||
 | 
					                header: "Выручка",
 | 
				
			||||||
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
 | 
					                    row.original.revenue.toLocaleString("ru-RU") + "₽",
 | 
				
			||||||
 | 
					                size: 50,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        [groupTableBy],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
				
			||||||
 | 
					import { useProfitTableColumns } from "./columns.tsx";
 | 
				
			||||||
 | 
					import { useMantineReactTable } from "mantine-react-table";
 | 
				
			||||||
 | 
					import { ProfitTableDataItem } from "../../../../../../../client";
 | 
				
			||||||
 | 
					import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    groupTableBy: GroupStatisticsTable;
 | 
				
			||||||
 | 
					    profitData: ProfitTableDataItem[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useProfitMantineTable = ({ groupTableBy, profitData }: Props) => {
 | 
				
			||||||
 | 
					    const columns = useProfitTableColumns({
 | 
				
			||||||
 | 
					        groupTableBy,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const defaultSorting = [{ id: "groupedValue", desc: true }];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const table = useMantineReactTable({
 | 
				
			||||||
 | 
					        enablePagination: false,
 | 
				
			||||||
 | 
					        data: profitData,
 | 
				
			||||||
 | 
					        columns,
 | 
				
			||||||
 | 
					        enableTopToolbar: false,
 | 
				
			||||||
 | 
					        enableBottomToolbar: false,
 | 
				
			||||||
 | 
					        enableSorting: true,
 | 
				
			||||||
 | 
					        initialState: {
 | 
				
			||||||
 | 
					            sorting: defaultSorting,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        localization: MRT_Localization_RU,
 | 
				
			||||||
 | 
					        enableRowVirtualization: true,
 | 
				
			||||||
 | 
					        mantineTableContainerProps: { style: { maxHeight: "86vh" } },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { table };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
 | 
					import { TableFormFilters } from "../../../../../types/TableFormFilters.ts";
 | 
				
			||||||
 | 
					import { dateToString, getDefaultDates } from "../../../../../utils/dates.ts";
 | 
				
			||||||
 | 
					import { GroupStatisticsTable } from "../../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { ProfitTableDataItem, StatisticsService } from "../../../../../../../client";
 | 
				
			||||||
 | 
					import { useProfitMantineTable } from "./useProfitMantineTable.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useProfitTable = () => {
 | 
				
			||||||
 | 
					    const form = useForm<TableFormFilters>({
 | 
				
			||||||
 | 
					        mode: "controlled",
 | 
				
			||||||
 | 
					        initialValues: {
 | 
				
			||||||
 | 
					            dateRange: getDefaultDates(),
 | 
				
			||||||
 | 
					            groupTableBy: GroupStatisticsTable.BY_DATES,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [profitData, setProfitData] = useState<ProfitTableDataItem[]>([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { table } = useProfitMantineTable({
 | 
				
			||||||
 | 
					        groupTableBy: form.values.groupTableBy,
 | 
				
			||||||
 | 
					        profitData,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getFilters = () => {
 | 
				
			||||||
 | 
					        const dateRange = form.values.dateRange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            dateRange: [
 | 
				
			||||||
 | 
					                dateToString(dateRange[0]),
 | 
				
			||||||
 | 
					                dateToString(dateRange[1]),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            groupTableBy: form.values.groupTableBy,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fetchProfitData = () => {
 | 
				
			||||||
 | 
					        setIsLoading(true);
 | 
				
			||||||
 | 
					        StatisticsService.getProfitTableData({
 | 
				
			||||||
 | 
					            requestBody: getFilters(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(res => {
 | 
				
			||||||
 | 
					                setProfitData(res.data);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsLoading(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        if (form.values.dateRange.length < 2 || form.values.dateRange[1] === null) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fetchProfitData();
 | 
				
			||||||
 | 
					    }, [form.values]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        table,
 | 
				
			||||||
 | 
					        form,
 | 
				
			||||||
 | 
					        isLoading,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum GroupStatisticsTable {
 | 
				
			||||||
 | 
					    BY_DATES,
 | 
				
			||||||
 | 
					    BY_CLIENTS,
 | 
				
			||||||
 | 
					    BY_STATUSES,
 | 
				
			||||||
 | 
					    BY_WAREHOUSES,
 | 
				
			||||||
 | 
					    BY_MARKETPLACES,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = Omit<SegmentedControlProps, "data">;
 | 
				
			||||||
 | 
					const data = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "По датам",
 | 
				
			||||||
 | 
					        value: GroupStatisticsTable.BY_DATES.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "По клиентам",
 | 
				
			||||||
 | 
					        value: GroupStatisticsTable.BY_CLIENTS.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "По статусам",
 | 
				
			||||||
 | 
					        value: GroupStatisticsTable.BY_STATUSES.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "По складам отгрузки",
 | 
				
			||||||
 | 
					        value: GroupStatisticsTable.BY_WAREHOUSES.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        label: "По маркетплейсам",
 | 
				
			||||||
 | 
					        value: GroupStatisticsTable.BY_MARKETPLACES.toString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitTableSegmentedControl: FC<Props> = props => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <SegmentedControl
 | 
				
			||||||
 | 
					            data={data}
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import { Button, Group, Modal } from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconFilter } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { Filters } from "../components/Filters/Filters.tsx";
 | 
				
			||||||
 | 
					import { UseFormReturnType } from "@mantine/form";
 | 
				
			||||||
 | 
					import { useDisclosure } from "@mantine/hooks";
 | 
				
			||||||
 | 
					import { ChartFormFilters } from "../../../types/ChartFormFilters.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    form: UseFormReturnType<ChartFormFilters>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitChartFiltersModal = ({ form }: Props) => {
 | 
				
			||||||
 | 
					    const [opened, { open, close }] = useDisclosure();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                onClick={open}
 | 
				
			||||||
 | 
					                mb={"lg"}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Group gap={"xs"}>
 | 
				
			||||||
 | 
					                    <IconFilter />
 | 
				
			||||||
 | 
					                    Фильтры графиков
 | 
				
			||||||
 | 
					                </Group>
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Modal
 | 
				
			||||||
 | 
					                opened={opened}
 | 
				
			||||||
 | 
					                onClose={close}
 | 
				
			||||||
 | 
					                title={"Фильтры графиков"}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Filters
 | 
				
			||||||
 | 
					                    datePickerProps={form.getInputProps("dateRange")}
 | 
				
			||||||
 | 
					                    clientSelectProps={form.getInputProps("client")}
 | 
				
			||||||
 | 
					                    onClientClear={() => form.setFieldValue("client", null)}
 | 
				
			||||||
 | 
					                    baseMarketplaceSelectProps={form.getInputProps("marketplace")}
 | 
				
			||||||
 | 
					                    onBaseMarketplaceClear={() => form.setFieldValue("marketplace", null)}
 | 
				
			||||||
 | 
					                    dealStatusSelectProps={form.getInputProps("dealStatus")}
 | 
				
			||||||
 | 
					                    onDealStatusClear={() => form.setFieldValue("dealStatus", null)}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </Modal>
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import { Button, Group, Modal } from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconFilter } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { Filters } from "../components/Filters/Filters.tsx";
 | 
				
			||||||
 | 
					import { TableFormFilters } from "../../../types/TableFormFilters.ts";
 | 
				
			||||||
 | 
					import { UseFormReturnType } from "@mantine/form";
 | 
				
			||||||
 | 
					import { useDisclosure } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    form: UseFormReturnType<TableFormFilters>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfitTableFiltersModal = ({ form }: Props) => {
 | 
				
			||||||
 | 
					    const [opened, { open, close }] = useDisclosure();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                onClick={open}
 | 
				
			||||||
 | 
					                mb={"lg"}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Group gap={"xs"}>
 | 
				
			||||||
 | 
					                    <IconFilter />
 | 
				
			||||||
 | 
					                    Фильтры таблицы
 | 
				
			||||||
 | 
					                </Group>
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Modal
 | 
				
			||||||
 | 
					                opened={opened}
 | 
				
			||||||
 | 
					                onClose={close}
 | 
				
			||||||
 | 
					                title={"Фильтры таблицы"}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Filters
 | 
				
			||||||
 | 
					                    datePickerProps={form.getInputProps("dateRange")}
 | 
				
			||||||
 | 
					                    groupTableByProps={{
 | 
				
			||||||
 | 
					                        value: form.values.groupTableBy.toString(),
 | 
				
			||||||
 | 
					                        onChange: (value: string) => form.setFieldValue("groupTableBy", parseInt(value)),
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </Modal>
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/pages/StatisticsPage/types/ChartFormFilters.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/pages/StatisticsPage/types/ChartFormFilters.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import { BaseMarketplaceSchema, ClientSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { DealStatusType } from "../../../shared/enums/DealStatus.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ChartFormFilters {
 | 
				
			||||||
 | 
					    dateRange: [Date | null, Date | null];
 | 
				
			||||||
 | 
					    client: ClientSchema | null;
 | 
				
			||||||
 | 
					    marketplace: BaseMarketplaceSchema | null;
 | 
				
			||||||
 | 
					    dealStatus: DealStatusType | null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/pages/StatisticsPage/types/TableFormFilters.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/pages/StatisticsPage/types/TableFormFilters.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import { GroupStatisticsTable } from "../tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface TableFormFilters {
 | 
				
			||||||
 | 
					    dateRange: [Date | null, Date | null];
 | 
				
			||||||
 | 
					    groupTableBy: GroupStatisticsTable;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/pages/StatisticsPage/ui/StatisticsPage.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/pages/StatisticsPage/ui/StatisticsPage.module.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					.container {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    flex: 1;
 | 
				
			||||||
 | 
					    gap: rem(10);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.page-container {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					    gap: rem(10);
 | 
				
			||||||
 | 
					    height: 96vh;
 | 
				
			||||||
 | 
					    flex-wrap: wrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.top-panel {
 | 
				
			||||||
 | 
					    padding: rem(5);
 | 
				
			||||||
 | 
					    gap: rem(10);
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/pages/StatisticsPage/ui/StatisticsPage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/pages/StatisticsPage/ui/StatisticsPage.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    StatisticsTab,
 | 
				
			||||||
 | 
					    StatisticsTabSegmentedControl,
 | 
				
			||||||
 | 
					} from "../components/StatisticsTabSegmentedControl/StatisticsTabSegmentedControl.tsx";
 | 
				
			||||||
 | 
					import styles from "./StatisticsPage.module.css";
 | 
				
			||||||
 | 
					import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
 | 
				
			||||||
 | 
					import { ProfitTab } from "../tabs/ProfitTab/ProfitTab.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const StatisticsPage = () => {
 | 
				
			||||||
 | 
					    const [serviceType, setServiceType] = useState(StatisticsTab.PROFIT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getBody = () => {
 | 
				
			||||||
 | 
					        switch (serviceType) {
 | 
				
			||||||
 | 
					            case StatisticsTab.PROFIT:
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                    <ProfitTab />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            case StatisticsTab.SALARIES:
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                    <>Статистика по ЗП</>
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <div className={styles["container"]}>
 | 
				
			||||||
 | 
					            {/*<PageBlock>*/}
 | 
				
			||||||
 | 
					            {/*    <StatisticsTabSegmentedControl*/}
 | 
				
			||||||
 | 
					            {/*        size={"md"}*/}
 | 
				
			||||||
 | 
					            {/*        value={serviceType.toString()}*/}
 | 
				
			||||||
 | 
					            {/*        onChange={event => setServiceType(parseInt(event))}*/}
 | 
				
			||||||
 | 
					            {/*    />*/}
 | 
				
			||||||
 | 
					            {/*</PageBlock>*/}
 | 
				
			||||||
 | 
					            {getBody()}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/pages/StatisticsPage/utils/dates.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/pages/StatisticsPage/utils/dates.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					export const getDefaultDates = (): [Date, Date] => {
 | 
				
			||||||
 | 
					    const dateTo = new Date();
 | 
				
			||||||
 | 
					    const dateFrom = new Date();
 | 
				
			||||||
 | 
					    dateFrom.setDate(dateTo.getDate() - 28);
 | 
				
			||||||
 | 
					    return [dateFrom, dateTo];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const dateToString = (date: Date | null) => {
 | 
				
			||||||
 | 
					    if (date === null) return null;
 | 
				
			||||||
 | 
					    const month = date.getMonth() + 1;
 | 
				
			||||||
 | 
					    const day = date.getDate();
 | 
				
			||||||
 | 
					    const monthStr = month < 10 ? `0${month}` : month;
 | 
				
			||||||
 | 
					    const dayStr = day < 10 ? `0${day}` : day;
 | 
				
			||||||
 | 
					    return `${date.getFullYear()}-${monthStr}-${dayStr}`;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/routes/statistics.lazy.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/routes/statistics.lazy.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import { createLazyFileRoute } from "@tanstack/react-router";
 | 
				
			||||||
 | 
					import { StatisticsPage } from "../pages/StatisticsPage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Route = createLazyFileRoute("/statistics")({
 | 
				
			||||||
 | 
					    component: StatisticsPage,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Reference in New Issue
	
	Block a user