'use client'; import React, { useState, useEffect, useMemo, useCallback, memo } from 'react'; import { useFormContext, useWatch, Control } from 'react-hook-form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Switch } from '@/components/ui/switch'; import { MainImage } from '../components/MainImage'; import { Combobox as OriginalCombobox, type ComboboxOption } from '@/components/ui/combobox'; import { useToast } from '@/hooks/use-toast'; import slugify from 'slugify'; import { type PlanFormValues } from '@/schemas/plan'; import { FormField, FormItem, FormControl, FormMessage, FormLabel } from '@/components/ui/form'; // ===================================================================================== // SECCIÓN UNIFICADA, SIN PASOS // ===================================================================================== const URLPreview = memo(({ categoryAlias, destinationSlug, articleAlias }: { categoryAlias: string; destinationSlug: string; articleAlias: string;}) => (
tudominio.com /{categoryAlias||'categoria'} /{destinationSlug||'destino'} /{articleAlias||'mi-plan'}
)); URLPreview.displayName = 'URLPreview'; export function BasicInfoSection() { const { control, setValue, watch } = useFormContext(); const { toast } = useToast(); const [destinations, setDestinations] = useState([]); const [isLoadingDest, setIsLoadingDest] = useState(true); const [categoryOptions, setCategoryOptions] = useState([]); const [planSlugOptions, setPlanSlugOptions] = useState([]); const mainTitle = watch('mainTitle'); const destinationId = watch('destinationId'); const articleAlias = watch('articleAlias'); const destinationSlug = useMemo(() => { const selected = destinations.find(d => d.value === destinationId); return selected ? slugify(selected.label, { lower: true, strict: true }) : ''; }, [destinations, destinationId]); const generateSmartSlug = (text: string, count: number = 7): string[] => { if (!text) return []; const [title] = text.split(/[|]/).map(part => part.trim()); const stopWords = new Set(['plan', 'tour', 'viaje', 'paquete', 'en', 'de', 'del', 'la', 'los', 'y', 'a', 'o']); const titleParts = title.replace(/^circuito\s+/i, '').split(/,|\sy\s/i).map(p => p.trim()).filter(Boolean); const dests = titleParts.map(p => (p.match(/\b[A-ZÀ-Ú][a-zà-ú']+\b/g) || []).map(d => d.toLowerCase()).filter(d => !stopWords.has(d))[0]).filter(Boolean).slice(0, 3) as string[]; const durationMatch = text.match(/(\d+)\s*d[ií]as?/i); const duration = durationMatch ? `${durationMatch[1]}dias` : ''; const suggestions = new Set(); if (dests.length > 0) { const base = dests.join('-'); suggestions.add(base); if (duration) suggestions.add(`${base}-${duration}`); } return Array.from(suggestions).map(s => slugify(s, { lower: true, strict: true })).filter(Boolean).slice(0, count); }; useEffect(() => { if (mainTitle) { const suggestions = generateSmartSlug(mainTitle, 7); setPlanSlugOptions(suggestions.map(s => ({ label: s, value: s }))); if (suggestions.length > 0 && !watch('articleAlias')) { setValue('articleAlias', suggestions[0]); } } else { setPlanSlugOptions([]); } }, [mainTitle, setValue, watch]); useEffect(() => { const fetchDests = async () => { try { const res = await fetch('/api/destinations'); const data = await res.json(); setDestinations(data.map((d: any) => ({ label: d.name, value: d.id }))); } catch (e) { toast({ variant: 'destructive', title: 'Error', description: 'No se pudieron cargar los destinos.' }); } finally { setIsLoadingDest(false); } }; fetchDests(); }, [toast]); // Cargar categorías existentes desde la base de datos useEffect(() => { const fetchCategories = async () => { try { const res = await fetch('/api/categories'); const data = await res.json(); setCategoryOptions(data); } catch (e) { console.error('Error loading categories:', e); // No mostrar toast para categorías ya que no es crítico } }; fetchCategories(); }, []); const handleCreateOption = (field: keyof PlanFormValues, optionsState: any, setOptionsState: any) => useCallback((inputValue: string) => { const newValue = slugify(inputValue, { lower: true, strict: true }); if (!optionsState.some((o: ComboboxOption) => o.value === newValue)) { const newOption = { label: inputValue, value: newValue }; setOptionsState((prev: ComboboxOption[]) => [newOption, ...prev]); setValue(field, newValue); toast({ title: 'Opción Añadida', description: `Se usará "${inputValue}".` }); } }, [optionsState, setOptionsState, setValue, toast]); const handleCreateCategory = handleCreateOption('categoryAlias', categoryOptions, setCategoryOptions); const handleCreatePlanSlug = handleCreateOption('articleAlias', planSlugOptions, setPlanSlugOptions); // Función especial para crear destinos (requiere API call) const handleCreateDestination = useCallback(async (inputValue: string) => { try { const trimmedValue = inputValue.trim(); // Verificar si ya existe por nombre if (destinations.some(d => d.label.toLowerCase() === trimmedValue.toLowerCase())) { toast({ title: 'Destino ya existe', description: `"${trimmedValue}" ya está en la lista.`, variant: 'destructive' }); return; } // Crear destino en la base de datos const response = await fetch('/api/destinations', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: trimmedValue }) }); if (!response.ok) { throw new Error('Error al crear destino'); } const newDestination = await response.json(); const newOption = { label: newDestination.name, value: newDestination.id }; // Actualizar la lista local y seleccionar el nuevo destino setDestinations(prev => [newOption, ...prev]); setValue('destinationId', newDestination.id); toast({ title: 'Destino Creado', description: `"${trimmedValue}" ha sido añadido exitosamente.` }); } catch (error) { console.error('Error creating destination:', error); toast({ title: 'Error', description: 'No se pudo crear el destino. Inténtalo de nuevo.', variant: 'destructive' }); } }, [destinations, setDestinations, setValue, toast]); return (
{/* Sección de Imagen */}

Imagen de Portada

Esta será la primera impresión de tu plan. Elige una imagen horizontal y de alta calidad.

()} />
{/* Sección de Destino y Transporte */}

Detalles del Destino

Selecciona el destino principal de tu base de datos y especifica sus características.

( Destino Principal )} />
¿Es un plan terrestre?

Actívalo si el principal medio de transporte para llegar es por tierra.

} />
{/* Sección de Título y URLs */}

Título y Dirección Web

El nombre de tu plan y cómo se verá en la barra de direcciones del navegador. Clave para el SEO.

( Título Principal del Plan )} />

Nuestras sugerencias inteligentes para la URL se basan en el título que escribas.

( Categoría )} /> Destino ( Slug del Plan )} />
); }