'use client'; import { Suspense, useEffect, useMemo, useState } from 'react'; import { useTranslations } from 'next-intl'; import { Link, useRouter } from '@/i18n/navigation'; import { useSearchParams } from 'next/navigation'; import { useForm, type FieldErrors, type UseFormRegister, type UseFormSetValue } from 'react-hook-form'; import type { OrganizationDetailsFormValues } from '@/components/ui/auth/OrganizationDetailsFields'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { Lock, Mail, User } from 'lucide-react'; import { Button } from '@/components/ui/shared/Button'; import { Input } from '@/components/ui/shared/Input'; import { OrganizationDetailsFields } from '@/components/ui/auth/OrganizationDetailsFields'; import { RegistrationProgressSteps } from '@/components/ui/auth/RegistrationProgressSteps'; import { organizationApi } from '@/lib/api/organization'; type AcceptOrganizationInviteForm = { ownerName: string; password: string; confirmPassword: string; organizationName: string; organizationEmail: string; organizationType: 'CLINIC' | 'LAB'; }; function AcceptOrganizationInviteContent() { const t = useTranslations('auth'); const tCommon = useTranslations('common'); const tValidation = useTranslations('validation'); const params = useSearchParams(); const router = useRouter(); const token = useMemo(() => params.get('token') || '', [params]); const acceptOrganizationInviteSchema = useMemo( () => z .object({ ownerName: z.string().min(2, tValidation('nameMinLength')), password: z .string() .min(8, tValidation('passwordMinLength')) .regex(/[A-Z]/, tValidation('passwordUppercase')) .regex(/[0-9]/, tValidation('passwordNumber')), confirmPassword: z.string(), organizationName: z.string().min(2, tValidation('organizationNameMinLength')), organizationEmail: z.string().email(tValidation('organizationEmailInvalid')), organizationType: z.enum(['CLINIC', 'LAB'], { message: tValidation('organizationTypeRequired'), }), }) .refine((data) => data.password === data.confirmPassword, { message: tValidation('passwordsDoNotMatch'), path: ['confirmPassword'], }), [tValidation], ); const [step, setStep] = useState(1); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [inviteInfo, setInviteInfo] = useState<{ ownerEmail: string; organizationName: string; organizationType: 'CLINIC' | 'LAB'; organizationEmail?: string; inviterOrganizationName: string; expiresAt: string; status: 'PENDING' | 'ACCEPTED'; } | null>(null); const { register, handleSubmit, watch, trigger, setValue, reset, formState: { errors }, } = useForm({ resolver: zodResolver(acceptOrganizationInviteSchema), mode: 'onChange', defaultValues: { organizationType: undefined, }, }); const organizationType = watch('organizationType'); useEffect(() => { if (!token) { setLoading(false); setError(t('invalidInvitationLink')); return; } void (async () => { setLoading(true); setError(''); try { const res = await organizationApi.previewInvite(token); setInviteInfo(res.data); reset({ ownerName: '', password: '', confirmPassword: '', organizationName: res.data.organizationName || '', organizationEmail: res.data.organizationEmail || '', organizationType: res.data.organizationType, }); if (res.data.status === 'ACCEPTED') { setSuccess(t('invitationAlreadyAccepted')); } } catch (e: unknown) { const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : ''; setError(message || t('errorLoadInvitation')); } finally { setLoading(false); } })(); }, [token, reset, t]); const handleNext = async () => { const isValid = await trigger(['ownerName', 'password', 'confirmPassword']); if (isValid) { setStep(2); setError(''); } }; const onSubmit = async (data: AcceptOrganizationInviteForm) => { if (!token) return; setError(''); setSuccess(''); setSubmitting(true); try { await organizationApi.acceptInvite({ token, ownerName: data.ownerName.trim(), password: data.password, organizationName: data.organizationName.trim(), organizationEmail: data.organizationEmail.trim(), organizationType: data.organizationType, }); setSuccess(t('organizationAcceptedRedirect')); setTimeout(() => router.replace('/login'), 1000); } catch (e: unknown) { const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : ''; setError(message || t('errorAcceptInvitation')); } finally { setSubmitting(false); } }; return (
{tCommon('appName')}

{t('acceptOrganizationTitle')}

{t('alreadyHaveAccount')}{' '} {t('signInLink')}

{loading ? (

{t('loadingInvitation')}

) : ( <> {inviteInfo && (

{t('invitedBy')}{' '} {inviteInfo.inviterOrganizationName}

)} {inviteInfo?.status !== 'ACCEPTED' && ( )} {error && (

{error}

)} {success && (
{success}
)} {inviteInfo?.status !== 'ACCEPTED' && (
{step === 1 && ( <> } /> } /> } /> } /> )} {step === 2 && ( <> } errors={errors as FieldErrors} organizationType={organizationType} setValue={setValue as unknown as UseFormSetValue} />
)} )} )}
); } function AcceptOrganizationInviteFallback() { const t = useTranslations('auth'); return (

{t('loadingInvitation')}

); } export default function AcceptOrganizationInvitePage() { return ( }> ); }