xtablo-source/backend/templates/auth_login.templ
Arthur Belleville 808eaecc85
feat(14-02): migrate auth_login.templ to AuthLayout with ui.FormField inputs
- Replace Layout+Card pattern with AuthLayout("Sign in to Xtablo", csrfToken)
- Wire GoogleButton and AuthDivider into LoginPage body
- Replace raw <input> elements with @ui.FormField/@ui.Input design system components
- Add signup-copy nav link ("Don't have an account? Sign up")
- Preserve HTMX swap: hx-post="/login" hx-target="#login-form" hx-swap="outerHTML"
- Remove loginCardBody, AuthProviderButtonsBlock, AuthProviderButtonControl helpers
- Update test assertions for new GoogleButton labels ("Sign in with Google" / disabled attr)
2026-05-16 19:10:08 +02:00

80 lines
2.3 KiB
Text

package templates
import "backend/internal/web/ui"
// LoginPage renders the full /login page wrapped in AuthLayout.
// It delegates the form section to LoginFormFragment so HTMX can swap just the
// form on validation errors without re-rendering the surrounding shell.
templ LoginPage(form LoginForm, errs LoginErrors, csrfToken string, providers AuthProviderButtons) {
@AuthLayout("Sign in to Xtablo", csrfToken) {
<div class="auth-card-topbar"></div>
<div class="brand-header">
<img class="brand-logo" src="/static/logo_dark.png" alt="Xtablo"/>
</div>
<div class="title-group">
<h1>Sign in to Xtablo</h1>
</div>
<div class="auth-body">
@GoogleButton(providers.Google.StartURL, providers.Google.Configured)
@AuthDivider()
@LoginFormFragment(form, errs, csrfToken)
</div>
}
}
// LoginFormFragment is the bare form used for HTMX swaps.
// hx-post targets this component itself so the form can be replaced inline
// on validation failure (D-19, D-20).
// The outer id="login-form" must match the hx-target on this element.
templ LoginFormFragment(form LoginForm, errs LoginErrors, csrfToken string) {
<form
id="login-form"
method="POST"
action="/login"
hx-post="/login"
hx-target="#login-form"
hx-swap="outerHTML"
class="login-form"
>
@ui.CSRFField(csrfToken)
@GeneralError(errs.General)
@ui.FormField(ui.FormFieldProps{
Label: "Email address",
For: "email",
Field: ui.Input(ui.InputProps{
ID: "email",
Name: "email",
Type: "email",
Placeholder: "you@example.com",
Value: form.Email,
Required: true,
Attrs: templ.Attributes{"autocomplete": "email"},
}),
Error: errs.Email,
})
@ui.FormField(ui.FormFieldProps{
Label: "Password",
For: "password",
Field: ui.Input(ui.InputProps{
ID: "password",
Name: "password",
Type: "password",
Placeholder: "Your password",
Required: true,
Attrs: templ.Attributes{"autocomplete": "current-password"},
}),
Error: errs.Password,
})
@ui.Button(ui.ButtonProps{
Label: "Sign in to Xtablo",
Variant: ui.ButtonVariantDefault,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "submit",
})
<p class="signup-copy">
Don't have an account?
<a class="signup-link" href="/signup">Sign up</a>
</p>
</form>
}