food-market/docs/sprint18-progress.md
nns 9bd4375ae4
Some checks are pending
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Public / Build + push Public (push) Waiting to run
Docker Public / Deploy Public on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
feat(s18): TODO cleanup — P0 race fix + helpTooltip + whats-new + contrast + currency + audit filters + notifications
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>
2026-06-07 18:50:35 +05:00

98 lines
6.2 KiB
Markdown
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.

# 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.
## Чек-лист
- [x] **1. P0: race в GenerateNumberAsync**`DocumentNumberRetry`
helper с двумя слоями: `WithOrgAdvisoryLockAsync` (PG advisory lock
per (orgHash, docTypeHash)) + `SaveWithRetryAsync` (exp backoff на
оставшихся 23505 от gap-cases). Применено к RetailSalesController
POST. После k6 baseline-replay: 23505 errors = **0** (было 53%).
- [x] **2. HelpTooltip integration**`ListPageShell` расширен
optional `helpTopic` пропом → tooltip рендерится inline в заголовке.
Применено: PromotionsPage, LoyaltyProgramsPage, LoyaltyCardsPage,
OrgAuditLogPage. Для не-ListPageShell страниц (MoySkladImportPage)
— отдельный inline `<HelpTooltip>` под `PageHeader`.
- [x] **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>`.
- [x] **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).
- [x] **5. Currency formatter**`useFormatCurrency()` хук в
`lib/useFormatCurrency.ts`. Берёт `defaultCurrencySymbol` из
useOrgSettings() + локаль из i18next. Возвращает stable `fmt(value, opts?)`.
DashboardWidgets (TopProducts/RecentSales/Margin) переведены на хук
— захардкоженный `₸` исчез из widget'ов. Бэкап fallback на тенге если
settings ещё не загрузились.
- [x] **6. Audit log UI filters** — OrgAuditLogPage расширен полями:
«Кто» (Select из /api/employees), «Дата с» / «по» (`<input type="date">`),
+ кнопка «Сбросить фильтры». Все 5 фильтров (entityType, action,
userId, from, to) триггерят refetch; параметры передаются в URL
query. Backend уже умел эти параметры (`OrgAuditLogController.List`).
- [x] **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.