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, }; }