'use client'; /** * NOVA CMS - SISTEMA DE TEMPLATES * ================================ * * Este componente es parte del SISTEMA DE TEMPLATES de Nova CMS. * Los templates son formularios predefinidos para tipos específicos de contenido. * * ARQUITECTURA DUAL: * 1. TEMPLATES (este archivo) - Formularios predefinidos como "Plan Turístico" * 2. HEADLESS CMS - Sistema flexible para crear tipos de contenido personalizados * * Este EditPlanForm es un TEMPLATE específico para planes turísticos que: * - Tiene secciones predefinidas (BasicInfo, Itinerary, Pricing, etc.) * - Usa el schema planSchema de Zod para validación * - Se guarda en las tablas específicas de planes (no en ContentEntry) * - Forma parte del sistema tradicional de gestión de planes */ import { useRouter } from 'next/navigation'; import { useToast } from '@/hooks/use-toast'; import { useTransition, useEffect, useCallback, useRef } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { planSchema, type PlanFormValues } from '@/schemas/plan'; // AdminLayout se maneja en la página padre import { BasicInfoSection } from './sections/BasicInfoSection'; import { IncludesSection } from './sections/IncludesSection'; import { ItinerarySection } from './sections/ItinerarySection'; import { PricingSection } from './sections/PricingSection'; import { VideoSection } from './sections/VideoSection'; import { updatePlanAction } from '@/app/actions/plan-actions'; import { useFormPersistence } from '@/hooks/use-form-persistence'; import { Button } from '@/components/ui/button'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { ChevronsUpDown, Info, ListOrdered, Check, DollarSign, Video, ArrowLeft } from 'lucide-react'; import { cn } from '@/lib/utils'; import { AutosaveStatus } from '@/components/cms/AutosaveStatus'; interface EditPlanFormProps { initialData: PlanFormValues; planId: string; } const FORM_STORAGE_KEY_PREFIX = 'planForm-edit-'; // Componente para una sección plegable con diseño Notion const SectionWrapper = ({ icon, title, description, children, defaultOpen = false }: { icon: React.ReactNode, title: string, description: string, children: React.ReactNode, defaultOpen?: boolean }) => (
{icon}

{title}

{description}

{children}
); export function EditPlanForm({ initialData, planId }: EditPlanFormProps) { const router = useRouter(); const { toast } = useToast(); const [isSubmitting, startTransition] = useTransition(); const form = useForm({ resolver: zodResolver(planSchema), defaultValues: initialData, mode: 'onChange', }); const { formState: { isDirty } } = form; const onSaveRef = useRef<((values: PlanFormValues) => Promise) | null>(null); const saveImplementation = useCallback(async (data: PlanFormValues) => { startTransition(async () => { const formData = new FormData(); Object.entries(data).forEach(([key, value]) => { if (value !== null && value !== undefined) { formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value)); } }); formData.append('planId', planId); const result = await updatePlanAction(null, formData); if (result.success) { form.clearErrors(); } else { if (result.error) { toast({ variant: "destructive", title: "Error de Guardado", description: result.error || "No se pudieron guardar los cambios." }); } else { toast({ variant: "destructive", title: "Error de Guardado", description: "No se pudieron guardar los cambios." }); } } }); }, [planId, toast, form, startTransition]); useEffect(() => { onSaveRef.current = saveImplementation; }, [saveImplementation]); useFormPersistence(form, `${FORM_STORAGE_KEY_PREFIX}${planId}`, initialData, undefined, { mode: 'edit', onSave: (values) => { if (onSaveRef.current) return onSaveRef.current(values); return Promise.resolve(); }, }); const handleFinalSubmit = async (data: PlanFormValues) => { await saveImplementation(data); toast({ title: "Plan Guardado", description: "Todos los cambios han sido guardados." }); router.push('/admin/dashboard/view-content'); }; const handleGoBack = () => { router.push('/admin/dashboard/templates/tourism'); }; return (
{/* Clean editorial background */}
{/* Header */}

Editando Plan Turístico

{/* Main Content */}
} title="1. Información Básica" description="Título, destino, URLs y la imagen principal de tu plan." defaultOpen={true} > } title="2. Incluye / No Incluye" description="Especifica qué servicios y productos están cubiertos por el precio." > } title="3. Itinerario Detallado" description="Organiza los días, las actividades y las imágenes de cada jornada." > } title="4. Precios y Opciones" description="Define los precios para diferentes cantidades de personas y monedas." > } title="5. Video Promocional" description="Añade un video de YouTube para hacerlo más atractivo." >
{/* Footer */}
); }