docs(admin): dashboard page — stats, watcher, Quick Actions, scan panel
Second page of the admin tree. Documents the four-card stats grid with what each number actually counts (instance-wide totals; Books = every media item including podcast episodes), the File Watcher card's data source (/api/scanner/watch/status, armed by Auto-Scan) and its zero-libraries meaning, Force Rescan All Libraries with its real mechanics (one job per library, skip-on-active, backfill tool vs. routine shallow refresh), and the Scanning panel's live progress + Cancel all scans. Links only to pages that exist (scanning.md); the Settings reference stays prose until that page lands. The real-tree nav test's Administration assertion loosens further: guide leads, everything else strictly follows usabilityOrder — durable across the remaining page landings. Also fixes the amend this commit replaces: its first version briefly carried a forward link to the unwritten settings.md (caught by the link gate, now removed) — the gate worked; the && chain around go test let the bad commit through, which is exactly why this amend exists.
This commit is contained in:
1 parent
89fc479bdb
commit
ed550fa914
2 files changed
+76
-4
No files matched your search
@@ -0,0 +1,72 @@
|
|||||||
|
<!-- Ground truth: templates/admin.templ (stats grid, File Watcher card,
|
||||||
|
Quick Actions, Scanning panel), internal/router/frontend.go (/admin
|
||||||
|
handler), internal/router/admin_library.go (getAdminStats),
|
||||||
|
web/src/admin.ts (scanAllLibraries one-job-per-library, watch status
|
||||||
|
fetch, progress rows), internal/handlers/scanner.go (watch status,
|
||||||
|
coalescing 409). Re-verify against these when the page changes. -->
|
||||||
|
|
||||||
|
# Admin Dashboard
|
||||||
|
|
||||||
|
The dashboard at `/admin` is the admin landing page: a four-number health
|
||||||
|
check, the file-watcher status, and the place where library-wide scans are
|
||||||
|
launched and watched.
|
||||||
|
|
||||||
|
## The stats grid
|
||||||
|
|
||||||
|
Four counts, straight from the database, refreshed on every page load:
|
||||||
|
|
||||||
|
| Card | What the number counts |
|
||||||
|
| --- | --- |
|
||||||
|
| **Libraries** | Every library — book, comic, audiobook, and podcast libraries alike |
|
||||||
|
| **Books** | Every item across all of them: books, comics, audiobooks, and podcast episodes |
|
||||||
|
| **Users** | Every account on the instance |
|
||||||
|
| **Devices** | Every registered sync device (Android, KOReader, Kobo) |
|
||||||
|
|
||||||
|
These are instance-wide totals — they don't filter by what any individual
|
||||||
|
user can see.
|
||||||
|
|
||||||
|
## File Watcher
|
||||||
|
|
||||||
|
The **File Watcher** card shows how many libraries are currently under
|
||||||
|
live watch for file changes. The watcher exists when **Auto-Scan** is
|
||||||
|
enabled on the Settings page (Settings → Scanning); the number updates on
|
||||||
|
page load.
|
||||||
|
|
||||||
|
- Watching **0** libraries usually means auto-scan is off — turn it on in
|
||||||
|
Settings → Scanning, and the watcher arms on the next server start.
|
||||||
|
- Watched libraries get shallow scans as files change, plus a periodic
|
||||||
|
poll on the scan interval — see [Scanning](scanning.md) for exactly what
|
||||||
|
those do.
|
||||||
|
|
||||||
|
## Quick Actions
|
||||||
|
|
||||||
|
### Force Rescan All Libraries
|
||||||
|
|
||||||
|
The big one. This is a **force rescan of every library**: each file is
|
||||||
|
re-read and re-extracted with the scanner's current parsing, and hash
|
||||||
|
identifiers are recomputed — nothing unchanged is skipped. Use it as the
|
||||||
|
backfill tool after mass edits or scanner improvements; don't use it as a
|
||||||
|
routine refresh (that's what the watcher and [per-library
|
||||||
|
scans](scanning.md) are for, which skip unchanged files).
|
||||||
|
|
||||||
|
Mechanically it starts **one scan job per library** and reports per
|
||||||
|
library: started, or skipped because that library already had a scan
|
||||||
|
running (only one scan per library can run at a time).
|
||||||
|
|
||||||
|
### Manage Libraries
|
||||||
|
|
||||||
|
A shortcut to [Libraries](libraries.md), where folders, access, and
|
||||||
|
per-library scans live.
|
||||||
|
|
||||||
|
## The Scanning panel
|
||||||
|
|
||||||
|
Whenever scans are running, a **Scanning Libraries** panel appears with a
|
||||||
|
live progress row per library (progress bar, current file counts, and a
|
||||||
|
per-library cancel button), plus:
|
||||||
|
|
||||||
|
- **Cancel all scans** — stops every queued or running scan job
|
||||||
|
- The close button — hides the panel; scans keep running without it
|
||||||
|
|
||||||
|
The panel appears for any scan — the dashboard's own force rescan, or one
|
||||||
|
started from the Libraries page. Progress updates live while the page is
|
||||||
|
open; the jobs themselves run on the server either way.
|
||||||
@@ -243,18 +243,18 @@ func TestBuildNavigationUsabilityOrder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Administration grows as its pages are written; pin its shape, not
|
// Administration grows as its pages are written; pin its shape, not
|
||||||
// its size: the guide leads, Libraries follows, and every other item
|
// its size: the guide leads (pinned landing), and every other item
|
||||||
// must appear in usabilityOrder's declared relative order.
|
// must appear in usabilityOrder's declared relative order.
|
||||||
admin := find("Administration")
|
admin := find("Administration")
|
||||||
if len(admin.Items) < 2 || admin.Items[0].Title != "Admin Guide" || admin.Items[1].Title != "Libraries" {
|
if len(admin.Items) < 1 || admin.Items[0].Title != "Admin Guide" {
|
||||||
t.Fatalf("Administration must lead with Admin Guide then Libraries, got %+v", admin.Items)
|
t.Fatalf("Administration must lead with the Admin Guide, got %+v", admin.Items)
|
||||||
}
|
}
|
||||||
pos := map[string]int{}
|
pos := map[string]int{}
|
||||||
for i, p := range usabilityOrder["Administration"] {
|
for i, p := range usabilityOrder["Administration"] {
|
||||||
pos[docURL(p)] = i
|
pos[docURL(p)] = i
|
||||||
}
|
}
|
||||||
last := -1
|
last := -1
|
||||||
for _, item := range admin.Items[2:] {
|
for _, item := range admin.Items[1:] {
|
||||||
r, ok := pos[item.URL]
|
r, ok := pos[item.URL]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("Administration item %q (%s) is not in usabilityOrder", item.Title, item.URL)
|
t.Errorf("Administration item %q (%s) is not in usabilityOrder", item.Title, item.URL)
|
||||||
|
|||||||
Reference in new issue
Block a user