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

8.6 KiB
Raw Blame History

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.
  • generateDiscKeywordserror-ветка 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.