From 62bdcb242828e343740526c16011da2bc8d389a6 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 17 Aug 2026 15:30:50 -0500 Subject: [PATCH] Consolidate Item Types, Users, and Settings into one Settings section The navbar previously listed Item Types, Users, and Settings as three separate top-level links. They now live under a single "Settings" entry in the navbar, backed by one unified section with a left sub-nav (General, Item Types, Users) shared across all three areas so it reads as one settings panel rather than three disconnected admin pages. - New templates/partials/settings_nav.html shared sub-nav, included by settings.html, itemtypes_list/form.html, and users_list/form.html. - Routes moved under /settings/: /item-types* -> /settings/item-types*, /users* -> /settings/users*. All redirects, form actions, and the item-type custom-field JS base action updated to match. --- internal/web/handlers_itemtypes.go | 14 +-- internal/web/handlers_users.go | 8 +- internal/web/router.go | 42 ++++----- web/templates/layout.html | 2 - web/templates/pages/itemtypes_form.html | 24 +++-- web/templates/pages/itemtypes_list.html | 52 ++++++----- web/templates/pages/settings.html | 10 ++- web/templates/pages/users_form.html | 110 ++++++++++++----------- web/templates/pages/users_list.html | 46 ++++++---- web/templates/partials/settings_nav.html | 13 +++ 10 files changed, 186 insertions(+), 135 deletions(-) create mode 100644 web/templates/partials/settings_nav.html diff --git a/internal/web/handlers_itemtypes.go b/internal/web/handlers_itemtypes.go index 95218c3..818c4c0 100644 --- a/internal/web/handlers_itemtypes.go +++ b/internal/web/handlers_itemtypes.go @@ -46,7 +46,7 @@ func (a *App) handleItemTypeCreate(w http.ResponseWriter, r *http.Request) { a.Render(w, http.StatusBadRequest, "itemtypes_form", itemTypeFormData{User: currentUser(r), ItemType: it, FieldTypes: models.AllFieldTypes, Error: err.Error()}) return } - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(it.ID, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(it.ID, 10)+"/edit", http.StatusSeeOther) } func (a *App) handleItemTypeEditForm(w http.ResponseWriter, r *http.Request) { @@ -87,7 +87,7 @@ func (a *App) handleItemTypeUpdate(w http.ResponseWriter, r *http.Request) { a.Render(w, http.StatusBadRequest, "itemtypes_form", itemTypeFormData{User: currentUser(r), ItemType: it, FieldTypes: models.AllFieldTypes, Error: err.Error()}) return } - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) } func (a *App) handleItemTypeDelete(w http.ResponseWriter, r *http.Request) { @@ -97,10 +97,10 @@ func (a *App) handleItemTypeDelete(w http.ResponseWriter, r *http.Request) { return } if err := a.ItemTypes.Delete(r.Context(), id); err != nil { - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(id, 10)+"/edit?error="+err.Error(), http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(id, 10)+"/edit?error="+err.Error(), http.StatusSeeOther) return } - http.Redirect(w, r, "/item-types", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types", http.StatusSeeOther) } func (a *App) handleFieldCreate(w http.ResponseWriter, r *http.Request) { @@ -124,7 +124,7 @@ func (a *App) handleFieldCreate(w http.ResponseWriter, r *http.Request) { if err := a.ItemTypes.CreateField(r.Context(), f); err != nil { a.Log.Warn("create field failed", "error", err) } - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) } func (a *App) handleFieldUpdate(w http.ResponseWriter, r *http.Request) { @@ -159,7 +159,7 @@ func (a *App) handleFieldUpdate(w http.ResponseWriter, r *http.Request) { if err := a.ItemTypes.UpdateField(r.Context(), f); err != nil { a.Log.Warn("update field failed", "error", err) } - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(itemTypeID, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(itemTypeID, 10)+"/edit", http.StatusSeeOther) } func (a *App) handleFieldDelete(w http.ResponseWriter, r *http.Request) { @@ -176,7 +176,7 @@ func (a *App) handleFieldDelete(w http.ResponseWriter, r *http.Request) { if err := a.ItemTypes.DeleteField(r.Context(), fieldID); err != nil { a.Log.Warn("delete field failed", "error", err) } - http.Redirect(w, r, "/item-types/"+strconv.FormatInt(itemTypeID, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/item-types/"+strconv.FormatInt(itemTypeID, 10)+"/edit", http.StatusSeeOther) } func atoiDefault(s string, def int) int { diff --git a/internal/web/handlers_users.go b/internal/web/handlers_users.go index 3017780..64b440b 100644 --- a/internal/web/handlers_users.go +++ b/internal/web/handlers_users.go @@ -50,7 +50,7 @@ func (a *App) handleUserCreate(w http.ResponseWriter, r *http.Request) { a.Render(w, http.StatusBadRequest, "users_form", userFormData{User: currentUser(r), EditUser: u, Error: err.Error()}) return } - http.Redirect(w, r, "/users", http.StatusSeeOther) + http.Redirect(w, r, "/settings/users", http.StatusSeeOther) } func (a *App) handleUserEditForm(w http.ResponseWriter, r *http.Request) { @@ -92,7 +92,7 @@ func (a *App) handleUserUpdate(w http.ResponseWriter, r *http.Request) { a.Render(w, http.StatusBadRequest, "users_form", userFormData{User: currentUser(r), EditUser: u, Error: err.Error()}) return } - http.Redirect(w, r, "/users", http.StatusSeeOther) + http.Redirect(w, r, "/settings/users", http.StatusSeeOther) } func (a *App) handleUserPassword(w http.ResponseWriter, r *http.Request) { @@ -111,7 +111,7 @@ func (a *App) handleUserPassword(w http.ResponseWriter, r *http.Request) { a.Log.Warn("set password failed", "error", err) } } - http.Redirect(w, r, "/users/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) + http.Redirect(w, r, "/settings/users/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther) } func (a *App) handleUserDelete(w http.ResponseWriter, r *http.Request) { @@ -123,5 +123,5 @@ func (a *App) handleUserDelete(w http.ResponseWriter, r *http.Request) { if err := a.Users.Delete(r.Context(), id); err != nil { a.Log.Warn("delete user failed", "error", err) } - http.Redirect(w, r, "/users", http.StatusSeeOther) + http.Redirect(w, r, "/settings/users", http.StatusSeeOther) } diff --git a/internal/web/router.go b/internal/web/router.go index 81c4bc4..700ac6c 100644 --- a/internal/web/router.go +++ b/internal/web/router.go @@ -59,27 +59,9 @@ func (a *App) Router() http.Handler { mux.HandleFunc("GET /export/assets.csv", a.requireLogin(a.handleExportCSV)) mux.HandleFunc("POST /import/assets.csv", a.requireAdmin(a.handleImportCSV)) - // Item types (admin, traditional pages) - mux.HandleFunc("GET /item-types", a.requireAdmin(a.handleItemTypesList)) - mux.HandleFunc("GET /item-types/new", a.requireAdmin(a.handleItemTypeNewForm)) - mux.HandleFunc("POST /item-types", a.requireAdmin(a.handleItemTypeCreate)) - mux.HandleFunc("GET /item-types/{id}/edit", a.requireAdmin(a.handleItemTypeEditForm)) - mux.HandleFunc("POST /item-types/{id}", a.requireAdmin(a.handleItemTypeUpdate)) - mux.HandleFunc("POST /item-types/{id}/delete", a.requireAdmin(a.handleItemTypeDelete)) - mux.HandleFunc("POST /item-types/{id}/fields", a.requireAdmin(a.handleFieldCreate)) - mux.HandleFunc("POST /item-types/{id}/fields/{fieldID}", a.requireAdmin(a.handleFieldUpdate)) - mux.HandleFunc("POST /item-types/{id}/fields/{fieldID}/delete", a.requireAdmin(a.handleFieldDelete)) - - // Users (admin, traditional pages) - mux.HandleFunc("GET /users", a.requireAdmin(a.handleUsersList)) - mux.HandleFunc("GET /users/new", a.requireAdmin(a.handleUserNewForm)) - mux.HandleFunc("POST /users", a.requireAdmin(a.handleUserCreate)) - mux.HandleFunc("GET /users/{id}/edit", a.requireAdmin(a.handleUserEditForm)) - mux.HandleFunc("POST /users/{id}", a.requireAdmin(a.handleUserUpdate)) - mux.HandleFunc("POST /users/{id}/password", a.requireAdmin(a.handleUserPassword)) - mux.HandleFunc("POST /users/{id}/delete", a.requireAdmin(a.handleUserDelete)) - - // Settings (admin, traditional page) + // Settings (admin, traditional pages) - Item Types, Users, and general + // settings all live under one unified /settings section with shared + // sub-navigation, rather than as separate top-level nav items. mux.HandleFunc("GET /settings", a.requireAdmin(a.handleSettingsPage)) mux.HandleFunc("POST /settings/general", a.requireAdmin(a.handleSettingsGeneral)) mux.HandleFunc("POST /settings/statuses", a.requireAdmin(a.handleStatusCreate)) @@ -87,6 +69,24 @@ func (a *App) Router() http.Handler { mux.HandleFunc("POST /settings/statuses/{id}/delete", a.requireAdmin(a.handleStatusDelete)) mux.HandleFunc("POST /settings/statuses/{id}/reorder", a.requireAdmin(a.handleStatusReorder)) + mux.HandleFunc("GET /settings/item-types", a.requireAdmin(a.handleItemTypesList)) + mux.HandleFunc("GET /settings/item-types/new", a.requireAdmin(a.handleItemTypeNewForm)) + mux.HandleFunc("POST /settings/item-types", a.requireAdmin(a.handleItemTypeCreate)) + mux.HandleFunc("GET /settings/item-types/{id}/edit", a.requireAdmin(a.handleItemTypeEditForm)) + mux.HandleFunc("POST /settings/item-types/{id}", a.requireAdmin(a.handleItemTypeUpdate)) + mux.HandleFunc("POST /settings/item-types/{id}/delete", a.requireAdmin(a.handleItemTypeDelete)) + mux.HandleFunc("POST /settings/item-types/{id}/fields", a.requireAdmin(a.handleFieldCreate)) + mux.HandleFunc("POST /settings/item-types/{id}/fields/{fieldID}", a.requireAdmin(a.handleFieldUpdate)) + mux.HandleFunc("POST /settings/item-types/{id}/fields/{fieldID}/delete", a.requireAdmin(a.handleFieldDelete)) + + mux.HandleFunc("GET /settings/users", a.requireAdmin(a.handleUsersList)) + mux.HandleFunc("GET /settings/users/new", a.requireAdmin(a.handleUserNewForm)) + mux.HandleFunc("POST /settings/users", a.requireAdmin(a.handleUserCreate)) + mux.HandleFunc("GET /settings/users/{id}/edit", a.requireAdmin(a.handleUserEditForm)) + mux.HandleFunc("POST /settings/users/{id}", a.requireAdmin(a.handleUserUpdate)) + mux.HandleFunc("POST /settings/users/{id}/password", a.requireAdmin(a.handleUserPassword)) + mux.HandleFunc("POST /settings/users/{id}/delete", a.requireAdmin(a.handleUserDelete)) + return a.loadSession(mux) } diff --git a/web/templates/layout.html b/web/templates/layout.html index b70d491..2978919 100644 --- a/web/templates/layout.html +++ b/web/templates/layout.html @@ -20,8 +20,6 @@ diff --git a/web/templates/pages/itemtypes_form.html b/web/templates/pages/itemtypes_form.html index c5a02c2..3011ae9 100644 --- a/web/templates/pages/itemtypes_form.html +++ b/web/templates/pages/itemtypes_form.html @@ -1,15 +1,21 @@ {{define "content"}} -
+
+

Settings

+
+
+ {{template "settings_nav" "itemtypes"}} +
+
-

{{if .ItemType}}Edit Item Type{{else}}New Item Type{{end}}

- Back to Item Types +
{{if .ItemType}}Edit Item Type{{else}}New Item Type{{end}}
+ Back to Item Types
{{if .Error}}
{{.Error}}
{{end}}
-
+
@@ -36,7 +42,7 @@
{{if .ItemType}} - + {{end}}
@@ -65,7 +71,7 @@ data-options="{{range $i, $o := .Options}}{{if $i}} {{end}}{{$o}}{{end}}" data-sort-order="{{.SortOrder}}"> Edit -
+
@@ -79,7 +85,7 @@
Add Field
-
+
@@ -112,9 +118,11 @@
- + {{else}}

Save the item type first to add custom fields.

{{end}} +
+
{{end}} diff --git a/web/templates/pages/itemtypes_list.html b/web/templates/pages/itemtypes_list.html index dd9f21e..5023d0e 100644 --- a/web/templates/pages/itemtypes_list.html +++ b/web/templates/pages/itemtypes_list.html @@ -1,27 +1,35 @@ {{define "content"}}
-
-

Item Types

- New Item Type -
-
- - - - {{range .ItemTypes}} - - - - - - - - - {{else}} - - {{end}} - -
IconNameDescriptionCustom FieldsStatus
{{.Name}}{{.Description}}{{len .Fields}}{{if .Enabled}}Enabled{{else}}Disabled{{end}}Edit
No item types defined yet.
+

Settings

+
+
+ {{template "settings_nav" "itemtypes"}} +
+
+
+
Item Types
+ New Item Type +
+
+ + + + {{range .ItemTypes}} + + + + + + + + + {{else}} + + {{end}} + +
IconNameDescriptionCustom FieldsStatus
{{.Name}}{{.Description}}{{len .Fields}}{{if .Enabled}}Enabled{{else}}Disabled{{end}}Edit
No item types defined yet.
+
+
{{end}} diff --git a/web/templates/pages/settings.html b/web/templates/pages/settings.html index c02b5eb..f3d8e77 100644 --- a/web/templates/pages/settings.html +++ b/web/templates/pages/settings.html @@ -1,6 +1,11 @@ {{define "content"}} -
+

Settings

+
+
+ {{template "settings_nav" "general"}} +
+
General
@@ -82,5 +87,8 @@
+ +
+
{{end}} diff --git a/web/templates/pages/users_form.html b/web/templates/pages/users_form.html index cd7916a..60df824 100644 --- a/web/templates/pages/users_form.html +++ b/web/templates/pages/users_form.html @@ -1,61 +1,69 @@ {{define "content"}} -
-
-

{{if .EditUser}}Edit User{{else}}New User{{end}}

- Back to Users -
+
+

Settings

+
+
+ {{template "settings_nav" "users"}} +
+
+
+
{{if .EditUser}}Edit User{{else}}New User{{end}}
+ Back to Users +
- {{if .Error}}
{{.Error}}
{{end}} + {{if .Error}}
{{.Error}}
{{end}} -
-
-
-
- - +
+
+ +
+ + +
+ {{if not .EditUser}} +
+ + +
+ {{end}} +
+ + +
+
+ + +
+ + + {{if .EditUser}} +
+ +
+ {{end}}
- {{if not .EditUser}} -
- - -
- {{end}} -
- - -
-
- - -
- - +
+ {{if .EditUser}} -
- -
+
+
Change Password
+
+
+
+ + +
+
+ +
+
+
+
{{end}}
- - {{if .EditUser}} -
-
Change Password
-
-
-
- - -
-
- -
-
-
-
- {{end}}
{{end}} diff --git a/web/templates/pages/users_list.html b/web/templates/pages/users_list.html index b40b2bd..5c38bcb 100644 --- a/web/templates/pages/users_list.html +++ b/web/templates/pages/users_list.html @@ -1,24 +1,32 @@ {{define "content"}}
-
-

Users

- New User -
-
- - - - {{range .Users}} - - - - - - - - {{end}} - -
UsernameRoleStatusCreated
{{.Username}}{{if .IsAdmin}}Admin{{else}}User{{end}}{{if .Enabled}}Enabled{{else}}Disabled{{end}}{{fmtDateShort .CreatedAt}}Edit
+

Settings

+
+
+ {{template "settings_nav" "users"}} +
+
+
+
Users
+ New User +
+
+ + + + {{range .Users}} + + + + + + + + {{end}} + +
UsernameRoleStatusCreated
{{.Username}}{{if .IsAdmin}}Admin{{else}}User{{end}}{{if .Enabled}}Enabled{{else}}Disabled{{end}}{{fmtDateShort .CreatedAt}}Edit
+
+
{{end}} diff --git a/web/templates/partials/settings_nav.html b/web/templates/partials/settings_nav.html new file mode 100644 index 0000000..d1dac96 --- /dev/null +++ b/web/templates/partials/settings_nav.html @@ -0,0 +1,13 @@ +{{define "settings_nav"}} + +{{end}}