Files
volumen/src/volumen/web/templates/list.html.jinja
T

116 lines
5.3 KiB
Django/Jinja

{% extends "layout.html.jinja" %}
{% block content %}
<div class="page-head">
<div>
<h1>Posts</h1>
<p class="lede">{{ posts|length }} post{{ '' if posts|length == 1 else 's' }} in total</p>
</div>
<a class="btn btn-primary" href="/admin/posts/new">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
New post
</a>
</div>
<div class="toolbar">
<div class="search">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input id="post-search" class="input" type="text" placeholder="Search posts...">
</div>
<div class="filters" id="post-filters">
<button type="button" class="filter-chip active" data-filter="all">All</button>
<button type="button" class="filter-chip" data-filter="published">Published</button>
<button type="button" class="filter-chip" data-filter="draft">Drafts</button>
<button type="button" class="filter-chip" data-filter="scheduled">Scheduled</button>
</div>
</div>
{% if not posts %}
<div class="empty">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
<h3>No posts yet</h3>
<p>Create your first post to get started.</p>
<a class="btn btn-primary" href="/admin/posts/new" style="margin-top: 0.75rem;">Create post</a>
</div>
{% else %}
<div class="post-grid" id="post-grid">
{% for post in posts %}
{% if post.draft %}
{% set status = "draft" %}
{% elif post.scheduled %}
{% set status = "scheduled" %}
{% else %}
{% set status = "published" %}
{% endif %}
<a href="/admin/posts/{{ post.slug }}/edit"
class="post-card"
data-title="{{ post.title | default('') | lower }}"
data-slug="{{ post.slug }}"
data-status="{{ status }}">
{% if post.cover %}
<div class="post-cover" style="background-image: url('{{ post.cover }}');"></div>
{% else %}
<div class="post-cover placeholder">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
</div>
{% endif %}
<div class="post-body">
<h3 class="post-title">{{ post.title }}</h3>
{% if post.excerpt and post.excerpt | string | trim | length > 0 %}
<p class="post-excerpt">{{ post.excerpt }}</p>
{% endif %}
<div class="post-meta">
<span class="badge badge-lang">{{ post.lang }}</span>
{% if post.draft %}<span class="badge badge-warn">Draft</span>{% endif %}
{% if post.scheduled %}<span class="badge badge-accent">Scheduled</span>{% endif %}
{% if post.date_string %}<span>{{ post.date_string }}</span>{% endif %}
</div>
</div>
<div class="post-actions">
<span class="btn btn-sm btn-ghost" style="cursor: default;">Edit</span>
<span class="spacer" style="flex: 1;"></span>
<form method="post" action="/admin/posts/{{ post.slug }}/delete"
onsubmit="return confirm('Delete this post?');" style="margin: 0;">
<input type="hidden" name="_csrf" value="{{ csrf_token }}">
<button type="submit" class="btn btn-sm btn-ghost" style="color: var(--danger);" onclick="event.preventDefault(); event.stopPropagation(); this.closest('form').submit();">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
</button>
</form>
</div>
</a>
{% endfor %}
</div>
{% endif %}
<script>
(function () {
var search = document.getElementById("post-search");
var filterButtons = document.querySelectorAll("#post-filters .filter-chip");
var cards = document.querySelectorAll("#post-grid .post-card");
var currentFilter = "all";
function applyFilters() {
var q = (search.value || "").toLowerCase().trim();
cards.forEach(function (card) {
var title = card.dataset.title;
var slug = card.dataset.slug;
var status = card.dataset.status;
var matchesSearch = !q || title.indexOf(q) !== -1 || slug.indexOf(q) !== -1;
var matchesFilter = currentFilter === "all" || status === currentFilter;
card.style.display = matchesSearch && matchesFilter ? "" : "none";
});
}
if (search) search.addEventListener("input", applyFilters);
filterButtons.forEach(function (btn) {
btn.addEventListener("click", function () {
filterButtons.forEach(function (b) { b.classList.remove("active"); });
btn.classList.add("active");
currentFilter = btn.dataset.filter;
applyFilters();
});
});
})();
</script>
{% endblock %}