From f3c9d4b4e8883730aedfca31bfe77b07a6773348 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Tue, 13 Oct 2026 12:29:43 -0400 Subject: [PATCH] fix(web): admin users list reworked to the library-management card pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The md:-flipped table/card split still broke at tablet widths and with the nav sidebar open — a viewport breakpoint cannot see the sidebar eating content width. Replaced the table entirely with the same fluid card list admin_library uses (flex + min-w-0/truncate identity block, fixed action cluster, flex-wrap): it reflows continuously at every width instead of stepping between two layouts. The duplicate-DOM-id suffixing from the previous two-rendering approach is gone with it. --- templates/admin_users.templ | 212 ++++-------- templates/admin_users_templ.go | 591 ++++++++++++--------------------- 2 files changed, 270 insertions(+), 533 deletions(-) diff --git a/templates/admin_users.templ b/templates/admin_users.templ index 33e48a74..4b2973c3 100644 --- a/templates/admin_users.templ +++ b/templates/admin_users.templ @@ -51,65 +51,6 @@ templ adminCreateUserForm() { } -// userRoleControl renders the role select (with last-admin protection). -// resultSuffix keeps DOM ids unique when the control is rendered twice -// (desktop table row + mobile card). -templ userRoleControl(user User, adminCount int, resultSuffix string) { - if user.Role == "admin" && adminCount == 1 { - -
- -
- } else { - -
- } -} - -// userDeleteButton renders the delete button (with last-admin protection). -// idSuffix keeps the swap-target ids unique when the control is rendered -// twice (desktop table row + mobile card). -templ userDeleteButton(user User, adminCount int, idSuffix string) { - if user.Role == "admin" && adminCount == 1 { - - } else { - - } -} - templ AdminUsers(currentUser User, users []User, adminCount int) { @@ -144,96 +85,53 @@ templ AdminUsers(currentUser User, users []User, adminCount int) { @adminCreateUserForm() - - - - -
+ +
for _, user := range users { -
-
- - @Icon("user", "h-4 w-4") - -
-
- { user.Username } - if user.ID == currentUser.ID { - You - } +
+
+
+ + @Icon("user", "h-5 w-5") + +
+
+

{ user.Username }

+ if user.ID == currentUser.ID { + You + } +
+

{ user.Email }

+

Created { FormatInTimezone(user.CreatedAt, currentUser.Timezone) }

-
{ user.Email }
-
-
-
-
Role
- @userRoleControl(user, adminCount, "mobile-") -
-
-
Created
-
{ FormatInTimezone(user.CreatedAt, currentUser.Timezone) }
-
-
+
+ if user.Role == "admin" && adminCount == 1 { + + + } else { + +
+ } + } else { + + }
diff --git a/templates/admin_users_templ.go b/templates/admin_users_templ.go index 523815d8..ebf74ca8 100644 --- a/templates/admin_users_templ.go +++ b/templates/admin_users_templ.go @@ -49,10 +49,7 @@ func adminCreateUserForm() templ.Component { }) } -// userRoleControl renders the role select (with last-admin protection). -// resultSuffix keeps DOM ids unique when the control is rendered twice -// (desktop table row + mobile card). -func userRoleControl(user User, adminCount int, resultSuffix string) templ.Component { +func AdminUsers(currentUser User, users []User, adminCount int) 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 { @@ -73,183 +70,7 @@ func userRoleControl(user User, adminCount int, resultSuffix string) templ.Compo templ_7745c5c3_Var2 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - if user.Role == "admin" && adminCount == 1 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - return nil - }) -} - -// userDeleteButton renders the delete button (with last-admin protection). -// idSuffix keeps the swap-target ids unique when the control is rendered -// twice (desktop table row + mobile card). -func userDeleteButton(user User, adminCount int, idSuffix string) 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_Var6 := templ.GetChildren(ctx) - if templ_7745c5c3_Var6 == nil { - templ_7745c5c3_Var6 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - if user.Role == "admin" && adminCount == 1 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - return nil - }) -} - -func AdminUsers(currentUser User, users []User, adminCount int) 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_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "Users - Bookhoard Admin") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "Users - Bookhoard Admin") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -257,7 +78,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -265,7 +86,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

User Management

Manage user accounts and permissions

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

User Management

Manage user accounts and permissions

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -273,103 +94,169 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - for _, user := range users { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = Icon("user", "h-4 w-4").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var16 string - templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 219, Col: 103} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if user.ID == currentUser.ID { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "You") + if user.Role == "admin" && adminCount == 1 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var17 string - templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 224, Col: 88} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "
Role
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = userRoleControl(user, adminCount, "mobile-").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "
Created
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var18 string - templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(FormatInTimezone(user.CreatedAt, currentUser.Timezone)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 234, Col: 123} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = userDeleteButton(user, adminCount, "mobile-").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "
") + 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, 47, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }