page wrapper

This commit is contained in:
2026-01-07 12:40:52 +01:00
parent e589c30bf8
commit 0db80fa98d
128 changed files with 7386 additions and 8096 deletions

View File

@@ -15,7 +15,6 @@ import {
User,
Check,
X,
Loader2,
Car,
Users,
Trophy,
@@ -29,6 +28,7 @@ import Input from '@/components/ui/Input';
import Heading from '@/components/ui/Heading';
import { useAuth } from '@/lib/auth/AuthContext';
import { useSignup } from '@/hooks/auth/useSignup';
import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper';
interface FormErrors {
firstName?: string;
@@ -45,6 +45,10 @@ interface PasswordStrength {
color: string;
}
interface SignupData {
placeholder: string;
}
function checkPasswordStrength(password: string): PasswordStrength {
let score = 0;
if (password.length >= 8) score++;
@@ -89,13 +93,12 @@ const FEATURES = [
'Access detailed performance analytics',
];
export default function SignupPage() {
const SignupTemplate = ({ data }: { data: SignupData }) => {
const router = useRouter();
const searchParams = useSearchParams();
const { refreshSession, session } = useAuth();
const { refreshSession } = useAuth();
const returnTo = searchParams.get('returnTo') ?? '/onboarding';
const [checkingAuth, setCheckingAuth] = useState(true);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [errors, setErrors] = useState<FormErrors>({});
@@ -107,32 +110,6 @@ export default function SignupPage() {
confirmPassword: '',
});
// Check if already authenticated
useEffect(() => {
if (session) {
// Already logged in, redirect to dashboard or return URL
router.replace(returnTo === '/onboarding' ? '/dashboard' : returnTo);
return;
}
// If no session, still check via API for consistency
async function checkAuth() {
try {
const response = await fetch('/api/auth/session');
const data = await response.json();
if (data.authenticated) {
// Already logged in, redirect to dashboard or return URL
router.replace(returnTo === '/onboarding' ? '/dashboard' : returnTo);
}
} catch {
// Not authenticated, continue showing signup page
} finally {
setCheckingAuth(false);
}
}
checkAuth();
}, [session, router, returnTo]);
const passwordStrength = checkPasswordStrength(formData.password);
const passwordRequirements = [
@@ -239,18 +216,6 @@ export default function SignupPage() {
}
};
// Loading state from mutation
const loading = signupMutation.isPending;
// Show loading while checking auth
if (checkingAuth) {
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center">
<Loader2 className="w-8 h-8 text-primary-blue animate-spin" />
</main>
);
}
return (
<main className="min-h-screen bg-deep-graphite flex">
{/* Background Pattern */}
@@ -375,7 +340,7 @@ export default function SignupPage() {
error={!!errors.firstName}
errorMessage={errors.firstName}
placeholder="John"
disabled={loading}
disabled={signupMutation.isPending}
className="pl-10"
autoComplete="given-name"
/>
@@ -397,7 +362,7 @@ export default function SignupPage() {
error={!!errors.lastName}
errorMessage={errors.lastName}
placeholder="Smith"
disabled={loading}
disabled={signupMutation.isPending}
className="pl-10"
autoComplete="family-name"
/>
@@ -428,7 +393,7 @@ export default function SignupPage() {
error={!!errors.email}
errorMessage={errors.email}
placeholder="you@example.com"
disabled={loading}
disabled={signupMutation.isPending}
className="pl-10"
autoComplete="email"
/>
@@ -450,7 +415,7 @@ export default function SignupPage() {
error={!!errors.password}
errorMessage={errors.password}
placeholder="••••••••"
disabled={loading}
disabled={signupMutation.isPending}
className="pl-10 pr-10"
autoComplete="new-password"
/>
@@ -517,7 +482,7 @@ export default function SignupPage() {
error={!!errors.confirmPassword}
errorMessage={errors.confirmPassword}
placeholder="••••••••"
disabled={loading}
disabled={signupMutation.isPending}
className="pl-10 pr-10"
autoComplete="new-password"
/>
@@ -536,29 +501,14 @@ export default function SignupPage() {
)}
</div>
{/* Error Message */}
<AnimatePresence>
{errors.submit && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="flex items-start gap-3 p-3 rounded-lg bg-red-500/10 border border-red-500/30"
>
<AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-0.5" />
<p className="text-sm text-red-400">{errors.submit}</p>
</motion.div>
)}
</AnimatePresence>
{/* Submit Button */}
<Button
type="submit"
variant="primary"
disabled={loading}
disabled={signupMutation.isPending}
className="w-full flex items-center justify-center gap-2"
>
{loading ? (
{signupMutation.isPending ? (
<>
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
Creating account...
@@ -620,4 +570,82 @@ export default function SignupPage() {
</div>
</main>
);
};
export default function SignupPage() {
const router = useRouter();
const searchParams = useSearchParams();
const { refreshSession, session } = useAuth();
const returnTo = searchParams.get('returnTo') ?? '/onboarding';
const [checkingAuth, setCheckingAuth] = useState(true);
// Check if already authenticated
useEffect(() => {
if (session) {
// Already logged in, redirect to dashboard or return URL
router.replace(returnTo === '/onboarding' ? '/dashboard' : returnTo);
return;
}
// If no session, still check via API for consistency
async function checkAuth() {
try {
const response = await fetch('/api/auth/session');
const data = await response.json();
if (data.authenticated) {
// Already logged in, redirect to dashboard or return URL
router.replace(returnTo === '/onboarding' ? '/dashboard' : returnTo);
}
} catch {
// Not authenticated, continue showing signup page
} finally {
setCheckingAuth(false);
}
}
checkAuth();
}, [session, router, returnTo]);
// Use signup mutation hook for state management
const signupMutation = useSignup({
onSuccess: async () => {
// Refresh session in context so header updates immediately
try {
await refreshSession();
} catch (error) {
console.error('Failed to refresh session after signup:', error);
}
// Always redirect to dashboard after signup
router.push('/dashboard');
},
onError: (error) => {
// Error will be handled in the template
console.error('Signup error:', error);
},
});
// Loading state from mutation
const loading = signupMutation.isPending;
// Show loading while checking auth
if (checkingAuth) {
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center">
<div className="w-8 h-8 border-2 border-primary-blue border-t-transparent rounded-full animate-spin" />
</main>
);
}
// Map mutation states to StatefulPageWrapper
return (
<StatefulPageWrapper
data={{ placeholder: 'data' } as SignupData}
isLoading={loading}
error={signupMutation.error}
retry={() => signupMutation.mutate({ email: '', password: '', displayName: '' })}
Template={SignupTemplate}
loading={{ variant: 'full-screen', message: 'Processing signup...' }}
errorConfig={{ variant: 'full-screen' }}
/>
);
}