import React, { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { ArrowLeft, CheckCircle } from 'lucide-react'; import { MainLayout } from '@/components/layout/MainLayout.tsx'; import PageHeader from '@/components/layout/PageHeader.tsx'; import Button from '@/components/ui/Button.tsx'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card.tsx'; import Input from '@/components/ui/Input.tsx'; import { Container, Flex, Stack } from '@/components/ui/layout'; import Select from '@/components/ui/Select.tsx'; import Spinner from '@/components/ui/Spinner.tsx'; import Textarea from '@/components/ui/Textarea.tsx'; import { useDynamicSectors } from '@/hooks/useDynamicSectors.ts'; import { useAuth } from '@/contexts/AuthContext.tsx'; import { useCreateOrganization, useOrganization } from '@/hooks/api/useOrganizationsAPI.ts'; import { useTranslation } from '@/hooks/useI18n.tsx'; import { useNavigation } from '@/hooks/useNavigation.tsx'; import { isValidEmail, sanitizeInput, validateInput } from '@/lib/api-client.ts'; import { getTranslatedSectorName } from '@/lib/sector-mapper.ts'; import { getOrganizationSubtypeLabel } from '@/schemas/organizationSubtype.ts'; const OrganizationEditPage = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { t } = useTranslation(); const { handleBackNavigation, handleFooterNavigate } = useNavigation(); const { user, isAuthenticated } = useAuth(); const isEditing = Boolean(id); const pageTitle = isEditing ? t('organizationEdit.editTitle') : t('organizationEdit.createTitle'); const pageSubtitle = isEditing ? t('organizationEdit.editSubtitle') : t('organizationEdit.createSubtitle'); // Data fetching const { data: existingOrganization, isLoading: isLoadingOrg, error: orgError } = useOrganization(id || ''); const { sectors: availableSectors } = useDynamicSectors(50); // Get all sectors for editing // Mutations const createOrgMutation = useCreateOrganization(); // Form state const [formData, setFormData] = useState({ name: '', sector: '', description: '', subtype: '', website: '', address: '', }); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); // Redirect to login if not authenticated useEffect(() => { if (!isAuthenticated) { navigate('/login'); } }, [isAuthenticated, navigate]); // Initialize form with existing data when editing useEffect(() => { if (isEditing && existingOrganization) { setFormData({ name: existingOrganization.Name || '', sector: existingOrganization.Sector || '', description: existingOrganization.Description || '', subtype: existingOrganization.Subtype || '', website: existingOrganization.Website || '', address: existingOrganization.Address || '', }); } }, [isEditing, existingOrganization]); const handleInputChange = (field: string, value: string) => { setFormData(prev => ({ ...prev, [field]: value })); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!user) return; try { setError(null); setIsSubmitting(true); // Input validation and sanitization const sanitizedName = sanitizeInput(formData.name.trim()); const sanitizedDescription = sanitizeInput(formData.description.trim()); const sanitizedWebsite = formData.website.trim(); const sanitizedAddress = sanitizeInput(formData.address.trim()); // Validate organization name const nameValidation = validateInput(sanitizedName, { minLength: 2, maxLength: 100, allowSpecialChars: true, allowNumbers: true }); if (!nameValidation.isValid) { throw new Error(`Organization name: ${nameValidation.error}`); } // Validate sector selection if (!formData.sector) { throw new Error('Please select an organization sector'); } // Validate description (optional but if provided, check length) if (sanitizedDescription && sanitizedDescription.length > 0) { const descValidation = validateInput(sanitizedDescription, { minLength: 0, maxLength: 500, allowSpecialChars: true, allowNumbers: true }); if (!descValidation.isValid) { throw new Error(`Description: ${descValidation.error}`); } } // Validate website URL if provided if (sanitizedWebsite && !isValidEmail(sanitizedWebsite) && !sanitizedWebsite.startsWith('http')) { // If it looks like an email, validate as email if (sanitizedWebsite.includes('@')) { if (!isValidEmail(sanitizedWebsite)) { throw new Error('Please enter a valid email address or website URL'); } } else { throw new Error('Please enter a valid website URL (starting with http:// or https://)'); } } // Validate address if provided if (sanitizedAddress && sanitizedAddress.length > 0) { const addressValidation = validateInput(sanitizedAddress, { minLength: 0, maxLength: 200, allowSpecialChars: true, allowNumbers: true }); if (!addressValidation.isValid) { throw new Error(`Address: ${addressValidation.error}`); } } // Create organization with sanitized data const orgPayload = { name: sanitizedName, sector: formData.sector, description: sanitizedDescription, subtype: formData.subtype || 'commercial', website: sanitizedWebsite, address: sanitizedAddress, }; const newOrg = await createOrgMutation.mutateAsync(orgPayload); // Navigate to the organization page navigate(`/organization/${newOrg.ID}`); } catch (err) { console.error('Error saving organization:', err); setError(err instanceof Error ? err.message : 'Failed to save organization'); } finally { setIsSubmitting(false); } }; const handleCancel = () => { if (isEditing) { navigate(`/organization/${id}`); } else { navigate('/organizations'); } }; // Sector options const sectorOptions = availableSectors.map(sector => ({ value: sector.backendName, label: t(sector.nameKey), })); // Subtype options (simplified) const subtypeOptions = [ { value: 'commercial', label: getOrganizationSubtypeLabel('commercial') }, { value: 'government', label: getOrganizationSubtypeLabel('government') }, { value: 'other', label: getOrganizationSubtypeLabel('other') }, ]; if (isLoadingOrg && isEditing) { return (

{t('organizationEdit.loading')}

); } if (orgError && isEditing) { return (

{orgError.message}

); } return ( {/* Action Bar */} {/* Error Message */} {error && (

{error}

)} {/* Organization Form */}
{/* Basic Information */} {t('organizationEdit.basicInfo')}
handleInputChange('name', e.target.value)} placeholder={t('organizationEdit.namePlaceholder')} required />
handleInputChange('subtype', value)} options={subtypeOptions} placeholder={t('organizationEdit.selectSubtype')} />