mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Add the admin page (phase 3)
services/admin is a client of the content service's HTTP API, served on the tailnet only. It renders on the server with plain forms and no client scripts, and holds the admin token so the browser never sees it. - Review queue: reasons, line diff against the live version, accept, reject, and reject with the live version put back into Obsidian - Notes and drafts: search, preview, fields, backlinks, publish toggle - History with compare and roll back - Quick capture for a status or a Library item (optionally both) - Store: orders open or closed per category, open slots and stock - Overview: counts, last scan and error, scan now, sync job health - Same-origin check on every post, strict CSP, sanitized previews The content service gains /api/status, sync run reports (/api/syncs) and lookup of a note by name. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { html, type Html } from "./html.ts";
|
||||
|
||||
export interface Flash {
|
||||
msg?: string;
|
||||
err?: string;
|
||||
reasons?: string[];
|
||||
}
|
||||
|
||||
const nav: [string, string][] = [
|
||||
["/", "Overview"],
|
||||
["/review", "Review"],
|
||||
["/notes", "Notes"],
|
||||
["/drafts", "Drafts"],
|
||||
["/capture", "Capture"],
|
||||
["/store", "Store"],
|
||||
["/problems", "Problems"],
|
||||
];
|
||||
|
||||
export function layout(p: { title: string; path: string; held: number; flash?: Flash; body: Html }): Html {
|
||||
const current = (href: string) => (href === "/" ? p.path === "/" : p.path.startsWith(href));
|
||||
return html`<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>${p.title} · GregKilmer.com admin</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cinzel:wght@600&family=Montserrat:wght@400;600;700&display=swap">
|
||||
<link rel="stylesheet" href="/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="top">
|
||||
<a class="brand" href="/">GregKilmer.com admin</a>
|
||||
<nav>${nav.map(
|
||||
([href, label]) =>
|
||||
html`<a href="${href}" ${current(href) ? html`aria-current="page"` : ""}>${label}${href === "/review" && p.held ? html`<span class="count">${p.held}</span>` : ""}</a>`,
|
||||
)}</nav>
|
||||
</header>
|
||||
<main>
|
||||
${p.flash?.msg ? html`<div class="flash" role="status">${p.flash.msg}</div>` : ""}
|
||||
${p.flash?.err
|
||||
? html`<div class="flash err" role="alert">${p.flash.err}${p.flash.reasons?.length ? html`<ul>${p.flash.reasons.map((r) => html`<li>${r}</li>`)}</ul>` : ""}</div>`
|
||||
: ""}
|
||||
${p.body}
|
||||
</main>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
Reference in New Issue
Block a user