From 7a9a66fcc5b02f9e702c906024857ba7aae6c109 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 12 Sep 2026 14:22:38 -0400 Subject: [PATCH] feat(ui): show file path and Reset to Scanned in the metadata editor Complete the metadata editor modal to match the backend override work: - File Path: read-only, monospace field showing the resolved on-disk location (MediaDetail.FileLocation), next to Format and File Size, so admins can see exactly which file backs the record without leaving the editor. Renders empty for non-admins, who never receive the path. - Reset to Scanned: new button beside Rescan. It confirms, then calls POST /api/media-items/:id/rescan?reset_overrides=true to drop all per-field user overrides and re-extract scanned defaults. Rescan alone keeps customizations; Reset discards them. --- templates/book_detail_modals.templ | 41 ++++++++++++++++++++------- templates/book_detail_modals_templ.go | 27 ++++++++++++++++-- web/src/book-detail.ts | 38 +++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/templates/book_detail_modals.templ b/templates/book_detail_modals.templ index 1e4242e..7ccb6fe 100644 --- a/templates/book_detail_modals.templ +++ b/templates/book_detail_modals.templ @@ -586,22 +586,41 @@ templ MetadataEditorModal(book handlers.MediaDetail) { class="input opacity-60" /> +
+ + +
- +
+ + +
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "Save") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/web/src/book-detail.ts b/web/src/book-detail.ts index aae5cb1..f845a73 100644 --- a/web/src/book-detail.ts +++ b/web/src/book-detail.ts @@ -96,6 +96,7 @@ interface MetadataEditorState { coverAction: string; saving: boolean; rescanning: boolean; + resetting: boolean; userRating: number; ratingHover: number; ratingSaving: boolean; @@ -109,6 +110,7 @@ interface MetadataEditorState { removeCover(): void; saveMetadata(): Promise; rescanBook(): Promise; + resetMetadata(): Promise; starFill(i: number): string; ratingText(): string; setRating(value: number): Promise; @@ -142,6 +144,7 @@ Alpine.data("bookDetail", () => { coverAction: "keep", saving: false, rescanning: false, + resetting: false, userRating: 0, ratingHover: 0, ratingSaving: false, @@ -550,6 +553,41 @@ Alpine.data("bookDetail", () => { } }, + async resetMetadata() { + if (this.resetting) return; + if ( + !window.confirm( + "Reset all metadata to scanned defaults? Manual edits and custom covers will be discarded.", + ) + ) { + return; + } + this.resetting = true; + const mediaId = getMediaId(); + try { + const resp = await fetch( + `/api/media-items/${mediaId}/rescan?reset_overrides=true`, + { + method: "POST", + headers: { Authorization: getAuthHeader() }, + }, + ); + if (!resp.ok) { + const err = await resp.json().catch(() => ({})); + throw new Error(err.error || "Failed to reset metadata"); + } + showToast("Metadata reset to scanned defaults", "success"); + setTimeout(() => window.location.reload(), 500); + } catch (e) { + showToast( + e instanceof Error ? e.message : "Failed to reset metadata", + "error", + ); + } finally { + this.resetting = false; + } + }, + async searchEditorTags() { const libraryId = document.body.getAttribute("data-library-id") || ""; if (!this.tagSearch || this.tagSearch.length < 2 || !libraryId) {