Deal — единая кодовая база
ci / build-test (push) Canceled after 0s

SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
This commit is contained in:
Rustam Khalimov
2026-09-11 23:56:47 +03:00
commit 27c7831910
1383 changed files with 158436 additions and 0 deletions
@@ -0,0 +1,49 @@
# Task 9 — Отчёт: Фронтенд — кандидаты, действия, чёрный список, история
## Статус
Выполнено. `cd /c/telbase/frontend && npm run build` — без ошибок (`✓ built`, 42 modules transformed).
`python -m py_compile backend/app/routers/settings_routes.py` — OK.
## Файлы
- Изменён: `frontend/src/views/DiscoveryView.vue` — табы кандидатов/истории/чёрного списка, карточки кандидатов, квоты.
- Изменён: `frontend/src/store.js``state.discCounts` + `loadDiscCounts(taskId)`; `loadDiscCandidates` обновляет счётчик текущего статуса.
- Изменён: `frontend/src/components/Icon.vue` — новые иконки `users`, `megaphone`, `list` (чипы вида источника, участники, темы).
- Изменён: `backend/app/routers/settings_routes.py``discPaused` добавлен в `_PUBLIC_BOOL` (одной строкой).
## Что сделано
### Табы панели задачи (DiscoveryView, правая колонка)
- Под «мастером» и действиями задачи — панель с табами: «В обработке» (`new`), «На рассмотрении» (`review`), «Вступили» (`joined`), «Отклонены» (`rejected`), «История» (лог), «Чёрный список».
- Бейджи-счётчики (`state.discCounts`) скрыты при 0; переключение таба вызывает `loadDiscCandidates(taskId, status)` / `loadDiscLog(taskId)` / `loadDiscBlacklist()`; смена активной задачи и повторный клик по ней — авто-загрузка панели (`loadPanel`).
- Счётчики всех четырёх статусов обновляет `loadDiscCounts` (4 параллельных GET по статусам). Защита от гонок при быстром переключении табов/задач — `panelSeq` (stale-ответ перезагружает актуальный таб).
- Пока задача `running` — список кандидатов/счётчики тихо обновляются каждый второй тик опроса (~9 c).
### Карточка кандидата
- Аватар по `hue` (как в ChannelsView), название, `@username`, чип вида (канал `megaphone`/brand, группа `users`/online, форум `list`/warn), участники (`N участник/а/ов`, «—» при None), метки `marks` чипами (важные — закрытая группа/история недоступна — подсвечиваются warn).
- Соответствие: для канала/группы — «подходит N%» (по `fitRatio`); для форума — «подходит тем: N из M» и раскрывающийся список тем «тема „{title}" — подходит {fitCount} из {total}» с passed-подсветкой (pass — online, нет — приглушённый).
- Кнопки «Вступить и мониторить» и «Отклонить» — только для `status === 'review'`; join без подтверждения, reject через `askConfirm` (уходит в чёрный список). Для `new` кнопок нет — подпись «воркер оценит источник». После join/reject счётчики перечитываются.
- Раскрытие тем форума — локальный `Set` `expanded` (chevron).
### Чёрный список
- Выбран отдельный таб «Чёрный список» в той же панели (читабельно и не спорит с макетом). Строка: иконка, имя, причина/дата, кнопка «Снять» (`removeDiscBlacklist`, guard от двойного клика `unbanId`).
### Квоты авто-вступлений
- Маленькая кнопка «Квоты» в шапке левой панели (рядом со списком задач) → inline-блок: суточный лимит `discJoinLimit`, паузы мин/макс `discJoinDelayMin/Max` (сек), стоп-кран-переключатель `discPaused`.
- Дефолты не хардкодятся: при первом открытии блока — `GET /api/settings` (локальная загрузка), сохранение чисел — `PATCH /api/settings` одним объектом (клампы 1–200 и 5–600 как на бэке, min ≤ max), стоп-кран патчится сразу при переключении.
- На бэке `discPaused` добавлен в `_PUBLIC_BOOL` — теперь принимается PATCH и отдаётся в GET.
### store.js
- `state.discCounts = { new:0, review:0, joined:0, rejected:0 }`.
- `loadDiscCandidates` дополнительно пишет `discCounts[status]`.
- `loadDiscCounts(taskId)` — Promise.all по 4 статусам, заполняет `discCounts` (не трогает `discCandidates`).
## Вывод проверок
1. `cd /c/telbase/frontend && npm run build``✓ built in 1.24s`, `42 modules transformed`, ошибок нет.
2. `diagnostics` по `DiscoveryView.vue`, `store.js`, `Icon.vue` — ошибок/предупреждений нет.
3. `python -m py_compile backend/app/routers/settings_routes.py` → OK.
## Concerns
1. Живой API/UI не гонялся (нет запущенного бэкенда/дев-сервера в этой сессии); поведение опирается на контракт Task 7. Визуальная проверка сценариев — за Task 10.
2. Кнопка «Квоты» дергает `GET/PATCH /api/settings` напрямую из вью (в store нет state-полей под диск-квоты, по брифу разрешена локальная загрузка/сохранение через `api.patch`). Это единственное место, где вью импортирует `api` напрямую — если захочется строгой «только через store», квоты стоит перевести в store-функции.
3. Счётчики табов получаются отдельными запросами по каждому статусу (у бэкенда нет эндпоинта counts); при активном воркере это ~4 лёгких GET каждые ~9 c — приемлемо для текущих объёмов, но при росте числа кандидатов можно добавить серверный `counts`.
4. «На рассмотрении» — таб по умолчанию для активной задачи (в `state.discCandidateStatus` изначально `review`); таб «История»/«Чёрный список» сохраняется при переключении задач.
5. `progress.md` не трогался.