Show form errors

This commit is contained in:
ekzyis 2024-09-11 12:21:58 +02:00
parent 5788d5b509
commit e868f91f84
3 changed files with 58 additions and 27 deletions

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"regexp"
"git.ekzyis.com/ekzyis/delphi.market/server/router/context" "git.ekzyis.com/ekzyis/delphi.market/server/router/context"
"git.ekzyis.com/ekzyis/delphi.market/server/router/pages" "git.ekzyis.com/ekzyis/delphi.market/server/router/pages"
@ -13,7 +14,7 @@ import (
func HandleUser(sc context.Context) echo.HandlerFunc { func HandleUser(sc context.Context) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
u := c.Get("session").(types.User) u := c.Get("session").(types.User)
return pages.User(&u).Render(context.RenderContext(sc, c), c.Response().Writer) return pages.User(&u, nil).Render(context.RenderContext(sc, c), c.Response().Writer)
} }
} }
@ -26,15 +27,25 @@ func HandleUserEdit(sc context.Context) echo.HandlerFunc {
name = c.FormValue("name") name = c.FormValue("name")
maxLength = 16 maxLength = 16
errors types.UserEditError
err error err error
) )
if name == "" { if name == "" {
return echo.NewHTTPError(http.StatusBadRequest, "name is required") errors.Name = "required"
} }
if len(name) > maxLength { if len(name) > maxLength {
echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("name cannot be longer than %d characters", maxLength)) errors.Name = fmt.Sprintf("%d characters too long", len(name)-maxLength)
}
if !regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString(name) {
errors.Name = "only letters, numbers, _ and - allowed"
}
if errors.Name != "" {
c.Response().WriteHeader(http.StatusBadRequest)
return pages.User(&u, &errors).Render(context.RenderContext(sc, c), c.Response().Writer)
} }
if err = db.QueryRowContext(ctx, if err = db.QueryRowContext(ctx,
@ -43,6 +54,6 @@ func HandleUserEdit(sc context.Context) echo.HandlerFunc {
return err return err
} }
return pages.User(&u).Render(context.RenderContext(sc, c), c.Response().Writer) return pages.User(&u, &errors).Render(context.RenderContext(sc, c), c.Response().Writer)
} }
} }

View File

@ -1,13 +1,14 @@
package pages package pages
import ( import (
"fmt"
"git.ekzyis.com/ekzyis/delphi.market/server/router/pages/components" "git.ekzyis.com/ekzyis/delphi.market/server/router/pages/components"
"git.ekzyis.com/ekzyis/delphi.market/types" "git.ekzyis.com/ekzyis/delphi.market/types"
"strconv" "strconv"
"time" "time"
) )
templ User(user *types.User) { templ User(user *types.User, errors *types.UserEditError) {
<html> <html>
@components.Head() @components.Head()
<body class="container"> <body class="container">
@ -25,27 +26,33 @@ templ User(user *types.User) {
<div class="font-bold">id</div> <div class="font-bold">id</div>
<div>{ strconv.Itoa(user.Id) }</div> <div>{ strconv.Itoa(user.Id) }</div>
<div class="font-bold">name</div> <div class="font-bold">name</div>
<div class="flex"> <div>
<span id="name">{ user.Name }</span> <div class="flex">
<form <span id="name" class={ maybeShow("", errors == nil || errors.Name == "") }>{ user.Name }</span>
class="hidden flex mb-0" <form
hx-put="/user" class="hidden flex mb-0"
hx-push-url="false" hx-put="/user"
hx-target="#name" hx-push-url="false"
hx-select="#name" hx-target="#name"
> hx-select="#name"
<input hx-select-oob="#error"
name="name" >
type="text" <input
value={ user.Name } name="name"
required type="text"
maxlength="16" value={ user.Name }
class="font-mono w-[160px]" class="font-mono w-[160px]"
/> />
<button class="ms-1 px-1" type="submit">save</button> <button class="ms-1 px-1" type="submit">save</button>
</form> </form>
<button id="cancel" class="hidden flex text-muted hover:text-reset text-xs items-center ms-1">cancel</button> <button id="cancel" class="hidden flex text-muted hover:text-reset text-xs items-center ms-1">cancel</button>
<button id="edit" class="flex text-muted hover:text-reset text-xs items-center ms-1" hx-preserve>edit</button> <button id="edit" class="flex text-muted hover:text-reset text-xs items-center ms-1">edit</button>
</div>
if errors != nil && errors.Name != "" {
<div id="error" class="text-xs text-error">{ errors.Name }</div>
} else {
<div id="error" class="hidden"></div>
}
</div> </div>
<div class="font-bold">joined</div> <div class="font-bold">joined</div>
<div>{ user.CreatedAt.Format(time.DateOnly) }</div> <div>{ user.CreatedAt.Format(time.DateOnly) }</div>
@ -69,10 +76,19 @@ templ User(user *types.User) {
htmx.on("#edit", "click", toggleForm) htmx.on("#edit", "click", toggleForm)
htmx.on("#cancel", "click", toggleForm) htmx.on("#cancel", "click", toggleForm)
htmx.on("form", "htmx:afterRequest", toggleForm) htmx.on("form", "htmx:afterRequest", ({ detail }) => {
if (detail.successful) toggleForm()
})
</script> </script>
</div> </div>
@components.Footer() @components.Footer()
</body> </body>
</html> </html>
} }
func maybeShow(class string, show bool) string {
if show {
return class
}
return fmt.Sprintf("%s %s", class, "hidden")
}

View File

@ -15,6 +15,10 @@ type User struct {
Msats int64 Msats int64
} }
type UserEditError struct {
Name string
}
type Invoice struct { type Invoice struct {
Id int Id int
UserId int UserId int