'use client'; import { useState, useRef, useEffect, useCallback } from 'react'; import { Save, MousePointerClick, CalendarDays } from 'lucide-react'; const days = [ { key: 'mon', label: 'Lunes' }, { key: 'tue', label: 'Martes' }, { key: 'wed', label: 'Miércoles' }, { key: 'thu', label: 'Jueves' }, { key: 'fri', label: 'Viernes' }, { key: 'sat', label: 'Sábado' }, { key: 'sun', label: 'Domingo' } ]; const slots = Array.from({length: 48}).map((_, i) => { const h = Math.floor(i/2).toString().padStart(2, '0'); const m = i % 2 === 0 ? '00' : '30'; return `${h}:${m}`; }); type Selection = { day: string; startIdx: number; endIdx: number; } | null; export default function ProgrammingMatrixEditor({ initialData, tab, onSaveAction }: { initialData: Record, tab: string, onSaveAction: (data: Record) => void }) { const [matrix, setMatrix] = useState>(initialData); const [isDragging, setIsDragging] = useState(false); const [selection, setSelection] = useState(null); const [dragStart, setDragStart] = useState<{day: string, idx: number} | null>(null); const [bulkInput, setBulkInput] = useState(''); const formRef = useRef(null); // Calc rowSpans to cleanly render unified cells visually const getSpans = useCallback(() => { const spans: Record = {}; days.forEach(d => { const list = slots.map(time => matrix[`${time}_${d.key}`] || ''); const spanList = new Array(48).fill(1); for(let i=0; i<48; i++){ if (spanList[i] === 0) continue; let count = 1; const val = list[i]; while(i + count < 48 && list[i + count] === val && val !== '') { spanList[i + count] = 0; count++; } spanList[i] = count; } spans[d.key] = spanList; }); return spans; }, [matrix]); const spans = getSpans(); const handleMouseDown = (day: string, idx: number) => { setDragStart({ day, idx }); setIsDragging(true); setSelection({ day, startIdx: idx, endIdx: idx }); setBulkInput(matrix[`${slots[idx]}_${day}`] || ''); }; const handleMouseEnter = (day: string, idx: number) => { if (isDragging && dragStart && dragStart.day === day) { setSelection({ day, startIdx: Math.min(dragStart.idx, idx), endIdx: Math.max(dragStart.idx, idx) }); } }; const handleMouseUp = () => { setIsDragging(false); }; useEffect(() => { window.addEventListener('mouseup', handleMouseUp); return () => window.removeEventListener('mouseup', handleMouseUp); }, []); const handleApplyBulk = () => { if (!selection) return; const newMatrix = { ...matrix }; for (let i = selection.startIdx; i <= selection.endIdx; i++) { newMatrix[`${slots[i]}_${selection.day}`] = bulkInput; } setMatrix(newMatrix); setSelection(null); // Close modal }; const handleClearBulk = () => { if (!selection) return; const newMatrix = { ...matrix }; for (let i = selection.startIdx; i <= selection.endIdx; i++) { newMatrix[`${slots[i]}_${selection.day}`] = ''; } setMatrix(newMatrix); setSelection(null); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSaveAction(matrix); }; return (
{/* Context Tooltip / Modal para la Selección */} {selection && !isDragging && (

Editando {slots[selection.startIdx]} a {slots[selection.endIdx]} ({days.find(d=>d.key===selection.day)?.label})

setBulkInput(e.target.value)} onKeyDown={e => { if(e.key === 'Enter') { e.preventDefault(); handleApplyBulk(); } }} placeholder="Nombre del Programa..." className="bg-slate-900 border border-slate-700 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-cyan-400" />
)}
Consejo: Haz clic y arrastra hacia abajo para unificar intervalos de 30 mins.
{days.map(d => ( ))} {slots.map((time, idx) => { const isHalfHour = time.endsWith('30'); return ( {days.map(d => { const isSelected = selection && selection.day === d.key && idx >= selection.startIdx && idx <= selection.endIdx; // Visual Unification logic during non-edit const span = spans[d.key][idx]; const isMergedChild = span === 0; const cellVal = matrix[`${time}_${d.key}`]; const hasProgram = !!cellVal; // Only apply unification hiding if NOT actively selecting this day const hideForMerge = isMergedChild && (!selection || selection.day !== d.key); if (hideForMerge) return null; // Force span to 1 when dragging this column to allow seeing individual cells const effectiveSpan = (selection && selection.day === d.key) ? 1 : span; return ( ) })} ) })}
Hora {d.label}
{time} handleMouseDown(d.key, idx)} onMouseEnter={() => handleMouseEnter(d.key, idx)} className={` border-r border-slate-800/50 relative cursor-crosshair transition-colors duration-75 ${isHalfHour && effectiveSpan === 1 ? 'border-b border-dashed border-slate-800/30' : 'border-b border-solid'} ${isSelected ? 'bg-cyan-900/60 shadow-[inset_0_0_10px_rgba(6,182,212,0.5)]' : 'hover:bg-slate-800/60'} ${hasProgram && !isSelected ? 'bg-slate-800/40 text-cyan-50' : ''} `} style={{ height: effectiveSpan > 1 ? `${effectiveSpan * 32}px` : '32px' }} >
{cellVal || ''}
{effectiveSpan > 1 && (
{effectiveSpan / 2}H
)}
); }