feat: add categories admin UI with CRUD

Wire up categories service, add CategoriesHandler with full CRUD, create index/edit templates, register routes in server.go, and add Categories nav link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Leon Mika 2026-03-18 21:42:17 +11:00
parent 3c80f63a55
commit ffa86b12e9
6 changed files with 198 additions and 0 deletions

View file

@ -10,6 +10,9 @@
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/posts">Posts</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/categories">Categories</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/uploads">Uploads</a>
</li>

View file

@ -0,0 +1,47 @@
<main class="container">
<div class="my-4">
<h4>{{ if .isNew }}New Category{{ else }}Edit Category{{ end }}</h4>
</div>
{{ if .isNew }}
<form method="post" action="/sites/{{ .site.ID }}/categories">
{{ else }}
<form method="post" action="/sites/{{ .site.ID }}/categories/{{ .category.ID }}">
{{ end }}
<input type="hidden" name="guid" value="{{ .category.GUID }}">
<div class="row mb-3">
<label for="catName" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="catName" name="name" value="{{ .category.Name }}">
</div>
</div>
<div class="row mb-3">
<label for="catSlug" class="col-sm-2 col-form-label">Slug</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="catSlug" name="slug" value="{{ .category.Slug }}">
<div class="form-text">Auto-generated from name if left blank.</div>
</div>
</div>
<div class="row mb-3">
<label for="catDesc" class="col-sm-2 col-form-label">Description</label>
<div class="col-sm-9">
<textarea class="form-control" id="catDesc" name="description" rows="5">{{ .category.Description }}</textarea>
<div class="form-text">Markdown supported. Displayed on the category archive page.</div>
</div>
</div>
<div class="row mb-3">
<div class="col-sm-2"></div>
<div class="col-sm-9">
<button type="submit" class="btn btn-primary">{{ if .isNew }}Create{{ else }}Save{{ end }}</button>
{{ if not .isNew }}
<button type="button" class="btn btn-outline-danger ms-2"
onclick="if(confirm('Delete this category? Posts will not be deleted.')) { document.getElementById('delete-form').submit(); }">Delete</button>
{{ end }}
</div>
</div>
</form>
{{ if not .isNew }}
<form id="delete-form" method="post" action="/sites/{{ .site.ID }}/categories/{{ .category.ID }}/delete" style="display:none;"></form>
{{ end }}
</main>

View file

@ -0,0 +1,35 @@
<main class="container">
<div class="my-4 d-flex justify-content-between align-items-baseline">
<h4>Categories</h4>
<div>
<a href="/sites/{{ .site.ID }}/categories/new" class="btn btn-success">New Category</a>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Slug</th>
<th>Posts</th>
<th></th>
</tr>
</thead>
<tbody>
{{ range .categories }}
<tr>
<td><a href="/sites/{{ $.site.ID }}/categories/{{ .ID }}">{{ .Name }}</a></td>
<td><code>{{ .Slug }}</code></td>
<td>{{ .PostCount }}</td>
<td>
<a href="/sites/{{ $.site.ID }}/categories/{{ .ID }}" class="btn btn-outline-secondary btn-sm">Edit</a>
</td>
</tr>
{{ else }}
<tr>
<td colspan="4" class="text-center text-muted py-4">No categories yet.</td>
</tr>
{{ end }}
</tbody>
</table>
</main>