// ============ Tareas, Foro, Certificados, Progreso, Calendario, Sala ============
function SubmitPanel({ onSubmit, submitted }) {
const [files, setFiles] = useState([]);
const [note, setNote] = useState("");
const fileInputRef = useRef(null);
if (submitted) {
return (
fileInputRef.current && fileInputRef.current.click()}>
Selecciona un archivo para tu entrega
Haz clic para seleccionar · PDF, DOCX, XLSX (máx 20 MB)
{files.map((f, i) => (
{f.n} {f.s}
setFiles(files.filter((_, k) => k !== i))}>
))}
);
}
function TareaInline({ title, course, go, courseId }) {
const [submitted, setSubmitted] = useState(false);
const assignment = (window.DATA && DATA.assignments)
? DATA.assignments.find(a => a.course_id === courseId && (a.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(a.title.toLowerCase())))
: null;
return (
{stats.map(([ic, n, l], i) => (
))}
Avance por curso
{DATA.courses.map((c) => {
const dl = c.modules.reduce((s, m) => s + m.lessons.length, 0);
const dd = c.modules.reduce((s, m) => s + m.lessons.filter((l) => l.done).length, 0);
return (
go("curso", { courseId: c.id })}>
0 ? "var(--orange)" : "var(--line)" }} />
{c.title} {dd}/{dl} lecciones · {c.hours}
{c.progress}%
);
})}
);
}
// ---------- Calendario ----------
function Calendar({ go }) {
const upcoming = [...DATA.sessions].sort((a, b) => parseD(a.date) - parseD(b.date));
const baseDate = upcoming.length > 0 ? parseD(upcoming[0].date) : new Date();
const year = baseDate.getFullYear();
const month = baseDate.getMonth();
const first = new Date(year, month, 1).getDay();
const days = new Date(year, month + 1, 0).getDate();
const byDay = {};
DATA.sessions.forEach((s) => { const d = parseD(s.date); if (d.getMonth() === month && d.getFullYear() === year) (byDay[d.getDate()] = byDay[d.getDate()] || []).push(s); });
const cells = [];
for (let i = 0; i < first; i++) cells.push(null);
for (let d = 1; d <= days; d++) cells.push(d);
return (
{MES_L[month].charAt(0).toUpperCase() + MES_L[month].slice(1)} {year}
{["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"].map((d) => {d} )}
{cells.map((d, i) => (
{d && {d} }
{d && byDay[d] && byDay[d].map((s) => (
go("sala", { sessionId: s.id })}>{s.time.split(" ")[0]} {s.title.split(":")[0]}
))}
))}
Próximas sesiones
{upcoming.map((s) => {
const d = parseD(s.date);
return (
{d.getDate()} {MES[d.getMonth()]}
{s.title} {s.time} · {s.host} {s.course}
{s.joinable ?
go("sala", { sessionId: s.id })}>Unirme :
Recordar }
);
})}
);
}
// ---------- Sala de sesión en vivo ----------
function LiveRoom({ sessionId, go }) {
const s = DATA.sessions.find((x) => x.id === sessionId) || DATA.sessions[0];
return (
go("inicio")}> Volver al inicio
EN VIVO
{s.title} {s.host} · {s.course}
);
}
function SupportModal({ onClose }) {
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");
const [done, setDone] = useState(false);
const [loading, setLoading] = useState(false);
const submit = (e) => {
e.preventDefault();
if (!subject.trim() || !message.trim()) return;
setLoading(true);
API.submitTicket({ subject, message })
.then(() => {
setDone(true);
})
.catch((err) => {
console.warn("No se pudo enviar el reclamo", err);
})
.finally(() => setLoading(false));
};
return (
e.stopPropagation()}>
Soporte DataVoices & SynapTech
Carga tu reclamo o duda y nos comunicaremos contigo.
{done ? (
Reclamo enviado correctamente
El equipo de soporte revisará tu caso y te contactará por email.
Cerrar
) : (
)}
);
}
Object.assign(window, { TareaInline, SubmitPanel, TasksScreen, Forum, Certificates, Progress, Calendar, LiveRoom, SupportModal });