feat(unit-2-auth): Implement authentication pages and guards
Completes Unit 2 Code Generation (Steps 1-21 of 24): - SetupPage: First Owner account creation with language preference - InviteCompletePage: User invitation completion with token validation - InitGuard: System initialization status checking - RoleGuard: Role-based access control for protected routes - API hooks: useSetup, useValidateInvitation, useCompleteInvitation - Shared password validation schema via Zod - Error display components: FormErrorBanner, FieldError - MSW mock handlers for setup and invitation flows - i18n translations (en/nl) for auth screens - Router updates: public routes (/setup, /invite/complete) + role guards - Unit/integration tests: 25/29 passing (86% pass rate) - Build: ✅ PASSED, Lint: ✅ PASSED, Tests: 86% PASSED Stories: US-01, US-02, US-03, US-04, US-05, US-06, US-07, US-13, US-14 Remaining: Step 22 (README), Step 23 (final verification), Step 24 (commit) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
interface FieldErrorProps {
|
||||
message?: string;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export function FieldError({ message, testId }: FieldErrorProps) {
|
||||
if (!message) return null;
|
||||
|
||||
return (
|
||||
<span
|
||||
className="block mt-1 text-xs text-red-600 font-medium"
|
||||
role="alert"
|
||||
data-testid={testId}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
interface FormError {
|
||||
message: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
interface FormErrorBannerProps {
|
||||
error: FormError | null;
|
||||
onDismiss: () => void;
|
||||
}
|
||||
|
||||
export function FormErrorBanner({ error, onDismiss }: FormErrorBannerProps) {
|
||||
if (!error) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="form-error-banner"
|
||||
className="mb-4 flex items-start justify-between gap-3 rounded-lg bg-red-50 p-4 text-red-800 border border-red-200 animate-in fade-in"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">{error.message}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
className="flex-shrink-0 text-red-600 hover:text-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 rounded"
|
||||
aria-label="Dismiss error"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user