import { useEffect, useState } from 'react' import { Plus, GripVertical, ChevronDown, ChevronRight, Edit3, Trash2, Clock, Save, } from 'lucide-react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { cn } from '@/lib/utils' //import { toast } from 'sonner'; export interface Tema { id: string nombre: string descripcion?: string horasEstimadas?: number } export interface UnidadTematica { id: string nombre: string numero: number temas: Tema[] } const initialData: UnidadTematica[] = [ { id: 'u1', numero: 1, nombre: 'Fundamentos de Inteligencia Artificial', temas: [ { id: 't1', nombre: 'Tipos de IA y aplicaciones', horasEstimadas: 6 }, { id: 't2', nombre: 'Ética en IA', horasEstimadas: 3 }, ], }, ] // Estructura que viene de tu JSON/API interface ContenidoApi { unidad: number titulo: string temas: string[] | any[] // Acepta strings o objetos [key: string]: any // Esta línea permite que haya más claves desconocidas } // Props del componente interface ContenidoTematicoProps { data: { contenido_tematico: ContenidoApi[] } isLoading: boolean } export function ContenidoTematico({ data, isLoading }: ContenidoTematicoProps) { const [unidades, setUnidades] = useState([]) const [expandedUnits, setExpandedUnits] = useState>( new Set(['u1']), ) const [deleteDialog, setDeleteDialog] = useState<{ type: 'unidad' | 'tema' id: string parentId?: string } | null>(null) const [editingUnit, setEditingUnit] = useState(null) const [editingTema, setEditingTema] = useState<{ unitId: string temaId: string } | null>(null) const [isSaving, setIsSaving] = useState(false) useEffect(() => { if (data?.contenido_tematico) { const transformed = data.contenido_tematico.map( (u: any, idx: number) => ({ id: `u-${idx}`, numero: u.unidad || idx + 1, nombre: u.titulo || 'Sin título', temas: Array.isArray(u.temas) ? u.temas.map((t: any, tidx: number) => ({ id: `t-${idx}-${tidx}`, nombre: typeof t === 'string' ? t : t.nombre || 'Tema', horasEstimadas: t.horasEstimadas || 0, })) : [], }), ) setUnidades(transformed) // Expandir la primera unidad automáticamente if (transformed.length > 0) { setExpandedUnits(new Set([transformed[0].id])) } } }, [data]) if (isLoading) return
Cargando contenido...
// 3. Cálculo de horas (ahora dinámico basado en los nuevos datos) const totalHoras = unidades.reduce( (acc, u) => acc + u.temas.reduce((sum, t) => sum + (t.horasEstimadas || 0), 0), 0, ) // --- Lógica de Unidades --- const toggleUnit = (id: string) => { const newExpanded = new Set(expandedUnits) newExpanded.has(id) ? newExpanded.delete(id) : newExpanded.add(id) setExpandedUnits(newExpanded) } const addUnidad = () => { const newId = `u-${Date.now()}` const newUnidad: UnidadTematica = { id: newId, nombre: 'Nueva Unidad', numero: unidades.length + 1, temas: [], } setUnidades([...unidades, newUnidad]) setExpandedUnits(new Set([...expandedUnits, newId])) setEditingUnit(newId) } const updateUnidadNombre = (id: string, nombre: string) => { setUnidades(unidades.map((u) => (u.id === id ? { ...u, nombre } : u))) } // --- Lógica de Temas --- const addTema = (unidadId: string) => { setUnidades( unidades.map((u) => { if (u.id === unidadId) { const newTemaId = `t-${Date.now()}` const newTema: Tema = { id: newTemaId, nombre: 'Nuevo tema', horasEstimadas: 2, } setEditingTema({ unitId: unidadId, temaId: newTemaId }) return { ...u, temas: [...u.temas, newTema] } } return u }), ) } const updateTema = ( unidadId: string, temaId: string, updates: Partial, ) => { setUnidades( unidades.map((u) => { if (u.id === unidadId) { return { ...u, temas: u.temas.map((t) => t.id === temaId ? { ...t, ...updates } : t, ), } } return u }), ) } const handleDelete = () => { if (!deleteDialog) return if (deleteDialog.type === 'unidad') { setUnidades( unidades .filter((u) => u.id !== deleteDialog.id) .map((u, i) => ({ ...u, numero: i + 1 })), ) } else if (deleteDialog.parentId) { setUnidades( unidades.map((u) => u.id === deleteDialog.parentId ? { ...u, temas: u.temas.filter((t) => t.id !== deleteDialog.id) } : u, ), ) } setDeleteDialog(null) //toast.success("Eliminado correctamente"); } return (

Contenido Temático

{unidades.length} unidades • {totalHoras} horas estimadas totales

{unidades.map((unidad) => ( toggleUnit(unidad.id)} >
Unidad {unidad.numero} {editingUnit === unidad.id ? ( updateUnidadNombre(unidad.id, e.target.value) } onBlur={() => setEditingUnit(null)} onKeyDown={(e) => e.key === 'Enter' && setEditingUnit(null) } className="h-8 max-w-md bg-white" autoFocus /> ) : ( setEditingUnit(unidad.id)} > {unidad.nombre} )}
{' '} {unidad.temas.reduce( (sum, t) => sum + (t.horasEstimadas || 0), 0, )} h
{unidad.temas.map((tema, idx) => ( setEditingTema({ unitId: unidad.id, temaId: tema.id }) } onStopEditing={() => setEditingTema(null)} onUpdate={(updates) => updateTema(unidad.id, tema.id, updates) } onDelete={() => setDeleteDialog({ type: 'tema', id: tema.id, parentId: unidad.id, }) } /> ))}
))}
) } // --- Componentes Auxiliares --- interface TemaRowProps { tema: Tema index: number isEditing: boolean onEdit: () => void onStopEditing: () => void onUpdate: (updates: Partial) => void onDelete: () => void } function TemaRow({ tema, index, isEditing, onEdit, onStopEditing, onUpdate, onDelete, }: TemaRowProps) { return (
{index}. {isEditing ? (
onUpdate({ nombre: e.target.value })} className="h-8 flex-1 bg-white" placeholder="Nombre" autoFocus /> onUpdate({ horasEstimadas: parseInt(e.target.value) || 0 }) } className="h-8 w-16 bg-white" />
) : ( <>

{tema.nombre}

{tema.horasEstimadas}h
)}
) } interface DeleteDialogState { type: 'unidad' | 'tema' id: string parentId?: string } interface DeleteConfirmDialogProps { dialog: DeleteDialogState | null setDialog: (value: DeleteDialogState | null) => void onConfirm: () => void } function DeleteConfirmDialog({ dialog, setDialog, onConfirm, }: DeleteConfirmDialogProps) { return ( setDialog(null)}> ¿Confirmar eliminación? Estás a punto de borrar un {dialog?.type}. Esta acción no se puede deshacer. Cancelar Eliminar ) }