# Sprint 8 — real-time + Telegram-бот владельца + локализация + MinIO

Цель: добавить «живой» опыт (live-обновление виджетов через SignalR),
канал для уведомлений владельцу (Telegram-бот с ежедневной сводкой),
английский UI (i18n) и перенести uploads в MinIO/S3.

Старт: 2026-05-31. Исполнитель: Claude Opus 4.7 (автономный режим).

## Принципы

- Multi-tenant обязателен — Hub-группы per-org, OwnerTelegramChatId на Organization, MinIO bucket общий но object-key `{orgId}/...`.
- Каждый пункт: dotnet build + локальные тесты + `~/deploy-stage.sh` + retest на `https://test.admin.food-market.kz`.
- НЕ трогать: global.json, прод-стек (admin.food-market.kz), POS WPF.

## Чек-лист

- [x] **1. P2-7 SignalR real-time** — Hub `/hubs/notifications` с группами per-org. События `SalePosted`/`SupplyPosted`/`LowStock`. JWT через query `?access_token=` (для WebSocket). Дашборд: live-индикатор Wifi, оптимистическое приращение «Выручка сегодня», toast.info на SupplyPosted, toast.error на LowStock. Тесты: SignalRNotificationsTests (multi-tenant 1/1) + `stage-ui-signalr.spec.ts` (1/1 ✓). Nginx `/hubs/` с upgrade-хедерами и 24h read_timeout.
- [x] **2. P2-14 Telegram-бот владельца** — миграция `Phase9a_OwnerTelegramChatId`. `TelegramBotClient` (sendMessage HTML), Disabled-mode когда `Telegram__BotToken` пустой. `OwnerDailySummaryJob.RunAsync` — cron `0 6 * * *` UTC = 09:00 МСК. Сводка: выручка вчера, чеков, средний чек, топ-3 по выручке, low-stock 5. `TelegramBindingController` (status/bind/unbind). UI секция в OrganizationSettings с deep-link + пошаговой инструкцией. Тесты: `TelegramOwnerSummaryTests` (рендер ✓), `stage-ui-telegram.spec.ts` (3/3 ✓ на стейдже).
- [x] **3. P2-6a Локализация UI (en) — базовая** — react-i18next 17 + i18next 26 + browser-language-detector. `ru.json`/`en.json` (common/nav/dashboard/products/settings/demoSeed/shortcuts/toast). `LanguageSwitcher` в sidebar. AppLayout (sidebar) + DashboardPage переведены. kz: TODO (требует человека-переводчика). Тесты: `stage-ui-i18n.spec.ts` (3/3 ✓). Остаток (Products/Counterparties/Enters/...) — задача следующих итераций; инфраструктура готова, добавлять переводы — strictly mechanical.
- [x] **4. P2-15 MinIO/S3 для uploads** — `Minio` 6.0.5 SDK + `IObjectStorage` (Local/Minio impls). DI: рантайм-fallback на Local если MinIO config пуст. `MinioBootstrap` (IHostedService) создаёт bucket `food-market-uploads` на старте (best-effort, не валит API). `ProductImagesController` отрефакторен на абстракцию. `UploadsController` (GET /uploads/{**path}) стримит из IObjectStorage с cache-control 7d. Stage compose обновлён (minio container + Storage__* env). Тесты: `StorageAbstractionTests` (3/3 ✓), `stage-ui-minio.spec.ts` (1/1 ✓ — UI upload PNG → MinIO bucket → download через `/uploads/...` byte-equal). Стейдж лог: «MinIO bucket food-market-uploads created».

## Журнал

### 2026-05-31 — старт

Sprint UI-deep закрыт (`docs/sprint-ui-deep-progress.md`, 59/59 ✓, 6 багов починены). Перехожу к Sprint 8 пункт 1 (SignalR).

### 2026-05-31 — итог

**Все 4 пункта ✓** на `https://test.admin.food-market.kz`. Stage 8/8 e2e + integration tests зелёные после последнего deploy-stage.

| # | Тема | Commits | Specs |
|---|---|---|---|
| 1 | SignalR | `dd2e1e7` + `2ea30bb` (nginx fix) | int 1/1 + stage 1/1 |
| 2 | Telegram бот | `3088237` | int 1/1 + stage 3/3 |
| 3 | i18n (ru/en) | `301bf15` | stage 3/3 |
| 4 | MinIO/S3 | `7de159d` | int 3/3 + stage 1/1 |

**Что было сделано:**

1. **SignalR**: Hub `/hubs/notifications` с группами per-org, событиями SalePosted / SupplyPosted / LowStock. JWT через `?access_token=` для WebSocket. Дашборд: live-индикатор Wifi, оптимистическое приращение «Выручка сегодня», toast.info на SupplyPosted, toast.error на LowStock. Nginx fix: `/hubs/` с upgrade-хедерами и 24h read_timeout.
2. **Telegram бот**: миграция `Phase9a_OwnerTelegramChatId`, `TelegramBotClient` с disabled-mode, `OwnerDailySummaryJob` cron `0 6 * * *` (09:00 МСК). Сводка: выручка вчера, чеков, средний чек, топ-3 по выручке, low-stock 5. `TelegramBindingController` (status/bind/unbind) + UI секция с deep-link и пошаговой инструкцией. На стейдже бот в disabled-mode (`Telegram__BotToken` не задан) — UI показывает «Бот не настроен».
3. **i18n базовая**: react-i18next 17 + i18next 26, ресурсы inline (без fetch). `ru.json`/`en.json` с разделами common/nav/dashboard/products/settings/demoSeed/shortcuts/toast. `LanguageSwitcher` в sidebar. AppLayout (11 секций + 26 nav-ссылок) и DashboardPage полностью переведены. kz — TODO для следующих итераций (нужен живой переводчик). Остальные ~25 страниц — инфраструктура готова, добавление переводов механическое.
4. **MinIO/S3**: `IObjectStorage` абстракция, Local + MinIO impls. Stage compose обновлён (minio container + Storage__* env). Bucket `food-market-uploads` создаётся автоматически на старте. Runtime fallback на Local если MinIO config пустой. `ProductImagesController` отрефакторен. `UploadsController` стримит из storage с cache-control 7d. Лог стейджа подтверждает: «MinIO bucket food-market-uploads created».

**Покрытие тестами на стейдже:** stage-ui-signalr (1) + stage-ui-telegram (3) + stage-ui-i18n (3) + stage-ui-minio (1) = **8/8 ✓**.
