Files
vesti/openspec/changes/keep-candidates-context/design.md
T
kpa39l 0999424baa fix(candidates): сохранять группировку после действий, чинить пустой ?status=rejected
- _fetch_candidates: жёсткое status='new' только при пустом status; явный
  статус (rejected/published) — единственный фильтр статуса → ?status=rejected
  больше не даёт пустой список
- формы approve/reclassify/rewrite/reject/comment: скрытые поля
  group_by/direction/own/q — контекст страницы
- POST-роуты: редирект через _cand_back() сохраняет группировку и фильтры
  (reject → ?status=rejected&group_by=...; approve → ?status=published&...)
2026-09-14 05:47:30 +00:00

3.7 KiB
Raw Blame History

Design

1. _fetch_candidates — статус по умолчанию vs явный

Текущее (строки ~151-166):

    where.append("p.is_own=0")
    where.append("p.status='new'")
    ...
    if status and status != "new":
        where.append("p.status=?")
        params.append(status)

Правка:

    where.append("p.is_own=0")
    if not status:
        # по умолчанию — только кандидаты (new); явный статус ниже
        where.append("p.status='new'")
    else:
        # явный статус (new/rejected/published) — единственный фильтр статуса
        where.append("p.status=?")
        params.append(status)
    if direction:
        where.append("p.direction=?")
        params.append(direction)
    if own in ("1", "0"):
        where.append("p.is_own=?")
        params.append(int(own))
    if q:
        where.append("(p.text LIKE ? OR p.summary LIKE ?)")
        params += [f"%{q}%", f"%{q}%"]

2. Скрытые поля в формах правой панели

В candidates.html в каждую форму правой панели (approve, reclassify, rewrite, reject, comment) добавить:

<input type="hidden" name="group_by" value="{{ group_by }}">
<input type="hidden" name="direction" value="{{ direction }}">
<input type="hidden" name="own" value="{{ own }}">
<input type="hidden" name="q" value="{{ q }}">

(Переменные group_by, direction, own, q уже доступны в контексте рендера роута /candidates.)

3. Роуты POST-действий — сохранять контекст

Общий хелпер для построения URL возврата:

def _cand_back(group_by="", direction="", own="", q="", **extra):
    parts = []
    if group_by:
        parts.append(f"group_by={group_by}")
    if direction:
        parts.append(f"direction={direction}")
    if own in ("0", "1"):
        parts.append(f"own={own}")
    if q:
        parts.append(f"q={quote(q)}")
    for k, v in extra.items():
        if v:
            parts.append(f"{k}={v}")
    return "/candidates?" + "&".join(parts) if parts else "/candidates"

Применение:

  • reject(post_id, request, group_by="", direction="", own="", q=""): return RedirectResponse(url=_cand_back(group_by, direction, own, q, status="rejected"), status_code=302) → пользователь остаётся в своей группировке, видит список отклонённых.
  • comment/reclassify/rewrite: _cand_back(group_by, direction, own, q, selected=post_id).
  • approve: после успеха — _cand_back(group_by, direction, own, q, status="published") (пользователь видит опубликованные в той же группировке) или /published. Решение: approve возвращает в кандидаты с status=published в той же группировке, чтобы контекст не терялся. Ошибки — _cand_back(..., selected=post_id, error=err).

FastAPI: параметры форм объявляются как group_by: str = Form("") и т.д.

Верификация

  • openspec validate keep-candidates-context — чисто.
  • Юнит: _fetch_candidates(c,'','rejected','','','date') → возвращает отклонённые (не пусто); _fetch_candidates(c,'','','','','date') → new внешние.
  • Живой: POST /posts/{id}/reject с form group_by=date → редирект на /candidates?group_by=date&status=rejected, список не пуст.
  • Рестарт vesti-web.