Files
kpa39l fc6d725bd1 candidates-mail-ui: двухпанельный интерфейс кандидатов (почтовый клиент)
- БД: posts.comment TEXT, posts.rewritten_text TEXT (ALTER TABLE, миграция применена)
- GET /candidates: двухпанельный рендер — слева список с группировкой
  (source/date/status, свертываемые <details>) + чекбоксы bulk,
  справа детали поста (действия, комментарий, пересказ, оригинал с медиа)
- Роуты: POST /candidates/select, /candidates/bulk (approve/reject/reject-old),
  /posts/{id}/comment, /posts/{id}/reclassify (qwen), /posts/{id}/rewrite (qwen)
- approve вынесен в impl_approve (переиспользуется bulk-approve: publisher + бандл)
- без JS (только формы); HTML: Jinja2 + Bootstrap 5.3
- Протестировано TestClient: группировка, bulk-reject, comment, reclassify
  (реальный qwen), rewrite (реальный qwen), поиск, media, published — зелёные
- openspec: change candidates-mail-ui архивирован (2026-09-13-candidates-mail-ui),
  spec влита в openspec/specs/candidates-mail-ui; validate — чисто
- docs: STATUS.md, WALKTHROUGH.md обновлены
2026-09-13 20:53:53 +00:00

6.3 KiB

candidates-mail-ui Specification

Purpose

TBD - created by archiving change candidates-mail-ui. Update Purpose after archive.

Requirements

Requirement: Двухпанельный интерфейс кандидатов

Страница кандидатов разделена на две панели: слева список кандидатов (с группировкой), справа — детальный просмотр выбранного кандидата с меню действий.

Scenario: Пользователь открывает список кандидатов

Given на странице GET /candidates When страница загружается Then левая панель показывает список кандидатов, правая — детали первого/последнего выбранного And активный кандидат подсвечивается

Scenario: Группировка списка по источнику

Given список кандидатов When пользователь выбирает группировку «по источнику» Then кандидаты сгруппированы по источнику (канал/сайт) And каждая группа сворачивается/разворачивается (без JS, через <details>)

Scenario: Группировка по дате поступления

When пользователь выбирает группировку «по дате поступления» Then кандидаты сгруппированы по дате поступления (сегодня, вчера, ранее) And порядок: новые сверху, старые ниже

Requirement: Bulk-операции над выбранными кандидатами

Пользователь может выбрать несколько кандидатов (чекбоксы) и выполнить массовое действие: одобрить все выбранные / отклонить все выбранные.

Scenario: Массовое отклонение выбранных

Given на левой панели несколько кандидатов с отмеченными чекбоксами When пользователь нажимает «Отклонить выбранные» Then все отмеченные кандидаты получают статус «отклонён» And действие выполняется транзакционно (все или ни одного)

Scenario: Массовое одобрение выбранных

When пользователь нажимает «Одобрить выбранные» Then каждый отмеченный кандидат публикуется (вызывается approve-логика)

Scenario: Отклонение всех старых постов группы

Given группа «ранее» (старые посты) When пользователь выбирает «отклонить все в группе» Then все посты группы получают статус «отклонён»

Requirement: Действия над конкретным кандидатом

Правая панель содержит меню действий: Опубликовать, Отклонить, Переклассифицировать, Переписать новость под себя, блок «Мой комментарий», оригинальный пост со всеми медиа.

Scenario: Опубликовать кандидата

Given выбран кандидат в правой панели When пользователь нажимает «Опубликовать» Then пост публикуется (существующая логика approve, publisher)

Scenario: Отклонить кандидата

When пользователь нажимает «Отклонить» Then пост получает статус «отклонён» и исчезает из активных кандидатов

Scenario: Переклассифицировать кандидата

When пользователь нажимает «Переклассифицировать» Then запускается классификатор для данного поста And направление/relevance/interest обновляются, изменения видны на карточке

Scenario: Переписать новость под себя

When пользователь нажимает «Переписать новость» Then LLM генерирует черновик пересказа от первого лица (без копипасты) And черновик отображается в текстовом поле для редактирования And пользователь может отредактировать и затем опубликовать

Scenario: Комментарий к кандидату

Given в правой панели блок «Мой комментарий» When пользователь вводит текст и сохраняет Then комментарий сохраняется и отображается при повторном открытии кандидата

Scenario: Оригинальный пост со всеми медиа

Given выбран кандидат с медиа Then правая панель показывает оригинальный текст поста, все изображения/видео (через роут /media/), мета источника (канал, время, views/reactions)

Requirement: Фильтрация и поиск по списку

Список кандидатов можно фильтровать по статусу (новые/одобренные/отклонённые) и искать по тексту.

Scenario: Фильтр по статусу

Given на странице кандидатов When пользователь выбирает фильтр «новые» Then в списке остаются только новые кандидаты

Scenario: Поиск по тексту

When пользователь вводит текст в поиск Then список фильтруется по вхождению в текст поста