Files
support_frontend/src/features/auth/sign-in-form.tsx
T
saqib mirandClaude Sonnet 5 95b5a1e03d feat(001-agent-admin-ui): Setup + Foundational + User Story 0 (sign-in)
Configures both previously-empty test runners (vitest.config.ts,
playwright.config.ts) and adds the typed lib/api client layer (axios +
interceptors), the session-cookie plumbing (lib/auth), TanStack Query
infrastructure (lib/query, providers), and a real sign-in flow consuming
supporthub-api's own login (010-identity-auth) - the true foundation
every other user story in this feature depends on.

Two structural fixes to the existing scaffold, both found only by
running the app rather than by inspection: middleware.ts belongs at
src/middleware.ts under this project's src/ layout, not the repo root;
and next.config.mjs's output:'export' is incompatible with Next.js
Middleware outright (the dev server refuses to start it), so this app
now runs as a standard Next.js server - confirmed with the user before
making that deployment-mode change.

Verified end-to-end with a real, locally-running supporthub-api: all 5
Playwright scenarios (unauthenticated redirect, sign-in, wrong-password
generic error, non-admin role gating, sign-out) pass against a live
backend, not a mock.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 14:27:06 +05:30

55 lines
1.8 KiB
TypeScript

'use client';
import { FormEvent, useState } from 'react';
import { Button, Input, Alert, AlertDescription } from '@/components/ui';
import { useLogin } from './use-login';
import { ApiError } from '@/lib/api/types';
/** US0 acceptance scenario 3: a login failure (wrong password or unknown email) is shown as
* one generic message — 010-identity-auth's own identical-failure-response guarantee — never
* a hint about which part was wrong. */
export function SignInForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const login = useLogin();
function handleSubmit(event: FormEvent) {
event.preventDefault();
login.mutate({ email, password });
}
// Renders the backend's own message verbatim (Principle IV) — 010-identity-auth already
// guarantees this single, generic message for every failure branch (wrong password, unknown
// email, inactive account), so there's nothing for the frontend to add or rephrase.
const errorMessage = login.error instanceof ApiError ? login.error.message : undefined;
return (
<form onSubmit={handleSubmit} className="w-full max-w-sm flex flex-col gap-4">
{errorMessage && (
<Alert variant="destructive">
<AlertDescription>{errorMessage}</AlertDescription>
</Alert>
)}
<Input
type="email"
label="Email"
required
autoComplete="username"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
type="password"
label="Password"
required
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" isLoading={login.isPending} className="w-full">
Sign in
</Button>
</form>
);
}