Files
Deal/.superpowers/sdd/channel-discovery/task-8-report.md
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
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 зелёные.
2026-09-11 23:56:47 +03:00

47 lines
8.6 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.
# Task 8 — Отчёт: Фронтенд — store + каркас подвкладки «Поиск»
## Статус
Выполнено. `npm run build` проходит без ошибок.
## Файлы
- Изменён: `frontend/src/store.js` — состояние Discovery, сброс при разлогине, 13 функций.
- Изменён: `frontend/src/views/ChannelsView.vue` — сегмент «Каналы | Поиск каналов» и рендер по табу.
- Создан: `frontend/src/views/DiscoveryView.vue` — список задач + панель активной задачи/мастер.
## Что сделано
### store.js
- Состояние (по интерфейсу брифа): `channelsTab: 'list' | 'search'` (init `'list'`), `discTasks`, `discCandidates`, `discBlacklist`, `discLog`, `discActiveTaskId: null`, `discCandidateStatus: 'review'`, `discBusy: false`. Добавлено в `state` (блок «Каналы») и в `resetLocal()` (разлогин → чистый Discovery, `channelsTab` возвращается в `'list'`).
- Функции названы точно по брифу: `gotoChannelsTab(tab)`, `loadDiscTasks()`, `saveDiscTask(form, id=null)`, `deleteDiscTask(id)`, `startDiscTask(id)`, `pauseDiscTask(id)`, `generateDiscKeywords(taskId)`, `loadDiscCandidates(taskId, status)`, `joinDiscCandidate(c)`, `rejectDiscCandidate(c)`, `loadDiscBlacklist()`, `removeDiscBlacklist(id)`, `loadDiscLog(taskId)`.
- Контракт в camelCase как в API: `minSubscribers`, `sampleSize`, `planJoins`, `autoJoin`, `dialogId` и т.д. Тела задач собираются в camelCase (`POST/PATCH /api/discovery/tasks`), списки читаются из `{"items": [...]}`.
- Паттерны проекта: `api.get/post/patch/delete`, `toast`/`errMsg` на действиях; тихие `catch → false` на фоновых чтениях списков (как `loadPipelineQueue`/`loadRejected`).
- `saveDiscTask` — создаёт/патчит, кладёт задачу в `discTasks`, ставит `discActiveTaskId`, возвращает задачу или `null`.
- `generateDiscKeywords`**error-ветка API** (`{"keywords": [], "error": "..."}`) → `toast(error)`, возвращает `null`; успех → массив `keywords` (может быть пустым).
- `deleteDiscTask` — после удаления активной задачи выбирает первую оставшуюся.
- `loadDiscTasks` — сохраняет активную задачу, если она ещё существует, иначе выбирает первую (правая панель не пустует).
- `join/rejectDiscCandidate` — на успехе убирают кандидата из текущего списка `discCandidates` + toast; `removeDiscBlacklist` фильтрует по `dialogId`. Поля кандидата/блэклиста не используются в UI до Task 9.
### ChannelsView.vue
- В шапке — сегмент «Каналы | Поиск каналов» (мелкие кнопки в контейнере `bg-ink/60 border border-white/5`, активный — `bg-white/8 text-hi`), переключение через `gotoChannelsTab`. Подзаголовок шапки и правые действия («Перечитать», «Включить все», поиск по имени) показываются только на табе `'list'`.
- Синхронизация списка каналов (watch по `state.view` + `onMounted`) ограничена условием `channelsTab === 'list'`; добавлен отдельный watch по `channelsTab` — возврат на таб «Каналы» освежает список.
- При `channelsTab === 'search'` вместо списка каналов рендерится `<DiscoveryView/>` (импорт из `./DiscoveryView.vue`).
### DiscoveryView.vue (каркас: задачи + мастер)
- Слева: колонка `w-[300px]` со списком задач (`state.discTasks`; имя + чип статуса + ключи/найдено/вступили), кнопка «Новая задача», refresh. Пустое состояние — с подсказкой.
- Справа: панель активной задачи (`state.discActiveTaskId`). Нет выбора — приветственный экран с кнопкой «Новая задача».
- Мастер: `name`, `description`, кнопка «Сгенерировать ключи ИИ» (иконка sparkles), редактируемые чипы ключей (ввод + Enter/плюс, удаление крестиком), числовые поля `minSubscribers`/`threshold`/`sampleSize`/`planJoins` (дефолты 0/40/10/1), `lang` select ru/any, toggle `autoJoin`. Стили в духе SettingsView: `rounded-xl border border-white/8 bg-raise/40 p-4`, инпуты `h-9 bg-ink/70 border-white/10 focus:border-brand/50`, чипы как у стоп-фраз/сферы.
- Кнопки: «Запустить» (градиент; disabled пока `keywords` пустые; подпись «Продолжить» для paused, «Запустить заново» для done/failed), «Сохранить», «Удалить» (с `askConfirm`, скрыта для черновика и на паузе выполнения), «Поставить на паузу» (только running).
- Статус задачи: чип статуса (draft/running/paused/done/failed с цветами online/warn/brand/danger), при прогрессе — счётчики found/evaluated/joined/rejected, дата создания. Пока задача running — поля задизейблены (overlay) и список тихо опрашивается раз в 4.5 c (статус/счётчики живут на бэке у воркера).
- Без заглушек-обещаний: панель кандидатов/лога не рисовалась (ожидаемо — Task 9).
- UX-детали: «Запустить» и «Сгенерировать ключи ИИ» при необходимости сначала сохраняют форму (`saveDiscTask`), т.к. старт и генерация идут по id задачи на сервере; черновик «Новой задачи» не затирается ручным refresh списка.
## Вывод проверок
1. `cd /c/telbase/frontend && npm run build``✓ built in 1.32s`, `42 modules transformed`, ошибок нет.
2. `diagnostics` по `store.js`, `ChannelsView.vue`, `DiscoveryView.vue` — ошибок/предупреждений нет.
## Concerns
1. Живой API/UI не гонялся (нет запущенного бэкенда/дев-сервера в этой сессии); поведение функций опирается на контракт Task 7 (`{"items": [...]}`, camelCase, 200/400/404). Ручная проверка сценариев — за Task 10.
2. Генерация ключей ИИ по API идёт от id задачи (`POST /tasks/{id}/generate-keywords`), поэтому при генерации из нового черновика кнопка сначала создаёт задачу (нужно название — при пустом показывается toast «Укажите название задачи»). После генерации ключи кладутся в форму, в БД фиксируются кнопкой «Сохранить» (или автоматически при «Запустить»).
3. Дефолты формы (threshold 40 / sampleSize 10) повторяют дефолты сервиса; при создании бэкенд может подставить значения настроек `discEvalThreshold/discEvalSample` — после «Сохранить» форма перечитывается из ответа сервера (`pickFromTask`), так что расхождение схлопывается.
4. `channelsTab` глобальный и переживает уход/возврат на экран «Каналы» (как `settingsTab`); активная задача Discovery тоже сохраняется между визитами, при её удалении/исчезновении выбирается первая.
5. Опрос статуса running-задачи — простой `setInterval` 4.5 c на время нахождения на вкладке; при большом числе задач можно позже перевести на точечный `GET /tasks/{id}` или SSE.