feat: task 1.6 login page

src/ui/pages/login.tsx — LoginPage component:
- Centred card on muted background.
- shadcn Form + FormField with react-hook-form + zodResolver.
  FormMessage renders field-level zod errors automatically.
- Email + password with proper autocomplete attrs for password managers.
  autoFocus on email.
- Submit calls useAuthStore.getState().login(); errors render in a
  destructive Alert above the form.
- In-flight via the auth store's 'authenticating' status (cross-tab safe).
- onAuthenticated callback fires on store transition to authenticated;
  1.7 wires this to a router redirect.

src/App.tsx branches on auth status:
- unknown / authenticating -> "Signing you in..." placeholder
  (avoids flashing the login page on hard refresh while the boot probe
  runs)
- anonymous -> <LoginPage />
- authenticated -> home placeholder (1.7 replaces with router shell)

Deviation: skipped src/routes/login.tsx — requires the router plugin
to have generated routeTree.gen.ts, which is 1.7's territory. The page
works standalone via App.tsx's status branch.

Search-param redirect (?redirect=...) also deferred to 1.7 — no router
yet to expose useSearch. onAuthenticated callback is the seam.
This commit is contained in:
2026-05-02 18:02:43 +02:00
parent a65ad428e6
commit ef18222edf
4 changed files with 162 additions and 5 deletions
+18 -3
View File
@@ -1,19 +1,34 @@
import { useAuthStore } from '@/auth';
import { LoginPage } from '@/ui/pages/login';
function App() {
const status = useAuthStore((s) => s.status);
const user = useAuthStore((s) => (s.status === 'authenticated' ? s.user : null));
// While the boot probe is in flight, render a minimal placeholder rather
// than flashing the login page for users who already have a session.
if (status === 'unknown' || status === 'authenticating') {
return (
<div className="min-h-screen grid place-items-center bg-muted text-sm text-muted-foreground">
Signing you in
</div>
);
}
if (status === 'anonymous') {
return <LoginPage />;
}
// Phase 1.7 replaces this branch with the TanStack Router shell.
return (
<div className="min-h-screen grid place-items-center bg-muted">
<div className="space-y-4 text-center">
<h1 className="text-3xl font-semibold">TRM</h1>
<p className="text-sm text-muted-foreground">
Phase 1 scaffold. Routing + login UI land in 1.6 / 1.7.
Phase 1 scaffold. Routing + protected pages land in 1.7.
</p>
<p className="text-xs text-muted-foreground">
Auth status: <code className="font-mono">{status}</code>
{user && <> · {user.first_name ?? user.email ?? user.id}</>}
Signed in as <code className="font-mono">{user?.email ?? user?.id}</code>
</p>
</div>
</div>