import { useEffect, useState } from 'react'; import { Container, Paper, Title, Text, TextInput, PasswordInput, Button, Stack, Alert, Center, Loader, Progress, Anchor, } from '@mantine/core'; import { useForm } from '@mantine/form'; import { IconAlertCircle, IconCheck, IconShieldCheck } from '@tabler/icons-react'; import { useSearchParams, useNavigate, Link } from 'react-router-dom'; import api from '../../services/api'; import { useAuthStore } from '../../stores/authStore'; import logoSrc from '../../assets/logo.png'; function getPasswordStrength(pw: string): number { let score = 0; if (pw.length >= 8) score += 25; if (pw.length >= 12) score += 15; if (/[A-Z]/.test(pw)) score += 20; if (/[a-z]/.test(pw)) score += 10; if (/[0-9]/.test(pw)) score += 15; if (/[^A-Za-z0-9]/.test(pw)) score += 15; return Math.min(score, 100); } function strengthColor(s: number): string { if (s < 40) return 'red'; if (s < 70) return 'orange'; return 'green'; } export function ActivatePage() { const [searchParams] = useSearchParams(); const navigate = useNavigate(); const setAuth = useAuthStore((s) => s.setAuth); const token = searchParams.get('token'); const [validating, setValidating] = useState(true); const [tokenInfo, setTokenInfo] = useState(null); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const form = useForm({ initialValues: { fullName: '', password: '', confirmPassword: '' }, validate: { fullName: (v) => (v.trim().length >= 2 ? null : 'Name is required'), password: (v) => (v.length >= 8 ? null : 'Password must be at least 8 characters'), confirmPassword: (v, values) => (v === values.password ? null : 'Passwords do not match'), }, }); useEffect(() => { if (!token) { setError('No activation token provided'); setValidating(false); return; } api.get(`/auth/activate?token=${token}`) .then(({ data }) => { setTokenInfo(data); setValidating(false); }) .catch((err) => { setError(err.response?.data?.message || 'Invalid or expired activation link'); setValidating(false); }); }, [token]); const handleSubmit = async (values: typeof form.values) => { setLoading(true); setError(''); try { const { data } = await api.post('/auth/activate', { token, password: values.password, fullName: values.fullName, }); setAuth(data.accessToken, data.user, data.organizations); navigate('/onboarding'); } catch (err: any) { setError(err.response?.data?.message || 'Activation failed'); } finally { setLoading(false); } }; const passwordStrength = getPasswordStrength(form.values.password); if (validating) { return (
Validating activation link...
); } if (error && !tokenInfo) { return (
HOA LedgerIQ
} color="red" variant="light" mb="md"> {error} Go to Login
); } return (
HOA LedgerIQ
Activate your account for {tokenInfo?.orgName || 'your organization'}
{error && ( } color="red" variant="light"> {error} )}
{form.values.password && ( )}
); }