166 lines
7.9 KiB
HTML
166 lines
7.9 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}{{ sequence.title }}{% endblock %}
|
||
|
||
{% block content %}
|
||
<p class="text-sm text-slate-500"><a href="/sequences" class="hover:underline">← Шаблоны сообщений</a></p>
|
||
<div class="mt-1 flex flex-wrap items-center justify-between gap-3">
|
||
<div>
|
||
<h1 class="text-2xl font-semibold">{{ sequence.title }}</h1>
|
||
<p class="font-mono text-xs text-slate-500">{{ sequence.key }}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{% if saved %}
|
||
<div class="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-800">
|
||
{% if saved == 'meta' %}Параметры шаблона сохранены.
|
||
{% elif saved == 'order' %}Порядок сообщений обновлён.
|
||
{% else %}Сообщение сохранено.{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if errors %}
|
||
<ul class="mt-4 list-disc rounded-lg border border-red-200 bg-red-50 p-3 pl-8 text-sm text-red-700">
|
||
{% for error in errors %}<li>{{ error }}</li>{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
|
||
<form method="post" action="/sequences/{{ sequence.key }}" class="mt-6 rounded-lg border border-slate-200 bg-white p-4">
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
<div class="grid gap-3 sm:grid-cols-3">
|
||
<div>
|
||
<label class="text-xs text-slate-500">Название</label>
|
||
<input name="title" value="{{ sequence.title }}"
|
||
class="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-sm focus:border-sky-500 focus:outline-none" />
|
||
</div>
|
||
<div class="sm:col-span-2">
|
||
<label class="text-xs text-slate-500">Описание</label>
|
||
<input name="description" value="{{ sequence.description }}"
|
||
class="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-sm focus:border-sky-500 focus:outline-none" />
|
||
</div>
|
||
</div>
|
||
<button type="submit" class="mt-3 rounded-lg bg-sky-600 px-4 py-2 text-sm font-medium text-white hover:bg-sky-700">
|
||
Сохранить параметры
|
||
</button>
|
||
</form>
|
||
|
||
<div class="mt-8 flex items-center justify-between">
|
||
<h2 class="text-lg font-medium">Сообщения ({{ items | length }})</h2>
|
||
<form method="post" action="/sequences/{{ sequence.key }}/items">
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
<button type="submit" class="rounded-lg border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">
|
||
Добавить сообщение
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="mt-4 space-y-5">
|
||
{% for item in items %}
|
||
<section class="rounded-lg border border-slate-200 bg-white">
|
||
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-slate-100 px-4 py-2">
|
||
<div class="text-sm text-slate-600">
|
||
<span class="font-medium">№ {{ loop.index }}</span>
|
||
<span class="ml-2 font-mono text-xs text-slate-400">id {{ item.id }}</span>
|
||
{% if item.enabled %}
|
||
<span class="ml-2 rounded bg-emerald-100 px-2 py-0.5 text-xs text-emerald-800">включено</span>
|
||
{% else %}
|
||
<span class="ml-2 rounded bg-slate-100 px-2 py-0.5 text-xs text-slate-500">выключено</span>
|
||
{% endif %}
|
||
</div>
|
||
<div class="flex items-center gap-2 text-sm">
|
||
<form method="post" action="/sequences/{{ sequence.key }}/items/{{ item.id }}/move">
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
<input type="hidden" name="direction" value="up" />
|
||
<button type="submit" class="text-slate-600 hover:underline" {% if loop.first %}disabled{% endif %}>Выше</button>
|
||
</form>
|
||
<form method="post" action="/sequences/{{ sequence.key }}/items/{{ item.id }}/move">
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
<input type="hidden" name="direction" value="down" />
|
||
<button type="submit" class="text-slate-600 hover:underline" {% if loop.last %}disabled{% endif %}>Ниже</button>
|
||
</form>
|
||
<form method="post" action="/sequences/{{ sequence.key }}/items/{{ item.id }}/delete" onsubmit="return confirm('Удалить сообщение?');">
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
<button type="submit" class="text-red-600 hover:underline">Удалить</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<form
|
||
method="post"
|
||
action="/sequences/{{ sequence.key }}/items/{{ item.id }}"
|
||
enctype="multipart/form-data"
|
||
class="p-4"
|
||
>
|
||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||
|
||
{% with editor_id="text-" ~ item.id, editor_name="text", editor_value=item.text, editor_max=(max_caption if item.image_name else max_message), editor_rows=12 %}
|
||
{% include "partials/editor.html" %}
|
||
{% endwith %}
|
||
|
||
<div class="mt-3 grid gap-3 lg:grid-cols-2">
|
||
<div>
|
||
<label class="text-xs text-slate-500">Кнопки — по одной в строке: <span class="font-mono">Текст | https://…</span></label>
|
||
<textarea
|
||
name="buttons"
|
||
rows="3"
|
||
placeholder="Канал | https://t.me/…"
|
||
class="mt-1 w-full rounded-lg border border-slate-300 p-2 font-mono text-xs focus:border-sky-500 focus:outline-none"
|
||
>{{ item.buttons_text }}</textarea>
|
||
<p class="mt-1 text-xs text-slate-500">До {{ max_buttons }} кнопок; каждая на своей строке.</p>
|
||
</div>
|
||
<div>
|
||
<label class="text-xs text-slate-500">Картинка (JPEG/PNG/WebP, до 5 МБ)</label>
|
||
{% if item.image_name %}
|
||
<div class="mt-1 flex items-center gap-3">
|
||
<img src="/sequences/media/{{ item.image_name }}" alt="" class="h-20 w-20 rounded object-cover" />
|
||
<label class="flex items-center gap-2 text-xs text-slate-600">
|
||
<input type="checkbox" name="remove_image" /> удалить
|
||
</label>
|
||
</div>
|
||
{% endif %}
|
||
<input type="file" name="image" accept="image/jpeg,image/png,image/webp"
|
||
class="mt-2 block w-full text-xs text-slate-600" />
|
||
<p class="mt-1 text-xs text-slate-500">
|
||
С картинкой текст уходит подписью — не больше {{ max_caption }} символов.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-3 flex items-center justify-between">
|
||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||
<input type="checkbox" name="enabled" {% if item.enabled %}checked{% endif %} /> включено
|
||
</label>
|
||
<button type="submit" class="rounded-lg bg-sky-600 px-4 py-2 text-sm font-medium text-white hover:bg-sky-700">
|
||
Сохранить сообщение
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</section>
|
||
{% else %}
|
||
<p class="text-sm text-slate-500">Сообщений пока нет — добавьте первое.</p>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<div class="mt-10">
|
||
<h2 class="text-lg font-medium">История изменений</h2>
|
||
{% if history %}
|
||
<ul class="mt-3 space-y-2">
|
||
{% for change in history %}
|
||
<li class="rounded-lg border border-slate-200 bg-white p-3 text-sm">
|
||
<div class="flex items-center justify-between text-slate-500">
|
||
<span>{{ change.changed_at.strftime("%d.%m.%Y %H:%M") }}</span>
|
||
<span>редактор: {{ change.editor_id or "—" }}</span>
|
||
</div>
|
||
<details class="mt-2">
|
||
<summary class="cursor-pointer text-slate-600">снимок</summary>
|
||
<pre class="mt-1 max-h-64 overflow-auto rounded bg-slate-50 p-2 text-xs">{{ change.snapshot | tojson(indent=2) }}</pre>
|
||
</details>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="mt-2 text-sm text-slate-500">Изменений ещё не было.</p>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|