T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/private_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/components/RegistrationForm.tsx
import { useState, useEffect, useRef, useMemo } from 'react';
import { useRouter } from 'next/router';
import { motion } from 'framer-motion';
import { useSession } from 'next-auth/react';
import { getCsrfToken } from 'next-auth/react';
import DocumentViewer from './DocumentViewer';
import CaseSelection from './CaseSelection';
import debounce from 'lodash.debounce';
import Draggable from 'react-draggable';

interface FormData {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  birthDate: string;
  detaineeInfo: {
    name: string;
    facility: string;
    inmateId: string;
  };
  relationship: string;
  preferredLanguage: 'fr' | 'en';
  preferredContactMethod: 'email' | 'phone' | 'mail';
  gender: string;
  address: {
    street: string;
    city: string;
    state: string;
    postalCode: string;
    country: string;
  };
  detaineeIncarcerationDates: string;
  detaineeExpectedReleaseDates: string;
  previousLegalActions: string;
  reasonForJoining: string;
  howDidYouHearAboutUs: string;
  representation: string;
  urgentNeeds: string;
  additionalNotes: string;
  lawyerName?: string;
  lawyerEmail?: string;
  lawyerPhone?: string;
  lawFirm?: string;
  status: string;
  // Multi-case system
  caseId?: string;
}

interface RegistrationFormProps {
  onSuccess?: () => void;
  initialData?: any;
  isEditing?: boolean;
  onSave?: (data: any) => void;
  isAdmin?: boolean;
  mode?: 'user' | 'admin' | 'edit';
  initialValues?: any;
  isFrench?: boolean;
  initialLocale?: string;
  isMobile?: boolean;
  preSelectedCaseId?: string;
  selectedCase?: any;
}

const RegistrationForm: React.FC<RegistrationFormProps> = ({ 
  onSuccess,
  initialData,
  isEditing = false,
  onSave,
  isAdmin = false,
  mode = 'user',
  initialValues,
  isFrench = false,
  initialLocale,
  isMobile: propIsMobile = false,
  preSelectedCaseId,
  selectedCase,
}) => {
  const router = useRouter();
  const { data: session } = useSession();
  const [csrfToken, setCsrfToken] = useState<string>('');

  const [formData, setFormData] = useState<FormData>(() => {
    // Ensure all nested objects are properly initialized
    const initialFormData = {
      firstName: '',
      lastName: '',
      email: '',
      phone: '',
      birthDate: '',
      detaineeInfo: {
        name: '',
        facility: '',
        inmateId: '',
      },
      relationship: '',
      preferredLanguage: initialLocale || 'en',
      preferredContactMethod: 'email',
      gender: '',
      address: {
        street: '',
        city: '',
        state: '',
        postalCode: '',
        country: 'CA', // default to CA
      },
      detaineeIncarcerationDates: '',
      detaineeExpectedReleaseDates: '',
      previousLegalActions: '',
      reasonForJoining: '',
      howDidYouHearAboutUs: '',
      representation: '',
      urgentNeeds: '',
      additionalNotes: '',
      lawyerName: '',
      lawyerEmail: '',
      lawyerPhone: '',
      lawFirm: '',
      status: 'PENDING',
      caseId: preSelectedCaseId || '',
    };

    // If we have initialData, merge it with our defaults
    if (initialData) {
      // Format dates for the form
      const formattedData = {
        ...initialData,
        birthDate: initialData.birthDate ? new Date(initialData.birthDate).toISOString().split('T')[0] : '',
        detaineeIncarcerationDates: initialData.detaineeInfo?.incarcerationDate 
          ? new Date(initialData.detaineeInfo.incarcerationDate).toISOString().split('T')[0] 
          : '',
        detaineeExpectedReleaseDates: initialData.detaineeInfo?.expectedReleaseDate 
          ? new Date(initialData.detaineeInfo.expectedReleaseDate).toISOString().split('T')[0] 
          : '',
      };

      // Normalize country code
      let countryCode = formattedData.address?.country;
      if (countryCode === 'Canada') countryCode = 'CA';
      if (countryCode === 'United States') countryCode = 'US';
      if (!countryCode) countryCode = 'CA';

      return {
        ...initialFormData,
        ...formattedData,
        // Ensure nested objects are properly merged
        address: {
          ...initialFormData.address,
          ...(formattedData.address || {}),
          country: countryCode,
        },
        detaineeInfo: {
          ...initialFormData.detaineeInfo,
          ...(formattedData.detaineeInfo || {}),
        },
      };
    }

    return initialFormData;
  });

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [isMobile, setIsMobile] = useState(propIsMobile);

  // Mobile detection (fallback if not provided via props)
  useEffect(() => {
    const checkMobile = () => {
      setIsMobile(propIsMobile || window.innerWidth < 768);
    };
    
    checkMobile();
    window.addEventListener('resize', checkMobile);
    return () => window.removeEventListener('resize', checkMobile);
  }, [propIsMobile]);

  const [selectedCountry, setSelectedCountry] = useState('CA');

  const provincesAndStates = {
    CA: [
      { value: 'AB', label: 'Alberta' },
      { value: 'BC', label: 'British Columbia' },
      { value: 'MB', label: 'Manitoba' },
      { value: 'NB', label: 'New Brunswick' },
      { value: 'NL', label: 'Newfoundland and Labrador' },
      { value: 'NS', label: 'Nova Scotia' },
      { value: 'NT', label: 'Northwest Territories' },
      { value: 'NU', label: 'Nunavut' },
      { value: 'ON', label: 'Ontario' },
      { value: 'PE', label: 'Prince Edward Island' },
      { value: 'QC', label: 'Quebec' },
      { value: 'SK', label: 'Saskatchewan' },
      { value: 'YT', label: 'Yukon' }
    ],
    US: [
      { value: 'AL', label: 'Alabama' },
      { value: 'AK', label: 'Alaska' },
      { value: 'AZ', label: 'Arizona' },
      { value: 'AR', label: 'Arkansas' },
      { value: 'CA', label: 'California' },
      { value: 'CO', label: 'Colorado' },
      { value: 'CT', label: 'Connecticut' },
      { value: 'DE', label: 'Delaware' },
      { value: 'FL', label: 'Florida' },
      { value: 'GA', label: 'Georgia' },
      { value: 'HI', label: 'Hawaii' },
      { value: 'ID', label: 'Idaho' },
      { value: 'IL', label: 'Illinois' },
      { value: 'IN', label: 'Indiana' },
      { value: 'IA', label: 'Iowa' },
      { value: 'KS', label: 'Kansas' },
      { value: 'KY', label: 'Kentucky' },
      { value: 'LA', label: 'Louisiana' },
      { value: 'ME', label: 'Maine' },
      { value: 'MD', label: 'Maryland' },
      { value: 'MA', label: 'Massachusetts' },
      { value: 'MI', label: 'Michigan' },
      { value: 'MN', label: 'Minnesota' },
      { value: 'MS', label: 'Mississippi' },
      { value: 'MO', label: 'Missouri' },
      { value: 'MT', label: 'Montana' },
      { value: 'NE', label: 'Nebraska' },
      { value: 'NV', label: 'Nevada' },
      { value: 'NH', label: 'New Hampshire' },
      { value: 'NJ', label: 'New Jersey' },
      { value: 'NM', label: 'New Mexico' },
      { value: 'NY', label: 'New York' },
      { value: 'NC', label: 'North Carolina' },
      { value: 'ND', label: 'North Dakota' },
      { value: 'OH', label: 'Ohio' },
      { value: 'OK', label: 'Oklahoma' },
      { value: 'OR', label: 'Oregon' },
      { value: 'PA', label: 'Pennsylvania' },
      { value: 'RI', label: 'Rhode Island' },
      { value: 'SC', label: 'South Carolina' },
      { value: 'SD', label: 'South Dakota' },
      { value: 'TN', label: 'Tennessee' },
      { value: 'TX', label: 'Texas' },
      { value: 'UT', label: 'Utah' },
      { value: 'VT', label: 'Vermont' },
      { value: 'VA', label: 'Virginia' },
      { value: 'WA', label: 'Washington' },
      { value: 'WV', label: 'West Virginia' },
      { value: 'WI', label: 'Wisconsin' },
      { value: 'WY', label: 'Wyoming' }
    ]
  };

  const genderOptions = [
    { value: '', label: 'Select' },
    { value: 'male', label: 'Male' },
    { value: 'female', label: 'Female' },
    { value: 'non_binary', label: 'Non-binary' },
    { value: 'prefer_not_to_say', label: 'Prefer not to say' }
  ];

  const countryOptions = [
    { value: 'CA', label: 'Canada' },
    { value: 'US', label: 'United States' }
  ];

  const facilityOptions = useMemo(() => [
    { value: '', label: 'Sélectionner' },
    { value: 'amos', label: "Établissement de détention d'Amos" },
    { value: 'baie-comeau', label: 'Établissement de détention de Baie-Comeau' },
    { value: 'bordeaux', label: 'Établissement de détention de Montréal (Bordeaux)' },
    { value: 'cowansville', label: 'Établissement de détention de Cowansville' },
    { value: 'drummondville', label: 'Établissement de détention de Drummondville' },
    { value: 'hull', label: 'Établissement de détention de Hull' },
    { value: 'leclerc', label: 'Établissement de détention de Laval (Leclerc)' },
    { value: 'new-carlisle', label: 'Établissement de détention de New Carlisle' },
    { value: 'perce', label: 'Établissement de détention de Percé' },
    { value: 'quebec', label: 'Établissement de détention de Québec' },
    { value: 'rimouski', label: 'Établissement de détention de Rimouski' },
    { value: 'roberval', label: 'Établissement de détention de Roberval' },
    { value: 'saint-jerome', label: 'Établissement de détention de Saint-Jérôme' },
    { value: 'sept-iles', label: 'Établissement de détention de Sept-Îles' },
    { value: 'sherbrooke', label: 'Établissement de détention de Sherbrooke' },
    { value: 'sorel-tracy', label: 'Établissement de détention de Sorel-Tracy' },
    { value: 'trois-rivieres', label: 'Établissement de détention de Trois-Rivières' },
    { value: 'other', label: 'Autre établissement' }
  ], []);

  const relationshipOptions = [
    { value: '', label: 'Select' },
    { value: 'self', label: 'I am the Detainee' },
    { value: 'spouse', label: 'Spouse' },
    { value: 'parent', label: 'Parent' },
    { value: 'child', label: 'Child' },
    { value: 'sibling', label: 'Sibling' },
    { value: 'friend', label: 'Friend' },
    { value: 'lawyer', label: 'Lawyer' },
    { value: 'other', label: 'Other' }
  ];

  const contactMethodOptions = [
    { value: 'email', label: 'Email' },
    { value: 'phone', label: 'Phone' },
    { value: 'mail', label: 'Mail' }
  ];

  const languageOptions = [
    { value: 'en', label: 'English' },
    { value: 'fr', label: 'French' }
  ];

  const representationOptions = [
    { value: '', label: 'Select' },
    { value: 'yes', label: 'Yes, I have a lawyer' },
    { value: 'no', label: 'No, I don\'t have a lawyer' },
    { value: 'looking', label: 'I\'m looking for a lawyer' }
  ];

  const howDidYouHearOptions = [
    { value: '', label: 'Select' },
    { value: 'social_media', label: 'Social Media' },
    { value: 'friend', label: 'Friend or Family' },
    { value: 'lawyer', label: 'Lawyer' },
    { value: 'news', label: 'News' },
    { value: 'search', label: 'Search Engine' },
    { value: 'other', label: 'Other' }
  ];

  const [files, setFiles] = useState<File[]>([]);
  const [filePreviews, setFilePreviews] = useState<{ file: File, url: string }[]>([]);

  const statusOptions = [
    { value: 'PENDING', label: 'Pending' },
    { value: 'MISSING_DOCUMENTS', label: 'Missing Documents' },
    { value: 'DOCUMENTS_UNDER_REVIEW', label: 'Documents Under Review' },
    { value: 'ADDITIONAL_INFO_NEEDED', label: 'Additional Info Needed' },
    { value: 'VERIFICATION_IN_PROGRESS', label: 'Verification In Progress' },
    { value: 'LAWYER_VERIFICATION', label: 'Lawyer Verification' },
    { value: 'FACILITY_VERIFICATION', label: 'Facility Verification' },
    { value: 'DOCUMENTS_EXPIRED', label: 'Documents Expired' },
    { value: 'DOCUMENTS_INCOMPLETE', label: 'Documents Incomplete' },
    { value: 'INFORMATION_MISMATCH', label: 'Information Mismatch' },
    { value: 'PENDING_PAYMENT', label: 'Pending Payment' },
    { value: 'PAYMENT_RECEIVED', label: 'Payment Received' },
    { value: 'PENDING_LAWYER_APPROVAL', label: 'Pending Lawyer Approval' },
    { value: 'PENDING_FACILITY_APPROVAL', label: 'Pending Facility Approval' },
    { value: 'ON_HOLD', label: 'On Hold' },
    { value: 'ESCALATED', label: 'Escalated' },
    { value: 'FINAL_REVIEW', label: 'Final Review' },
    { value: 'APPROVED', label: 'Approved' },
    { value: 'REJECTED', label: 'Rejected' },
    { value: 'COMPLETED', label: 'Completed' }
  ];

  const [lastSaved, setLastSaved] = useState<Date | null>(null);

  const autosaveTimeout = useRef<NodeJS.Timeout | null>(null);

  const [saving, setSaving] = useState(false);
  const [saved, setSaved] = useState(false);
  const isEditingRegistration = Boolean(initialValues && initialValues.id);

  // Debounced autosave function
  const autosave = debounce(async (data) => {
    if (!isEditingRegistration) return;
    setSaving(true);
    setSaved(false);
    try {
      const endpoint = isAdmin
        ? `/api/admin/registrations/${initialValues.id}`
        : `/api/user/registrations/${initialValues.id}`;
      await fetch(endpoint, {
        method: isAdmin ? 'PATCH' : 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
      setSaved(true);
      setLastSaved(new Date());
    } catch {
      // Optionally show error
    } finally {
      setSaving(false);
    }
  }, 1000);

  useEffect(() => {
    // Create object URLs for each file
    const previews = files.map(file => ({
      file,
      url: URL.createObjectURL(file)
    }));
    setFilePreviews(previews);

    // Cleanup: revoke all object URLs when files change or component unmounts
    return () => {
      previews.forEach(p => URL.revokeObjectURL(p.url));
    };
  }, [files]);

  useEffect(() => {
    // Get CSRF token when component mounts
    const getToken = async () => {
      const token = await getCsrfToken();
      if (token) setCsrfToken(token);
    };
    getToken();
  }, []);

  useEffect(() => {
    // Only autosave if editing an existing registration (has initialValues.id)
    if (!initialValues || !initialValues.id) return;
    // Don't autosave if loading or submitting
    if (loading) return;

    // Debounce autosave
    if (autosaveTimeout.current) clearTimeout(autosaveTimeout.current);
    autosaveTimeout.current = setTimeout(async () => {
      try {
        setLoading(true);
        // Format the data to match the database schema
        const formattedData = {
          firstName: formData.firstName,
          lastName: formData.lastName,
          email: formData.email,
          phone: formData.phone,
          birthDate: new Date(formData.birthDate).toISOString(),
          gender: formData.gender,
          relationship: formData.relationship,
          preferredLanguage: formData.preferredLanguage,
          preferredContactMethod: formData.preferredContactMethod,
          message: formData.additionalNotes,
          additionalNotes: formData.additionalNotes,
          previousLegalActions: formData.previousLegalActions,
          reasonForJoining: formData.reasonForJoining,
          howDidYouHearAboutUs: formData.howDidYouHearAboutUs,
          representation: formData.representation,
          urgentNeeds: formData.urgentNeeds,
          address: {
            street: formData.address.street || '',
            city: formData.address.city || '',
            state: formData.address.state || '',
            postalCode: formData.address.postalCode || '',
            country: formData.address.country || '',
          },
          detaineeInfo: {
            name: formData.detaineeInfo.name || '',
            facility: formData.detaineeInfo.facility || '',
            inmateId: formData.detaineeInfo.inmateId || '',
            incarcerationDate: formData.detaineeIncarcerationDates
              ? new Date(formData.detaineeIncarcerationDates).toISOString()
              : undefined,
            expectedReleaseDate: formData.detaineeExpectedReleaseDates
              ? new Date(formData.detaineeExpectedReleaseDates).toISOString()
              : null,
          },
          lawyerName: formData.lawyerName,
          lawyerEmail: formData.lawyerEmail,
          lawyerPhone: formData.lawyerPhone,
          lawFirm: formData.lawFirm,
        };
        const endpoint = isAdmin
          ? `/api/admin/registrations/${initialValues.id}`
          : `/api/user/registrations/${initialValues.id}`;
        await fetch(endpoint, {
          method: isAdmin ? 'PATCH' : 'PUT',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': csrfToken || '',
          },
          body: JSON.stringify(formattedData),
        });
        setLastSaved(new Date());
      } catch (err) {
        // Optionally handle autosave errors
      } finally {
        setLoading(false);
      }
    }, 1000); // 1 second debounce
    return () => {
      if (autosaveTimeout.current) clearTimeout(autosaveTimeout.current);
    };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [formData]);

  useEffect(() => {
    if (preSelectedCaseId) {
      setFormData(prev => ({
        ...prev,
        caseId: preSelectedCaseId
      }));
    }
  }, [preSelectedCaseId]);

  const validateForm = () => {
    const missingFields = [];
    
    // Case Selection Validation (only for non-admin users)
    if (!isAdmin && !formData.caseId) missingFields.push('Legal Case Selection');
    
    if (!formData.firstName) missingFields.push('First Name');
    if (!formData.lastName) missingFields.push('Last Name');
    if (!formData.email) missingFields.push('Email');
    if (!formData.phone) missingFields.push('Phone');
    if (!formData.birthDate) missingFields.push('Birth Date');
    if (!formData.gender) missingFields.push('Gender');
    if (!formData.address.street) missingFields.push('Street Address');
    if (!formData.address.city) missingFields.push('City');
    if (!formData.address.state) missingFields.push('State/Province');
    if (!formData.address.postalCode) missingFields.push('Postal Code');
    if (!formData.address.country) missingFields.push('Country');
    if (!formData.detaineeInfo.name) missingFields.push('Detainee Name');
    if (!formData.detaineeInfo.facility) missingFields.push('Facility');
    if (!formData.detaineeInfo.inmateId) missingFields.push('Inmate ID');
    if (!formData.relationship) missingFields.push('Relationship to Detainee');
    if (!formData.preferredLanguage) missingFields.push('Preferred Language');
    if (!formData.preferredContactMethod) missingFields.push('Preferred Contact Method');
    if (!formData.representation) missingFields.push('Legal Representation');
    if (!formData.howDidYouHearAboutUs) missingFields.push('How did you hear about us');
    if (!formData.detaineeIncarcerationDates) missingFields.push('Incarceration Date');

    if (missingFields.length > 0) {
      setError('Please fill in all required fields. Missing fields: ' + missingFields.join(', '));
      return false;
    }

    return true;
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    if (!validateForm()) {
      setLoading(false);
      return;
    }

    try {
      // Use the correct endpoint for user vs admin
      const endpoint = isAdmin ? '/api/register' : '/api/user/registrations';
      
      // Construct the payload with proper detaineeInfo structure
      const payload = {
        ...formData,
        detaineeInfo: {
          name: formData.detaineeInfo.name,
          facility: formData.detaineeInfo.facility,
          inmateId: formData.detaineeInfo.inmateId,
          incarcerationDate: formData.detaineeIncarcerationDates
            ? new Date(formData.detaineeIncarcerationDates).toISOString()
            : undefined,
          expectedReleaseDate: formData.detaineeExpectedReleaseDates
            ? new Date(formData.detaineeExpectedReleaseDates).toISOString()
            : null,
        },
      };
      // Remove fields not expected by backend
      delete (payload as any).detaineeIncarcerationDates;
      delete (payload as any).detaineeExpectedReleaseDates;

      const response = await fetch(endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-CSRF-Token': csrfToken,
        },
        body: JSON.stringify(payload),
      });

      const data = await response.json();

      if (!response.ok) {
        // Show error popup/message
        setError(data.message || 'Registration error');
        alert(data.message || 'Registration error');
        setLoading(false);
        return;
      }

      // Show success popup/message
      alert('Application submitted successfully!');
      if (onSuccess) {
        onSuccess();
      } else {
        router.push('/success');
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
      alert(err instanceof Error ? err.message : 'Unknown error');
      console.error('Form submission error:', err);
    } finally {
      setLoading(false);
    }
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
    const { name, value, type } = e.target;
    
    if (name.includes('.')) {
      const [parent, child] = name.split('.');
      setFormData(prev => ({
        ...prev,
        [parent]: {
          ...((prev[parent as keyof FormData] || {}) as object),
          [child]: value
        }
      }));
    } else {
      setFormData(prev => ({
        ...prev,
        [name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value
      }));
    }
    if (isEditingRegistration) autosave(formData);
  };

  const handleCountryChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const { value } = e.target;
    setSelectedCountry(value);
    setFormData(prev => ({
      ...prev,
      address: {
        ...prev.address,
        country: value,
        state: ''
      }
    }));
  };

  // Update useEffect to handle initial country value as code
  useEffect(() => {
    if (initialValues?.address?.country) {
      // If the country is already a code, use it; otherwise, map name to code
      let countryCode = initialValues.address.country;
      if (countryCode === 'Canada') countryCode = 'CA';
      if (countryCode === 'United States') countryCode = 'US';
      setSelectedCountry(countryCode);
      setFormData(prev => ({
        ...prev,
        address: {
          ...prev.address,
          country: countryCode
        }
      }));
    }
  }, [initialValues]);

  const [openViewers, setOpenViewers] = useState<{ id: number, file: File }[]>([]);
  let viewerId = 0;

  // File input handler for multiple files
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newFiles = Array.from(e.target.files || []);
    setFiles(prev => [...prev, ...newFiles]);
  };

  // Remove file from list
  const handleRemoveFile = (index: number) => {
    setFiles(prev => prev.filter((_, i) => i !== index));
  };

  // Open DocumentViewer modal
  const handleViewFile = (file: File) => {
    setOpenViewers(prev => [...prev, { id: ++viewerId, file }]);
  };

  // Close DocumentViewer modal
  const handleCloseViewer = (id: number) => {
    setOpenViewers(prev => prev.filter(v => v.id !== id));
  };

  const [openDocument, setOpenDocument] = useState<null | { url: string; type: string; name: string }>(null);

  return (
    <div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-3xl mx-auto">
        <div className="bg-white shadow sm:rounded-lg">
          <div className="px-4 py-5 sm:p-6">
            <form onSubmit={handleSubmit} className="space-y-10">
              {/* Last Saved Timestamp */}
              {isEditingRegistration && lastSaved && (
                <div className="mb-4 text-sm text-gray-500">
                  Last saved: {lastSaved.toLocaleString()}
                </div>
              )}
              
              {/* Personal Information */}
              <div>
                <h2 className="text-xl font-bold mb-4">Personal Information</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      First Name <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="firstName" value={formData.firstName} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Last Name <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="lastName" value={formData.lastName} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Email <span className="text-red-500">*</span>
                    </label>
                    <input type="email" name="email" value={formData.email} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Phone <span className="text-red-500">*</span>
                    </label>
                    <input type="tel" name="phone" value={formData.phone} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Birth Date <span className="text-red-500">*</span>
                    </label>
                    <input type="date" name="birthDate" value={formData.birthDate} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Gender <span className="text-red-500">*</span>
                    </label>
                    <select name="gender" value={formData.gender} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      {genderOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
                    </select>
                  </div>
                </div>
              </div>

              {/* Address */}
              <div>
                <h2 className="text-xl font-bold mb-4">Address</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Street Address <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="address.street" value={formData.address.street} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      City <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="address.city" value={formData.address.city} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Country <span className="text-red-500">*</span>
                    </label>
                    <select 
                      value={selectedCountry} 
                      onChange={handleCountryChange} 
                      required 
                      className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
                    >
                      {countryOptions.map(option => (
                        <option key={option.value} value={option.value}>{option.label}</option>
                      ))}
                    </select>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      State/Province <span className="text-red-500">*</span>
                    </label>
                    <select 
                      name="address.state" 
                      value={formData.address.state} 
                      onChange={handleInputChange} 
                      required 
                      className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
                    >
                      <option value="">Select</option>
                      {(selectedCountry === 'CA' || selectedCountry === 'US') &&
                        provincesAndStates[selectedCountry].map(option => (
                          <option key={option.value} value={option.value}>{option.label}</option>
                        ))
                      }
                    </select>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Postal Code <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="address.postalCode" value={formData.address.postalCode} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                </div>
              </div>

              {/* Detainee Information */}
              <div>
                <h2 className="text-xl font-bold mb-4">Detainee Information</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Detainee Name <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="detaineeInfo.name" value={formData.detaineeInfo.name} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Facility <span className="text-red-500">*</span>
                    </label>
                    <select name="detaineeInfo.facility" value={formData.detaineeInfo.facility} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      {facilityOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
                    </select>
                    <p className="text-xs text-gray-500 mt-1">Select the facility where the detainee is currently held.</p>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Inmate ID <span className="text-red-500">*</span>
                    </label>
                    <input type="text" name="detaineeInfo.inmateId" value={formData.detaineeInfo.inmateId} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                    <p className="text-xs text-gray-500 mt-1">As provided by the facility.</p>
                  </div>
                  <div className="md:col-span-2">
                    <div className="flex flex-col md:flex-row gap-6">
                      <div className="flex-1">
                        <label className="block text-sm font-medium text-gray-700 mb-1">
                          Incarceration Date <span className="text-red-500">*</span>
                        </label>
                        <input type="date" name="detaineeIncarcerationDates" value={formData.detaineeIncarcerationDates} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                      </div>
                      <div className="flex-1">
                        <label className="block text-sm font-medium text-gray-700 mb-1">
                          Expected Release Date <span className="text-gray-400">(optional)</span>
                        </label>
                        <input type="date" name="detaineeExpectedReleaseDates" value={formData.detaineeExpectedReleaseDates} onChange={handleInputChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                      </div>
                    </div>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Relationship to Detainee <span className="text-red-500">*</span>
                    </label>
                    <select name="relationship" value={formData.relationship} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      {relationshipOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
                    </select>
                  </div>
                </div>
              </div>

              {/* Contact Preferences */}
              <div>
                <h2 className="text-xl font-bold mb-4">Contact Preferences</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Preferred Language <span className="text-red-500">*</span>
                    </label>
                    <select name="preferredLanguage" value={formData.preferredLanguage} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      {languageOptions.map(option => (
                        <option key={option.value} value={option.value}>{option.label}</option>
                      ))}
                    </select>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Preferred Contact Method <span className="text-red-500">*</span>
                    </label>
                    <select name="preferredContactMethod" value={formData.preferredContactMethod} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      <option value="email">Email</option>
                      <option value="phone">Phone</option>
                      <option value="mail">Mail</option>
                    </select>
                  </div>
                </div>
              </div>

              {/* Legal Representation */}
              <div>
                <h2 className="text-xl font-bold mb-4">Legal Representation</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      Legal Representation <span className="text-red-500">*</span>
                    </label>
                    <select name="representation" value={formData.representation} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      <option value="">Select</option>
                      <option value="yes">Yes</option>
                      <option value="no">No</option>
                    </select>
                  </div>
                </div>
                {formData.representation === 'yes' && (
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
                    <div>
                      <label className="block text-sm font-medium text-gray-700 mb-1">Lawyer Name <span className="text-gray-400">(optional)</span></label>
                      <input type="text" name="lawyerName" value={formData.lawyerName} onChange={handleInputChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                    </div>
                    <div>
                      <label className="block text-sm font-medium text-gray-700 mb-1">Law Firm <span className="text-gray-400">(optional)</span></label>
                      <input type="text" name="lawFirm" value={formData.lawFirm} onChange={handleInputChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                    </div>
                    <div>
                      <label className="block text-sm font-medium text-gray-700 mb-1">Lawyer Email <span className="text-gray-400">(optional)</span></label>
                      <input type="email" name="lawyerEmail" value={formData.lawyerEmail} onChange={handleInputChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                    </div>
                    <div>
                      <label className="block text-sm font-medium text-gray-700 mb-1">Lawyer Phone <span className="text-gray-400">(optional)</span></label>
                      <input type="tel" name="lawyerPhone" value={formData.lawyerPhone} onChange={handleInputChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                    </div>
                  </div>
                )}
              </div>

              {/* Additional Information */}
              <div>
                <h2 className="text-xl font-bold mb-4">Additional Information</h2>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">Previous Legal Actions <span className="text-gray-400">(optional)</span></label>
                    <textarea name="previousLegalActions" value={formData.previousLegalActions} onChange={handleInputChange} rows={2} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">Reason for Joining <span className="text-gray-400">(optional)</span></label>
                    <textarea name="reasonForJoining" value={formData.reasonForJoining} onChange={handleInputChange} rows={2} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">How did you hear about us? <span className="text-red-500">*</span></label>
                    <select name="howDidYouHearAboutUs" value={formData.howDidYouHearAboutUs} onChange={handleInputChange} required className="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
                      {howDidYouHearOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
                    </select>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">Urgent Needs <span className="text-gray-400">(optional)</span></label>
                    <textarea name="urgentNeeds" value={formData.urgentNeeds} onChange={handleInputChange} rows={2} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">Additional Notes <span className="text-gray-400">(optional)</span></label>
                    <textarea name="additionalNotes" value={formData.additionalNotes} onChange={handleInputChange} rows={2} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm" />
                  </div>
                </div>
              </div>

              {/* Admin: Existing Documents */}
              {isAdmin && initialData?.documents && initialData.documents.length > 0 && (
                <div className="mb-8">
                  <h2 className="text-xl font-bold mb-2">Uploaded Documents</h2>
                  <ul className="space-y-2">
                    {initialData.documents.map((doc: any) => (
                      <li key={doc.id} className="flex items-center gap-2">
                        <span className="truncate flex-1">{doc.name || doc.title || doc.url}</span>
                        <button
                          type="button"
                          className="px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700"
                          onClick={() => setOpenDocument({ url: doc.url, type: doc.type, name: doc.name || doc.title || doc.url })}
                        >
                          View
                        </button>
                      </li>
                    ))}
                  </ul>
                </div>
              )}

              {error && <div className="text-red-600 text-sm mt-2">{error}</div>}
              {saving && <div className="text-sm text-blue-500">Saving...</div>}
              {saved && !saving && <div className="text-sm text-green-500">Saved</div>}
              {!isEditingRegistration && (
                <button type="submit" disabled={loading} className="w-full py-3 px-4 bg-indigo-600 text-white rounded-md text-lg font-semibold hover:bg-indigo-700 disabled:opacity-50 mt-8">
                  {loading ? 'Submitting...' : 'Submit'}
                </button>
              )}
            </form>
          </div>
        </div>
      </div>

      {/* DocumentViewer modals */}
      {openViewers.map(({ id, file }) => (
        <Draggable key={id} handle=".modal-header">
          <div className="fixed z-50 top-20 left-20 bg-white shadow-lg rounded-lg border border-gray-300 w-[90vw] max-w-2xl h-[80vh] flex flex-col">
            <div className="modal-header flex items-center justify-between bg-indigo-600 text-white px-4 py-2 cursor-move rounded-t-lg">
              <span>{file.name}</span>
              <div>
                <button onClick={() => handleCloseViewer(id)} className="ml-2 px-2 py-1 bg-red-500 rounded text-white">Close</button>
              </div>
            </div>
            <div className="flex-1 overflow-auto">
              <DocumentViewer 
                url={URL.createObjectURL(file)}
                type={file.type}
                name={file.name}
              />
            </div>
          </div>
        </Draggable>
      ))}

      {/* DocumentViewer modal for existing documents */}
      {openDocument && (
        <div className="fixed z-50 inset-0 flex items-center justify-center bg-black bg-opacity-50">
          <div className="bg-white rounded-lg shadow-lg max-w-2xl w-full h-[80vh] flex flex-col">
            <div className="flex items-center justify-between px-4 py-2 bg-indigo-600 text-white rounded-t-lg">
              <span>{openDocument.name}</span>
              <button onClick={() => setOpenDocument(null)} className="ml-2 px-2 py-1 bg-red-500 rounded text-white">Close</button>
            </div>
            <div className="flex-1 overflow-auto">
              <DocumentViewer url={openDocument.url} type={openDocument.type} name={openDocument.name} />
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default RegistrationForm;

CasperSecurity Mini