Files
web-frontend/app/composables/useOrderStatusPresentation.ts
2026-04-04 10:42:40 +07:00

214 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

type OrderStatusCode =
| 'NEW'
| 'MANAGER_PROCESSING'
| 'WAITING_DOUBLE_CONFIRM'
| 'CLIENT_REJECTED'
| 'MANAGER_REJECTED'
| 'MANAGER_BLOCKED'
| 'CONFIRMED'
| 'IN_PROGRESS'
| 'COMPLETED';
type TimelineStage = {
code: string;
label: string;
note: string;
dateLabel: string;
state: 'done' | 'current' | 'upcoming';
};
type StatusPresentation = {
title: string;
summary: string;
stages: TimelineStage[];
};
const STAGE_ORDER: OrderStatusCode[] = [
'NEW',
'MANAGER_PROCESSING',
'WAITING_DOUBLE_CONFIRM',
'CONFIRMED',
'IN_PROGRESS',
'COMPLETED',
];
const DAY_FORMATTER = new Intl.DateTimeFormat('ru-RU', {
day: 'numeric',
month: 'long',
});
function addDays(date: Date, days: number) {
const next = new Date(date);
next.setDate(next.getDate() + days);
return next;
}
function formatDay(date: Date) {
return DAY_FORMATTER.format(date);
}
function stageIndex(status: string) {
const index = STAGE_ORDER.indexOf(status as OrderStatusCode);
return index >= 0 ? index : 0;
}
function buildDates(createdAt: string | Date) {
const base = new Date(createdAt);
return {
created: base,
offer: addDays(base, 1),
approval: addDays(base, 2),
production: addDays(base, 4),
shipment: addDays(base, 6),
delivered: addDays(base, 8),
};
}
export function getOrderStatusPresentation(status: string, createdAt: string | Date): StatusPresentation {
const dates = buildDates(createdAt);
if (status === 'CLIENT_REJECTED') {
return {
title: 'Заказ остановлен клиентом',
summary: 'Согласование остановлено. Если нужно, заказ можно собрать заново с новыми условиями.',
stages: [
{
code: 'NEW',
label: 'Заявка принята',
note: 'Заказ попал в обработку.',
dateLabel: formatDay(dates.created),
state: 'done',
},
{
code: 'CLIENT_REJECTED',
label: 'Клиент отказался от продолжения',
note: 'Текущий заказ закрыт без запуска в работу.',
dateLabel: formatDay(dates.approval),
state: 'current',
},
],
};
}
if (status === 'MANAGER_REJECTED' || status === 'MANAGER_BLOCKED') {
return {
title: status === 'MANAGER_BLOCKED' ? 'Заказ ждёт уточнения' : 'Заказ остановлен менеджером',
summary: status === 'MANAGER_BLOCKED'
? 'Сейчас ждём уточнение параметров, после него заказ вернётся в работу.'
: 'Менеджер остановил обработку. При необходимости можно собрать новый заказ.',
stages: [
{
code: 'NEW',
label: 'Заявка принята',
note: 'Заказ попал в обработку.',
dateLabel: formatDay(dates.created),
state: 'done',
},
{
code: status,
label: status === 'MANAGER_BLOCKED' ? 'Нужно уточнение по заказу' : 'Обработка остановлена',
note: status === 'MANAGER_BLOCKED'
? 'Менеджер запросил уточнение перед продолжением.'
: 'Текущий заказ завершён без запуска в производство.',
dateLabel: formatDay(dates.approval),
state: 'current',
},
],
};
}
const currentIndex = stageIndex(status);
const stages: TimelineStage[] = [
{
code: 'NEW',
label: 'Заявка принята',
note: 'Получили состав заказа и начали обработку.',
dateLabel: formatDay(dates.created),
state: currentIndex > 0 ? 'done' : 'current',
},
{
code: 'MANAGER_PROCESSING',
label: 'Готовим предложение',
note: 'Уточняем цену, сроки и условия доставки.',
dateLabel: formatDay(dates.offer),
state: currentIndex > 1 ? 'done' : currentIndex === 1 ? 'current' : 'upcoming',
},
{
code: 'WAITING_DOUBLE_CONFIRM',
label: 'Ждём подтверждение',
note: 'Показываем согласованные условия и ждём финальное подтверждение.',
dateLabel: formatDay(dates.approval),
state: currentIndex > 2 ? 'done' : currentIndex === 2 ? 'current' : 'upcoming',
},
{
code: 'CONFIRMED',
label: 'Заказ подтверждён',
note: `Планируем передать в производство и ориентируемся на выпуск ${formatDay(dates.production)}.`,
dateLabel: formatDay(dates.production),
state: currentIndex > 3 ? 'done' : currentIndex === 3 ? 'current' : 'upcoming',
},
{
code: 'IN_PROGRESS',
label: 'Заказ в работе',
note: `Сейчас идёт исполнение. Следующее ожидаемое обновление около ${formatDay(dates.shipment)}.`,
dateLabel: formatDay(dates.shipment),
state: currentIndex > 4 ? 'done' : currentIndex === 4 ? 'current' : 'upcoming',
},
{
code: 'COMPLETED',
label: 'Заказ завершён',
note: 'Исполнение закрыто, заказ готов к выдаче или уже доставлен.',
dateLabel: formatDay(dates.delivered),
state: currentIndex === 5 ? 'current' : currentIndex > 5 ? 'done' : 'upcoming',
},
];
if (status === 'NEW') {
return {
title: 'Собираем предложение по заказу',
summary: `Сейчас уточняем стоимость и комплектацию. Следующее обновление ориентировочно ${formatDay(dates.offer)}.`,
stages,
};
}
if (status === 'MANAGER_PROCESSING') {
return {
title: 'Готовим условия по заказу',
summary: `Менеджер собирает финальные условия. Ориентир по следующему апдейту ${formatDay(dates.approval)}.`,
stages,
};
}
if (status === 'WAITING_DOUBLE_CONFIRM') {
return {
title: 'Ожидаем подтверждение условий',
summary: 'Нужно подтвердить текущие условия, после этого сразу двинем заказ дальше.',
stages,
};
}
if (status === 'CONFIRMED') {
return {
title: 'Заказ подтверждён',
summary: `Ожидаем выпуск с производства ориентировочно ${formatDay(dates.production)}.`,
stages,
};
}
if (status === 'IN_PROGRESS') {
return {
title: 'Заказ в работе',
summary: `Сейчас заказ исполняется. Следующее ожидаемое обновление около ${formatDay(dates.shipment)}.`,
stages,
};
}
return {
title: 'Заказ завершён',
summary: 'Работы по заказу завершены, история этапов сохранена ниже.',
stages,
};
}