mirror of
https://gitverse.ru/kpa39l/vesti.git
synced 2026-09-29 09:55:03 +00:00
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 обновлены
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# tasks.md — candidates-mail-ui
|
||||
|
||||
## Proposal
|
||||
- [x] proposal.md — двухпанельный интерфейс кандидатов (почтовый клиент), группировка, bulk
|
||||
- [x] design.md — архитектура (роуты, БД, шаблоны, минимум JS)
|
||||
|
||||
## Implement
|
||||
- [x] Проверить схему posts (status, поля) — уточнить, какие статусы есть (new/rejected/published)
|
||||
- [x] Миграция: `ALTER TABLE posts ADD COLUMN comment TEXT` + `rewritten_text TEXT` (сделано в БД)
|
||||
- [x] GET /candidates — двухпанельный рендер (левая колонка список+группировка, правая — детально)
|
||||
- [x] Левая панель: группировка по source/date/status (сворачиваемые <details>), чекбоксы
|
||||
- [x] POST /candidates/bulk — approve/reject/reject-old по ids[] (транзакция)
|
||||
- [x] POST /posts/{id}/comment — сохранить комментарий (comment TEXT)
|
||||
- [x] POST /posts/{id}/reclassify — перезапуск классификатора на посте (реальный qwen, протестирован)
|
||||
- [x] POST /posts/{id}/rewrite — LLM-пересказ (qwen), возврат черновика для редактирования (реальный qwen, протестирован)
|
||||
- [x] Правая панель: меню действий, блок комментария, оригинальный пост со всеми медиа
|
||||
- [x] Шаблоны: candidates.html (двухколоночная), формы новых действий
|
||||
- [x] Тест: группировка, bulk-отклонение, коммент, реклассификация, rewrite, поиск (TestClient, все зелёные)
|
||||
- [x] `openspec validate candidates-mail-ui` — чисто
|
||||
- [x] Обновить STATUS.md / WALKTHROUGH.md
|
||||
@@ -1,20 +0,0 @@
|
||||
# tasks.md — candidates-mail-ui
|
||||
|
||||
## Proposal
|
||||
- [x] proposal.md — двухпанельный интерфейс кандидатов (почтовый клиент), группировка, bulk
|
||||
- [x] design.md — архитектура (роуты, БД, шаблоны, минимум JS)
|
||||
|
||||
## Implement
|
||||
- [ ] Проверить схему posts (status, поля) — уточнить, какие статусы есть
|
||||
- [ ] Миграция: `ALTER TABLE posts ADD COLUMN comment TEXT` (или таблица comments)
|
||||
- [ ] GET /candidates — двухпанельный рендер (левая колонка список+группировка, правая — детально)
|
||||
- [ ] Левая панель: группировка по source/date/status (сворачиваемые <details>), чекбоксы
|
||||
- [ ] POST /candidates/bulk — approve/reject по ids[] (транзакция)
|
||||
- [ ] POST /posts/{id}/comment — сохранить комментарий (comment TEXT)
|
||||
- [ ] POST /posts/{id}/reclassify — перезапуск классификатора на посте
|
||||
- [ ] POST /posts/{id}/rewrite — LLM-пересказ (qwen), возврат черновика для редактирования
|
||||
- [ ] Правая панель: меню действий, блок комментария, оригинальный пост со всеми медиа
|
||||
- [ ] Шаблоны: candidates.html (двухколоночная), формы новых действий
|
||||
- [ ] Тест: создание 2-3 тестовых постов, группировка, bulk-отклонение, коммент, реклассификация
|
||||
- [ ] `openspec validate candidates-mail-ui` — чисто
|
||||
- [ ] Обновить STATUS.md / WALKTHROUGH.md
|
||||
@@ -0,0 +1,111 @@
|
||||
# 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 список фильтруется по вхождению в текст поста
|
||||
Reference in New Issue
Block a user