From d53114cc20e795755390f2414a2459780eb0974c Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Fri, 14 Feb 2025 23:15:51 +1100 Subject: [PATCH 01/31] Added account page with subpage select --- handlers/account.go | 25 +++++++++++ handlers/profile.go | 2 +- server/routes.go | 12 +++++- view/component/account/content.templ | 15 +++++++ view/component/account/selectmenu.templ | 55 +++++++++++++++++++++++++ view/page/account.templ | 10 +++++ 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 handlers/account.go create mode 100644 view/component/account/content.templ create mode 100644 view/component/account/selectmenu.templ create mode 100644 view/page/account.templ diff --git a/handlers/account.go b/handlers/account.go new file mode 100644 index 0000000..36c180f --- /dev/null +++ b/handlers/account.go @@ -0,0 +1,25 @@ +package handlers + +import ( + "net/http" + "projectreshoot/view/component/account" + "projectreshoot/view/page" +) + +func HandleAccountPage() http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + page.Account("General").Render(r.Context(), w) + }, + ) +} + +func HandleAccountSubpage() http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + subpage := r.FormValue("subpage") + account.AccountContent(subpage).Render(r.Context(), w) + }, + ) +} diff --git a/handlers/profile.go b/handlers/profile.go index 91d21b2..91f381f 100644 --- a/handlers/profile.go +++ b/handlers/profile.go @@ -5,7 +5,7 @@ import ( "projectreshoot/view/page" ) -func HandleProfile() http.Handler { +func HandleProfilePage() http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { page.Profile().Render(r.Context(), w) diff --git a/server/routes.go b/server/routes.go index 19c87f7..fc54841 100644 --- a/server/routes.go +++ b/server/routes.go @@ -63,6 +63,16 @@ func addRoutes( // Profile page mux.Handle("GET /profile", middleware.RequiresLogin( - handlers.HandleProfile(), + handlers.HandleProfilePage(), + )) + + // Account page + mux.Handle("GET /account", + middleware.RequiresLogin( + handlers.HandleAccountPage(), + )) + mux.Handle("POST /account-select-page", + middleware.RequiresLogin( + handlers.HandleAccountSubpage(), )) } diff --git a/view/component/account/content.templ b/view/component/account/content.templ new file mode 100644 index 0000000..2e84536 --- /dev/null +++ b/view/component/account/content.templ @@ -0,0 +1,15 @@ +package account + +templ AccountContent(subpage string) { +
+
+ @SelectMenu(subpage) +
+
+ { subpage } +
+ // page content +
+
+
+} diff --git a/view/component/account/selectmenu.templ b/view/component/account/selectmenu.templ new file mode 100644 index 0000000..c8c1ea9 --- /dev/null +++ b/view/component/account/selectmenu.templ @@ -0,0 +1,55 @@ +package account + +import "fmt" + +type MenuItem struct { + name string + href string +} + +func getMenuItems() []MenuItem { + return []MenuItem{ + { + name: "General", + href: "general", + }, + { + name: "Security", + href: "security", + }, + { + name: "Preferences", + href: "preferences", + }, + } +} + +templ SelectMenu(activePage string) { + {{ + menuItems := getMenuItems() + page := fmt.Sprintf("{page:'%s'}", activePage) + }} +
+
+
    + for _, item := range menuItems { + {{ + activebind := fmt.Sprintf("page === '%s' && 'bg-mantle'", item.name) + }} +
  • + +
  • + } +
+
+
+} diff --git a/view/page/account.templ b/view/page/account.templ new file mode 100644 index 0000000..896f657 --- /dev/null +++ b/view/page/account.templ @@ -0,0 +1,10 @@ +package page + +import "projectreshoot/view/layout" +import "projectreshoot/view/component/account" + +templ Account(subpage string) { + @layout.Global() { + @account.AccountContent(subpage) + } +} From eaf40b3d915474a88ab611a5192356b6e14b0fa3 Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Fri, 14 Feb 2025 23:19:26 +1100 Subject: [PATCH 02/31] Fixed cursor on profile button hover --- view/component/nav/navbarright.templ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/component/nav/navbarright.templ b/view/component/nav/navbarright.templ index 7ed2c11..4d3a114 100644 --- a/view/component/nav/navbarright.templ +++ b/view/component/nav/navbarright.templ @@ -35,7 +35,7 @@ templ navRight() { > + + + +

+ +} diff --git a/view/component/account/container.templ b/view/component/account/container.templ new file mode 100644 index 0000000..37b4a0a --- /dev/null +++ b/view/component/account/container.templ @@ -0,0 +1,24 @@ +package account + +templ AccountContainer(subpage string) { +
+ @SelectMenu(subpage) +
+
+ { subpage } +
+ switch subpage { + case "General": + @AccountGeneral() + case "Security": + @AccountGeneral() + } +
+
+} diff --git a/view/component/account/content.templ b/view/component/account/content.templ deleted file mode 100644 index 2e84536..0000000 --- a/view/component/account/content.templ +++ /dev/null @@ -1,15 +0,0 @@ -package account - -templ AccountContent(subpage string) { -
-
- @SelectMenu(subpage) -
-
- { subpage } -
- // page content -
-
-
-} diff --git a/view/component/account/general.templ b/view/component/account/general.templ new file mode 100644 index 0000000..c79fe56 --- /dev/null +++ b/view/component/account/general.templ @@ -0,0 +1,7 @@ +package account + +templ AccountGeneral() { +
+ @ChangeUsername("", "") +
+} diff --git a/view/component/account/selectmenu.templ b/view/component/account/selectmenu.templ index c8c1ea9..c202e39 100644 --- a/view/component/account/selectmenu.templ +++ b/view/component/account/selectmenu.templ @@ -29,27 +29,35 @@ templ SelectMenu(activePage string) { menuItems := getMenuItems() page := fmt.Sprintf("{page:'%s'}", activePage) }} -
-
-
    - for _, item := range menuItems { - {{ +
    +
    +
    +
      + for _, item := range menuItems { + {{ activebind := fmt.Sprintf("page === '%s' && 'bg-mantle'", item.name) - }} -
    • - -
    • - } -
    + :class={ activebind } + > + { item.name } + + + } +
+
- + } diff --git a/view/page/account.templ b/view/page/account.templ index 896f657..c11b900 100644 --- a/view/page/account.templ +++ b/view/page/account.templ @@ -5,6 +5,6 @@ import "projectreshoot/view/component/account" templ Account(subpage string) { @layout.Global() { - @account.AccountContent(subpage) + @account.AccountContainer(subpage) } } From 6bee6edd16bf8459e298692c067f6f6dfa21b02a Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Sat, 15 Feb 2025 14:32:15 +1100 Subject: [PATCH 05/31] Modified account page to be mobile friendly --- view/component/account/changeusername.templ | 18 +++++------ view/component/account/container.templ | 4 ++- view/component/account/selectmenu.templ | 36 ++++++++++++++++++--- view/component/nav/sidenav.templ | 2 +- view/layout/global.templ | 2 +- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/view/component/account/changeusername.templ b/view/component/account/changeusername.templ index d70a2f3..ddf5223 100644 --- a/view/component/account/changeusername.templ +++ b/view/component/account/changeusername.templ @@ -16,11 +16,11 @@ templ ChangeUsername(err string, username string) { x-data={ "{ err: '" + err + "'}" } >
-
+

@SelectMenu(subpage) -

+
-
-
+
+
+ +
+
    for _, item := range menuItems { {{ diff --git a/view/component/nav/sidenav.templ b/view/component/nav/sidenav.templ index 4487a61..b4bfd62 100644 --- a/view/component/nav/sidenav.templ +++ b/view/component/nav/sidenav.templ @@ -8,7 +8,7 @@ templ sideNav(navItems []NavItem) {
      diff --git a/view/layout/global.templ b/view/layout/global.templ index 96bb1f6..248992e 100644 --- a/view/layout/global.templ +++ b/view/layout/global.templ @@ -55,7 +55,7 @@ templ Global() { class="flex flex-col h-screen justify-between" > @nav.Navbar() -
      +
      { children... }
      @footer.Footer() From 42ea74fd63a8f5615a077e827c760fbffd52f911 Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Sat, 15 Feb 2025 20:12:30 +1100 Subject: [PATCH 06/31] Added reauthentication (token freshness) and protected username change --- contexts/user.go | 11 +- handlers/account.go | 3 + handlers/reauthenticatate.go | 119 ++++++++++++++++++ middleware/authentication.go | 15 ++- middleware/reauthentication.go | 21 ++++ server/routes.go | 8 +- view/component/form/confirmpass.templ | 84 +++++++++++++ .../popup/confirmPasswordModal.templ | 20 +++ view/component/{ => popup}/errorPopup.templ | 2 +- view/layout/global.templ | 46 ++++++- 10 files changed, 315 insertions(+), 14 deletions(-) create mode 100644 handlers/reauthenticatate.go create mode 100644 middleware/reauthentication.go create mode 100644 view/component/form/confirmpass.templ create mode 100644 view/component/popup/confirmPasswordModal.templ rename view/component/{ => popup}/errorPopup.templ (99%) diff --git a/contexts/user.go b/contexts/user.go index 3a752a5..0ef041f 100644 --- a/contexts/user.go +++ b/contexts/user.go @@ -5,14 +5,19 @@ import ( "projectreshoot/db" ) +type AuthenticatedUser struct { + *db.User + Fresh int64 +} + // Return a new context with the user added in -func SetUser(ctx context.Context, u *db.User) context.Context { +func SetUser(ctx context.Context, u *AuthenticatedUser) context.Context { return context.WithValue(ctx, contextKeyAuthorizedUser, u) } // Retrieve a user from the given context. Returns nil if not set -func GetUser(ctx context.Context) *db.User { - user, ok := ctx.Value(contextKeyAuthorizedUser).(*db.User) +func GetUser(ctx context.Context) *AuthenticatedUser { + user, ok := ctx.Value(contextKeyAuthorizedUser).(*AuthenticatedUser) if !ok { return nil } diff --git a/handlers/account.go b/handlers/account.go index 08ed745..727f8ee 100644 --- a/handlers/account.go +++ b/handlers/account.go @@ -12,6 +12,7 @@ import ( "github.com/rs/zerolog" ) +// Renders the account page on the 'General' subpage func HandleAccountPage() http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { @@ -20,6 +21,7 @@ func HandleAccountPage() http.Handler { ) } +// Handles a request to change the subpage for the Account page func HandleAccountSubpage() http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { @@ -30,6 +32,7 @@ func HandleAccountSubpage() http.Handler { ) } +// Handles a request to change the users username func HandleChangeUsername( logger *zerolog.Logger, conn *sql.DB, diff --git a/handlers/reauthenticatate.go b/handlers/reauthenticatate.go new file mode 100644 index 0000000..9e188d8 --- /dev/null +++ b/handlers/reauthenticatate.go @@ -0,0 +1,119 @@ +package handlers + +import ( + "database/sql" + "net/http" + + "projectreshoot/config" + "projectreshoot/contexts" + "projectreshoot/cookies" + "projectreshoot/jwt" + "projectreshoot/view/component/form" + + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +// Get the tokens from the request +func getTokens( + config *config.Config, + conn *sql.DB, + r *http.Request, +) (*jwt.AccessToken, *jwt.RefreshToken, error) { + // get the existing tokens from the cookies + atStr, rtStr := cookies.GetTokenStrings(r) + aT, err := jwt.ParseAccessToken(config, conn, atStr) + if err != nil { + return nil, nil, errors.Wrap(err, "jwt.ParseAccessToken") + } + rT, err := jwt.ParseRefreshToken(config, conn, rtStr) + if err != nil { + return nil, nil, errors.Wrap(err, "jwt.ParseRefreshToken") + } + return aT, rT, nil +} + +// Revoke the given token pair +func revokeTokenPair( + conn *sql.DB, + aT *jwt.AccessToken, + rT *jwt.RefreshToken, +) error { + err := jwt.RevokeToken(conn, aT) + if err != nil { + return errors.Wrap(err, "jwt.RevokeToken") + } + err = jwt.RevokeToken(conn, rT) + if err != nil { + return errors.Wrap(err, "jwt.RevokeToken") + } + return nil +} + +// Issue new tokens for the user, invalidating the old ones +func refreshTokens( + config *config.Config, + conn *sql.DB, + w http.ResponseWriter, + r *http.Request, +) error { + aT, rT, err := getTokens(config, conn, r) + if err != nil { + return errors.Wrap(err, "getTokens") + } + rememberMe := map[string]bool{ + "session": false, + "exp": true, + }[aT.TTL] + // issue new tokens for the user + user := contexts.GetUser(r.Context()) + err = cookies.SetTokenCookies(w, r, config, user.User, true, rememberMe) + if err != nil { + return errors.Wrap(err, "cookies.SetTokenCookies") + } + err = revokeTokenPair(conn, aT, rT) + if err != nil { + return errors.Wrap(err, "revokeTokenPair") + } + + return nil +} + +// Validate the provided password +func validatePassword( + r *http.Request, +) error { + r.ParseForm() + password := r.FormValue("password") + user := contexts.GetUser(r.Context()) + err := user.CheckPassword(password) + if err != nil { + return errors.Wrap(err, "user.CheckPassword") + } + return nil +} + +// Handle request to reauthenticate (i.e. make token fresh again) +func HandleReauthenticate( + logger *zerolog.Logger, + config *config.Config, + conn *sql.DB, +) http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + err := validatePassword(r) + if err != nil { + w.WriteHeader(445) + form.ConfirmPassword("Incorrect password").Render(r.Context(), w) + return + } + err = refreshTokens(config, conn, w, r) + if err != nil { + logger.Error().Err(err).Msg("Failed to refresh user tokens") + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) + }, + ) +} diff --git a/middleware/authentication.go b/middleware/authentication.go index 38cabbe..23f40f0 100644 --- a/middleware/authentication.go +++ b/middleware/authentication.go @@ -3,6 +3,7 @@ package middleware import ( "database/sql" "net/http" + "time" "projectreshoot/config" "projectreshoot/contexts" @@ -52,7 +53,7 @@ func getAuthenticatedUser( conn *sql.DB, w http.ResponseWriter, r *http.Request, -) (*db.User, error) { +) (*contexts.AuthenticatedUser, error) { // Get token strings from cookies atStr, rtStr := cookies.GetTokenStrings(r) // Attempt to parse the access token @@ -69,14 +70,22 @@ func getAuthenticatedUser( return nil, errors.Wrap(err, "refreshAuthTokens") } // New token pair sent, return the authorized user - return user, nil + authUser := contexts.AuthenticatedUser{ + User: user, + Fresh: time.Now().Unix(), + } + return &authUser, nil } // Access token valid user, err := aT.GetUser(conn) if err != nil { return nil, errors.Wrap(err, "rT.GetUser") } - return user, nil + authUser := contexts.AuthenticatedUser{ + User: user, + Fresh: aT.Fresh, + } + return &authUser, nil } // Attempt to authenticate the user and add their account details diff --git a/middleware/reauthentication.go b/middleware/reauthentication.go new file mode 100644 index 0000000..41fad65 --- /dev/null +++ b/middleware/reauthentication.go @@ -0,0 +1,21 @@ +package middleware + +import ( + "net/http" + "projectreshoot/contexts" + "time" +) + +func RequiresFresh( + next http.Handler, +) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user := contexts.GetUser(r.Context()) + isFresh := time.Now().Before(time.Unix(user.Fresh, 0)) + if !isFresh { + w.WriteHeader(444) + return + } + next.ServeHTTP(w, r) + }) +} diff --git a/server/routes.go b/server/routes.go index 2bada29..2804a08 100644 --- a/server/routes.go +++ b/server/routes.go @@ -77,6 +77,12 @@ func addRoutes( )) mux.Handle("POST /change-username", middleware.RequiresLogin( - handlers.HandleChangeUsername(logger, conn), + middleware.RequiresFresh( + handlers.HandleChangeUsername(logger, conn), + ), + )) + mux.Handle("POST /reauthenticate", + middleware.RequiresLogin( + handlers.HandleReauthenticate(logger, config, conn), )) } diff --git a/view/component/form/confirmpass.templ b/view/component/form/confirmpass.templ new file mode 100644 index 0000000..6976ddf --- /dev/null +++ b/view/component/form/confirmpass.templ @@ -0,0 +1,84 @@ +package form + +import "fmt" + +templ ConfirmPassword(err string) { + {{ + xdata := fmt.Sprintf( + "{ errMsg: '%s'}", + err, + ) + }} +
      +
      +
      +
      + +
      + +
      +
      +

      +
      + + +
      +
      +} diff --git a/view/component/popup/confirmPasswordModal.templ b/view/component/popup/confirmPasswordModal.templ new file mode 100644 index 0000000..4179a12 --- /dev/null +++ b/view/component/popup/confirmPasswordModal.templ @@ -0,0 +1,20 @@ + +package popup + +import "projectreshoot/view/component/form" + +templ ConfirmPasswordModal() { +
      +
      +
      + To complete this action you need to confirm your password +
      + @form.ConfirmPassword("") +
      +
      +} diff --git a/view/component/errorPopup.templ b/view/component/popup/errorPopup.templ similarity index 99% rename from view/component/errorPopup.templ rename to view/component/popup/errorPopup.templ index 968beab..f809230 100644 --- a/view/component/errorPopup.templ +++ b/view/component/popup/errorPopup.templ @@ -1,4 +1,4 @@ -package component +package popup templ ErrorPopup() {
      + - @component.ErrorPopup() + @popup.ErrorPopup() + @popup.ConfirmPasswordModal()
      Date: Sun, 16 Feb 2025 10:44:04 +1100 Subject: [PATCH 07/31] Added ability for user to change their bio --- db/user.go | 10 ++ handlers/account.go | 32 +++++- server/routes.go | 10 +- view/component/account/changebio.templ | 117 ++++++++++++++++++++ view/component/account/changeusername.templ | 8 +- view/component/account/general.templ | 1 + 6 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 view/component/account/changebio.templ diff --git a/db/user.go b/db/user.go index a330357..fe62349 100644 --- a/db/user.go +++ b/db/user.go @@ -48,3 +48,13 @@ func (user *User) ChangeUsername(conn *sql.DB, newUsername string) error { } return nil } + +// Change the user's bio +func (user *User) ChangeBio(conn *sql.DB, newBio string) error { + query := `UPDATE users SET bio = ? WHERE id = ?` + _, err := conn.Exec(query, newBio, user.ID) + if err != nil { + return errors.Wrap(err, "conn.Exec") + } + return nil +} diff --git a/handlers/account.go b/handlers/account.go index 727f8ee..4ebac66 100644 --- a/handlers/account.go +++ b/handlers/account.go @@ -49,7 +49,8 @@ func HandleChangeUsername( return } if !unique { - account.ChangeUsername("Usename is taken", newUsername).Render(r.Context(), w) + account.ChangeUsername("Username is taken", newUsername). + Render(r.Context(), w) return } user := contexts.GetUser(r.Context()) @@ -59,7 +60,34 @@ func HandleChangeUsername( w.WriteHeader(http.StatusInternalServerError) return } - w.Header().Set("HX-Redirect", "/account") + w.Header().Set("HX-Refresh", "true") + }, + ) +} + +// Handles a request to change the users bio +func HandleChangeBio( + logger *zerolog.Logger, + conn *sql.DB, +) http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + newBio := r.FormValue("bio") + leng := len([]rune(newBio)) + if leng > 128 { + account.ChangeBio("Bio limited to 128 characters", newBio). + Render(r.Context(), w) + return + } + user := contexts.GetUser(r.Context()) + err := user.ChangeBio(conn, newBio) + if err != nil { + logger.Error().Err(err).Msg("Error updating bio") + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("HX-Refresh", "true") }, ) } diff --git a/server/routes.go b/server/routes.go index 2804a08..c89272a 100644 --- a/server/routes.go +++ b/server/routes.go @@ -60,6 +60,12 @@ func addRoutes( // Logout mux.Handle("POST /logout", handlers.HandleLogout(config, logger, conn)) + // Reauthentication request + mux.Handle("POST /reauthenticate", + middleware.RequiresLogin( + handlers.HandleReauthenticate(logger, config, conn), + )) + // Profile page mux.Handle("GET /profile", middleware.RequiresLogin( @@ -81,8 +87,8 @@ func addRoutes( handlers.HandleChangeUsername(logger, conn), ), )) - mux.Handle("POST /reauthenticate", + mux.Handle("POST /change-bio", middleware.RequiresLogin( - handlers.HandleReauthenticate(logger, config, conn), + handlers.HandleChangeBio(logger, conn), )) } diff --git a/view/component/account/changebio.templ b/view/component/account/changebio.templ new file mode 100644 index 0000000..e48d5c4 --- /dev/null +++ b/view/component/account/changebio.templ @@ -0,0 +1,117 @@ +package account + +import "projectreshoot/contexts" + +templ ChangeBio(err string, bio string) { + {{ + user := contexts.GetUser(ctx) + if bio == "" { + bio = user.Bio + } + }} +
      +
      +
      + +
      + + +
      +
      +
      + + +
      +
      +

      + +
      +} diff --git a/view/component/account/changeusername.templ b/view/component/account/changeusername.templ index ddf5223..00e5bf5 100644 --- a/view/component/account/changeusername.templ +++ b/view/component/account/changeusername.templ @@ -24,7 +24,7 @@ templ ChangeUsername(err string, username string) { >
      @@ -85,7 +85,7 @@ templ ChangeUsername(err string, username string) {

      @ChangeUsername("", "") + @ChangeBio("", "")

      } From fa64b0541503015d36a3fa0fe9cd31b53aec60d3 Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Sun, 16 Feb 2025 11:26:57 +1100 Subject: [PATCH 08/31] Refactored alpinejs in forms for better maintainability --- view/component/account/changebio.templ | 70 ++++++++++----------- view/component/account/changeusername.templ | 24 +++++-- view/component/form/confirmpass.templ | 29 +++++---- view/component/form/loginform.templ | 35 ++++++----- view/component/form/registerform.templ | 44 +++++++------ view/layout/global.templ | 4 +- 6 files changed, 120 insertions(+), 86 deletions(-) diff --git a/view/component/account/changebio.templ b/view/component/account/changebio.templ index e48d5c4..ba79b7b 100644 --- a/view/component/account/changebio.templ +++ b/view/component/account/changebio.templ @@ -15,6 +15,41 @@ templ ChangeBio(err string, bio string) { class="w-[90%] mx-auto mt-5" x-data={ templ.JSFuncCall("bioComponent", bio, user.Bio, err).CallInline } > +
      @@ -78,40 +113,5 @@ templ ChangeBio(err string, bio string) { x-show="err" x-text="err" >

      - } diff --git a/view/component/account/changeusername.templ b/view/component/account/changeusername.templ index 00e5bf5..25cc151 100644 --- a/view/component/account/changeusername.templ +++ b/view/component/account/changeusername.templ @@ -13,11 +13,25 @@ templ ChangeUsername(err string, username string) { hx-post="/change-username" hx-swap="outerHTML" class="w-[90%] mx-auto mt-5" - x-data={ "{ err: '" + err + "'}" } + x-data={ templ.JSFuncCall( + "usernameComponent", username, user.Username, err, + ).CallInline } > +
      Update @@ -76,9 +90,9 @@ templ ChangeUsername(err string, username string) { type="button" href="#" x-cloak - x-show={ "username !=='" + user.Username + "'" } + x-show="username !== initialUsername" x-transition.opacity.duration.500ms - @click={ "username='" + user.Username + "';err=''" } + @click="resetUsername()" > Cancel diff --git a/view/component/form/confirmpass.templ b/view/component/form/confirmpass.templ index 6976ddf..90bb192 100644 --- a/view/component/form/confirmpass.templ +++ b/view/component/form/confirmpass.templ @@ -1,22 +1,27 @@ package form -import "fmt" - templ ConfirmPassword(err string) { - {{ - xdata := fmt.Sprintf( - "{ errMsg: '%s'}", - err, - ) - }}
      +
      @@ -30,6 +35,7 @@ templ ConfirmPassword(err string) { placeholder="Confirm password" required aria-describedby="password-error" + @input="reset()" />
      -
      +
      - change username + - confirm password

      +

      @@ -44,6 +47,7 @@ templ LoginForm(loginError string) { disabled:pointer-events-none" required aria-describedby="username-error" + @input="resetErr()" />
      +
      -