// App entry — gestiona auth (login → dashboard) y Tweaks. const { useState: useStateA, useEffect: useEffectA } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#00B147", "density": "comfortable", "showStats": true }/*EDITMODE-END*/; // ---- Helpers de color ---- function hexToRgb(h) { h = h.replace("#",""); if (h.length === 3) h = h.split("").map(c=>c+c).join(""); const n = parseInt(h, 16); return { r:(n>>16)&255, g:(n>>8)&255, b:n&255 }; } function rgbToHex({r,g,b}) { const c = (x) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2,"0"); return "#" + c(r) + c(g) + c(b); } function shade(hex, amt) { const {r,g,b} = hexToRgb(hex); if (amt < 0) { const f = 1 + amt; return rgbToHex({ r: r*f, g: g*f, b: b*f }); } else { return rgbToHex({ r: r + (255-r)*amt, g: g + (255-g)*amt, b: b + (255-b)*amt }); } } function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); // ── Auth state ────────────────────────────────────────────── const [authed, setAuthed] = useStateA(false); const [currentUser, setCurrentUser] = useStateA(null); const [view, setView] = useStateA("library"); const [appLoading, setAppLoading] = useStateA(true); // ── Global data (cargado tras autenticar) ────────────────── const [sections, setSections] = useStateA([]); const [allFolders, setAllFolders] = useStateA({}); // { [sectionId]: [...] } const [roles, setRoles] = useStateA([]); const [areas, setAreas] = useStateA([]); const [users, setUsers] = useStateA([]); // ── CSS variables ────────────────────────────────────────── useEffectA(() => { const root = document.documentElement; root.style.setProperty("--green", t.accent); root.style.setProperty("--green-deep", shade(t.accent, -0.22)); root.style.setProperty("--green-tint", shade(t.accent, 0.88)); }, [t.accent]); useEffectA(() => { const root = document.documentElement; root.style.setProperty("--card-pad", t.density === "compact" ? "10px 12px 12px" : "12px 14px 14px"); }, [t.density]); useEffectA(() => { let s = document.getElementById("__stats_toggle"); if (!s) { s = document.createElement("style"); s.id = "__stats_toggle"; document.head.appendChild(s); } s.textContent = t.showStats ? "" : ".lb-stats { display: none !important; }"; }, [t.showStats]); // ── Verificar sesión al cargar ───────────────────────────── useEffectA(() => { const token = API.getToken(); if (!token) { setAppLoading(false); return; } API.Auth.me() .then(function(user) { setCurrentUser(user); setAuthed(true); }) .catch(function() { API.clearToken(); setAppLoading(false); }); }, []); // ── Cargar datos globales al autenticar ──────────────────── useEffectA(() => { if (!authed) return; Promise.all([ API.Sections.list(), API.Roles.list(), API.Areas.list(), API.Users.list({ per_page: 200 }), ]).then(function(results) { const secs = results[0] || []; const rols = results[1] || []; const arss = results[2] || []; const usrs = (results[3] && results[3].data) || []; setSections(secs); setRoles(rols); setAreas(arss); setUsers(usrs); // Cargar carpetas de cada sección Promise.all(secs.map(function(s) { return API.Folders.bySection(s.id); })) .then(function(folderArrays) { const folderMap = {}; secs.forEach(function(s, i) { folderMap[s.id] = folderArrays[i] || []; }); setAllFolders(folderMap); }) .catch(function() {}); setAppLoading(false); }).catch(function() { setAppLoading(false); }); }, [authed]); // ── Handlers ─────────────────────────────────────────────── const handleEnter = function(user) { setCurrentUser(user); setAuthed(true); }; const handleLogout = async function() { try { await API.Auth.logout(); } catch(e) {} setAuthed(false); setCurrentUser(null); setSections([]); setAllFolders({}); setRoles([]); setAreas([]); setUsers([]); setView("library"); }; // ── Carga inicial ────────────────────────────────────────── if (appLoading) { return (