// ============ UI base ============ const { useState, useEffect, useRef } = React; // ---- Iconos (lucide-style, stroke) ---- const ICONS = { home: "M3 10.5 12 3l9 7.5M5 9.5V21h14V9.5", book: "M4 4.5A2.5 2.5 0 0 1 6.5 2H20v17.5H6.5A2.5 2.5 0 0 0 4 22zM20 19.5H6.5", calendar: "M8 2v3M16 2v3M3.5 8.5h17M5 5h14a1.5 1.5 0 0 1 1.5 1.5V20A1.5 1.5 0 0 1 19 21.5H5A1.5 1.5 0 0 1 3.5 20V6.5A1.5 1.5 0 0 1 5 5z", task: "M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2M9 13l2 2 4-4", chat: "M21 11.5a8.38 8.38 0 0 1-9 8.3 8.5 8.5 0 0 1-3.8-.9L3 20.5l1.6-4.2A8.4 8.4 0 0 1 3.7 12 8.5 8.5 0 0 1 21 11.5z", award: "M12 15a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM8.2 13.5 7 22l5-3 5 3-1.2-8.5", trending: "M3 17l6-6 4 4 8-8M21 7h-5M21 7v5", search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3", bell: "M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0", play: "M6 4.5v15l13-7.5z", playc: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM10 8.5l5 3.5-5 3.5z", check: "M5 12.5l4.5 4.5L19 7.5", checkc: "M22 11.1V12a10 10 0 1 1-5.9-9.1M22 4 12 14l-3-3", clock: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 7v5l3.5 2", file: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8zM14 3v5h5", download: "M12 3v12m0 0 4.5-4.5M12 15l-4.5-4.5M4 18.5V20a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-1.5", lock: "M6 11h12a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1zM8 11V8a4 4 0 0 1 8 0v3", video: "M15 8.5 22 5v14l-7-3.5M3 6.5h10a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2z", read: "M4 5.5A2.5 2.5 0 0 1 6.5 3H12v16H6.5A2.5 2.5 0 0 0 4 21.5zM20 5.5A2.5 2.5 0 0 0 17.5 3H12v16h5.5a2.5 2.5 0 0 1 2.5 2.5z", chevR: "M9 5l7 7-7 7", chevD: "M5 9l7 7 7-7", chevL: "M15 5l-7 7 7 7", arrowL: "M19 12H5M11 18l-6-6 6-6", x: "M6 6l12 12M18 6 6 18", send: "M22 2 11 13M22 2l-7 20-4-9-9-4z", paperclip: "M21 11.5 12.5 20a5 5 0 0 1-7-7l9-9a3.5 3.5 0 0 1 5 5l-9 9a2 2 0 0 1-3-3l8-8", heart: "M19.5 12.6 12 20l-7.5-7.4a4.5 4.5 0 0 1 6.4-6.3l1.1 1 1-1a4.5 4.5 0 0 1 6.5 6.3z", star: "M12 3l2.6 5.5 6 .9-4.3 4.2 1 6L12 17.8 6.7 19.6l1-6L3.4 9.4l6-.9z", user: "M20 21a8 8 0 1 0-16 0M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z", pin: "M9 4h6l-1 5 3 3v2H7v-2l3-3-1-5zM12 14v6", dot: "M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z", logout: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9", }; function Icon({ name, size = 20, sw = 1.7, style, className }) { const d = ICONS[name] || ""; const fill = name === "play" ? "currentColor" : "none"; return ( ); } const TYPE_META = { video: { icon: "playc", label: "Grabación" }, lectura: { icon: "read", label: "Lectura" }, material: { icon: "file", label: "Material" }, quiz: { icon: "checkc", label: "Quiz" }, tarea: { icon: "task", label: "Tarea" }, }; function ProgressBar({ value, color = "var(--orange)", height = 8 }) { return (
); } function Ring({ value, size = 56, sw = 6, color = "var(--orange)" }) { const r = (size - sw) / 2; const c = 2 * Math.PI * r; return ( {value}% ); } function Avatar({ text, size = 38, bg = "var(--navy)" }) { return (
{text}
); } function Badge({ children, tone = "neutral" }) { return {children}; } function Btn({ children, variant = "primary", icon, onClick, size = "md", style, disabled }) { return ( ); } // Spanish date helpers const MES = ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"]; const MES_L = ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]; const DIA = ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"]; const TODAY = new Date(); function parseD(s) { return new Date(s + "T00:00:00"); } function fmtDate(s) { const d = parseD(s); return `${DIA[d.getDay()]} ${d.getDate()} ${MES[d.getMonth()]}`; } function daysUntil(s) { const todayZero = new Date(); todayZero.setHours(0, 0, 0, 0); return Math.round((parseD(s) - todayZero) / 86400000); } function dueLabel(s) { const n = daysUntil(s); if (n < 0) return { txt: `Venció hace ${-n} d`, tone: "danger" }; if (n === 0) return { txt: "Vence hoy", tone: "danger" }; if (n === 1) return { txt: "Vence mañana", tone: "warn" }; if (n <= 3) return { txt: `Vence en ${n} días`, tone: "warn" }; return { txt: `Vence en ${n} días`, tone: "neutral" }; } Object.assign(window, { Icon, ICONS, TYPE_META, ProgressBar, Ring, Avatar, Badge, Btn, MES, MES_L, DIA, TODAY, parseD, fmtDate, daysUntil, dueLabel, useState, useEffect, useRef });