115 lines
7.6 KiB
Markdown
115 lines
7.6 KiB
Markdown
# Дизайн: Доработки админки заказов (маркер цены + быстрые статусы)
|
||
|
||
**Дата:** 2026-05-28
|
||
**Статус:** Draft
|
||
|
||
## Контекст
|
||
|
||
Нужно улучшить UX в админке заказов по двум направлениям:
|
||
|
||
1. Явно показывать в списке заказов, что требуется подтверждение цены (итоговой стоимости для оплаты).
|
||
2. Сделать смену статуса заказа более удобной для администратора.
|
||
|
||
Ограничения, согласованные в обсуждении:
|
||
|
||
- Не вводим новый статус заказа для подтверждения цены.
|
||
- Используем короткий текст для индикатора.
|
||
- Для смены статуса выбираем формат быстрых кнопок доступных переходов (вместо выпадающего списка).
|
||
|
||
## Цели
|
||
|
||
- Сократить время на обнаружение заказов, требующих подтверждения цены.
|
||
- Упростить и ускорить смену статуса до 1 клика.
|
||
- Сохранить существующие API-контракты и логику допустимых переходов.
|
||
|
||
## Не в рамках этой итерации
|
||
|
||
- Изменение серверной статусной модели.
|
||
- Добавление новых серверных полей или эндпоинтов.
|
||
- Массовая смена статусов из таблицы без открытия деталки.
|
||
- Редизайн всей таблицы заказов.
|
||
|
||
## Решение (утвержденный вариант A)
|
||
|
||
### 1) Маркер в списке заказов
|
||
|
||
В `client/src/pages/admin-orders/ui/AdminOrdersPage.tsx` добавить короткий бейдж `Цена не подтверждена` для строк, где:
|
||
|
||
- `status === 'PENDING_PAYMENT'`
|
||
- `deliveryType === 'delivery'`
|
||
- `deliveryFeeLocked === false`
|
||
|
||
Размещение: в колонке `ID` рядом с коротким номером заказа (`o.id.slice(-8)`), чтобы не расширять таблицу и чтобы сигнал был виден до открытия деталки.
|
||
|
||
### 2) Быстрая смена статуса в деталке заказа
|
||
|
||
В `client/src/features/order-detail/ui/OrderDetailContent.tsx` заменить текущий `Select` "Сменить статус" на набор кнопок быстрых переходов:
|
||
|
||
- Источник переходов остается прежним: `getAdminNextOrderStatuses(detail.status, detail.deliveryType)`
|
||
- Для каждого допустимого статуса рендерится отдельная кнопка
|
||
- Клик вызывает текущую мутацию `setAdminOrderStatus` через `statusMut.mutate(next)`
|
||
|
||
Поведение кнопок:
|
||
|
||
- Пока мутация выполняется (`statusMut.isPending`) все кнопки отключены
|
||
- Если доступных переходов нет — показываем текст `Статус финальный, смена недоступна`
|
||
- Для `CANCELLED` использовать визуально менее акцентную кнопку (`outlined`), чтобы снизить риск случайного нажатия
|
||
|
||
## Архитектурные границы и переиспользование
|
||
|
||
- Используем существующие данные `deliveryFeeLocked`, `status`, `deliveryType`.
|
||
- Правила переходов не дублируем в UI, берем из `getAdminNextOrderStatuses`.
|
||
- API слой (`client/src/entities/order/api/admin-order-api.ts`) без изменений.
|
||
- React Query инвалидация сохраняется текущая:
|
||
- `['admin', 'orders']`
|
||
- `['admin', 'orders', 'detail']`
|
||
- `['admin', 'orders', 'summary']`
|
||
|
||
## Поток данных
|
||
|
||
1. Страница списка получает `items` из `fetchAdminOrders`.
|
||
2. Для каждой строки вычисляется `needsPriceApproval`.
|
||
3. Если `needsPriceApproval === true`, отображается бейдж `Цена не подтверждена`.
|
||
4. В деталке рассчитывается `nextStatuses`.
|
||
5. Клик по кнопке статуса вызывает `statusMut`.
|
||
6. После успеха инвалидация обновляет список/деталку/summary; маркер в списке исчезает автоматически, когда условие перестает выполняться.
|
||
|
||
## Обработка ошибок
|
||
|
||
- Ошибка смены статуса: показать локальный `Alert` в деталке с текстом `Не удалось сменить статус`, дать возможность повторить действие.
|
||
- Ошибка загрузки списка: сохранить текущее поведение (`Не удалось загрузить заказы.`).
|
||
- Пустой список переходов: показать явное сообщение о финальном статусе.
|
||
|
||
## Тестирование
|
||
|
||
Минимальный набор:
|
||
|
||
1. Unit: проверка условия `needsPriceApproval`.
|
||
2. Component (`OrderDetailContent`):
|
||
- рендерит кнопки только допустимых переходов
|
||
- вызывает `setAdminOrderStatus` при клике
|
||
- блокирует кнопки в pending-состоянии
|
||
3. Component/Smoke (`AdminOrdersPage`):
|
||
- показывает `Цена не подтверждена` для целевых заказов
|
||
- не показывает бейдж для остальных
|
||
|
||
## Критерии готовности
|
||
|
||
- В списке заказов видно короткий маркер `Цена не подтверждена` для релевантных заказов.
|
||
- В деталке смена статуса выполняется через быстрые кнопки допустимых переходов.
|
||
- Список и деталка синхронизируются после успешной смены статуса.
|
||
- Для `CANCELLED` используется менее акцентный стиль кнопки.
|
||
- Проходят `npm run lint` и `npm run format:check` в `client`.
|
||
|
||
## План изменений по файлам
|
||
|
||
- `client/src/pages/admin-orders/ui/AdminOrdersPage.tsx` — маркер `Цена не подтверждена` в строках списка.
|
||
- `client/src/features/order-detail/ui/OrderDetailContent.tsx` — замена select на быстрые кнопки.
|
||
- (опционально) `client/src/shared/lib/` — небольшой хелпер `needsPriceApproval`, если нужно переиспользование или unit-тест вне компонента.
|
||
|
||
## Риски и меры
|
||
|
||
- Риск перегруза UI в деталке при большом количестве кнопок: низкий, так как число допустимых переходов обычно 1-2.
|
||
- Риск случайного нажатия на отмену: снижается стилем `outlined` для `CANCELLED`.
|
||
- Риск расхождения логики переходов: отсутствует, т.к. используется единый источник `getAdminNextOrderStatuses`.
|