7.3 KiB
Sprint 18 — TODO cleanup + P0 fix + UX polish
Цель: разгрести оставшиеся TODO из спринтов 14, 15, 17. Закрыть P0 из performance-baseline (race в GenerateNumberAsync), доделать HelpTooltip integration, whats-new banner, color contrast, добавить currency formatter, audit log filters, notification center.
Старт: 2026-06-07 (после Sprint 17). Исполнитель: Claude Opus 4.7.
Принципы
- Каждый пункт — реальный фикс/измерение, не обещание.
- НЕ трогать:
global.json, prod admin.food-market.kz, POS WPF.
Чек-лист
- 1. P0: race в GenerateNumberAsync —
DocumentNumberRetryhelper с двумя слоями:WithOrgAdvisoryLockAsync(PG advisory lock per (orgHash, docTypeHash)) +SaveWithRetryAsync(exp backoff на оставшихся 23505 от gap-cases). Применено к RetailSalesController POST. После k6 baseline-replay: 23505 errors = 0 (было 53%). - 2. HelpTooltip integration —
ListPageShellрасширен optionalhelpTopicпропом → tooltip рендерится inline в заголовке. Применено: PromotionsPage, LoyaltyProgramsPage, LoyaltyCardsPage, OrgAuditLogPage. Для не-ListPageShell страниц (MoySkladImportPage) — отдельный inline<HelpTooltip>подPageHeader. - 3. Whats-new banner toast —
<WhatsNewBanner>компонент опрашивает/api/whats-new(staleTime=1h), сравниваетbuildVersionсlocalStorage.fm.lastSeenBuildVersion. На mismatch + items за 30 дней → узкий emerald banner сверху с count'ом feat/fix + ссылкой на /whats-new. Кнопка X / клик по ссылке сохраняют новую версию. Не показывается на buildVersion="dev". Вшит в AppLayout<main>. - 4. Color contrast sweep — bulk fix: bare
text-slate-400на body-text-узлах (empty-states, table-cells, помощи, hints) →text-slate-500 dark:text-slate-400. Затронуло 19 файлов: DashboardWidgets, DataTable, CommandPalette, EmptyStateWithDemo, ProductPicker, SupplyLineQuickAdd, ProductGroupTree, Field, ProductImageGallery, ShortcutsOverlay, SuperAdminLayout, + 8 pages. Иконки (text-slate-400 на SVG) оставлены — на них axe color-contrast не срабатывает (decorative). - 5. Currency formatter —
useFormatCurrency()хук вlib/useFormatCurrency.ts. БерётdefaultCurrencySymbolиз useOrgSettings() + локаль из i18next. Возвращает stablefmt(value, opts?). DashboardWidgets (TopProducts/RecentSales/Margin) переведены на хук — захардкоженный₸исчез из widget'ов. Бэкап fallback на тенге если settings ещё не загрузились. - 6. Audit log UI filters — OrgAuditLogPage расширен полями:
«Кто» (Select из /api/employees), «Дата с» / «по» (
<input type="date">),- кнопка «Сбросить фильтры». Все 5 фильтров (entityType, action,
userId, from, to) триггерят refetch; параметры передаются в URL
query. Backend уже умел эти параметры (
OrgAuditLogController.List).
- кнопка «Сбросить фильтры». Все 5 фильтров (entityType, action,
userId, from, to) триггерят refetch; параметры передаются в URL
query. Backend уже умел эти параметры (
- 7. Notification center —
<NotificationCenter>компонент в sidebar footer'е. Bell icon с unread badge (max 9+). Popover с максимум 30 последних событий (SalePosted/SupplyPosted/LowStock через существующийuseNotificationsHub). Каждое событие clickable: ведёт на документ. «Очистить» обнуляет ленту. Esc / click-outside закрывают. Storage: in-memory (ephemeral) — для постоянной истории есть /audit-log.
Журнал
2026-06-07 старт
Sprint 17 закрыт (7/7 ✓). Поехали по TODO cleanup.
2026-06-07 п.1 (P0 race fix)
Сначала ретрай-loop 5→10 на 23505 в SaveOrFkErrorAsync — сократил
ошибки 53%→24%→21%, но не убрал. Перешёл на PostgreSQL advisory
lock: pg_advisory_xact_lock(orgHash, docTypeHash) внутри transactions.
После — 0 ошибок 23505 на k6 baseline-replay (5 VUs, 100 RPS, single
org). Осталось 31% 40001 Serializable conflict'ов на stock_movements —
это другой issue (over-sell prevention), решается отдельно.
2026-06-07 п.2-3 (HelpTooltip + WhatsNewBanner)
HelpTooltip integration — расставлен в 4 страницах через ListPageShell prop + 1 страницу через inline (MoySklad). WhatsNewBanner — узкий toast сверху layout'a, dismiss persistent в localStorage.
2026-06-07 п.4 (color contrast)
Bulk-sed по 19 файлам — text-slate-400 в текстовом content'е
заменён на text-slate-500 dark:text-slate-400. Иконки оставлены.
Получено 2 raunda doubled-class'ов от sed (text-slate-500
dark:text-slate-500 dark:text-slate-400) — почищено отдельным perl-passом.
2026-06-07 п.5-7 (currency + audit filters + notifications)
useFormatCurrency() + интеграция в DashboardWidgets. OrgAuditLogPage
получил Select сотрудников + 2 date-input'a + кнопку сброса.
NotificationCenter с bell-icon в sidebar — реюзает useNotificationsHub.
2026-06-07 deploy + retest
Deploy через ~/deploy-stage.sh → test.admin.food-market.kz. После
первого деплоя — 404 на /api/employees (фронт обращался по
несуществующему пути; реальный — /api/organization/employees).
Hotfix: правильный endpoint + DTO mapping (lastName+firstName+
middleName → fullName на клиенте). Второй deploy + retest:
- stage-smoke: 5/5 ✓
- stage-audit-log (api): 7/7 ✓
- stage-2fa: 6/6 ✓
- stage-catalog: 6/6 ✓
- stage-inventory: 8/8 ✓
- stage-pos: 7/7 ✓
- stage-reports: 8/8 ✓
- Playwright UI-1..16 (44 specs): 44/44 ✓ за 3.2 минуты
- UI-10 audit-log (с новыми фильтрами): 2/2 ✓ за 22с
Итог
Все 7 пунктов ✓. Локальные цифры:
- P0 race: 23505 errors 53% → 0 в локальной k6 baseline-replay (k6 на stage-хосте не установлен → итоговая верификация in-code через advisory lock; стресс-проба остаётся для следующего раунда на dev-vm с установленным k6).
- HelpTooltip: 5 страниц получили deep-link на /help#topic.
- WhatsNewBanner: 1 emerald баннер в AppLayout, dismissible.
- Contrast: 19 файлов почищено, WCAG-AA для body text.
- Currency: 1 hook + 4 интеграции в DashboardWidgets.
- Audit filters: 5 серверных фильтров теперь имеют UI.
- Notifications: bell-popover с 30 событий, 3 типа, in-memory.
- Regression: 5 stage-сценариев + 44 Playwright UI specs зелёные.