panel-redesign: sign-in without an administrator follows its mockup

- panel.css: .sp-signin-form .sp-form, as theme.css has it now.
  - templates/login.html: the state is div.sp-form with the mockup's
    heading and line; the empty form of ef8a82e is gone.
  - tests: the hint is checked by its new wording, and that the page
    carries no form at all.

Evidence
  - docs/assets/panel-redesign/evidence/login-no-admin.png: 1440x900,
    headless Edge, real templates served locally (no Docker here).
    login.png re-shot after the selector change: byte-identical.
  - go test -count=1 ./internal/web/... : ok (web, auth, handlers,
    validate, view)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-09 23:33:26 +03:00
co-authored by Claude Opus 5.5
parent 2b4a158423
commit f59f4778c9
6 changed files with 15 additions and 14 deletions
+1 -1
View File
@@ -133,7 +133,7 @@ func TestLoginPointsAtSetupBeforeTheFirstAdministrator(t *testing.T) {
t.Fatalf("status = %d, want 200", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, "No administrator has been created yet") {
if !strings.Contains(body, "No administrator yet") || !strings.Contains(body, "one-time setup link") {
t.Errorf("the login page does not point at the setup link:\n%s", body)
}
if strings.Contains(body, `name="password"`) {
+3 -4
View File
@@ -53,12 +53,11 @@ func TestLoginShowsAnErrorEscaped(t *testing.T) {
func TestLoginWithoutAnAdministratorHasNoForm(t *testing.T) {
out := renderSignedOut(t, "login", NewLoginSetupHint("mail.example.org"))
if !strings.Contains(out, "No administrator has been created yet") {
if !strings.Contains(out, "No administrator yet") || !strings.Contains(out, "one-time setup link") {
t.Error("the setup hint is missing")
}
// The element stays (the signed-out layout centres the column through it) but
// nothing in it can be filled in or sent.
for _, bad := range []string{`action=`, `<input`, `<button`, `name="password"`, `name="username"`} {
// Nothing on it can be filled in or sent.
for _, bad := range []string{`<form`, `action=`, `<input`, `<button`, `name="password"`, `name="username"`} {
if strings.Contains(out, bad) {
t.Errorf("the hint page carries %q", bad)
}
+1 -1
View File
@@ -306,7 +306,7 @@ code { background: var(--sp-well); color: var(--sp-ink); border-radius: 3px; pad
.sp-signin-brand img { width: 20rem; max-width: 100%; height: auto; }
.sp-signin-brand p { font-family: var(--sp-mono); letter-spacing: .18em; font-size: var(--sp-fs-xs); color: var(--sp-on-brick); margin-top: 1.6rem; }
.sp-signin-form { display: flex; flex-direction: column; justify-content: center; padding: 3rem; }
.sp-signin-form form { width: 22rem; max-width: 100%; margin-inline: auto; }
.sp-signin-form .sp-form { width: 22rem; max-width: 100%; margin-inline: auto; }
.sp-foot { color: var(--sp-muted); font-size: var(--sp-fs-s); padding-block: 1.5rem 0; }
.sp-foot a { color: inherit; text-decoration: underline; }
.sp-signin-form .sp-foot { text-align: center; margin-top: 2.5rem; }
+5 -8
View File
@@ -4,15 +4,12 @@
in as, so the form gives way to a pointer at the setup link. */}}
{{define "content"}}
{{if .SetupHint}}
{{/* The signed-out layout sizes and centres its column through "form" alone, so
this state, which has no fields, keeps the element; it has no action and no
control and cannot be submitted. */}}
<form class="sp-form">
{{/* Mockup login-no-admin.html: the same column, not a form. */}}
<div class="sp-form">
{{if .Host}}<p>{{template "kicker" .Host}}</p>{{end}}
<h1 class="title is-3">Sign in</h1>
<p class="sp-muted">No administrator has been created yet. Open the one-time
setup link printed in the container log to get started.</p>
</form>
<h1 class="title is-3">No administrator yet</h1>
<p class="sp-muted">Open the one-time setup link printed in the container log to create one.</p>
</div>
{{else}}
<form class="sp-form" method="post" action="/login">
{{if .Host}}<p>{{template "kicker" .Host}}</p>{{end}}