import { useState, useEffect } from 'react' import { useSearchParams, useNavigate } from 'react-router-dom' import { useMutation, useQuery } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { Card } from '../components/ui/Card' import { Button } from '../components/ui/Button' import { Input } from '../components/ui/Input' import { PasswordStrengthMeter } from '../components/PasswordStrengthMeter' import { toast } from '../utils/toast' import { validateInvite, acceptInvite } from '../api/users' import { Loader2, CheckCircle2, XCircle, UserCheck } from 'lucide-react' export default function AcceptInvite() { const { t } = useTranslation() const [searchParams] = useSearchParams() const navigate = useNavigate() const token = searchParams.get('token') || '' const [name, setName] = useState('') const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [accepted, setAccepted] = useState(false) const { data: validation, isLoading: isValidating, error: validationError, } = useQuery({ queryKey: ['validate-invite', token], queryFn: () => validateInvite(token), enabled: !!token, retry: false, }) const acceptMutation = useMutation({ mutationFn: async () => { return acceptInvite({ token, name, password }) }, onSuccess: (data) => { setAccepted(true) toast.success(t('acceptInvite.welcomeMessage', { email: data.email })) }, onError: (error: unknown) => { const err = error as { response?: { data?: { error?: string } } } toast.error(err.response?.data?.error || t('acceptInvite.acceptFailed')) }, }) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (password !== confirmPassword) { toast.error(t('acceptInvite.passwordsDoNotMatch')) return } if (password.length < 8) { toast.error(t('errors.passwordTooShort')) return } acceptMutation.mutate() } // Redirect to login after successful acceptance useEffect(() => { if (accepted) { const timer = setTimeout(() => { navigate('/login') }, 3000) return () => clearTimeout(timer) } }, [accepted, navigate]) if (!token) { return (

{t('acceptInvite.invalidLink')}

{t('acceptInvite.invalidLinkMessage')}

) } if (isValidating) { return (

{t('acceptInvite.validating')}

) } if (validationError || !validation?.valid) { const errorData = validationError as { response?: { data?: { error?: string } } } | undefined const errorMessage = errorData?.response?.data?.error || t('acceptInvite.expiredOrInvalid') return (

{t('acceptInvite.invitationInvalid')}

{errorMessage}

) } if (accepted) { return (

{t('acceptInvite.accountCreated')}

{t('acceptInvite.accountCreatedMessage')}

) } return (
Charon
{t('acceptInvite.youveBeenInvited')}

{t('acceptInvite.completeSetup')} {validation.email}

setName(e.target.value)} placeholder={t('acceptInvite.namePlaceholder')} required />
setPassword(e.target.value)} placeholder="••••••••" required autoComplete="new-password" />
setConfirmPassword(e.target.value)} placeholder="••••••••" required error={ confirmPassword && password !== confirmPassword ? t('acceptInvite.passwordsDoNotMatch') : undefined } autoComplete="new-password" />
) }