116 lines
5.3 KiB
Django/Jinja
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 %}
|