diff --git a/docs/admin/dashboard.md b/docs/admin/dashboard.md new file mode 100644 index 00000000..690ca052 --- /dev/null +++ b/docs/admin/dashboard.md @@ -0,0 +1,72 @@ + + +# 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. diff --git a/internal/docs/navigation_test.go b/internal/docs/navigation_test.go index 69a781a9..c9dae5cb 100644 --- a/internal/docs/navigation_test.go +++ b/internal/docs/navigation_test.go @@ -243,18 +243,18 @@ func TestBuildNavigationUsabilityOrder(t *testing.T) { } // 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. admin := find("Administration") - if len(admin.Items) < 2 || admin.Items[0].Title != "Admin Guide" || admin.Items[1].Title != "Libraries" { - t.Fatalf("Administration must lead with Admin Guide then Libraries, got %+v", admin.Items) + if len(admin.Items) < 1 || admin.Items[0].Title != "Admin Guide" { + t.Fatalf("Administration must lead with the Admin Guide, got %+v", admin.Items) } pos := map[string]int{} for i, p := range usabilityOrder["Administration"] { pos[docURL(p)] = i } last := -1 - for _, item := range admin.Items[2:] { + for _, item := range admin.Items[1:] { r, ok := pos[item.URL] if !ok { t.Errorf("Administration item %q (%s) is not in usabilityOrder", item.Title, item.URL)