mts-crm/src/store/OrdersStore.ts
2026-04-29 15:27:21 +03:00

243 lines
5.8 KiB
TypeScript

import { makeAutoObservable, reaction, runInAction } from "mobx";
import { assignManager, changeStatus, getManagers, getOrders } from "~/api/api";
import {
AuthUserResponseRole,
ChangeOrderStatusApiRequestStatus,
type GetManagersParams,
type GetOrdersParams,
type ManagerListItemResponse,
type OrderApiResponse,
type PageManagerListItemResponse,
type PageOrderApiResponse,
} from "~/api/models";
import type { RootStore } from "./RootStore";
export class OrdersStore {
private readonly rootStore: RootStore;
orders: OrderApiResponse[] = [];
managers: ManagerListItemResponse[] = [];
isLoading = false;
isManagersLoading = false;
isSaving = false;
error = "";
constructor(rootStore: RootStore) {
this.rootStore = rootStore;
makeAutoObservable(this, {}, { autoBind: true });
reaction(
() => ({
userId: this.rootStore.authStore.user?.id,
role: this.rootStore.authStore.user?.role,
}),
({ userId, role }) => {
void this.syncSession(userId, role);
},
{ fireImmediately: true },
);
reaction(
() => this.rootStore.notificationsStore.messages[0],
(message) => {
if (!message) {
return;
}
if (!this.shouldRefetchByNotification(message.type)) {
return;
}
void this.fetchOrders();
},
);
}
get newOrdersCount() {
return this.orders.filter((order) => order.status === "NEW").length;
}
get placedOrdersCount() {
return this.orders.filter((order) => order.status === "PLACED").length;
}
get rejectedOrdersCount() {
return this.orders.filter((order) => order.status === "REJECTED").length;
}
async fetchOrders() {
this.isLoading = true;
this.error = "";
try {
const data = await this.fetchAllOrders();
runInAction(() => {
this.orders = data;
});
} catch (error) {
runInAction(() => {
this.error = this.extractErrorMessage(error);
});
throw error;
} finally {
runInAction(() => {
this.isLoading = false;
});
}
}
private async syncSession(userId?: string, role?: AuthUserResponseRole) {
if (!userId || !role) {
this.rootStore.notificationsStore.disconnect();
runInAction(() => {
this.orders = [];
this.managers = [];
});
return;
}
this.rootStore.notificationsStore.connect(userId, role);
try {
await this.fetchOrders();
if (role === AuthUserResponseRole.SENIOR_MANAGER) {
await this.fetchManagers();
return;
}
runInAction(() => {
this.managers = [];
});
} catch {
}
}
async fetchManagers() {
this.isManagersLoading = true;
this.error = "";
try {
const data = await this.fetchAllManagers();
runInAction(() => {
this.managers = data;
});
} catch (error) {
runInAction(() => {
this.error = this.extractErrorMessage(error);
});
throw error;
} finally {
runInAction(() => {
this.isManagersLoading = false;
});
}
}
async assignOrder(orderId: string, managerId: string) {
this.isSaving = true;
this.error = "";
try {
const updated = await assignManager(orderId, { managerId });
runInAction(() => {
this.upsertOrder(updated);
});
} catch (error) {
runInAction(() => {
this.error = this.extractErrorMessage(error);
});
throw error;
} finally {
runInAction(() => {
this.isSaving = false;
});
}
}
async setStatus(orderId: string, status: ChangeOrderStatusApiRequestStatus) {
this.isSaving = true;
this.error = "";
try {
const updated = await changeStatus(orderId, { status });
runInAction(() => {
this.upsertOrder(updated);
});
} catch (error) {
runInAction(() => {
this.error = this.extractErrorMessage(error);
});
throw error;
} finally {
runInAction(() => {
this.isSaving = false;
});
}
}
private upsertOrder(order: OrderApiResponse) {
const index = this.orders.findIndex((item) => item.id === order.id);
if (index === -1) {
this.orders = [order, ...this.orders];
return;
}
const next = [...this.orders];
next[index] = order;
this.orders = next;
}
private extractErrorMessage(error: unknown) {
if (typeof error === "object" && error !== null && "response" in error) {
const maybeResponse = error as { response?: { data?: { message?: string } } };
if (maybeResponse.response?.data?.message) {
return maybeResponse.response.data.message;
}
}
if (error instanceof Error && error.message) {
return error.message;
}
return "Произошла ошибка запроса.";
}
private shouldRefetchByNotification(type: string) {
return type === "NEW_ORDER_CREATED" || type === "ORDER_ASSIGNED" || type === "ORDER_STATUS_CHANGED";
}
private async fetchAllOrders() {
return this.fetchAllPages<OrderApiResponse, GetOrdersParams, PageOrderApiResponse>((params) => getOrders(params));
}
private async fetchAllManagers() {
return this.fetchAllPages<ManagerListItemResponse, GetManagersParams, PageManagerListItemResponse>((params) =>
getManagers(params),
);
}
private async fetchAllPages<TItem, TParams extends { page?: number; size?: number }, TPage extends {
content?: TItem[];
last?: boolean;
}>(loader: (params: TParams) => Promise<TPage>, size = 100) {
let page = 0;
const items: TItem[] = [];
while (true) {
const response = await loader({ page, size } as TParams);
items.push(...(response.content ?? []));
if (response.last ?? true) {
break;
}
page += 1;
}
return items;
}
}