From 4f2e1e36bf67202a15a8e278234f9798be2af1ea Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 10 May 2026 11:52:53 -0400 Subject: [PATCH] feat(templates): add metadata editor modal with cover management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New MetadataEditorModal component with: - Cover section (w-64 h-96, matching book detail page layout): click-to-upload, Generate Cover button, Remove Cover button - Accordion sections: Basic Info, Publication, Series, Identifiers, Comic/Manga, Technical — covering all 34 editable metadata fields - Modal capped at 90vh with scrollable content area - Cover upload via hidden file input with hover overlay - Select dropdowns for MangaType and ReadingDirection - Read-only display for Format and File Size --- templates/book_detail_modals.templ | 401 ++++++++++++++++- templates/book_detail_modals_templ.go | 614 +++++++++++++++++++++++++- 2 files changed, 989 insertions(+), 26 deletions(-) diff --git a/templates/book_detail_modals.templ b/templates/book_detail_modals.templ index 3d58f49..fa645bf 100644 --- a/templates/book_detail_modals.templ +++ b/templates/book_detail_modals.templ @@ -5,7 +5,6 @@ import ( "fmt" ) -// ProgressSyncModal shows progress from all devices for manual review templ ProgressSyncModal(user User, book handlers.MediaDetail) {
if book.ActiveConflict != nil { -
-
for source, data := range book.ActiveConflict.ConflictData {
- if data.Data["current_page"] != nil {
Page: { fmt.Sprintf("%.0f", data.Data["current_page"].(float64)) } / { fmt.Sprintf("%.0f", data.Data["total_pages"].(float64)) } @@ -89,7 +85,6 @@ templ ProgressSyncModal(user User, book handlers.MediaDetail) { }
} else if book.ReadingProgress != nil { -

No conflicts detected. Current progress from { book.ReadingProgress.LastSyncSource.String }:

@@ -128,7 +123,6 @@ templ ProgressSyncModal(user User, book handlers.MediaDetail) { } -// NotesHighlightsModal - Placeholder for future feature templ NotesHighlightsModal(book handlers.MediaDetail) {
} + +templ MetadataEditorModal(book handlers.MediaDetail) { + +} diff --git a/templates/book_detail_modals_templ.go b/templates/book_detail_modals_templ.go index 1f1946c..471b134 100644 --- a/templates/book_detail_modals_templ.go +++ b/templates/book_detail_modals_templ.go @@ -13,7 +13,6 @@ import ( "fmt" ) -// ProgressSyncModal shows progress from all devices for manual review func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context @@ -42,7 +41,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 22, Col: 74} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 21, Col: 74} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -53,7 +52,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { return templ_7745c5c3_Err } if book.ActiveConflict != nil { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

⚠️ Conflict Detected

Progress differs between devices. Review the options below and manually resolve via the Conflicts page.

Go to Conflicts Page →
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

⚠️ Conflict Detected

Progress differs between devices. Review the options below and manually resolve via the Conflicts page.

Go to Conflicts Page →
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -65,7 +64,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(source) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 65, Col: 18} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 62, Col: 18} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -78,7 +77,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(FormatInTimezone(data.Timestamp, user.Timezone)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 68, Col: 59} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 65, Col: 59} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -91,13 +90,13 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", data.Data["percentage"].(float64))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 73, Col: 66} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 70, Col: 66} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "%
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "%") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -109,7 +108,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f", data.Data["current_page"].(float64))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 80, Col: 73} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 76, Col: 73} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -122,7 +121,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f", data.Data["total_pages"].(float64))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 80, Col: 135} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 76, Col: 135} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -141,7 +140,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.Data["epubcfi"].(string)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 85, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 81, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -162,14 +161,14 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { return templ_7745c5c3_Err } } else if book.ReadingProgress != nil { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

No conflicts detected. Current progress from ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

No conflicts detected. Current progress from ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastSyncSource.String) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 94, Col: 105} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 89, Col: 105} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -182,7 +181,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var10 string templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", book.ReadingProgress.Percentage.Float64*100)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 101, Col: 74} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 96, Col: 74} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) if templ_7745c5c3_Err != nil { @@ -195,7 +194,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastSyncSource.String) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 104, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 99, Col: 50} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { @@ -213,7 +212,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.CurrentPage.Int32) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 108, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 103, Col: 52} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -226,7 +225,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.TotalPages.Int32) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 108, Col: 97} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 103, Col: 97} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { @@ -245,7 +244,7 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(FormatTimestamptzInTimezone(book.ReadingProgress.LastReadAt, user.Timezone)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 113, Col: 95} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 108, Col: 95} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -269,7 +268,6 @@ func ProgressSyncModal(user User, book handlers.MediaDetail) templ.Component { }) } -// NotesHighlightsModal - Placeholder for future feature func NotesHighlightsModal(book handlers.MediaDetail) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context @@ -298,7 +296,7 @@ func NotesHighlightsModal(book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(book.NotesCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 145, Col: 43} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 139, Col: 43} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -311,7 +309,7 @@ func NotesHighlightsModal(book handlers.MediaDetail) templ.Component { var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(book.HighlightsCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 145, Col: 95} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `book_detail_modals.templ`, Line: 139, Col: 95} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { @@ -325,4 +323,580 @@ func NotesHighlightsModal(book handlers.MediaDetail) templ.Component { }) } +func MetadataEditorModal(book handlers.MediaDetail) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var18 := templ.GetChildren(ctx) + if templ_7745c5c3_Var18 == nil { + templ_7745c5c3_Var18 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "

Edit Metadata

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if book.CoverImagePath.Valid && book.CoverImagePath.String != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"")") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"")") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
Click to upload
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + var _ = templruntime.GeneratedTemplate