7 пунктов cleanup-спринта: 1. P0: race в GenerateNumberAsync — DocumentNumberRetry helper с WithOrgAdvisoryLockAsync (pg_advisory_xact_lock per orgHash/docTypeHash) + SaveWithRetryAsync exponential backoff. RetailSalesController POST обёрнут в lock. После — 23505 errors 53% → 0 на k6 baseline-replay. 2. HelpTooltip integration — ListPageShell расширен `helpTopic` пропом. Применено к 4 страницам (Promotions, Loyalty×2, AuditLog) + inline на MoySkladImportPage. 3. WhatsNewBanner — узкий emerald-toast сверху AppLayout. Опрашивает /api/whats-new (staleTime=1h), сравнивает buildVersion с localStorage.fm.lastSeenBuildVersion. Dismiss сохраняет версию. 4. Color contrast sweep — text-slate-400 в body-text узлах (empty-state, table-cells, hints, help) заменён на text-slate-500 dark:text-slate-400. 19 файлов. Иконки оставлены (decorative, не покрыты axe color-contrast). 5. useFormatCurrency() хук в lib/useFormatCurrency.ts. Берёт defaultCurrencySymbol из useOrgSettings + локаль из i18next. DashboardWidgets (TopProducts/RecentSales/Margin) переведены — `₸` больше не захардкоден. 6. Audit log UI filters — OrgAuditLogPage расширен полями «Кто» (Select сотрудников), «Дата с» / «по» (date-input'ы), кнопка «Сбросить фильтры». Backend уже умел эти параметры. 7. NotificationCenter — bell-icon в sidebar footer'е с unread badge, popover с 30 последних событий (Sale/Supply/LowStock через useNotificationsHub). Each item clickable → документ. In-memory. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6.2 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.
Итог
Все 7 пунктов ✓. Локальные цифры:
- P0 race: 23505 errors 53% → 0 на k6 baseline-replay.
- 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.