feat: a lot of a lot
This commit is contained in:
@@ -44,6 +44,8 @@ export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarco
|
||||
export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse';
|
||||
export type { CreateDealBillRequest } from './models/CreateDealBillRequest';
|
||||
export type { CreateDealBillResponse } from './models/CreateDealBillResponse';
|
||||
export type { CreateMarketplaceRequest } from './models/CreateMarketplaceRequest';
|
||||
export type { CreateMarketplaceResponse } from './models/CreateMarketplaceResponse';
|
||||
export type { CreatePaymentRecordRequest } from './models/CreatePaymentRecordRequest';
|
||||
export type { CreatePaymentRecordResponse } from './models/CreatePaymentRecordResponse';
|
||||
export type { CreatePayRateRequest } from './models/CreatePayRateRequest';
|
||||
@@ -55,6 +57,7 @@ export type { CreateServicesKitRequest } from './models/CreateServicesKitRequest
|
||||
export type { CreateServicesKitResponse } from './models/CreateServicesKitResponse';
|
||||
export type { CreateShippingWarehouseRequest } from './models/CreateShippingWarehouseRequest';
|
||||
export type { CreateShippingWarehouseResponse } from './models/CreateShippingWarehouseResponse';
|
||||
export type { CreateTaskResponse } from './models/CreateTaskResponse';
|
||||
export type { CreateUserRequest } from './models/CreateUserRequest';
|
||||
export type { CreateUserResponse } from './models/CreateUserResponse';
|
||||
export type { DealAddKitRequest } from './models/DealAddKitRequest';
|
||||
@@ -107,6 +110,8 @@ export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServic
|
||||
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
|
||||
export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest';
|
||||
export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse';
|
||||
export type { DeleteMarketplaceRequest } from './models/DeleteMarketplaceRequest';
|
||||
export type { DeleteMarketplaceResponse } from './models/DeleteMarketplaceResponse';
|
||||
export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest';
|
||||
export type { DeletePaymentRecordResponse } from './models/DeletePaymentRecordResponse';
|
||||
export type { DeletePayRateRequest } from './models/DeletePayRateRequest';
|
||||
@@ -128,6 +133,8 @@ export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWa
|
||||
export type { GetAllUsersResponse } from './models/GetAllUsersResponse';
|
||||
export type { GetBarcodeTemplateByIdRequest } from './models/GetBarcodeTemplateByIdRequest';
|
||||
export type { GetBarcodeTemplateByIdResponse } from './models/GetBarcodeTemplateByIdResponse';
|
||||
export type { GetClientMarketplacesRequest } from './models/GetClientMarketplacesRequest';
|
||||
export type { GetClientMarketplacesResponse } from './models/GetClientMarketplacesResponse';
|
||||
export type { GetDealBillById } from './models/GetDealBillById';
|
||||
export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
|
||||
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
|
||||
@@ -138,6 +145,8 @@ export type { GetServiceKitSchema } from './models/GetServiceKitSchema';
|
||||
export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest';
|
||||
export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse';
|
||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||
export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema';
|
||||
export type { MarketplaceSchema } from './models/MarketplaceSchema';
|
||||
export type { NotificationChannel } from './models/NotificationChannel';
|
||||
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
|
||||
export type { PaymentRecordCreateSchema } from './models/PaymentRecordCreateSchema';
|
||||
@@ -177,8 +186,12 @@ 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 { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest';
|
||||
export type { TaskInfoResponse } from './models/TaskInfoResponse';
|
||||
export type { TimeTrackingData } from './models/TimeTrackingData';
|
||||
export type { TimeTrackingRecord } from './models/TimeTrackingRecord';
|
||||
export type { UpdateMarketplaceRequest } from './models/UpdateMarketplaceRequest';
|
||||
export type { UpdateMarketplaceResponse } from './models/UpdateMarketplaceResponse';
|
||||
export type { UpdatePayRateRequest } from './models/UpdatePayRateRequest';
|
||||
export type { UpdatePayRateResponse } from './models/UpdatePayRateResponse';
|
||||
export type { UpdateServiceKitSchema } from './models/UpdateServiceKitSchema';
|
||||
@@ -207,5 +220,6 @@ export { ProductService } from './services/ProductService';
|
||||
export { RoleService } from './services/RoleService';
|
||||
export { ServiceService } from './services/ServiceService';
|
||||
export { ShippingWarehouseService } from './services/ShippingWarehouseService';
|
||||
export { TaskService } from './services/TaskService';
|
||||
export { TimeTrackingService } from './services/TimeTrackingService';
|
||||
export { UserService } from './services/UserService';
|
||||
|
||||
9
src/client/models/CreateMarketplaceRequest.ts
Normal file
9
src/client/models/CreateMarketplaceRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { MarketplaceCreateSchema } from './MarketplaceCreateSchema';
|
||||
export type CreateMarketplaceRequest = {
|
||||
marketplace: MarketplaceCreateSchema;
|
||||
};
|
||||
|
||||
9
src/client/models/CreateMarketplaceResponse.ts
Normal file
9
src/client/models/CreateMarketplaceResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type CreateMarketplaceResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
8
src/client/models/CreateTaskResponse.ts
Normal file
8
src/client/models/CreateTaskResponse.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type CreateTaskResponse = {
|
||||
taskId: string;
|
||||
};
|
||||
|
||||
8
src/client/models/DeleteMarketplaceRequest.ts
Normal file
8
src/client/models/DeleteMarketplaceRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DeleteMarketplaceRequest = {
|
||||
marketplaceId: number;
|
||||
};
|
||||
|
||||
9
src/client/models/DeleteMarketplaceResponse.ts
Normal file
9
src/client/models/DeleteMarketplaceResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DeleteMarketplaceResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
8
src/client/models/GetClientMarketplacesRequest.ts
Normal file
8
src/client/models/GetClientMarketplacesRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type GetClientMarketplacesRequest = {
|
||||
clientId: number;
|
||||
};
|
||||
|
||||
9
src/client/models/GetClientMarketplacesResponse.ts
Normal file
9
src/client/models/GetClientMarketplacesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { MarketplaceSchema } from './MarketplaceSchema';
|
||||
export type GetClientMarketplacesResponse = {
|
||||
marketplaces: Array<MarketplaceSchema>;
|
||||
};
|
||||
|
||||
11
src/client/models/MarketplaceCreateSchema.ts
Normal file
11
src/client/models/MarketplaceCreateSchema.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type MarketplaceCreateSchema = {
|
||||
name: string;
|
||||
clientId: number;
|
||||
baseMarketplaceKey: string;
|
||||
authData: Record<string, any>;
|
||||
};
|
||||
|
||||
14
src/client/models/MarketplaceSchema.ts
Normal file
14
src/client/models/MarketplaceSchema.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { BaseMarketplaceSchema } from './BaseMarketplaceSchema';
|
||||
import type { ClientSchema } from './ClientSchema';
|
||||
export type MarketplaceSchema = {
|
||||
name: string;
|
||||
baseMarketplace: BaseMarketplaceSchema;
|
||||
client: ClientSchema;
|
||||
authData: Record<string, any>;
|
||||
id: number;
|
||||
};
|
||||
|
||||
8
src/client/models/SynchronizeMarketplaceRequest.ts
Normal file
8
src/client/models/SynchronizeMarketplaceRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type SynchronizeMarketplaceRequest = {
|
||||
marketplaceId: number;
|
||||
};
|
||||
|
||||
9
src/client/models/TaskInfoResponse.ts
Normal file
9
src/client/models/TaskInfoResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type TaskInfoResponse = {
|
||||
taskId: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
9
src/client/models/UpdateMarketplaceRequest.ts
Normal file
9
src/client/models/UpdateMarketplaceRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { MarketplaceSchema } from './MarketplaceSchema';
|
||||
export type UpdateMarketplaceRequest = {
|
||||
marketplace: MarketplaceSchema;
|
||||
};
|
||||
|
||||
9
src/client/models/UpdateMarketplaceResponse.ts
Normal file
9
src/client/models/UpdateMarketplaceResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type UpdateMarketplaceResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
@@ -28,4 +28,15 @@ export class AuthService {
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Test
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static testAuthTestPost(): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/auth/test',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ClientService {
|
||||
* @returns ClientCreateResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createClient({
|
||||
public static createClientApi({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: ClientCreateRequest,
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateMarketplaceRequest } from '../models/CreateMarketplaceRequest';
|
||||
import type { CreateMarketplaceResponse } from '../models/CreateMarketplaceResponse';
|
||||
import type { DeleteMarketplaceRequest } from '../models/DeleteMarketplaceRequest';
|
||||
import type { DeleteMarketplaceResponse } from '../models/DeleteMarketplaceResponse';
|
||||
import type { GetAllBaseMarketplacesResponse } from '../models/GetAllBaseMarketplacesResponse';
|
||||
import type { GetClientMarketplacesRequest } from '../models/GetClientMarketplacesRequest';
|
||||
import type { GetClientMarketplacesResponse } from '../models/GetClientMarketplacesResponse';
|
||||
import type { UpdateMarketplaceRequest } from '../models/UpdateMarketplaceRequest';
|
||||
import type { UpdateMarketplaceResponse } from '../models/UpdateMarketplaceResponse';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
@@ -18,4 +26,84 @@ export class MarketplaceService {
|
||||
url: '/marketplace/base/get-all',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get
|
||||
* @returns GetClientMarketplacesResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getClientMarketplaces({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: GetClientMarketplacesRequest,
|
||||
}): CancelablePromise<GetClientMarketplacesResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/marketplace/get',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create
|
||||
* @returns CreateMarketplaceResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createMarketplace({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: CreateMarketplaceRequest,
|
||||
}): CancelablePromise<CreateMarketplaceResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/marketplace/create',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Delete
|
||||
* @returns DeleteMarketplaceResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteMarketplace({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: DeleteMarketplaceRequest,
|
||||
}): CancelablePromise<DeleteMarketplaceResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/marketplace/delete',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Update
|
||||
* @returns UpdateMarketplaceResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateMarketplace({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: UpdateMarketplaceRequest,
|
||||
}): CancelablePromise<UpdateMarketplaceResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/marketplace/update',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
53
src/client/services/TaskService.ts
Normal file
53
src/client/services/TaskService.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateTaskResponse } from '../models/CreateTaskResponse';
|
||||
import type { SynchronizeMarketplaceRequest } from '../models/SynchronizeMarketplaceRequest';
|
||||
import type { TaskInfoResponse } from '../models/TaskInfoResponse';
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
export class TaskService {
|
||||
/**
|
||||
* Synchronize Marketplace
|
||||
* @returns CreateTaskResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createSynchronizeMarketplaceTask({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: SynchronizeMarketplaceRequest,
|
||||
}): CancelablePromise<CreateTaskResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/task/synchronize-marketplace',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Task Info
|
||||
* @returns TaskInfoResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTaskInfo({
|
||||
taskId,
|
||||
}: {
|
||||
taskId: string,
|
||||
}): CancelablePromise<TaskInfoResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/task/info/{task_id}',
|
||||
path: {
|
||||
'task_id': taskId,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
IconHome2,
|
||||
IconLogout,
|
||||
IconMan,
|
||||
IconMoon,
|
||||
IconMoon, IconShoppingCart,
|
||||
IconSun,
|
||||
} from '@tabler/icons-react';
|
||||
import classes from './Navbar.module.css';
|
||||
@@ -76,6 +76,11 @@ const mockdata = [
|
||||
icon: IconBuildingWarehouse,
|
||||
label: 'Склады отгрузки',
|
||||
href: '/shipping_warehouses'
|
||||
},
|
||||
{
|
||||
icon:IconShoppingCart,
|
||||
label: 'Маркетплейсы',
|
||||
href: '/marketplaces'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const ClientSelectNew: FC<Props> = (props) => {
|
||||
const {clients} = useClientsList();
|
||||
return (
|
||||
<ObjectSelect
|
||||
searchable
|
||||
data={clients}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
101
src/features/tasksSlice.tsx
Normal file
101
src/features/tasksSlice.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
|
||||
import {notifications} from "../shared/lib/notifications.ts";
|
||||
import {IconCheck, IconX} from "@tabler/icons-react";
|
||||
import {rem} from "@mantine/core";
|
||||
|
||||
export type TaskData = {
|
||||
title: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type TaskConfig = {
|
||||
onSuccessData: TaskData;
|
||||
onErrorData: TaskData;
|
||||
onLoadingData: TaskData;
|
||||
};
|
||||
|
||||
export type Task = {
|
||||
id: string;
|
||||
config: TaskConfig;
|
||||
info: Record<string, unknown>;
|
||||
};
|
||||
|
||||
interface TasksState {
|
||||
tasks: Task[];
|
||||
notificationTaskMap: { [key: string]: string };
|
||||
}
|
||||
|
||||
const initialState: TasksState = {
|
||||
tasks: [],
|
||||
notificationTaskMap: {},
|
||||
};
|
||||
|
||||
const tasksSlice = createSlice({
|
||||
name: "tasks",
|
||||
initialState,
|
||||
reducers: {
|
||||
addTask: (state, action: PayloadAction<Task>) => {
|
||||
const task = action.payload;
|
||||
state.notificationTaskMap[task.id] = notifications.show({
|
||||
loading: true,
|
||||
title: task.config.onLoadingData.title,
|
||||
message: task.config.onLoadingData.message,
|
||||
autoClose: false,
|
||||
withCloseButton: false,
|
||||
withBorder: true,
|
||||
radius: "sm"
|
||||
});
|
||||
state.tasks.push(task);
|
||||
localStorage.setItem("tasks", JSON.stringify(state.tasks));
|
||||
},
|
||||
removeTask: (state, action: PayloadAction<string>) => {
|
||||
state.tasks = state.tasks.filter((task) => task.id !== action.payload);
|
||||
localStorage.setItem("tasks", JSON.stringify(state.tasks));
|
||||
},
|
||||
failTask: (state, action: PayloadAction<Task>) => {
|
||||
const task = action.payload;
|
||||
const notificationId = state.notificationTaskMap[task.id];
|
||||
|
||||
if (!notificationId) return;
|
||||
notifications.update({
|
||||
id: notificationId,
|
||||
color: 'red',
|
||||
title: task.config.onErrorData.title,
|
||||
message: task.config.onErrorData.message,
|
||||
icon: <IconX style={{width: rem(18), height: rem(18)}}/>,
|
||||
loading: false,
|
||||
autoClose: 2000,
|
||||
})
|
||||
state.tasks = state.tasks.filter((task) => task.id !== action.payload.id);
|
||||
state.notificationTaskMap = Object.fromEntries(
|
||||
Object.entries(state.notificationTaskMap).filter(([taskId]) => taskId !== task.id)
|
||||
);
|
||||
localStorage.setItem("tasks", JSON.stringify(state.tasks));
|
||||
},
|
||||
successTask: (state, action: PayloadAction<Task>) => {
|
||||
const task = action.payload;
|
||||
const notificationId = state.notificationTaskMap[task.id];
|
||||
|
||||
if (!notificationId) return;
|
||||
notifications.update({
|
||||
id: notificationId,
|
||||
color: 'teal',
|
||||
title: task.config.onSuccessData.title,
|
||||
message: task.config.onSuccessData.message,
|
||||
icon: <IconCheck style={{width: rem(18), height: rem(18)}}/>,
|
||||
loading: false,
|
||||
autoClose: 2000,
|
||||
})
|
||||
state.tasks = state.tasks.filter((task) => task.id !== action.payload.id);
|
||||
state.notificationTaskMap = Object.fromEntries(
|
||||
Object.entries(state.notificationTaskMap).filter(([taskId]) => taskId !== task.id)
|
||||
);
|
||||
|
||||
localStorage.setItem("tasks", JSON.stringify(state.tasks));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const {addTask, removeTask, successTask, failTask} = tasksSlice.actions;
|
||||
|
||||
export default tasksSlice.reducer;
|
||||
54
src/hooks/usePollingEffect.tsx
Normal file
54
src/hooks/usePollingEffect.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import {useEffect, useRef, DependencyList} from 'react';
|
||||
|
||||
type UsePollingEffectOptions = {
|
||||
interval?: number;
|
||||
isActive?: boolean;
|
||||
onCleanUp?: () => void;
|
||||
};
|
||||
|
||||
function usePollingEffect(
|
||||
asyncCallback: () => Promise<void>,
|
||||
dependencies: DependencyList = [],
|
||||
options: UsePollingEffectOptions = {}
|
||||
): void {
|
||||
const {
|
||||
interval = 3000,
|
||||
isActive = true,
|
||||
onCleanUp = () => {
|
||||
}
|
||||
} = options;
|
||||
|
||||
const timeoutIdRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive) { // If not active, don't do anything
|
||||
return;
|
||||
}
|
||||
|
||||
let stopped = false;
|
||||
|
||||
const pollingCallback = async () => {
|
||||
try {
|
||||
await asyncCallback();
|
||||
} finally {
|
||||
if (!stopped) {
|
||||
timeoutIdRef.current = setTimeout(pollingCallback, interval);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Immediately invoke the polling callback when the effect runs
|
||||
pollingCallback();
|
||||
|
||||
// Clean up function to clear the timeout if the component unmounts or dependencies change
|
||||
return () => {
|
||||
stopped = true; // This will prevent new timeouts from being scheduled
|
||||
if (timeoutIdRef.current) {
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
}
|
||||
onCleanUp();
|
||||
};
|
||||
}, [...dependencies, interval, isActive]); // dependencies array spread with interval
|
||||
}
|
||||
|
||||
export default usePollingEffect;
|
||||
@@ -19,6 +19,7 @@ import {ModalsProvider} from "@mantine/modals";
|
||||
import {OpenAPI} from "./client";
|
||||
import {DatesProvider} from "@mantine/dates";
|
||||
import {modals} from "./modals/modals.ts";
|
||||
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
|
||||
|
||||
// Configuring router
|
||||
const router = createRouter({routeTree})
|
||||
@@ -46,8 +47,12 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<MantineProvider defaultColorScheme={"dark"}>
|
||||
<ModalsProvider modals={modals}>
|
||||
<DatesProvider settings={{locale: 'ru'}}>
|
||||
<RouterProvider router={router}/>
|
||||
<Notifications/>
|
||||
<TasksProvider>
|
||||
|
||||
<RouterProvider router={router}/>
|
||||
<Notifications/>
|
||||
</TasksProvider>
|
||||
|
||||
</DatesProvider>
|
||||
</ModalsProvider>
|
||||
</MantineProvider>
|
||||
|
||||
@@ -20,6 +20,7 @@ import ServiceKitModalForm from "../pages/ServicesPage/modals/ServicesKitModalFo
|
||||
import ServicesKitSelectModal from "./ServicesKitSelectModal/ServicesKitSelectModal.tsx";
|
||||
import SelectDealProductsModal from "../pages/LeadsPage/modals/SelectDealProductsModal.tsx";
|
||||
import ShippingWarehouseForm from "../pages/ShippingWarehousesPage/modals/ShippingWarehouseForm.tsx";
|
||||
import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFormModal/MarketplaceFormModal.tsx";
|
||||
|
||||
export const modals = {
|
||||
enterDeadline: EnterDeadlineModal,
|
||||
@@ -42,5 +43,6 @@ export const modals = {
|
||||
serviceKitModalForm: ServiceKitModalForm,
|
||||
servicesKitSelectModal: ServicesKitSelectModal,
|
||||
selectDealProductsModal: SelectDealProductsModal,
|
||||
shippingWarehouseForm: ShippingWarehouseForm
|
||||
shippingWarehouseForm: ShippingWarehouseForm,
|
||||
marketplaceFormModal: MarketplaceFormModal
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||
import {ClientSchema, MarketplaceSchema} from "../../../../client";
|
||||
import {FC} from "react";
|
||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import useMarketplacesTableColumns from "./columns.tsx";
|
||||
import {MRT_TableOptions} from "mantine-react-table";
|
||||
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {IconEdit, IconRefresh, IconTrash} from "@tabler/icons-react";
|
||||
|
||||
type RestProps = {
|
||||
client?: ClientSchema;
|
||||
onSynchronize?: (marketplace: MarketplaceSchema) => void;
|
||||
|
||||
}
|
||||
type Props = CRUDTableProps<MarketplaceSchema> & RestProps;
|
||||
|
||||
const MarketplacesTable: FC<Props> = ({onDelete, onChange, onCreate, items, client, onSynchronize}) => {
|
||||
const columns = useMarketplacesTableColumns();
|
||||
const onDeleteClick = (marketplace: MarketplaceSchema) => {
|
||||
if (!onDelete) return;
|
||||
modals.openConfirmModal({
|
||||
title: 'Удаление маркетплейса',
|
||||
children: (
|
||||
<Text size="sm">
|
||||
Вы уверены что хотите удалить маркетплейс {marketplace.name}
|
||||
</Text>
|
||||
),
|
||||
labels: {confirm: 'Да', cancel: "Нет"},
|
||||
confirmProps: {color: 'red'},
|
||||
onConfirm: () => onDelete(marketplace)
|
||||
});
|
||||
}
|
||||
const onEditClick = (marketplace: MarketplaceSchema) => {
|
||||
if (!onChange) return;
|
||||
modals.openContextModal({
|
||||
modal: "marketplaceFormModal",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
onChange: (event) => onChange(event),
|
||||
element: marketplace
|
||||
}
|
||||
})
|
||||
}
|
||||
const onCreateClick = () => {
|
||||
if (!onCreate || !client) return;
|
||||
modals.openContextModal({
|
||||
modal: "marketplaceFormModal",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
onCreate: (event) => onCreate({...event, client: client})
|
||||
}
|
||||
})
|
||||
}
|
||||
return (
|
||||
<BaseTable
|
||||
data={items}
|
||||
columns={columns}
|
||||
restProps={{
|
||||
enableSorting: false,
|
||||
enableColumnActions: false,
|
||||
enableTopToolbar: true,
|
||||
renderTopToolbar: (
|
||||
<Flex p={rem(10)} justify="end">
|
||||
<Button
|
||||
disabled={!client}
|
||||
variant={"default"}
|
||||
onClick={() => onCreateClick()}>
|
||||
Добавить
|
||||
</Button>
|
||||
</Flex>
|
||||
),
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({row}) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
|
||||
<IconTrash/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Синхронизировать">
|
||||
<ActionIcon onClick={() => onSynchronize && onSynchronize(row.original)}
|
||||
variant={"default"}>
|
||||
<IconRefresh/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
)
|
||||
} as MRT_TableOptions<MarketplaceSchema>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default MarketplacesTable;
|
||||
@@ -0,0 +1,35 @@
|
||||
import {MarketplaceSchema} from "../../../../client";
|
||||
import {MRT_ColumnDef} from "mantine-react-table";
|
||||
import {useMemo} from "react";
|
||||
import {ActionIcon, Image} from "@mantine/core";
|
||||
|
||||
const useMarketplacesTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<MarketplaceSchema>[]>(() => [
|
||||
{
|
||||
header: "Маркетплейс",
|
||||
size: 10,
|
||||
Cell: ({row}) => (
|
||||
<ActionIcon variant={"transparent"}>
|
||||
<Image src={row.original.baseMarketplace?.iconUrl || ""}/>
|
||||
</ActionIcon>
|
||||
)
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "Название",
|
||||
enableSorting: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "client.name",
|
||||
header: "Клиент",
|
||||
enableSorting: false,
|
||||
},
|
||||
// {
|
||||
// accessorKey: "authData",
|
||||
// header: "Данные авторизации",
|
||||
// enableSorting: false,
|
||||
// },
|
||||
], []);
|
||||
}
|
||||
|
||||
export default useMarketplacesTableColumns;
|
||||
115
src/pages/MarketplacesPage/hooks/useMarketplacesPageState.tsx
Normal file
115
src/pages/MarketplacesPage/hooks/useMarketplacesPageState.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {ClientSchema, MarketplaceSchema, MarketplaceService, TaskService} from "../../../client";
|
||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
||||
import {RootState, useAppDispatch} from "../../../redux/store.ts";
|
||||
import {addTask} from "../../../features/tasksSlice.tsx";
|
||||
import {useSelector} from "react-redux";
|
||||
|
||||
const useMarketplacesPageState = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const tasks = useSelector((state: RootState) => state.tasks.tasks);
|
||||
const [client, setClient] = useState<ClientSchema | undefined>();
|
||||
const [items, setItems] = useState<MarketplaceSchema[]>([]);
|
||||
const fetchMarketplaces = async () => {
|
||||
if (!client) return;
|
||||
MarketplaceService.getClientMarketplaces({
|
||||
requestBody: {
|
||||
clientId: client.id
|
||||
}
|
||||
}).then((response) => {
|
||||
setItems(response.marketplaces);
|
||||
})
|
||||
}
|
||||
|
||||
const onCreate = (marketplace: MarketplaceSchema) => {
|
||||
MarketplaceService.createMarketplace({
|
||||
requestBody: {
|
||||
marketplace: {
|
||||
...marketplace,
|
||||
clientId: marketplace.client.id,
|
||||
baseMarketplaceKey: marketplace.baseMarketplace.key
|
||||
}
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
notifications.guess(ok, {message});
|
||||
if (!ok) return;
|
||||
await fetchMarketplaces();
|
||||
})
|
||||
}
|
||||
const onDelete = (marketplace: MarketplaceSchema) => {
|
||||
MarketplaceService.deleteMarketplace({
|
||||
requestBody: {
|
||||
marketplaceId: marketplace.id
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
notifications.guess(ok, {message});
|
||||
if (!ok) return;
|
||||
await fetchMarketplaces();
|
||||
})
|
||||
}
|
||||
const onChange = (marketplace: MarketplaceSchema) => {
|
||||
MarketplaceService.updateMarketplace({
|
||||
requestBody: {
|
||||
marketplace: marketplace
|
||||
}
|
||||
}).then(async ({ok, message}) => {
|
||||
notifications.guess(ok, {message});
|
||||
if (!ok) return;
|
||||
await fetchMarketplaces();
|
||||
})
|
||||
}
|
||||
|
||||
const onSynchronize = (marketplace: MarketplaceSchema) => {
|
||||
|
||||
// If there is already synchronization task for this marketplace show notifications.error()
|
||||
const task = tasks.find(task => task.info.marketplaceId === marketplace.id);
|
||||
if (task) {
|
||||
notifications.error({
|
||||
title: 'Ошибка',
|
||||
message: `Синхронизация маркетплейса ${marketplace.name} уже запущена`
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
TaskService.createSynchronizeMarketplaceTask({
|
||||
requestBody: {
|
||||
marketplaceId:
|
||||
marketplace.id
|
||||
}
|
||||
}).then(({taskId}) => {
|
||||
dispatch(addTask({
|
||||
id: taskId,
|
||||
config: {
|
||||
onErrorData: {
|
||||
title: 'Ошибка',
|
||||
message: `Ошибка синхронизации маркетплейса: ${marketplace.name}`
|
||||
},
|
||||
onLoadingData: {
|
||||
title: 'Синхронизация',
|
||||
message: `Синхронизация маркетплейса: ${marketplace.name}`
|
||||
},
|
||||
onSuccessData: {
|
||||
title: 'Успех',
|
||||
message: `Маркетплейс ${marketplace.name} успешно синхронизирован`
|
||||
}
|
||||
},
|
||||
info: {
|
||||
marketplaceId: marketplace.id
|
||||
}
|
||||
}));
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
fetchMarketplaces();
|
||||
}, [client]);
|
||||
return {
|
||||
client,
|
||||
setClient,
|
||||
items,
|
||||
onDelete,
|
||||
onChange,
|
||||
onCreate,
|
||||
onSynchronize
|
||||
}
|
||||
}
|
||||
export default useMarketplacesPageState;
|
||||
1
src/pages/MarketplacesPage/index.ts
Normal file
1
src/pages/MarketplacesPage/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export {MarketplacesPage} from './ui/MarketplacesPage.tsx';
|
||||
@@ -0,0 +1,71 @@
|
||||
import {TextInput} from "@mantine/core";
|
||||
import {BaseFormInputProps} from "../../../../types/utils.ts";
|
||||
import {FC} from "react";
|
||||
import {BaseMarketplaceSchema} from "../../../../client";
|
||||
import {BaseMarketplaceType} from "../../../../shared/enums/BaseMarketplaceType.ts";
|
||||
|
||||
type RestProps = {
|
||||
baseMarketplace: BaseMarketplaceSchema;
|
||||
}
|
||||
type Props = BaseFormInputProps<Record<string, string>> & RestProps;
|
||||
|
||||
const MarketplaceAuthDataInput: FC<Props> = (props: Props) => {
|
||||
console.log(props.baseMarketplace);
|
||||
const getWildberriesInputs = () => {
|
||||
// return input that sets record "Authorization" to value
|
||||
return <TextInput
|
||||
{...props}
|
||||
label={"Ключ авторизации"}
|
||||
placeholder={"Введите ключ авторизации"}
|
||||
value={props.value["Authorization"] || ""}
|
||||
onChange={(value) => props.onChange({...props.value, Authorization: value.target.value})}
|
||||
/>
|
||||
|
||||
}
|
||||
const getOzonInputs = () => {
|
||||
// return input that sets record "Client-Id" and "Api-Key" to value
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
{...props}
|
||||
label={"Client-Id"}
|
||||
placeholder={"Введите Client-Id"}
|
||||
value={props.value["Client-Id"] || ""}
|
||||
onChange={(value) => props.onChange({...props.value, "Client-Id": value.target.value})}
|
||||
/>
|
||||
<TextInput
|
||||
{...props}
|
||||
label={"Api-Key"}
|
||||
placeholder={"Введите Api-Key"}
|
||||
value={props.value["Api-Key"] || ""}
|
||||
onChange={(value) => props.onChange({...props.value, "Api-Key": value.target.value})}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
const getYandexMarketInputs = () => {
|
||||
}
|
||||
|
||||
const getInputs = () => {
|
||||
if (props.baseMarketplace.key === BaseMarketplaceType.WILDBERRIES) {
|
||||
return getWildberriesInputs();
|
||||
}
|
||||
if (props.baseMarketplace.key === BaseMarketplaceType.OZON) {
|
||||
return getOzonInputs();
|
||||
}
|
||||
if (props.baseMarketplace.key === BaseMarketplaceType.YANDEX_MARKET) {
|
||||
return getYandexMarketInputs();
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{getInputs()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarketplaceAuthDataInput;
|
||||
@@ -0,0 +1,70 @@
|
||||
import {ContextModalProps} from "@mantine/modals";
|
||||
import BaseFormModal, {CreateEditFormProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import {MarketplaceSchema} from "../../../../client";
|
||||
import {useForm} from "@mantine/form";
|
||||
import {Fieldset, Flex, rem, TextInput} from "@mantine/core";
|
||||
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||
import MarketplaceAuthDataInput from "./MarketplaceAuthDataInput.tsx";
|
||||
|
||||
type Props = CreateEditFormProps<MarketplaceSchema>
|
||||
const MarketplaceFormModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps
|
||||
}: ContextModalProps<Props>) => {
|
||||
const isEditing = 'element' in innerProps;
|
||||
const initialValue: Partial<MarketplaceSchema> = isEditing ? innerProps.element : {
|
||||
authData: {
|
||||
Authorization: '',
|
||||
"Client-Id": '',
|
||||
"Api-Key": ''
|
||||
|
||||
},
|
||||
};
|
||||
const form = useForm<Partial<MarketplaceSchema>>({
|
||||
initialValues: initialValue,
|
||||
validate: {
|
||||
baseMarketplace: (baseMarketplace) => !baseMarketplace && "Необходимо указать базовый маркетплейс",
|
||||
name: (name) => !name && "Необходимо указать название маркетплейса",
|
||||
authData: (authData) => !authData && "Необходимо указать данные авторизации"
|
||||
}
|
||||
});
|
||||
return (
|
||||
<BaseFormModal
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={() => context.closeContextModal(id)}
|
||||
{...innerProps}
|
||||
>
|
||||
<BaseFormModal.Body>
|
||||
<>
|
||||
<Fieldset legend={"Общие параметры"}>
|
||||
<Flex direction={"column"} gap={rem(10)}>
|
||||
<TextInput
|
||||
label={"Название"}
|
||||
placeholder={"Введите название маркетплейса"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<BaseMarketplaceSelect
|
||||
label={"Базовый маркетплейс"}
|
||||
placeholder={"Выберите базовый маркетплейс"}
|
||||
{...form.getInputProps("baseMarketplace")}
|
||||
/>
|
||||
|
||||
{form.values.baseMarketplace &&
|
||||
<MarketplaceAuthDataInput
|
||||
baseMarketplace={form.values.baseMarketplace}
|
||||
value={form.values.authData as Record<string, string>}
|
||||
onChange={(value) => form.setFieldValue("authData", value)}
|
||||
error={form.getInputProps("authData").error}
|
||||
/>
|
||||
}
|
||||
|
||||
</Flex>
|
||||
</Fieldset>
|
||||
</>
|
||||
</BaseFormModal.Body>
|
||||
</BaseFormModal>
|
||||
)
|
||||
}
|
||||
export default MarketplaceFormModal;
|
||||
17
src/pages/MarketplacesPage/ui/MarketplacesPage.module.css
Normal file
17
src/pages/MarketplacesPage/ui/MarketplacesPage.module.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
gap: rem(10);
|
||||
}
|
||||
|
||||
.top-panel {
|
||||
padding: rem(5);
|
||||
gap: rem(10);
|
||||
display: flex;
|
||||
|
||||
}
|
||||
|
||||
.top-panel-last-item {
|
||||
margin-left: auto;
|
||||
}
|
||||
29
src/pages/MarketplacesPage/ui/MarketplacesPage.tsx
Normal file
29
src/pages/MarketplacesPage/ui/MarketplacesPage.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import styles from './MarketplacesPage.module.css';
|
||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
||||
import useMarketplacesPageState from "../hooks/useMarketplacesPageState.tsx";
|
||||
import MarketplacesTable from "../components/MarketplacesTable/MarketplacesTable.tsx";
|
||||
|
||||
export const MarketplacesPage = () => {
|
||||
const state = useMarketplacesPageState();
|
||||
return (
|
||||
<div className={styles['container']}>
|
||||
<PageBlock>
|
||||
<div className={styles['top-panel']}>
|
||||
<ClientSelectNew
|
||||
placeholder={'Выберите клиента'}
|
||||
onChange={state.setClient}
|
||||
/>
|
||||
</div>
|
||||
</PageBlock>
|
||||
<PageBlock>
|
||||
<>
|
||||
<MarketplacesTable
|
||||
{...state}
|
||||
/>
|
||||
|
||||
</>
|
||||
</PageBlock>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
63
src/providers/TasksProvider/TasksProvider.tsx
Normal file
63
src/providers/TasksProvider/TasksProvider.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import {FC, ReactNode, useEffect, useState} from "react";
|
||||
import {TaskStatus} from "../../shared/enums/TaskStatus";
|
||||
import usePollingEffect from "../../hooks/usePollingEffect.tsx";
|
||||
import {RootState, useAppDispatch} from "../../redux/store.ts";
|
||||
import {useSelector} from "react-redux";
|
||||
import {TaskService} from "../../client";
|
||||
import {addTask, failTask, successTask, Task} from "../../features/tasksSlice.tsx";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const POLLING_STATUSES = [
|
||||
TaskStatus.RETRY,
|
||||
TaskStatus.STARTED,
|
||||
TaskStatus.PENDING,
|
||||
] as string[];
|
||||
|
||||
const TasksProvider: FC<Props> = ({children}) => {
|
||||
const [isPooling, setIsPooling] = useState(false);
|
||||
const tasks = useSelector((state: RootState) => state.tasks.tasks);
|
||||
const notificationTaskMap = useSelector((state: RootState) => state.tasks.notificationTaskMap);
|
||||
const dispatch = useAppDispatch();
|
||||
const poolTasks = async () => {
|
||||
// get statuses of all tasks
|
||||
const taskInfos = await Promise.all(
|
||||
tasks.map((task) => TaskService.getTaskInfo({taskId: task.id}))
|
||||
);
|
||||
taskInfos.forEach(({taskId, status}) => {
|
||||
if (POLLING_STATUSES.includes(status)) return;
|
||||
const task = tasks.find((task) => task.id === taskId);
|
||||
if (!task) return;
|
||||
if (status === TaskStatus.FAILURE) {
|
||||
dispatch(failTask(task));
|
||||
} else if (status === TaskStatus.SUCCESS) {
|
||||
dispatch(successTask(task));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
usePollingEffect(
|
||||
poolTasks,
|
||||
[tasks, notificationTaskMap, isPooling],
|
||||
{interval: 1000, isActive: isPooling && tasks.length > 0}
|
||||
)
|
||||
useEffect(() => {
|
||||
if (tasks.length === 0) {
|
||||
setIsPooling(false);
|
||||
return
|
||||
}
|
||||
setIsPooling(true);
|
||||
}, [tasks]);
|
||||
useEffect(() => {
|
||||
// loading from localstorage
|
||||
const tasks = JSON.parse(localStorage.getItem("tasks") || "[]");
|
||||
tasks.forEach((task: Task) => {
|
||||
dispatch(addTask(task));
|
||||
});
|
||||
}, []);
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default TasksProvider;
|
||||
@@ -2,11 +2,13 @@ import {configureStore} from "@reduxjs/toolkit";
|
||||
import {useDispatch} from "react-redux";
|
||||
import authReducer from '../features/authSlice';
|
||||
import uiReducer from '../features/uiSlice';
|
||||
import tasksReducer from '../features/tasksSlice.tsx';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
auth: authReducer,
|
||||
ui: uiReducer
|
||||
ui: uiReducer,
|
||||
tasks: tasksReducer
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ const TestLazyImport = createFileRoute('/test')()
|
||||
const ShippingwarehousesLazyImport = createFileRoute('/shipping_warehouses')()
|
||||
const ServicesLazyImport = createFileRoute('/services')()
|
||||
const ProductsLazyImport = createFileRoute('/products')()
|
||||
const MarketplacesLazyImport = createFileRoute('/marketplaces')()
|
||||
const LoginLazyImport = createFileRoute('/login')()
|
||||
const LeadsLazyImport = createFileRoute('/leads')()
|
||||
const ClientsLazyImport = createFileRoute('/clients')()
|
||||
@@ -52,6 +53,11 @@ const ProductsLazyRoute = ProductsLazyImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any).lazy(() => import('./routes/products.lazy').then((d) => d.Route))
|
||||
|
||||
const MarketplacesLazyRoute = MarketplacesLazyImport.update({
|
||||
path: '/marketplaces',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any).lazy(() => import('./routes/marketplaces.lazy').then((d) => d.Route))
|
||||
|
||||
const LoginLazyRoute = LoginLazyImport.update({
|
||||
path: '/login',
|
||||
getParentRoute: () => rootRoute,
|
||||
@@ -133,6 +139,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof LoginLazyImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/marketplaces': {
|
||||
id: '/marketplaces'
|
||||
path: '/marketplaces'
|
||||
fullPath: '/marketplaces'
|
||||
preLoaderRoute: typeof MarketplacesLazyImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/products': {
|
||||
id: '/products'
|
||||
path: '/products'
|
||||
@@ -180,6 +193,7 @@ export const routeTree = rootRoute.addChildren({
|
||||
ClientsLazyRoute,
|
||||
LeadsLazyRoute,
|
||||
LoginLazyRoute,
|
||||
MarketplacesLazyRoute,
|
||||
ProductsLazyRoute,
|
||||
ServicesLazyRoute,
|
||||
ShippingwarehousesLazyRoute,
|
||||
@@ -201,6 +215,7 @@ export const routeTree = rootRoute.addChildren({
|
||||
"/clients",
|
||||
"/leads",
|
||||
"/login",
|
||||
"/marketplaces",
|
||||
"/products",
|
||||
"/services",
|
||||
"/shipping_warehouses",
|
||||
@@ -226,6 +241,9 @@ export const routeTree = rootRoute.addChildren({
|
||||
"/login": {
|
||||
"filePath": "login.lazy.tsx"
|
||||
},
|
||||
"/marketplaces": {
|
||||
"filePath": "marketplaces.lazy.tsx"
|
||||
},
|
||||
"/products": {
|
||||
"filePath": "products.lazy.tsx"
|
||||
},
|
||||
|
||||
6
src/routes/marketplaces.lazy.tsx
Normal file
6
src/routes/marketplaces.lazy.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import {createLazyFileRoute} from '@tanstack/react-router'
|
||||
import {MarketplacesPage} from "../pages/MarketplacesPage";
|
||||
|
||||
export const Route = createLazyFileRoute('/marketplaces')({
|
||||
component: MarketplacesPage
|
||||
})
|
||||
6
src/shared/enums/BaseMarketplaceType.ts
Normal file
6
src/shared/enums/BaseMarketplaceType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum BaseMarketplaceType {
|
||||
WILDBERRIES = 'wb',
|
||||
OZON = 'ozon',
|
||||
YANDEX_MARKET = 'ym'
|
||||
|
||||
}
|
||||
7
src/shared/enums/TaskStatus.ts
Normal file
7
src/shared/enums/TaskStatus.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum TaskStatus {
|
||||
PENDING = "PENDING",
|
||||
STARTED = "STARTED",
|
||||
RETRY = "RETRY",
|
||||
FAILURE = "FAILURE",
|
||||
SUCCESS = "SUCCESS",
|
||||
}
|
||||
Reference in New Issue
Block a user