From 5f5aac5e3e15811f66afa737fa195e2afe3a71ba Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 16 Apr 2026 12:48:33 +0500 Subject: [PATCH] fix(admin): UX and bug fixes across admin pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import: fix "1/1/1" date display (Go zero time.Time → show dash) - AI Jobs: guard fmtDate against zero-time dates - AI Jobs: add "Cancel all in-flight (N)" bulk action button - AI Jobs sidebar: show live running+pending count badge from layout - Notifications: add broadcast panel linking to push.libnovel.cc, relabel inbox section Co-Authored-By: Claude Sonnet 4.6 --- ui/src/routes/admin/+layout.server.ts | 8 +++++ ui/src/routes/admin/+layout.svelte | 12 +++++-- ui/src/routes/admin/ai-jobs/+page.svelte | 34 ++++++++++++++++++- ui/src/routes/admin/import/+page.svelte | 4 ++- .../routes/admin/notifications/+page.svelte | 24 ++++++++++++- 5 files changed, 77 insertions(+), 5 deletions(-) diff --git a/ui/src/routes/admin/+layout.server.ts b/ui/src/routes/admin/+layout.server.ts index 24471a4..072808d 100644 --- a/ui/src/routes/admin/+layout.server.ts +++ b/ui/src/routes/admin/+layout.server.ts @@ -1,8 +1,16 @@ import { redirect } from '@sveltejs/kit'; import type { LayoutServerLoad } from './$types'; +import { listAIJobs } from '$lib/server/pocketbase'; +import { log } from '$lib/server/logger'; export const load: LayoutServerLoad = async ({ locals }) => { if (locals.user?.role !== 'admin') { redirect(302, '/'); } + const jobs = await listAIJobs().catch((e) => { + log.warn('admin/layout', 'failed to load ai jobs for sidebar badge', { err: String(e) }); + return []; + }); + const runningAiJobs = jobs.filter((j) => j.status === 'running' || j.status === 'pending').length; + return { runningAiJobs }; }; diff --git a/ui/src/routes/admin/+layout.svelte b/ui/src/routes/admin/+layout.svelte index 0577f62..f5ef1ed 100644 --- a/ui/src/routes/admin/+layout.svelte +++ b/ui/src/routes/admin/+layout.svelte @@ -1,6 +1,7 @@ @@ -136,6 +138,7 @@ diff --git a/ui/src/routes/admin/ai-jobs/+page.svelte b/ui/src/routes/admin/ai-jobs/+page.svelte index a8c8b6a..32c3817 100644 --- a/ui/src/routes/admin/ai-jobs/+page.svelte +++ b/ui/src/routes/admin/ai-jobs/+page.svelte @@ -57,6 +57,7 @@ // ── Cancel ──────────────────────────────────────────────────────────────────── let cancellingIds = $state(new Set()); let cancelErrors: Record = $state({}); + let cancellingAll = $state(false); async function cancelJob(id: string) { if (cancellingIds.has(id)) return; @@ -77,6 +78,14 @@ } } + async function cancelAllRunning() { + if (cancellingAll) return; + cancellingAll = true; + const inFlight = jobs.filter((j) => j.status === 'running' || j.status === 'pending'); + await Promise.all(inFlight.map((j) => cancelJob(j.id))); + cancellingAll = false; + } + // ── Review & Apply (chapter-names jobs) ────────────────────────────────────── interface ProposedTitle { @@ -411,7 +420,9 @@ function fmtDate(s: string | undefined) { if (!s) return '—'; - return new Date(s).toLocaleString(undefined, { + const d = new Date(s); + if (d.getFullYear() < 2000) return '—'; + return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', @@ -482,6 +493,27 @@ {/each} + + {#if stats.running + stats.pending > 0} +
+ +
+ {/if} +
+ +
+ + + +
+

Broadcast to users

+

To send push notifications or in-app messages to all subscribers, use the push dashboard.

+ + Open push.libnovel.cc + + + + +
+
+
-

Notifications

+

Your Notification Inbox

{#if unreadCount > 0}

{unreadCount} unread

{/if}