/* ── Biblioteca metodologica - vizual Windows Explorer (spec 24.07, agent 002) ────────── 3 niveluri: credinte -> tipuri de material -> materialele. 4 trepte size-driven (Extra Large / Large / Medium / Details), geometrie IDENTICA platformei: celula fixa 240 / 160 / 104 px, reflow auto-fill; coperta PDF la raport A4 cu numele la hover. Materialele: PDF (tab nou, viewer nativ) · Excel (download-only) · audio (modal cu player). Datele: biblio_data.js (window.PRP_BIBLIO), generat de biblioteca/build_biblioteca.py - scheletul 7x5 exista chiar fara documente (structura e produsul, cerinta Marian). Iconita PDF = fisierul oficial din platforma (public/file-icons/pdf.webp), inline base64; XLS si AUDIO = sistemul „Solid" al platformei (DOC_STYLE din type-icon.tsx). */ const IcBiblio = (p) => ( ); window.IcBiblio = IcBiblio; const BIB_DATE = () => window.PRP_BIBLIO || { prefix: "PENDING", foldere: [] }; const BIB_TILE_W = { xl: "240px", large: "160px", medium: "104px", details: "160px" }; /* store.biblio = { view: "xl"|"large"|"medium"|"details", deschise: { [idDoc]: "YYYY-MM-DD" } } */ function bibStarea(store) { const b = store.biblio || {}; const v = ["xl", "large", "medium", "details"].includes(b.view) ? b.view : "large"; return { view: v, deschise: b.deschise || {} }; } /* folderul galben Windows 11 - acelasi desen ca fisierul ales de Marian pentru platforma: clapa aurie stanga-sus, dunga alba de accent, corp lucios cu gradient */ function BibFolder({ w }) { return ( ); } /* foaie „Solid" ca in platforma: corp colorat, colt pliat deschis, glyph + eticheta albe */ function BibFoaie({ culoare, fold, eticheta, glyph, w }) { return ( {glyph} {eticheta} ); } const BibIcXls = ({ w }) => ( } /> ); const BibIcAudio = ({ w }) => ( } /> ); /* PDF: iconita oficiala Adobe Acrobat - acelasi fisier ca in platforma (pdf.webp), inline */ const BibIcPdf = ({ w }) => ( ); function bibIconita(fel, w) { if (fel === "xlsx") return ; if (fel === "audio") return ; return ; } const BIB_FEL_NUME = { pdf: "PDF", xlsx: "Excel", audio: "Audio" }; function bibMeta(d) { if (d.fel === "pdf") return d.pagini ? d.pagini + (d.pagini === 1 ? " pagină" : " pagini") : "-"; if (d.fel === "xlsx") return d.foi ? d.foi + (d.foi === 1 ? " foaie" : " foi") : "-"; return d.durata || "-"; } /* comutatorul celor 4 trepte - aceleasi etichete si ordine ca in platforma */ function BibViewSwitch({ view, setView }) { const M = [["xl", "Extra Large"], ["large", "Large"], ["medium", "Medium"], ["details", "Details"]]; return (
{M.map(([k, et]) => ( ))}
); } /* breadcrumb: Biblioteca / / */ function BibNav({ nav, setNav }) { const D = BIB_DATE(); const f = D.foldere.find((x) => x.slug === nav.folder); const t = f && f.tipuri.find((x) => x.tip === nav.tip); return (
{f && ( / )} {t && /}
); } /* ── nivelul 3: materialele unui subfolder ── */ function BibMateriale({ documente, view, deschise, onDeschide }) { if (!documente.length) { return (
Încă nu sunt materiale aici
Materialele acestei secțiuni se adaugă imediat ce sosesc de la Dr. Petre - structura îi așteaptă deja.
); } const badge = (d) => deschise[d.id] ? : !; if (view === "details") { return (
{documente.map((d) => ( onDeschide(d)}> {d.fel === "audio" && ( )} ))}
MaterialTipFoi / DuratăStare
{bibIconita(d.fel, "100%")} {d.titlu} {BIB_FEL_NUME[d.fel]} {bibMeta(d)} {deschise[d.id] ? deschis : nedeschis}
); } const cuCoperta = (d) => d.fel === "pdf" && d.coperta && (view === "xl" || view === "large"); return ( /* grila sta in ACELASI bloc alb ca tabelul Details (cerinta Marian 24.07: „details are bloc in jurul folderelor, trebuie sa avem unul identic si pentru medium, large...") */
{documente.map((d) => ( ))}
); } /* ── view-ul principal: rutare pe 3 niveluri, hash + history real ── */ function BibliotecaView({ store, set }) { const st = bibStarea(store); const dinHash = () => { const m = (location.hash || "").match(/^#biblioteca(?:\/([a-z-]+))?(?:\/([a-z]+))?$/); if (!m) return { folder: null, tip: null }; return { folder: m[1] || null, tip: m[1] ? (m[2] || null) : null }; }; const [nav, setNavRaw] = React.useState(dinHash); const [audioActiv, setAudioActiv] = React.useState(null); const [eroare, setEroare] = React.useState(null); const setNav = (n) => { setNavRaw(n); setEroare(null); try { const h = "#biblioteca" + (n.folder ? "/" + n.folder + (n.tip ? "/" + n.tip : "") : ""); history.pushState({ view: "biblioteca", bibNav: n }, "", h); } catch (e) { /* file:// restrictiv - navigarea ramane in state local */ } }; React.useEffect(() => { const onPop = (e) => { if (e.state && e.state.bibNav) setNavRaw(e.state.bibNav); else if (e.state && e.state.view === "biblioteca") setNavRaw({ folder: null, tip: null }); }; window.addEventListener("popstate", onPop); return () => window.removeEventListener("popstate", onPop); }, []); React.useEffect(() => { if (!audioActiv) return; const onKey = (e) => { if (e.key === "Escape") setAudioActiv(null); }; document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [audioActiv]); const setView = (v) => set((s) => { const b = bibStarea(s); return { ...s, biblio: { ...s.biblio, view: v, deschise: b.deschise } }; }); const marcheaza = (id) => set((s) => { const b = bibStarea(s); const azi = window.PRP_UTIL ? window.PRP_UTIL.fmtISO(new Date()) : new Date().toISOString().slice(0, 10); return { ...s, biblio: { ...s.biblio, view: b.view, deschise: { ...b.deschise, [id]: azi } } }; }); /* „deschis" se marcheaza DUPA ce materialul chiar s-a deschis: daca browserul blocheaza fereastra noua, badge-ul verde ar minti (audit 25.07) */ const deschide = (d) => { if (!d.url || d.url === "PENDING") { setEroare({ ...d, motiv: "link" }); return; } if (d.fel === "audio") { setAudioActiv(d); marcheaza(d.id); return; } if (d.fel === "xlsx") { const a = document.createElement("a"); a.href = d.url; a.download = d.fisier || ""; document.body.appendChild(a); a.click(); a.remove(); marcheaza(d.id); return; } const w = window.open(d.url, "_blank", "noopener"); if (w) marcheaza(d.id); else setEroare({ ...d, motiv: "blocat" }); }; const D = BIB_DATE(); const folder = D.foldere.find((x) => x.slug === nav.folder) || null; const tipCurent = folder ? (folder.tipuri.find((x) => x.tip === nav.tip) || null) : null; const nDoc = (f) => f.tipuri.reduce((n, t) => n + t.documente.length, 0); const nDeschise = (lista) => lista.filter((d) => st.deschise[d.id]).length; const grid = { "--bib-w": BIB_TILE_W[st.view] }; /* badge agregat pe folder: verde cand toate materialele au fost deschise, galben n/total */ const badgeFolder = (lista) => { if (!lista.length) return null; const n = nDeschise(lista); if (n === 0) return null; if (n === lista.length) return ; return {n}/{lista.length}; }; const toateDin = (f) => f.tipuri.reduce((a, t) => a.concat(t.documente), []); const celulaFolder = (key, nume, lista, onClick) => ( ); const randFolder = (key, nume, lista, onClick) => ( {nume} Folder {lista.length || "-"} {lista.length > 0 ? {lista.length} {lista.length === 1 ? "material" : "materiale"} : în pregătire} ); const tabelFoldere = (randuri) => (
{randuri}
FolderTipMaterialeStare
); let continut; if (!folder) { continut = st.view === "details" ? tabelFoldere(D.foldere.map((f) => randFolder(f.slug, f.nume, toateDin(f), () => setNav({ folder: f.slug, tip: null })))) : (
{D.foldere.map((f) => celulaFolder(f.slug, f.nume, toateDin(f), () => setNav({ folder: f.slug, tip: null })))}
); } else if (!tipCurent) { continut = st.view === "details" ? tabelFoldere(folder.tipuri.map((t) => randFolder(t.tip, t.nume, t.documente, () => setNav({ folder: folder.slug, tip: t.tip })))) : (
{folder.tipuri.map((t) => celulaFolder(t.tip, t.nume, t.documente, () => setNav({ folder: folder.slug, tip: t.tip })))}
); } else { continut = ; } return (
{!folder && (

Biblioteca metodologică

Materialele metodologice ale programului, organizate pe credințe și pe tipuri de material. Documentele se deschid în vizualizator; instrumentele Excel se descarcă; materialele audio se ascultă direct aici.

)} {eroare && (

{eroare.titlu} - {eroare.motiv === "blocat" ? "browserul a blocat fereastra nouă. Permite ferestrele pentru această pagină și încearcă din nou; materialul nu a fost marcat ca deschis." : "materialul se descarcă de pe serverul PRP, iar linkul nu este încă disponibil (sau conexiunea lipsește). Restul bibliotecii funcționează normal."}

)} {continut} {audioActiv && (
setAudioActiv(null)}>
e.stopPropagation()}>
{audioActiv.titlu}
)}
); } window.BibliotecaView = BibliotecaView;