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 зелёные.
47 lines
8.6 KiB
Markdown
47 lines
8.6 KiB
Markdown
# 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.
|