feat(candidates): список отобранных между кандидатами и опубликованными

- новый статус selected; в кандидатах только «⭐ Отобрать» (single+bulk)
- /selected — отдельный список: вся обработка (классификация/переписывание/комментарий/публикация)
- классификация только по кнопке (токены на кандидатов не тратятся)
- рядом с оригиналом — поле «✍️ Мой пост» (rewritten_text всегда виден), туда ложится черновик «Переписать»
- все обработчики учитывают src (candidates|selected) для редиректов
This commit is contained in:
kpa39l
2026-09-15 17:37:11 +00:00
parent b542e9b4fa
commit ae8af8de9c
9 changed files with 469 additions and 98 deletions
+20 -63
View File
@@ -99,23 +99,21 @@
</div>
<form method="post" action="/candidates/bulk" id="bulk-form"
onsubmit="return confirm('Выполнить групповое действие над выбранными?')">
<input type="hidden" name="direction" value="{{ direction }}">
<input type="hidden" name="status" value="{{ status }}">
<input type="hidden" name="own" value="{{ own }}">
<input type="hidden" name="q" value="{{ q }}">
<input type="hidden" name="group_by" value="{{ group_by }}">
<div class="border-top pt-2 mt-1">
<div class="small text-muted mb-1">⚡ Массовые операции <span class="badge bg-secondary" id="sel-count">0</span></div>
<div class="btn-group btn-group-sm w-100 d-flex flex-wrap gap-1">
<button class="btn btn-sm btn-success flex-fill" name="action" value="approve">✅ Опубликовать</button>
<button class="btn btn-sm btn-info flex-fill text-dark" name="action" value="process">🤖 Обработать моделью</button>
<button class="btn btn-sm btn-outline-danger flex-fill" name="action" value="reject">🚫 Отклонить</button>
<button class="btn btn-sm btn-outline-secondary flex-fill" name="action" value="reject-old">🗑 Старьё</button>
</div>
<div class="form-text mt-1">Отметьте галочками слева и нажмите действие. «Обработать» — переклассификация локальной моделью (qwen).</div>
</div>
</form>
onsubmit="return confirm('Отобрать выбранные посты?')\n || true">
<input type="hidden" name="direction" value="{{ direction }}">
<input type="hidden" name="status" value="{{ status }}">
<input type="hidden" name="own" value="{{ own }}">
<input type="hidden" name="q" value="{{ q }}">
<input type="hidden" name="group_by" value="{{ group_by }}">
<input type="hidden" name="src" value="candidates">
<div class="border-top pt-2 mt-1">
<div class="small text-muted mb-1">⚡ Массовые операции <span class="badge bg-secondary" id="sel-count">0</span></div>
<div class="btn-group btn-group-sm w-100 d-flex flex-wrap gap-1">
<button class="btn btn-sm btn-warning flex-fill text-dark" name="action" value="select">⭐ Отобрать</button>
</div>
<div class="form-text mt-1">Отберите понравившиеся посты — они улетят в «Отобранные», где их можно обработать, переписать и опубликовать. Классификация моделью — только там, по кнопке.</div>
</div>
</form>
</div>
</div>
@@ -149,54 +147,13 @@
<div class="mb-3">
{% set hid %}<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 }}">{% endset %}
<div class="d-flex flex-wrap gap-2 mb-2">
{% if selected.status != 'published' %}
<form method="post" action="/posts/{{ selected.id }}/reclassify">
{% if selected.status != 'selected' and selected.status != 'rejected' and selected.status != 'published' %}
<form method="post" action="/posts/{{ selected.id }}/select">
{{ hid }}
<button class="btn btn-info" type="submit" title="Заново прогнать через модель (qwen3:8b): направление, релевантность, интерес — обновит бейджи">🤖 Обработать моделью</button>
<button class="btn btn-warning" type="submit" title="Отобрать пост — он уйдёт в «Отобранные», где доступны обработка моделью, переписывание, комментарий и публикация">⭐ Отобрать</button>
</form>
{% endif %}
{% if selected.status != 'published' %}
<form method="post" action="/posts/{{ selected.id }}/rewrite">
{{ hid }}
<button class="btn btn-outline-primary" type="submit" title="Сгенерировать черновик пересказа через модель — затем можно отредактировать и сохранить (пойдёт в канал вместо оригинала, от вашего имени)">✍️ Переписать</button>
</form>
{% endif %}
{% if selected.status != 'rejected' and selected.status != 'published' %}
<form method="post" action="/posts/{{ selected.id }}/reject">
{{ hid }}
<button class="btn btn-outline-danger" type="submit">🚫 Отклонить</button>
</form>
{% endif %}
</div>
{% if selected.status != 'published' %}
<form method="post" action="/posts/{{ selected.id }}/approve" id="approve-form">
{{ hid }}
<label class="form-label small text-muted mb-0 d-block">💬 Мой комментарий (будет первым в канале):</label>
<textarea class="form-control form-control-sm mt-1" name="comment" rows="2"
placeholder="Комментарий/анонс…">{{ selected.comment or '' }}</textarea>
<div class="mt-2 d-flex flex-wrap gap-2">
<button class="btn btn-success" type="submit" title="Опубликовать в канал: комментарий (если есть) первым, затем текст поста/пересказ, картинка, ссылка">✅ Опубликовать</button>
<button class="btn btn-sm btn-outline-secondary" type="submit"
formaction="/posts/{{ selected.id }}/comment"
title="Сохранить комментарий в черновик (не публикует). Он подтянется при следующей публикации">💾 Сохранить комментарий</button>
</div>
</form>
{% endif %}
</div>
<div class="mb-3">
{% if selected.rewritten_text %}
<div class="mt-2">
<label class="form-label small text-muted">✍️ Пересказ (редактируйте и сохраните — он пойдёт в канал от вашего имени):</label>
<form method="post" action="/posts/{{ selected.id }}/rewrite-save" class="row g-1">
{{ hid }}
<textarea class="form-control form-control-sm" name="rewritten_text" rows="5">{{ selected.rewritten_text }}</textarea>
<div class="col-auto mt-1">
<button class="btn btn-sm btn-outline-primary" type="submit">💾 Сохранить пересказ</button>
</div>
</form>
</div>
{% else %}
<div class="alert alert-info py-2 mb-1">Этот пост уже в «Отобранных» — обработка там.</div>
{% endif %}
</div>