From c6e5e0b1a903e62ab3b043f4d3178817221134f0 Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 18 Oct 2026 18:49:18 +0200 Subject: [PATCH] feat(auth): unify and polish login, register and homepage auth UI Login and register each carried their own copy of the promo panel and the error banner, so the two pages drifted apart and every caller had to wire role="alert" by hand. Extract a shared AuthPromoCard and AuthAlert, use them in both forms and for the login notices, and link errors to their fields via aria-invalid / aria-describedby. Focus now lands on the 2FA code field the moment it appears, the credential fields lock while a request is in flight, and invalid inputs get a danger tint. The homepage top bar was a byte-for-byte duplicate of AuthTopBar; it now renders the shared component, and the compact homepage login uses the tighter spacing its comment already promised. --- src/app/(site)/login/page.tsx | 106 +++-------------------- src/app/(site)/page.tsx | 89 +------------------- src/app/(site)/register/page.tsx | 102 +++------------------- src/app/globals.css | 14 ++++ src/components/auth/auth-alert.tsx | 38 +++++++++ src/components/auth/auth-promo-card.tsx | 107 ++++++++++++++++++++++++ src/components/auth/login-form.tsx | 44 ++++++---- src/components/auth/register-form.tsx | 11 +-- 8 files changed, 215 insertions(+), 296 deletions(-) create mode 100644 src/components/auth/auth-alert.tsx create mode 100644 src/components/auth/auth-promo-card.tsx diff --git a/src/app/(site)/login/page.tsx b/src/app/(site)/login/page.tsx index 95b61337..101f1952 100644 --- a/src/app/(site)/login/page.tsx +++ b/src/app/(site)/login/page.tsx @@ -1,15 +1,15 @@ import { desc, eq } from "drizzle-orm"; import type { Metadata } from "next"; import { headers } from "next/headers"; -import Image from "next/image"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AuthAlert } from "@/components/auth/auth-alert"; import { AuthPageFrame, AuthUsersCards, } from "@/components/auth/auth-page-frame"; +import { AuthPromoCard } from "@/components/auth/auth-promo-card"; import { LoginForm } from "@/components/auth/login-form"; -import { SurfaceCard } from "@/components/surface-card"; import { auth } from "@/lib/auth"; import { safeRedirectPath } from "@/lib/auth/safe-redirect"; import { cached } from "@/lib/cache"; @@ -90,22 +90,16 @@ export default async function LoginPage({ // exists. const notice = sp.registered === "1" ? ( -

+ {t("registeredSuccess")} -

+ ) : sp.reset === "1" ? ( // `?reset=1` comes from a successful password reset; saying so matters // because the visitor just changed their password and a silent form // reads like the reset failed. -

+ {t("passwordChanged")} -

+ ) : undefined; return ( @@ -127,89 +121,11 @@ export default async function LoginPage({ /> } > - -
-