Files
website/app/templates/admin.templ
jimmy 45b31be9a7 Add gallery admin and video media support.
This updates gallery handling to support video playback with generated poster thumbnails, adds authenticated admin upload/delete flows, and improves dev/runtime behavior including reliable thumbnail generation and media-safe response handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 23:01:02 +12:00

157 lines
4.4 KiB
Plaintext

package templates
import (
"fmt"
"technical.kiwi/website/internal/gallery"
)
templ AdminLogin(errMsg string) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Admin login — Technical Kiwi</title>
<link rel="stylesheet" href="/static/style.css"/>
</head>
<body class="admin-body">
<main class="admin-login">
<h1>Gallery admin</h1>
if errMsg != "" {
<div class="alert alert-error" role="alert"><p>{ errMsg }</p></div>
}
<form method="post" action="/admin/login" class="contact-form">
<div class="form-row">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autocomplete="username"/>
</div>
<div class="form-row">
<label for="password">Password</label>
<input type="password" id="password" name="password" required autocomplete="current-password"/>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
<p class="admin-back"><a href="/">← Back to site</a></p>
</main>
</body>
</html>
}
templ AdminDashboard(images []gallery.Image, albums []string, flash string) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Gallery admin — Technical Kiwi</title>
<link rel="stylesheet" href="/static/style.css"/>
<script src="/static/htmx.min.js" defer></script>
</head>
<body class="admin-body" hx-boost="false">
<header class="admin-header">
<h1>Gallery admin</h1>
<form method="post" action="/admin/logout">
<button type="submit" class="btn btn-ghost">Sign out</button>
</form>
</header>
<main class="admin-main">
<div id="admin-flash">
if flash != "" {
@AdminFlash(flash)
}
</div>
<section class="admin-panel">
<h2>Upload image</h2>
<form
class="contact-form"
method="post"
action="/admin/upload"
enctype="multipart/form-data"
hx-post="/admin/upload"
hx-target="#admin-images"
hx-swap="innerHTML"
hx-encoding="multipart/form-data"
>
<div class="form-row">
<label for="album">Album</label>
<select id="album" name="album" required>
for _, a := range albums {
<option value={ a }>{ gallery.AlbumLabel(a) }</option>
}
<option value="new">+ New album</option>
</select>
</div>
<div class="form-row">
<label for="album_new">New album name</label>
<input type="text" id="album_new" name="album_new" placeholder="e.g. my-project" pattern="[a-z0-9][a-z0-9_-]{0,63}"/>
</div>
<div class="form-row">
<label for="image">JPEG image</label>
<input type="file" id="image" name="image" accept="image/jpeg,.jpg,.jpeg" required/>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</section>
<section class="admin-panel">
<h2>Images ({ fmt.Sprintf("%d", len(images)) })</h2>
<div id="admin-images">
@AdminImageTable(images)
</div>
</section>
<p class="admin-back"><a href="/">← View public site</a></p>
</main>
</body>
</html>
}
templ AdminFlash(msg string) {
if msg != "" {
<div class="alert alert-error" role="alert"><p>{ msg }</p></div>
}
}
templ AdminImageTable(images []gallery.Image) {
if len(images) == 0 {
<p class="gallery-empty">No images yet.</p>
} else {
<div class="admin-table-wrap">
<table class="admin-table">
<thead>
<tr>
<th>Preview</th>
<th>Path</th>
<th>Album</th>
<th></th>
</tr>
</thead>
<tbody>
for _, img := range images {
<tr>
<td class="admin-thumb">
if img.ThumbURL != "" {
<img src={ img.ThumbURL } alt="" loading="lazy"/>
}
</td>
<td><code>{ img.RelPath }</code></td>
<td>{ gallery.AlbumLabel(img.Album) }</td>
<td>
<form
method="post"
action="/admin/delete"
hx-post="/admin/delete"
hx-target="#admin-images"
hx-swap="innerHTML"
hx-confirm="Delete this image and its thumbnails?"
>
<input type="hidden" name="path" value={ img.RelPath }/>
<button type="submit" class="btn btn-ghost admin-delete">Delete</button>
</form>
</td>
</tr>
}
</tbody>
</table>
</div>
}
}