feat(users): edit form with role/group edit, delete on edit page

This commit is contained in:
Claus Lohmar 2026-06-15 06:04:58 +00:00
parent f193765dad
commit 19f1cf2c2b
5 changed files with 381 additions and 76 deletions

View file

@ -100,6 +100,8 @@ templ adminStyles() {
.btn-sm:hover{background:var(--surface2)}
.btn-sm.danger{color:var(--red);border-color:var(--red)}
.btn-sm.danger:hover{background:rgba(248,81,73,0.1)}
.btn.danger{background:var(--red)}
.btn.danger:hover{opacity:0.9}
.table-toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
.table-title{font-weight:600;font-size:0.9rem}
.user-table-wrap{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:16px}

File diff suppressed because one or more lines are too long

View file

@ -48,10 +48,7 @@ templ UserList(users []UserRow) {
} else {
<span class="status-dot on"></span>
}
<button class="btn-sm danger"
hx-delete={ "/admin/api/users/" + u.Username }
hx-confirm={ "Delete " + u.Username + "?" }
hx-target="closest tr">Delete</button>
<button class="btn-sm" hx-get={ "/admin/users/edit-form/" + u.Username } hx-target="#user-detail" hx-swap="innerHTML">Edit</button>
</div>
</div>
}
@ -124,6 +121,35 @@ func initials(s string) string {
return string(s[0])
}
func hasGroup(userGroups, group string) bool {
for _, g := range splitGroups(userGroups) {
if g == group {
return true
}
}
return false
}
func splitGroups(s string) []string {
if s == "" { return nil }
var result []string
start := 0
for i := 0; i < len(s); i++ {
if s[i] == ',' {
result = append(result, trimSpace(s[start:i]))
start = i + 1
}
}
result = append(result, trimSpace(s[start:]))
return result
}
func trimSpace(s string) string {
for len(s) > 0 && s[0] == ' ' { s = s[1:] }
for len(s) > 0 && s[len(s)-1] == ' ' { s = s[:len(s)-1] }
return s
}
type UserRow struct {
Username string
DisplayName string
@ -138,3 +164,54 @@ type CreateUserResultRow struct {
GeneratedPassword string
Error string
}
templ EditUserForm(u UserRow, groups []string) {
<div class="detail-card">
<div class="section-header">
<h3>Edit User</h3>
<button class="btn-sm" hx-get="/admin/users/cancel-form" hx-target="#user-detail" hx-swap="innerHTML">Cancel</button>
</div>
<form hx-put={ "/admin/users/" + u.Username } hx-target="#user-list-body" hx-swap="innerHTML"
hx-on::after-request="htmx.trigger('#user-detail', 'click')">
<div class="form-group">
<label>Username</label>
<input type="text" class="input" value={ u.Username } readonly style="opacity:0.6"/>
</div>
<div class="form-group">
<label>Display Name</label>
<input type="text" name="display_name" class="input" value={ u.DisplayName }/>
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="input" value={ u.Email }/>
</div>
<div class="form-group">
<label>Role</label>
<select name="role" class="input">
<option value="user" if u.Role == "user" { selected }>User</option>
<option value="admin" if u.Role == "admin" { selected }>Admin</option>
</select>
</div>
if len(groups) > 0 {
<div class="form-group">
<label>Groups</label>
<div class="checkbox-list">
for _, g := range groups {
<label class="checkbox-item">
<input type="checkbox" name="group" value={ g } if hasGroup(u.Groups, g) { checked }/>
<span>{ g }</span>
</label>
}
</div>
</div>
}
<div style="display:flex;gap:8px;margin-top:12px">
<button type="submit" class="btn" style="flex:1">Save</button>
<button type="button" class="btn danger"
hx-delete={ "/admin/api/users/" + u.Username }
hx-target="#user-list-body" hx-swap="innerHTML"
hx-on::after-request="htmx.trigger('#user-detail', 'click')">Delete</button>
</div>
</form>
</div>
}

View file

@ -150,33 +150,20 @@ func UserList(users []UserRow) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<button class=\"btn-sm danger\" hx-delete=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<button class=\"btn-sm\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/api/users/" + u.Username)
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/users/edit-form/" + u.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 52, Col: 50}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 51, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" hx-confirm=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue("Delete " + u.Username + "?")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 53, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" hx-target=\"closest tr\">Delete</button></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\">Edit</button></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -202,58 +189,58 @@ func CreateUserForm(groups []string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
if templ_7745c5c3_Var10 == nil {
templ_7745c5c3_Var10 = templ.NopComponent
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, 13, "<div class=\"detail-card\"><div class=\"section-header\"><h3>New User</h3><button class=\"btn-sm\" hx-get=\"/admin/users/cancel-form\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\">Cancel</button></div><form hx-post=\"/admin/users/create\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\"><div class=\"form-group\"><label>Username *</label> <input type=\"text\" name=\"username\" class=\"input\" required placeholder=\"jdoe\"></div><div class=\"form-group\"><label>Display Name</label> <input type=\"text\" name=\"display_name\" class=\"input\" placeholder=\"John Doe\"></div><div class=\"form-group\"><label>Email</label> <input type=\"email\" name=\"email\" class=\"input\" placeholder=\"john@example.com\"></div><div class=\"form-group\"><label>Role</label> <select name=\"role\" class=\"input\"><option value=\"user\">User</option> <option value=\"admin\">Admin</option></select></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"detail-card\"><div class=\"section-header\"><h3>New User</h3><button class=\"btn-sm\" hx-get=\"/admin/users/cancel-form\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\">Cancel</button></div><form hx-post=\"/admin/users/create\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\"><div class=\"form-group\"><label>Username *</label> <input type=\"text\" name=\"username\" class=\"input\" required placeholder=\"jdoe\"></div><div class=\"form-group\"><label>Display Name</label> <input type=\"text\" name=\"display_name\" class=\"input\" placeholder=\"John Doe\"></div><div class=\"form-group\"><label>Email</label> <input type=\"email\" name=\"email\" class=\"input\" placeholder=\"john@example.com\"></div><div class=\"form-group\"><label>Role</label> <select name=\"role\" class=\"input\"><option value=\"user\">User</option> <option value=\"admin\">Admin</option></select></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(groups) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"form-group\"><label>Groups</label><div class=\"checkbox-list\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div class=\"form-group\"><label>Groups</label><div class=\"checkbox-list\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, g := range groups {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<label class=\"checkbox-item\"><input type=\"checkbox\" name=\"group\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<label class=\"checkbox-item\"><input type=\"checkbox\" name=\"group\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(g)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 90, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(g)
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(g)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 93, Col: 53}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 91, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(g)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 94, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</span></label>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></label>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<button type=\"submit\" class=\"btn\" style=\"width:100%;margin-top:8px\">Create User</button></form></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<button type=\"submit\" class=\"btn\" style=\"width:100%;margin-top:8px\">Create User</button></form></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -277,64 +264,64 @@ func CreateUserSuccess(results []CreateUserResultRow) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var13 := templ.GetChildren(ctx)
if templ_7745c5c3_Var13 == nil {
templ_7745c5c3_Var13 = templ.NopComponent
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
if templ_7745c5c3_Var12 == nil {
templ_7745c5c3_Var12 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
for _, r := range results {
if r.Error != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div class=\"alert error\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<div class=\"alert error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(r.Error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 105, Col: 37}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"alert success\"><strong>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(r.Error)
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(r.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 108, Col: 37}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 108, Col: 24}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div class=\"alert success\"><strong>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</strong> created<div class=\"password-box\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(r.Username)
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(r.GeneratedPassword)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 111, Col: 24}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 109, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</strong> created<div class=\"password-box\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(r.GeneratedPassword)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 112, Col: 51}
}
_, 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, 24, "</div><div class=\"password-hint\">Save this password — it won't be shown again</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div><div class=\"password-hint\">Save this password — it won't be shown again</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<script type=\"text/javascript\">\n\t\tsetTimeout(function() { htmx.trigger('#user-list-body', 'click'); }, 500);\n\t</script>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<script type=\"text/javascript\">\n\t\tsetTimeout(function() { htmx.trigger('#user-list-body', 'click'); }, 500);\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -349,6 +336,41 @@ func initials(s string) string {
return string(s[0])
}
func hasGroup(userGroups, group string) bool {
for _, g := range splitGroups(userGroups) {
if g == group {
return true
}
}
return false
}
func splitGroups(s string) []string {
if s == "" {
return nil
}
var result []string
start := 0
for i := 0; i < len(s); i++ {
if s[i] == ',' {
result = append(result, trimSpace(s[start:i]))
start = i + 1
}
}
result = append(result, trimSpace(s[start:]))
return result
}
func trimSpace(s string) string {
for len(s) > 0 && s[0] == ' ' {
s = s[1:]
}
for len(s) > 0 && s[len(s)-1] == ' ' {
s = s[:len(s)-1]
}
return s
}
type UserRow struct {
Username string
DisplayName string
@ -364,4 +386,174 @@ type CreateUserResultRow struct {
Error string
}
func EditUserForm(u UserRow, groups []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_Var16 := templ.GetChildren(ctx)
if templ_7745c5c3_Var16 == nil {
templ_7745c5c3_Var16 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"detail-card\"><div class=\"section-header\"><h3>Edit User</h3><button class=\"btn-sm\" hx-get=\"/admin/users/cancel-form\" hx-target=\"#user-detail\" hx-swap=\"innerHTML\">Cancel</button></div><form hx-put=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/users/" + u.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 174, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" hx-target=\"#user-list-body\" hx-swap=\"innerHTML\" hx-on::after-request=\"htmx.trigger('#user-detail', 'click')\"><div class=\"form-group\"><label>Username</label> <input type=\"text\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(u.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 178, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" readonly style=\"opacity:0.6\"></div><div class=\"form-group\"><label>Display Name</label> <input type=\"text\" name=\"display_name\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(u.DisplayName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 182, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"></div><div class=\"form-group\"><label>Email</label> <input type=\"email\" name=\"email\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(u.Email)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 186, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\"></div><div class=\"form-group\"><label>Role</label> <select name=\"role\" class=\"input\"><option value=\"user\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if u.Role == "user" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, ">User</option> <option value=\"admin\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if u.Role == "admin" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, ">Admin</option></select></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(groups) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"form-group\"><label>Groups</label><div class=\"checkbox-list\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, g := range groups {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<label class=\"checkbox-item\"><input type=\"checkbox\" name=\"group\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(g)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 201, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if hasGroup(u.Groups, g) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " checked")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(g)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 202, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</span></label>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<div style=\"display:flex;gap:8px;margin-top:12px\"><button type=\"submit\" class=\"btn\" style=\"flex:1\">Save</button> <button type=\"button\" class=\"btn danger\" hx-delete=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/api/users/" + u.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/user-dashboard.templ`, Line: 211, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" hx-target=\"#user-list-body\" hx-swap=\"innerHTML\" hx-on::after-request=\"htmx.trigger('#user-detail', 'click')\">Delete</button></div></form></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View file

@ -19,6 +19,8 @@ func (h *Handler) RegisterUIRoutes(mux *http.ServeMux, authMiddleware func(http.
mux.Handle("GET /admin/users", authMiddleware(http.HandlerFunc(h.adminUsers)))
mux.Handle("GET /admin/users/create-form", authMiddleware(http.HandlerFunc(h.createUserForm)))
mux.Handle("GET /admin/users/cancel-form", authMiddleware(http.HandlerFunc(h.cancelForm)))
mux.Handle("GET /admin/users/edit-form/{username}", authMiddleware(http.HandlerFunc(h.editUserForm)))
mux.Handle("PUT /admin/users/{username}", authMiddleware(http.HandlerFunc(h.updateUser)))
// Groups
mux.Handle("GET /admin/groups", authMiddleware(http.HandlerFunc(h.groupsPage)))
mux.Handle("GET /admin/groups/list", authMiddleware(http.HandlerFunc(h.groupList)))
@ -396,3 +398,35 @@ func (h *Handler) updateGroup(w http.ResponseWriter, r *http.Request) {
}
h.groupList(w, r)
}
func (h *Handler) editUserForm(w http.ResponseWriter, r *http.Request) {
username := r.PathValue("username")
u, _ := h.store.GetByUsername(username)
if u == nil {
http.Error(w, "user not found", http.StatusNotFound)
return
}
grps, _ := h.groupStore.List()
groupNames := make([]string, 0, len(grps))
for _, g := range grps {
groupNames = append(groupNames, g.Name)
}
row := UserToRow(*u)
component := templates.EditUserForm(row, groupNames)
component.Render(r.Context(), w)
}
func (h *Handler) updateUser(w http.ResponseWriter, r *http.Request) {
username := r.PathValue("username")
r.ParseForm()
role := r.FormValue("role")
groupVals := r.Form["group"]
groups := ""
for i, g := range groupVals {
if i > 0 { groups += "," }
groups += g
}
h.store.GetDB().Exec(`UPDATE users SET role = ?, groups = ?, updated_at = CURRENT_TIMESTAMP WHERE username = ?`, role, groups, username)
h.syncWriter.Sync()
h.userRowsHandler(w, r)
}