import React, { useState, useEffect } from 'react';
import { Calendar, Clock, MapPin, Users, CheckSquare, Printer, Save, Upload, ChevronLeft, Music, Star, ListMusic, Trash2, Mail, Copy, X, FileText, Plus, Filter, Paperclip, Edit, CheckCircle, FileSpreadsheet } from 'lucide-react';
// --- UTILIDAD PARA FECHAS SEGURAS (CORRECCIÓN DE ZONA HORARIA) ---
// Esta función evita que las fechas retrocedan un día al visualizarse en México/Latam
const safeDate = (dateString) => {
if (!dateString) return new Date();
// Si la fecha ya viene con hora (T...), la respetamos.
// Si es solo YYYY-MM-DD, le agregamos T12:00:00 para fijarla al mediodía y evitar el desfase de zona horaria.
if (dateString.includes('T')) return new Date(dateString);
return new Date(dateString + 'T12:00:00');
};
// --- CONFIGURACIÓN DE DATOS MAESTROS ---
const MUSIC_ACADEMIES = [
{ name: 'Guitarra Eléctrica', teacher: 'Carlos Alcalá' },
{ name: 'Guitarra Clásica', teacher: 'Carlos Alcalá' },
{ name: 'Guitarra Clásica', teacher: 'Eduardo Barbosa' },
{ name: 'Piano', teacher: 'Paula Rodríguez' },
{ name: 'Piano', teacher: 'Natasha Cruz' },
{ name: 'Piano', teacher: 'Victor García' },
{ name: 'Batería', teacher: 'Jorge Arriaga' },
{ name: 'Batería', teacher: 'Fernanda Chavez' },
{ name: 'Violín', teacher: 'Roberto Tueme' },
{ name: 'Violín', teacher: 'Bibiana Hernandez' },
{ name: 'Cello', teacher: 'José Parra' },
{ name: 'Canto', teacher: 'Thamar Hernandez' },
{ name: 'Canto', teacher: 'Cintli Cruz' }
];
const INSTRUMENT_FAMILIES = {
'Guitarras': ['Guitarra'],
'Pianos': ['Piano'],
'Cuerdas Frotadas': ['Violín', 'Cello'],
'Percusiones': ['Batería'],
'Voces': ['Canto']
};
const DANCE_ACADEMIES = ['Ballet', 'Tap', 'Jazz'];
const ENSEMBLE_TYPES = ['Solista', 'Dueto', 'Trío', 'Cuarteto', 'Grupo Pequeño', 'Grupo Completo'];
const GRADES = ['Baby Steps', 'Little Steps', 'K1', 'K2', 'K3', '1° Primaria', '2° Primaria', '3° Primaria', '4° Primaria', '5° Primaria', '6° Primaria'];
const SECTIONS = ['A', 'B', 'C', 'D'];
const CHECKLIST_CATEGORIES = [
'General & Administrativo',
'Producción Técnica (Audio/Luces/Backline)',
'Contenido Multimedia (Audio/Video)',
'Vestuario & Imagen',
'Cine & Guiones',
'Utilería & Escenografía',
'Logística & Transporte'
];
// --- DATOS INICIALES (FECHAS CORREGIDAS DEFINITIVAS 2026) ---
const INITIAL_EVENTS = [
{
id: 'evt-001',
title: 'SMART Week - Recitales Instrumentales',
date: '2026-05-18', // Lunes 18 de Mayo
endDate: '2026-05-21', // Hasta Jueves 21 de Mayo
type: 'recital',
categoryMode: 'music_academy',
description: 'Semana de recitales por academia (Guitarra, Piano, Violín, Canto, etc). Lunes a Jueves.',
observations: '',
checklist: [
{ id: 'ck-1', title: 'Avisos a Padres', category: 'General & Administrativo', status: false, details: '', file: null, subItems: [] },
{ id: 'ck-2', title: 'Programas de Mano', category: 'General & Administrativo', status: false, details: 'Desglosar por maestro', file: null, subItems: [] },
{ id: 'ck-3', title: 'Lista Oficial de Alumnos', category: 'General & Administrativo', status: false, details: 'Actualizar con bajas y altas', file: null, subItems: [] },
{ id: 'ck-4', title: 'Afinación de Pianos', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Contactar técnico externo', file: null, subItems: [] },
{ id: 'ck-5', title: 'Backline (Amplis/Batería)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Revisar cables y parches', file: null, subItems: [] },
{ id: 'ck-6', title: 'Guion Técnico (Entradas/Salidas)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Formato de flujo de alumnos', file: null, subItems: [] },
{ id: 'ck-7', title: 'Maestro de Ceremonias', category: 'General & Administrativo', status: false, details: 'Definir presentadores', file: null, subItems: [] }
],
program: []
},
{
id: 'evt-002',
title: 'Recital de Baile & Smart Rock (Cierre)',
date: '2026-05-22', // Viernes 22 de Mayo
type: 'concert',
categoryMode: 'dance_academy',
description: 'Cierre de Smart Week: Alumnos avanzados de instrumentos (Rock) y Academia de Baile.',
observations: '',
checklist: [
{ id: 'ck-8', title: 'Diseño de Vestuarios', category: 'Vestuario & Imagen', status: false, details: 'Definir paleta de colores', file: null, subItems: [] },
{ id: 'ck-9', title: 'Medidas de Alumnos', category: 'Vestuario & Imagen', status: false, details: 'Tabla de tallas', file: null, subItems: [] },
{ id: 'ck-10', title: 'Edición de Canciones', category: 'Contenido Multimedia (Audio/Video)', status: false, details: 'Cortes para coreografías', file: null, subItems: [] },
{ id: 'ck-11', title: 'Edición de Videos de Fondo', category: 'Contenido Multimedia (Audio/Video)', status: false, details: 'Visuales pantalla LED', file: null, subItems: [] },
{ id: 'ck-12', title: 'Renta de Audio e Iluminación', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Cotizar proveedor', file: null, subItems: [] },
{ id: 'ck-13', title: 'Guion Técnico (Entradas/Salidas)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Vital para cambios rápidos', file: null, subItems: [] },
{ id: 'ck-14', title: 'Lista Oficial de Alumnos', category: 'General & Administrativo', status: false, details: '', file: null, subItems: [] },
{ id: 'ck-15', title: 'Venta de Boletos', category: 'Logística & Transporte', status: false, details: 'Asignación de asientos', file: null, subItems: [] }
],
program: []
},
{
id: 'evt-003',
title: 'Largometraje en Cines',
date: '2026-05-28', // Jueves 28 de Mayo
type: 'cinema',
categoryMode: 'grade',
description: 'Proyección del largometraje escolar en sala de cine.',
observations: '',
checklist: [
{ id: 'ck-16', title: 'Guion Literario', category: 'Cine & Guiones', status: false, details: 'Subir PDF final', file: null, subItems: [] },
{ id: 'ck-17', title: 'Plan de Rodaje', category: 'Cine & Guiones', status: false, details: 'Calendario de filmación', file: null, subItems: [] },
{ id: 'ck-18', title: 'Lista de Escenas', category: 'Cine & Guiones', status: false, details: 'Breakdown por locación', file: null, subItems: [] },
{ id: 'ck-19', title: 'Edición de Video (Película)', category: 'Contenido Multimedia (Audio/Video)', status: false, details: 'Post-producción', file: null, subItems: [] },
{ id: 'ck-20', title: 'Venta de Boletos (Cine)', category: 'Logística & Transporte', status: false, details: 'Coordinar con sala de cine', file: null, subItems: [] },
{ id: 'ck-21', title: 'Lista Oficial de Alumnos', category: 'General & Administrativo', status: false, details: 'Para créditos', file: null, subItems: [] },
{ id: 'ck-22', title: 'Guion Técnico (Protocolo)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Alfombra roja', file: null, subItems: [] }
],
program: []
},
{
id: 'evt-004',
title: 'Festival Parents Blast',
date: '2026-06-11', // Jueves 11 de Junio
type: 'festival',
categoryMode: 'grade',
description: 'Festival principal (Baby Steps a Primaria).',
observations: '',
checklist: [
{ id: 'ck-23', title: 'Renta de Teatro', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Anticipo y contrato', file: null, subItems: [] },
{ id: 'ck-24', title: 'Edición de Canciones (Tracks)', category: 'Contenido Multimedia (Audio/Video)', status: false, details: 'Pistas para grados', file: null, subItems: [] },
{ id: 'ck-25', title: 'Edición de Videos (Proyecciones)', category: 'Contenido Multimedia (Audio/Video)', status: false, details: 'Loops de fondo', file: null, subItems: [] },
{ id: 'ck-26', title: 'Diseño de Vestuarios', category: 'Vestuario & Imagen', status: false, details: 'Temática por grado', file: null, subItems: [] },
{ id: 'ck-27', title: 'Medidas de Alumnos', category: 'Vestuario & Imagen', status: false, details: 'Toma general', file: null, subItems: [] },
{ id: 'ck-28', title: 'Guion Técnico (Entradas/Salidas)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Formato maestro', file: null, subItems: [] },
{ id: 'ck-29', title: 'Utilería General', category: 'Utilería & Escenografía', status: false, details: 'Props por grado', file: null, subItems: [] },
{ id: 'ck-30', title: 'Transporte de Alumnos', category: 'Logística & Transporte', status: false, details: 'Autobuses', file: null, subItems: [] },
{ id: 'ck-31', title: 'Venta de Boletos', category: 'Logística & Transporte', status: false, details: 'Taquilla', file: null, subItems: [] },
{ id: 'ck-32', title: 'Lista Oficial de Alumnos', category: 'General & Administrativo', status: false, details: 'Listas de cotejo', file: null, subItems: [] }
],
program: []
},
{
id: 'evt-005',
title: 'Graduaciones',
date: '2026-06-25', // Jueves 25 de Junio
type: 'graduation',
categoryMode: 'grade',
description: 'Ceremonia de fin de cursos.',
observations: '',
checklist: [
{ id: 'ck-33', title: 'Diplomas y Reconocimientos', category: 'General & Administrativo', status: false, details: 'Revisar ortografía de nombres', file: null, subItems: [] },
{ id: 'ck-34', title: 'Lista Oficial de Graduados', category: 'General & Administrativo', status: false, details: '', file: null, subItems: [] },
{ id: 'ck-35', title: 'Maestro de Ceremonias', category: 'General & Administrativo', status: false, details: 'Guion solemne', file: null, subItems: [] },
{ id: 'ck-36', title: 'Guion Técnico (Protocolo)', category: 'Producción Técnica (Audio/Luces/Backline)', status: false, details: 'Orden de paso', file: null, subItems: [] },
{ id: 'ck-37', title: 'Montaje de Escenario', category: 'Utilería & Escenografía', status: false, details: 'Mesa de honor, pódium', file: null, subItems: [] }
],
program: []
}
];
// --- COMPONENTES AUXILIARES ---
const Card = ({ children, className = "" }) => (
{children}
);
const Badge = ({ type }) => {
const styles = {
recital: 'bg-purple-100 text-purple-700',
concert: 'bg-pink-100 text-pink-700',
cinema: 'bg-blue-100 text-blue-700',
festival: 'bg-amber-100 text-amber-700',
graduation: 'bg-emerald-100 text-emerald-700',
default: 'bg-slate-100 text-slate-700'
};
return (
{type}
);
};
// --- MODAL DE PROPUESTA SMART WEEK (ACTUALIZADO 2026 - COMPACTO EDITABLE) ---
const SmartWeekProposalModal = ({ onClose }) => {
const handlePrint = () => {
window.print();
};
return (
{/* Header (Solo pantalla) */}
Propuesta de Horarios (Mayo 2026)
Tip: Haz clic en el texto para editar antes de imprimir
Imprimir
Cerrar
{/* CONTENIDO IMPRIMIBLE - TAMAÑO CARTA */}
{/* ENCABEZADO */}
SMART
North Hill
SMART CENTER - Eventos 2026
Propuesta SMART WEEK
{/* GRID COMPACTO DE HORARIOS (2 COLUMNAS) */}
{/* LUNES 18 */}
Lunes 18 de Mayo - Guitarras
5:00 PM Guitarra Clásica - Nivel Inicial
6:30 PM Guitarra Clásica - Nivel Intermedio
8:00 PM Guitarra Clásica & Eléctrica - Avanzado
{/* MARTES 19 */}
Martes 19 de Mayo - Pianos
5:00 PM Piano - Inicial (Baby/Little Steps)
6:30 PM Piano - Intermedio (Primaria Menor)
8:00 PM Piano - Avanzado (Primaria Mayor)
{/* MIERCOLES 20 */}
Miércoles 20 - Cuerdas
5:00 PM Violín & Cello - Nivel Inicial
6:30 PM Violín & Cello - Nivel Intermedio
8:00 PM Ensamble de Cuerdas Avanzado
{/* JUEVES 21 */}
Jueves 21 - Voces y Ritmo
5:00 PM Canto & Batería - Nivel Inicial
6:30 PM Canto & Batería - Nivel Intermedio
8:00 PM Ensamble Vocal y Percusiones Av.
{/* VIERNES 22 - FULL WIDTH */}
Viernes 22 de Mayo - GRAN CIERRE
6:00 PM Recital de Academias de Baile (Ballet, Tap, Jazz)
8:00 PM CONCIERTO SMART ROCK (Ensamble Avanzado)
{/* NOTA EDITABLE COMPACTA */}
NOTA: Esta programación es una propuesta tentativa. Los horarios y conformación de ensambles pueden variar según el avance técnico de los alumnos. Agradecemos su flexibilidad.
North Hill | SMART CENTER | {safeDate(new Date().toISOString()).getFullYear()}
);
};
// --- MODAL DE CORREO ---
const EmailDraftModal = ({ event, rehearsals, onClose }) => {
const eventRehearsals = rehearsals.filter(r => r.relatedEventId === event.id).sort((a,b) => (a.date + a.time).localeCompare(b.date + b.time));
const generateEmailText = () => {
const header = `ASUNTO: Información Importante - ${event.title}\n\n`;
const greeting = `Estimados Padres de Familia y Alumnos del Colegio SMART,\n\nEsperando que se encuentren muy bien, les hacemos llegar los detalles y la programación de ensayos para nuestro próximo evento: "${event.title}".\n\n`;
let schedule = `📅 CALENDARIO DE ENSAYOS:\n`;
if (eventRehearsals.length === 0) schedule += `(Aún no hay ensayos programados en el sistema)\n`;
else eventRehearsals.forEach(r => { schedule += `🔹 ${r.title}\n Fecha: ${safeDate(r.date).toLocaleDateString('es-MX', { weekday: 'long', day: 'numeric', month: 'long' })}\n Hora: ${r.time}\n Lugar: ${r.location}\n Maestro: ${r.teacher || 'Titular'}\n\n`; });
const notes = event.observations ? `📝 OBSERVACIONES IMPORTANTES:\n${event.observations}\n\n` : '';
const closing = `Agradecemos de antemano su apoyo con la puntualidad y asistencia, elementos clave para el éxito de nuestras presentaciones.\n\nAtentamente,\nCoordinación de Eventos SMART`;
return header + greeting + schedule + notes + closing;
};
const [text, setText] = useState(generateEmailText());
const [copied, setCopied] = useState(false);
const handleCopy = () => { navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); };
return (
Borrador de Aviso
Este texto se ha generado automáticamente con los ensayos programados. Puedes editarlo aquí antes de copiarlo.
Cancelar {copied ? : }{copied ? '¡Copiado!' : 'Copiar Texto'}
);
};
// --- MODAL DE DETALLE DE ÍTEM (ChecklistItemModal) ---
const ChecklistItemModal = ({ item, onSave, onClose, onDelete }) => {
const [formData, setFormData] = useState({ ...item, subItems: item.subItems || [] });
const [subTaskInput, setSubTaskInput] = useState('');
const [isCustomInput, setIsCustomInput] = useState(false);
const handleFileChange = (e) => { if (e.target.files[0]) setFormData({ ...formData, file: e.target.files[0].name }); };
const addSubItem = () => { if (!subTaskInput.trim()) return; const newSubItem = { id: Date.now().toString(), label: subTaskInput, completed: false }; setFormData({ ...formData, subItems: [...formData.subItems, newSubItem] }); setSubTaskInput(''); setIsCustomInput(false); };
const toggleSubItem = (id) => { setFormData({ ...formData, subItems: formData.subItems.map(sub => sub.id === id ? { ...sub, completed: !sub.completed } : sub) }); };
const deleteSubItem = (id) => { setFormData({ ...formData, subItems: formData.subItems.filter(sub => sub.id !== id) }); };
const completedSubs = formData.subItems.filter(s => s.completed).length;
const totalSubs = formData.subItems.length;
const progress = totalSubs > 0 ? Math.round((completedSubs / totalSubs) * 100) : 0;
return (
Detalles del Requerimiento
Seguimiento / Desglose {progress}% Completado {isCustomInput ? (
setSubTaskInput(e.target.value)} autoFocus />) : (
{ if (e.target.value === 'NEW') { setIsCustomInput(true); setSubTaskInput(''); } else { setSubTaskInput(e.target.value); } }}>Seleccionar Maestro / Área... {MUSIC_ACADEMIES.map((ac, idx) => ({ac.name} - {ac.teacher} ))} {DANCE_ACADEMIES.map((d, idx) => ({d} ))} + Agregar Nuevo... )}{isCustomInput && (
setIsCustomInput(false)} className="px-2 text-slate-400 hover:text-slate-600"> )}
{formData.subItems.map(sub => (
))}
Notas / Detalles
setFormData({...formData, status: e.target.checked})} className="w-5 h-5 text-indigo-600 rounded focus:ring-indigo-500" />Marcar Ítem Principal como COMPLETADO
onDelete(formData.id)} className="text-red-500 text-sm hover:underline flex items-center gap-1"> Eliminar Cancelar onSave(formData)} className="px-4 py-2 bg-indigo-600 text-white hover:bg-indigo-700 rounded-lg text-sm font-medium">Guardar Cambios
);
};
// --- COMPONENTE: MODAL DE IMPRESIÓN ---
const PrintModal = ({ mode, events, rehearsals, targetEventId, onClose }) => {
const targetEvent = mode === 'single' ? events.find(e => e.id === targetEventId) : null;
useEffect(() => { const timer = setTimeout(() => { window.print(); }, 500); return () => clearTimeout(timer); }, []);
return (
Vista Previa de Impresión window.print()} className="bg-indigo-600 text-white px-4 py-2 rounded text-sm hover:bg-indigo-700 flex items-center gap-2"> Imprimir Cerrar
{/* REPORTE GENERAL (Resumen de progreso) */}
{mode === 'general' && (
North Hill Reporte Maestro de Producción Generado el: {safeDate(new Date().toISOString()).toLocaleString()}
{events.sort((a,b) => new Date(a.date) - new Date(b.date)).map(evt => { const totalItems = Array.isArray(evt.checklist) ? evt.checklist.length : 0; const completedItems = Array.isArray(evt.checklist) ? evt.checklist.filter(i => i.status).length : 0; const progress = totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0; const evtRehearsals = rehearsals.filter(r => r.relatedEventId === evt.id).length; return (
{evt.title} {safeDate(evt.date).toLocaleDateString('es-MX', { weekday: 'long', day: 'numeric', month: 'long' })}
Pendientes Clave: {Array.isArray(evt.checklist) && evt.checklist.filter(i => !i.status).slice(0, 3).map(item => ({item.title} ))}{Array.isArray(evt.checklist) && evt.checklist.filter(i => !i.status).length === 0 && (¡Todo listo! )} Estado: Ensayos: {evtRehearsals}
); })}
)}
{/* EVENTO INDIVIDUAL */}
{mode === 'single' && targetEvent && (
North Hill {targetEvent.title} Fecha: {safeDate(targetEvent.date).toLocaleDateString('es-MX', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })}
Información {targetEvent.description}
Observaciones {targetEvent.observations || "Sin observaciones."}
Requerimientos {Array.isArray(targetEvent.checklist) && targetEvent.checklist.map((item) => ({item.title} [{item.status ? 'LISTO' : '___'}] ))} )}
{/* NUEVO: LISTA MAESTRA COMPLETA (Optimizada para impresión) */}
{mode === 'master_list' && (
North Hill
Lista Maestra de Requerimientos
SMART CENTER | Producción General 2026
{/* Layout de columnas estilo periódico: El contenido fluye automáticamente */}
{CHECKLIST_CATEGORIES.map(cat => {
const categoryItems = [];
events.forEach(ev => {
if(Array.isArray(ev.checklist)) {
ev.checklist.filter(i => i.category === cat).forEach(item => {
categoryItems.push({ ...item, eventTitle: ev.title });
});
}
});
if (categoryItems.length === 0) return null;
return (
);
})}
)}
);
};
// --- VISTA: LISTA MAESTRA (MasterChecklistView) ---
const MasterChecklistView = ({ events, onToggleStatus, onOpenItem, onPrintList }) => {
const [filterCategory, setFilterCategory] = useState('Todas');
const [filterStatus, setFilterStatus] = useState('Todos');
let allItems = [];
events.forEach(ev => { if(Array.isArray(ev.checklist)) { ev.checklist.forEach(item => { allItems.push({ ...item, eventTitle: ev.title, eventDate: ev.date, eventId: ev.id }); }); } });
if (filterCategory !== 'Todas') allItems = allItems.filter(i => i.category === filterCategory);
if (filterStatus === 'Pendientes') allItems = allItems.filter(i => !i.status); else if (filterStatus === 'Listos') allItems = allItems.filter(i => i.status);
const groupedItems = allItems.reduce((acc, item) => { if (!acc[item.category]) acc[item.category] = []; acc[item.category].push(item); return acc; }, {});
return (
Lista Maestra de Requerimientos
Visualiza el progreso de producción.
Imprimir Lista
setFilterCategory(e.target.value)}>Todas las Categorías {CHECKLIST_CATEGORIES.map(c => {c} )}
setFilterStatus(e.target.value)}>Todos los Estados Solo Pendientes Solo Completados
{Object.keys(groupedItems).sort().map(cat => (
{cat} {groupedItems[cat].length} {groupedItems[cat].map(item => (
onToggleStatus(item.eventId, item.id)} className="mt-1 w-4 h-4 text-indigo-600 rounded cursor-pointer"/>
onOpenItem(item.eventId, item)}>
{item.title}
{item.eventTitle}
))}
))}
);
};
// --- EVENT DETAIL VIEW ---
const EventDetailView = ({
event,
rehearsals,
onBack,
onUpdateObservations,
onUpdateChecklistItem,
onAddChecklistItem,
onDeleteChecklistItem,
onAddRehearsal,
onDeleteRehearsal,
onAddProgramItem,
onDeleteProgramItem,
onPrint,
onGenerateEmail
}) => {
const [activeTab, setActiveTab] = useState('checklist');
const [editingItem, setEditingItem] = useState(null);
const [programFilter, setProgramFilter] = useState('all');
const [showProposal, setShowProposal] = useState(false); // Estado para el modal de propuesta
const eventRehearsals = rehearsals.filter(r => r.relatedEventId === event.id);
const [newRehearsal, setNewRehearsal] = useState({ date: event.date, time: '10:00', groupName: '', subGroup: '', location: 'Salón de Música', teacher: '' });
const [newProgramItem, setNewProgramItem] = useState({ pieceName: '', performers: '', academy: '' });
useEffect(() => {
if (event.categoryMode === 'music_academy') {
const first = MUSIC_ACADEMIES[0];
setNewRehearsal(prev => ({...prev, groupName: `${first.name} - ${first.teacher}`, teacher: first.teacher, subGroup: '' }));
setNewProgramItem(prev => ({...prev, academy: `${first.name} - ${first.teacher}`}));
} else if (event.categoryMode === 'dance_academy') {
setNewRehearsal(prev => ({...prev, groupName: DANCE_ACADEMIES[0], subGroup: ENSEMBLE_TYPES[0] }));
setNewProgramItem(prev => ({...prev, academy: DANCE_ACADEMIES[0]}));
} else {
setNewRehearsal(prev => ({...prev, groupName: GRADES[0], subGroup: SECTIONS[0] }));
setNewProgramItem(prev => ({...prev, academy: GRADES[0]}));
}
}, [event.categoryMode]);
const handleMusicAcademyChange = (e) => {
const val = e.target.value;
const found = MUSIC_ACADEMIES.find(ma => `${ma.name} - ${ma.teacher}` === val);
setNewRehearsal({ ...newRehearsal, groupName: val, teacher: found ? found.teacher : '' });
};
const handleAddRehearsalSubmit = (e) => {
e.preventDefault();
let displayTitle = "";
if (event.categoryMode === 'grade') displayTitle = `${newRehearsal.groupName} ${newRehearsal.subGroup}`;
else if (event.categoryMode === 'dance_academy') displayTitle = `${newRehearsal.groupName} (${newRehearsal.subGroup})`;
else displayTitle = newRehearsal.groupName;
onAddRehearsal({ ...newRehearsal, relatedEventId: event.id, title: displayTitle });
};
const handleAddProgramSubmit = (e) => {
e.preventDefault();
if(!newProgramItem.pieceName) return;
onAddProgramItem(event.id, newProgramItem);
setNewProgramItem({...newProgramItem, pieceName: '', performers: ''});
};
const createNewChecklistItem = () => {
const newItem = { id: Date.now().toString(), title: 'Nuevo Requerimiento', category: 'General & Administrativo', status: false, details: '', file: null, subItems: [] };
onAddChecklistItem(event.id, newItem);
setEditingItem(newItem);
};
const getFilteredProgramItems = () => {
if (!event.program) return [];
if (programFilter === 'all') return event.program;
if (INSTRUMENT_FAMILIES[programFilter]) {
const keywords = INSTRUMENT_FAMILIES[programFilter];
return event.program.filter(item => {
return keywords.some(keyword => item.academy.includes(keyword));
});
}
return event.program.filter(item => item.academy === programFilter);
};
const filteredProgram = getFilteredProgramItems();
const uniqueAcademiesInProgram = Array.from(new Set(filteredProgram.map(i => i.academy)));
return (
Volver al Tablero
{/* Header del Evento */}
{event.title}
{safeDate(event.date).toLocaleDateString('es-MX', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })}
{event.endDate && ` al ${safeDate(event.endDate).toLocaleDateString('es-MX', { day: 'numeric', month: 'long' })}`}
{/* BOTÓN PROPUESTA SOLO PARA SMART WEEK */}
{event.id === 'evt-001' && (
setShowProposal(true)} className="flex items-center gap-2 bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors text-xs font-bold">
Propuesta Horarios
)}
AVISO
IMPRIMIR EVENTO
Observaciones Generales
{/* Tabs */}
setActiveTab('checklist')} className={`pb-2 px-4 font-medium text-sm whitespace-nowrap transition-colors ${activeTab === 'checklist' ? 'text-indigo-600 border-b-2 border-indigo-600' : 'text-slate-500 hover:text-slate-700'}`}>
Checklist & Producción
setActiveTab('rehearsals')} className={`pb-2 px-4 font-medium text-sm whitespace-nowrap transition-colors ${activeTab === 'rehearsals' ? 'text-indigo-600 border-b-2 border-indigo-600' : 'text-slate-500 hover:text-slate-700'}`}>
Gestión de Ensayos
setActiveTab('program')} className={`pb-2 px-4 font-medium text-sm whitespace-nowrap transition-colors ${activeTab === 'program' ? 'text-indigo-600 border-b-2 border-indigo-600' : 'text-slate-500 hover:text-slate-700'}`}>
Programa Musical
{/* --- TAB: CHECKLIST MANAGER --- */}
{activeTab === 'checklist' && (
Requerimientos por Área
Agregar Ítem
{CHECKLIST_CATEGORIES.map(cat => {
const items = Array.isArray(event.checklist) ? event.checklist.filter(i => i.category === cat) : [];
if (items.length === 0) return null;
return (
{cat}
{items.map(item => {
const hasSubs = item.subItems && item.subItems.length > 0;
const progress = hasSubs ? Math.round((item.subItems.filter(s=>s.completed).length / item.subItems.length) * 100) : 0;
return (
setEditingItem(item)}>
{item.title}
{item.details &&
{item.details}
}
{hasSubs && (
)}
onUpdateChecklistItem(event.id, { ...item, status: !item.status })} className="mt-1 w-4 h-4 text-indigo-600 rounded" />
setEditingItem(item)} className="text-[10px] text-indigo-500 mt-2 font-medium opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-1"> Editar / Detalles
);
})}
);
})}
{editingItem && (
setEditingItem(null)} onSave={(updatedItem) => { onUpdateChecklistItem(event.id, updatedItem); setEditingItem(null); }} onDelete={(itemId) => { onDeleteChecklistItem(event.id, itemId); setEditingItem(null); }} />
)}
)}
{/* --- TAB: REHEARSALS --- */}
{activeTab === 'rehearsals' && (
<>
Ensayos ({eventRehearsals.length})
{eventRehearsals.sort((a,b) => (a.date + a.time).localeCompare(b.date + b.time)).map(r => (
{r.title}
{safeDate(r.date).toLocaleDateString()} {r.time}
{r.location}
onDeleteRehearsal(r.id)} className="text-slate-300 hover:text-red-500 transition-colors">
))}
>
)}
{/* --- TAB: PROGRAM --- */}
{activeTab === 'program' && (
<>
Programa Musical
setProgramFilter(e.target.value)}
>
Ver Todo (Completo)
{Object.keys(INSTRUMENT_FAMILIES).map(f => (
{f}
))}
{event.categoryMode === 'music_academy' && (
{MUSIC_ACADEMIES.map((ac, idx) => (
{ac.name} - {ac.teacher}
))}
)}
{!event.program || event.program.length === 0 ? (
Aún no hay piezas registradas.
) : (
{uniqueAcademiesInProgram.length === 0 ? (
No hay piezas que coincidan con este filtro.
) : (
uniqueAcademiesInProgram.map(academyName => (
{academyName}
{filteredProgram.filter(i => i.academy === academyName).map((item) => (
{item.pieceName}
{item.performers}
onDeleteProgramItem(event.id, item.id)} className="text-slate-300 hover:text-red-500">
))}
))
)}
)}
>
)}
{/* --- MODAL DE PROPUESTA (NUEVO) --- */}
{showProposal && (
setShowProposal(false)} />
)}
);
};
// --- APP PRINCIPAL ---
export default function SmartApp() {
const [view, setView] = useState('dashboard');
const [events, setEvents] = useState(INITIAL_EVENTS);
const [rehearsals, setRehearsals] = useState([]);
const [selectedEventId, setSelectedEventId] = useState(null);
const [showPrintModal, setShowPrintModal] = useState(false);
const [printMode, setPrintMode] = useState('single');
const [showEmailModal, setShowEmailModal] = useState(false);
const [masterEditingItem, setMasterEditingItem] = useState(null);
const selectedEvent = events.find(e => e.id === selectedEventId);
useEffect(() => {
// ACTUALIZACIÓN VERSIÓN 10 (HARD RESET FINAL & TIMEZONE FIX)
// Cambiamos la key para obligar a que cargue INITIAL_EVENTS limpios
const savedEvents = localStorage.getItem('smart_events_v10_reset');
const savedRehearsals = localStorage.getItem('smart_rehearsals_v10_reset');
if (savedEvents) {
setEvents(JSON.parse(savedEvents));
} else {
setEvents(INITIAL_EVENTS);
}
if (savedRehearsals) {
setRehearsals(JSON.parse(savedRehearsals));
}
}, []);
useEffect(() => {
localStorage.setItem('smart_events_v10_reset', JSON.stringify(events));
localStorage.setItem('smart_rehearsals_v10_reset', JSON.stringify(rehearsals));
}, [events, rehearsals]);
// Manejadores
const handlePrintSingle = () => { setPrintMode('single'); setShowPrintModal(true); };
const handlePrintGeneral = () => { setPrintMode('general'); setShowPrintModal(true); };
const handlePrintMasterList = () => { setPrintMode('master_list'); setShowPrintModal(true); }; // Nuevo manejador
const updateChecklistItem = (eventId, updatedItem) => { setEvents(events.map(ev => { if (ev.id === eventId) return { ...ev, checklist: ev.checklist.map(item => item.id === updatedItem.id ? updatedItem : item) }; return ev; })); };
const addChecklistItem = (eventId, newItem) => { setEvents(events.map(ev => { if (ev.id === eventId) return { ...ev, checklist: [...ev.checklist, newItem] }; return ev; })); };
const deleteChecklistItem = (eventId, itemId) => { setEvents(events.map(ev => { if (ev.id === eventId) return { ...ev, checklist: ev.checklist.filter(i => i.id !== itemId) }; return ev; })); };
const handleMasterItemOpen = (eventId, item) => setMasterEditingItem({ ...item, eventId });
const updateEventObservations = (eventId, text) => { setEvents(events.map(ev => { if (ev.id === eventId) return { ...ev, observations: text }; return ev; })); };
const addProgramItem = (eventId, item) => { setEvents(events.map(ev => { if (ev.id === eventId) { const currentProgram = ev.program || []; return { ...ev, program: [...currentProgram, { ...item, id: Date.now().toString() }] }; } return ev; })); };
const deleteProgramItem = (eventId, itemId) => { setEvents(events.map(ev => { if (ev.id === eventId) return { ...ev, program: ev.program.filter(i => i.id !== itemId) }; return ev; })); };
const addRehearsal = (rehearsal) => setRehearsals([...rehearsals, { ...rehearsal, id: Date.now().toString() }]);
const deleteRehearsal = (id) => setRehearsals(rehearsals.filter(r => r.id !== id));
const renderDashboard = () => (
{events.sort((a,b) => new Date(a.date) - new Date(b.date)).map(evt => (
{ setSelectedEventId(evt.id); setView('event-detail'); }}>
{safeDate(evt.date).toLocaleDateString('es-MX', { day: 'numeric', month: 'short' })}
{evt.title}
0 ? (evt.checklist.filter(i=>i.status).length / evt.checklist.length) * 100 : 0}%` }}>
))}
);
const renderCalendar = () => {
const allItems = [
...events.map(e => ({ ...e, isEvent: true })),
...rehearsals.map(r => ({ ...r, isRehearsal: true }))
].sort((a, b) => new Date(a.date) - new Date(b.date));
return (
Calendario Global de Actividades
Eventos Principales (⭐) y Ensayos Programados
Fecha
Hora
Actividad
Ubicación
Maestro / Responsable
{allItems.map((item, idx) => (
{safeDate(item.date).toLocaleDateString('es-MX', { weekday: 'short', day: 'numeric', month: 'short' })}
{item.isRehearsal ? item.time : Todo el día }
{item.isEvent ? : }
{item.title}
{item.location || 'Campus SMART'}
{item.teacher || '-'}
{item.isEvent && (
{ setSelectedEventId(item.id); setView('event-detail'); }} className="text-indigo-600 hover:text-indigo-800 text-xs font-bold border border-indigo-200 px-3 py-1 rounded hover:bg-indigo-50 transition-colors">
Gestionar
)}
))}
);
};
return (
{view === 'dashboard' && renderDashboard()}
{view === 'calendar' && renderCalendar()}
{view === 'event-detail' && selectedEvent && (
setView('dashboard')}
onUpdateObservations={updateEventObservations}
onUpdateChecklistItem={updateChecklistItem}
onAddChecklistItem={addChecklistItem}
onDeleteChecklistItem={deleteChecklistItem}
onAddRehearsal={addRehearsal}
onDeleteRehearsal={deleteRehearsal}
onAddProgramItem={addProgramItem}
onDeleteProgramItem={deleteProgramItem}
onPrint={handlePrintSingle}
onGenerateEmail={() => setShowEmailModal(true)}
/>
)}
{view === 'master-checklist' && (
{ const evt = events.find(e => e.id === evtId); const item = evt.checklist.find(i => i.id === itemId); updateChecklistItem(evtId, { ...item, status: !item.status }); }}
onOpenItem={handleMasterItemOpen}
onPrintList={handlePrintMasterList}
/>
)}
{masterEditingItem && (
setMasterEditingItem(null)} onSave={(updatedItem) => { updateChecklistItem(masterEditingItem.eventId, updatedItem); setMasterEditingItem(null); }} onDelete={(itemId) => { deleteChecklistItem(masterEditingItem.eventId, itemId); setMasterEditingItem(null); }} />
)}
{showPrintModal && (
setShowPrintModal(false)}
/>
)}
{showEmailModal && selectedEvent && (
setShowEmailModal(false)}
/>
)}
);
}