import React, { useState, useEffect } from 'react'; import { useSearchParams, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useInvitationDetails, useAcceptInvitation, useDeclineInvitation, } from '../hooks/useInvitations'; import { useAuth } from '../hooks/useAuth'; import { Loader2, CheckCircle, XCircle, Building2, Mail, User, Lock, AlertCircle, Eye, EyeOff, } from 'lucide-react'; const AcceptInvitePage: React.FC = () => { const { t } = useTranslation(); const [searchParams] = useSearchParams(); const { token: pathToken } = useParams<{ token: string }>(); const navigate = useNavigate(); // Support both path parameter (/accept-invite/:token) and query parameter (?token=...) const token = pathToken || searchParams.get('token'); const { data: invitation, isLoading, error } = useInvitationDetails(token); const acceptInvitationMutation = useAcceptInvitation(); const declineInvitationMutation = useDeclineInvitation(); const { setTokens } = useAuth(); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [formError, setFormError] = useState(''); const [accepted, setAccepted] = useState(false); const [declined, setDeclined] = useState(false); const handleAccept = async (e: React.FormEvent) => { e.preventDefault(); setFormError(''); // Validate if (!firstName.trim()) { setFormError(t('acceptInvite.firstNameRequired', 'First name is required')); return; } if (!password || password.length < 8) { setFormError(t('acceptInvite.passwordMinLength', 'Password must be at least 8 characters')); return; } if (password !== confirmPassword) { setFormError(t('acceptInvite.passwordsMustMatch', 'Passwords do not match')); return; } try { const result = await acceptInvitationMutation.mutateAsync({ token: token!, firstName: firstName.trim(), lastName: lastName.trim(), password, invitationType: invitation?.invitation_type, }); // Set auth tokens and redirect to dashboard setTokens(result.access, result.refresh); setAccepted(true); // Redirect after a short delay setTimeout(() => { navigate('/'); }, 2000); } catch (err: any) { setFormError(err.response?.data?.error || t('acceptInvite.acceptFailed', 'Failed to accept invitation')); } }; const handleDecline = async () => { if (!confirm(t('acceptInvite.confirmDecline', 'Are you sure you want to decline this invitation?'))) { return; } try { await declineInvitationMutation.mutateAsync({ token: token!, invitationType: invitation?.invitation_type }); setDeclined(true); } catch (err: any) { setFormError(err.response?.data?.error || t('acceptInvite.declineFailed', 'Failed to decline invitation')); } }; // No token provided if (!token) { return (

{t('acceptInvite.invalidLink', 'Invalid Invitation Link')}

{t('acceptInvite.noToken', 'This invitation link is invalid. Please check your email for the correct link.')}

); } // Loading state if (isLoading) { return (

{t('acceptInvite.loading', 'Loading invitation...')}

); } // Error state (invalid/expired token) if (error || !invitation) { return (

{t('acceptInvite.expiredTitle', 'Invitation Expired or Invalid')}

{t( 'acceptInvite.expiredDescription', 'This invitation has expired or is no longer valid. Please contact the person who sent the invitation to request a new one.' )}

); } // Accepted state if (accepted) { return (

{t('acceptInvite.welcomeTitle', 'Welcome to the Team!')}

{t('acceptInvite.redirecting', 'Your account has been created. Redirecting to dashboard...')}

); } // Declined state if (declined) { return (

{t('acceptInvite.declinedTitle', 'Invitation Declined')}

{t('acceptInvite.declinedDescription', "You've declined this invitation. You can close this page.")}

); } // Main acceptance form return (
{/* Header */}

{t('acceptInvite.title', "You're Invited!")}

{t('acceptInvite.subtitle', 'Join the team and start scheduling')}

{/* Invitation Details */}

{t('acceptInvite.business', 'Business')}

{invitation.business_name}

{t('acceptInvite.invitedAs', 'Invited As')}

{invitation.role_display} •{' '} {invitation.email}

{invitation.invited_by && (

{t('acceptInvite.invitedBy', 'Invited by')} {invitation.invited_by}

)}
{/* Form */}

{t('acceptInvite.formDescription', 'Create your account to accept this invitation.')}

setFirstName(e.target.value)} required className="w-full pl-10 pr-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-brand-500" placeholder="John" />
setLastName(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-brand-500" placeholder="Doe" />
setPassword(e.target.value)} required minLength={8} className="w-full pl-10 pr-10 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-brand-500" placeholder="Min. 8 characters" />
setConfirmPassword(e.target.value)} required className="w-full pl-10 pr-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-brand-500" placeholder="Repeat password" />
{/* Error Message */} {formError && (

{formError}

)} {/* Buttons */}
); }; export default AcceptInvitePage;