import { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { useUpdateOperationalPage } from '@/hooks/useOperationalPage'; import { useCreateStatusPageComponent, useStatusPageComponentsByOperationalId, useDeleteStatusPageComponent } from '@/hooks/useStatusPageComponents'; import { ComponentsSelector } from './ComponentsSelector'; import { OperationalPageRecord } from '@/types/operational.types'; import { StatusPageComponentRecord } from '@/types/statusPageComponents.types'; const formSchema = z.object({ title: z.string().min(1, 'Title is required'), description: z.string().min(1, 'Description is required'), slug: z.string().min(1, 'Slug is required'), theme: z.string().min(1, 'Theme is required'), status: z.enum(['operational', 'degraded', 'maintenance', 'major_outage']), is_public: z.boolean(), logo_url: z.string().optional(), custom_domain: z.string().optional(), custom_css: z.string().optional(), page_style: z.string().optional(), }); type FormData = z.infer; interface EditOperationalPageDialogProps { page: OperationalPageRecord | null; open: boolean; onOpenChange: (open: boolean) => void; } export const EditOperationalPageDialog = ({ page, open, onOpenChange }: EditOperationalPageDialogProps) => { const [selectedComponents, setSelectedComponents] = useState[]>([]); const [existingComponents, setExistingComponents] = useState([]); const [isFormSubmitting, setIsFormSubmitting] = useState(false); const updateMutation = useUpdateOperationalPage(); const createComponentMutation = useCreateStatusPageComponent(); const deleteComponentMutation = useDeleteStatusPageComponent(); // Fetch existing components for this operational page const { data: components = [] } = useStatusPageComponentsByOperationalId(page?.id || ''); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { title: '', description: '', slug: '', theme: 'default', status: 'operational', is_public: true, logo_url: '', custom_domain: '', custom_css: '', page_style: '', }, }); useEffect(() => { if (page) { form.reset({ title: page.title, description: page.description, slug: page.slug, theme: page.theme, status: page.status, is_public: page.is_public === 'true', logo_url: page.logo_url || '', custom_domain: page.custom_domain || '', custom_css: page.custom_css || '', page_style: page.page_style || '', }); } }, [page, form]); useEffect(() => { if (components && components.length > 0) { console.log('Loading existing components:', components); setExistingComponents(components); // Convert existing components to the format expected by ComponentsSelector const existingComponentsForSelector = components.map(comp => ({ id: comp.id, name: comp.name, description: comp.description, service_id: comp.service_id, server_id: comp.server_id, display_order: comp.display_order, operational_status_id: comp.operational_status_id, })); setSelectedComponents(existingComponentsForSelector); } else { setExistingComponents([]); setSelectedComponents([]); } }, [components]); const handleComponentDelete = async (componentId: string) => { try { console.log('Deleting component:', componentId); await deleteComponentMutation.mutateAsync(componentId); // Update local state to remove the deleted component setSelectedComponents(prev => prev.filter(comp => comp.id !== componentId)); setExistingComponents(prev => prev.filter(comp => comp.id !== componentId)); } catch (error) { console.error('Error deleting component:', error); } }; const onSubmit = async (data: FormData) => { if (!page) return; try { setIsFormSubmitting(true); const payload = { title: data.title, description: data.description, slug: data.slug, theme: data.theme, status: data.status, is_public: data.is_public ? 'true' : 'false', logo_url: data.logo_url || '', custom_domain: data.custom_domain || '', custom_css: data.custom_css || '', page_style: data.page_style || '', }; console.log('Updating operational page with payload:', payload); await updateMutation.mutateAsync({ id: page.id, data: payload }); // Handle component changes const currentComponentIds = existingComponents.map(c => c.id); const newComponentsToCreate = selectedComponents.filter(comp => !comp.id); const componentsToKeep = selectedComponents.filter(comp => comp.id && currentComponentIds.includes(comp.id)); const componentsToDelete = existingComponents.filter(comp => !selectedComponents.some(selected => selected.id === comp.id)); // Delete removed components (only if not already deleted via handleComponentDelete) for (const component of componentsToDelete) { if (selectedComponents.some(selected => selected.id === component.id)) { continue; // Skip if already handled by handleComponentDelete } console.log('Deleting component during save:', component.id); await deleteComponentMutation.mutateAsync(component.id); } // Create new components for (const component of newComponentsToCreate) { const componentPayload = { operational_status_id: page.id, name: component.name || '', description: component.description || '', service_id: component.service_id || '', server_id: component.server_id || '', display_order: component.display_order || 1, }; console.log('Creating component with payload:', componentPayload); await createComponentMutation.mutateAsync(componentPayload); } onOpenChange(false); } catch (error) { console.error('Error updating operational page:', error); } finally { setIsFormSubmitting(false); } }; return ( Edit Operational Page Update your operational status page settings and manage components.
( Title )} /> ( Slug )} />
( Description