import { useEffect, useState } from 'react'; import type { ReactElement } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Loader2 } from 'lucide-react'; import { Modal, FormField, FormSelect, PrimaryButton, SecondaryButton } from '@/components/shared'; import type { Tenant } from '@/types/tenant'; // Validation schema - matches backend validation const editTenantSchema = z.object({ name: z .string() .min(1, 'name is required') .min(3, 'name must be at least 3 characters') .max(100, 'name must be at most 100 characters'), slug: z .string() .min(1, 'slug is required') .min(3, 'slug must be at least 3 characters') .max(100, 'slug must be at most 100 characters') .regex(/^[a-z0-9-]+$/, 'slug format is invalid'), status: z.enum(['active', 'suspended', 'deleted'], { message: 'Status is required', }), settings: z.any().optional().nullable(), subscription_tier: z.string().max(50, 'subscription_tier must be at most 50 characters').optional().nullable(), max_users: z.number().int().min(1, 'max_users must be at least 1').optional().nullable(), max_modules: z.number().int().min(1, 'max_modules must be at least 1').optional().nullable(), }); type EditTenantFormData = z.infer; interface EditTenantModalProps { isOpen: boolean; onClose: () => void; tenantId: string | null; onLoadTenant: (id: string) => Promise; onSubmit: (id: string, data: EditTenantFormData) => Promise; isLoading?: boolean; } const statusOptions = [ { value: 'active', label: 'Active' }, { value: 'suspended', label: 'Suspended' }, { value: 'deleted', label: 'Deleted' }, ]; export const EditTenantModal = ({ isOpen, onClose, tenantId, onLoadTenant, onSubmit, isLoading = false, }: EditTenantModalProps): ReactElement | null => { const [isLoadingTenant, setIsLoadingTenant] = useState(false); const [loadError, setLoadError] = useState(null); const { register, handleSubmit, setValue, watch, reset, setError, clearErrors, formState: { errors }, } = useForm({ resolver: zodResolver(editTenantSchema), }); const statusValue = watch('status'); // Load tenant data when modal opens useEffect(() => { if (isOpen && tenantId) { const loadTenant = async (): Promise => { try { setIsLoadingTenant(true); setLoadError(null); clearErrors(); const tenant = await onLoadTenant(tenantId); reset({ name: tenant.name, slug: tenant.slug, status: tenant.status, settings: tenant.settings, subscription_tier: tenant.subscription_tier, max_users: tenant.max_users, max_modules: tenant.max_modules, }); } catch (err: any) { setLoadError(err?.response?.data?.error?.message || 'Failed to load tenant details'); } finally { setIsLoadingTenant(false); } }; loadTenant(); } else { reset({ name: '', slug: '', status: 'active', settings: null, subscription_tier: null, max_users: null, max_modules: null, }); setLoadError(null); clearErrors(); } }, [isOpen, tenantId, onLoadTenant, reset, clearErrors]); const handleFormSubmit = async (data: EditTenantFormData): Promise => { if (!tenantId) return; clearErrors(); try { await onSubmit(tenantId, data); } catch (error: any) { // Handle validation errors from API if (error?.response?.data?.details && Array.isArray(error.response.data.details)) { const validationErrors = error.response.data.details; validationErrors.forEach((detail: { path: string; message: string }) => { if ( detail.path === 'name' || detail.path === 'slug' || detail.path === 'status' || detail.path === 'settings' || detail.path === 'subscription_tier' || detail.path === 'max_users' || detail.path === 'max_modules' ) { setError(detail.path as keyof EditTenantFormData, { type: 'server', message: detail.message, }); } }); } else { // Handle general errors const errorMessage = error?.response?.data?.error || error?.response?.data?.message || error?.message || 'Failed to update tenant. Please try again.'; setError('root', { type: 'server', message: typeof errorMessage === 'string' ? errorMessage : 'Failed to update tenant. Please try again.', }); } } }; return ( Cancel {isLoading ? 'Updating...' : 'Update Tenant'} } >
{isLoadingTenant && (
)} {loadError && (

{loadError}

)} {!isLoadingTenant && (
{/* General Error Display */} {errors.root && (

{errors.root.message}

)} {/* Tenant Name */} {/* Slug */} {/* Status */} setValue('status', value as 'active' | 'suspended' | 'deleted')} error={errors.status?.message} />
)}
); };