Files
avandClaude Opus 4.8 1f4267a046 Ревью: единый блок выбора источника, клик = выбор (review-unified-source-block)
Три секции экрана ревью (Догадка/Источник/Раскладка) слиты в один блок:
список вариантов (радио) → инфо о выбранном → предпросмотр раскладки. Клик
по варианту сразу выбирает и сохраняет источник и обновляет инфо+раскладку
частичным htmx-свопом блока, без полной перезагрузки и без кнопки «выбрать».

- httpapi: reviewBlockAction (htmx-aware, детект HX-Request) для
  candidate/nobase/source; вынос buildReviewView; поля SeasonSummary и
  BlockError; сводка сезонов (seasonSummary/seasonRanges)
- тип movie↔series убран из UI (read-only); удалён веб-роут /type и
  handleSetType, метод SetType из интерфейса httpapi (worker/Telegram не тронуты)
- шаблон: партиал review_source_block, ссылка «запись ↗» вне кликабельного
  label, фокус радио с клавиатуры; чистка мёртвого sourceView.Files/IsSeries
- тесты: htmx-своп выбора, htmx-путь ошибки, юнит-тесты сводки сезонов
- openspec: спеки review/web-ui синхронизированы, change заархивирован
- беклог: сложные сериальные раздачи; oob-обновление панели действий

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 08:12:28 +03:00

71 lines
6.0 KiB
Markdown
Raw Permalink 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.
## 1. Транспорт: htmx-aware выбор источника
- [x] 1.1 Вынести построение `reviewView` из `handleReview` в общий хелпер
(напр. `buildReviewView(id, rd, errMsg)`), чтобы им пользовались и
`handleReview`, и `reviewBlockAction` (без дублирования логики сборки view).
- [x] 1.2 Добавить в `reviewView` поле `SeasonSummary string` (пусто для
фильма); собирать сводку по различным сезонам файлов `rd.Plan` с ролью
`episode` («Сезон 2» / «Сезоны 1–3» / «Сезоны 1, 3, 4» / «Спецвыпуски»),
учитывая что `Season == nil`/`0` — спецвыпуск.
- [x] 1.3 Добавить в `reviewView` отдельное поле `BlockError string` для ошибки
выбора внутри блока (не путать с верхним `.Error` из `?err=`).
- [x] 1.4 Ввести помощник `reviewBlockAction` (по образцу `reviewAction`): на
`r.Header.Get("HX-Request")` после успешной операции перечитать `ReviewData`,
отрендерить партиал блока (`200`); без htmx — прежний PRG-редирект. При ошибке
на htmx-пути — рендер партиала с `BlockError`, активный источник не меняется.
- [x] 1.5 Перевести `handleChooseCandidate`, `handleNoBase`, `handleAddSource`
на `reviewBlockAction`.
- [x] 1.6 Удалить веб-роут `POST /ui/downloads/{id}/type` и `handleSetType` из
`internal/httpapi`; grep'ом убедиться, что на них больше нет ссылок (Telegram
зовёт `worker.SetType` напрямую через свой интерфейс — доменный метод
оставить).
## 2. Шаблоны: единый блок выбора источника
- [x] 2.1 Создать партиал `web/templates/partials/review_source_block.html` с
корневым контейнером `id="source-block"`: (а) список вариантов из `.Sources`
радиокнопками, label оборачивает всю строку; (б) инфо-часть выбранного
источника (тип read-only, название, ориг. название, год, сезон для сериала,
место под режиссёра); (в) предпросмотр раскладки из `.Files`
(`{{template "layout_widget" .Files}}`).
- [x] 2.2 На радио вариантов повесить `hx-post` (кандидат → `/candidate` с
`candidate_id`; нейронка → `/nobase`), `hx-trigger="change"`,
`hx-target="#source-block"`, `hx-swap="outerHTML"`. Активный вариант отмечен
выбранным радио.
- [x] 2.3 Форму ручного добавления источника (`/source`) перенести под список
внутрь партиала; сделать её `hx-post` с тем же `hx-target`/`hx-swap`.
- [x] 2.4 На радио вариантов задать общий `name` (взаимная эксклюзивность):
кандидат — `value`=`candidate_id`, `hx-post` `/candidate`; нейронка — пустое
`value`, `hx-post` `/nobase`. Внешнюю ссылку «запись ↗» вынести из
кликабельной зоны label (или гасить всплытие), чтобы клик по ней не
переключал источник.
- [x] 2.5 В `review.html` заменить три секции (Догадка/Источник/Раскладка) на
`{{template "review_source_block" .}}`, сохранив обёртку `{{if .HasPlan}}`;
убрать per-source `<details>` и кнопки «выбрать», убрать форму переключения
типа.
- [x] 2.6 Баннер ошибки выбора рендерить внутри блока из `.BlockError`, чтобы
htmx-своп его показывал/очищал; верхний `.Error` (`?err=`) не трогать.
## 3. Стили
- [x] 3.1 Стили единого блока в `web/static/css/jellybit.css` (без инлайн-стилей
и хардкода цветов вне токенов): крупная кликабельная строка-вариант,
разделение частей блока (список → инфо → предпросмотр), корректный вид на
узком экране (мобильный тап).
## 4. Проверка
- [x] 4.0 Обновить тесты `internal/httpapi/httpapi_test.go`: ассерты на удаляемую
разметку («Источник совпадения», кнопка «выбрать») и тест `POST /type`
(`rv.typed`) — переписать под новый блок / удалить; добавить проверку
htmx-свопа выбора (ответ-партиал по `HX-Request`).
- [x] 4.1 `task build` / `task lint` / `task test` — зелёные.
- [x] 4.2 Прогнать вручную (или через verify): открыть ревью, кликнуть по
разным вариантам — инфо и предпросмотр обновляются без перезагрузки; выбор
сохраняется (перезагрузка страницы показывает тот же активный источник);
ручное добавление источника работает через htmx; «Применить» создаёт
раскладку, совпадающую с показанным предпросмотром; тип показан read-only.
- [x] 4.3 Проверить деградацию/ошибки: невалидный ручной ввод показывает ошибку
в блоке и не меняет активный источник.
- [x] 4.4 `openspec validate review-unified-source-block --strict` — без ошибок.