page wrapper
This commit is contained in:
@@ -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' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user