*/
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 (
setNav({ folder: null, tip: null })}>Biblioteca
{f && (
/
setNav({ folder: f.slug, tip: null })}>{f.nume}
)}
{t && / {t.nume} }
);
}
/* ── 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 (
Material Tip Foi / Durată Stare
{documente.map((d) => (
onDeschide(d)}>
{bibIconita(d.fel, "100%")}
{d.titlu}
{BIB_FEL_NUME[d.fel]}
{bibMeta(d)}
{deschise[d.id]
? deschis
: nedeschis }
{d.fel === "audio" && (
)}
))}
);
}
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) => (
onDeschide(d)} title={d.titlu}
aria-label={d.titlu + " - " + BIB_FEL_NUME[d.fel] + (deschise[d.id] ? ", deschis" : ", nedeschis")}>
{cuCoperta(d) ? (
{badge(d)}
{d.titlu}
) : (
{bibIconita(d.fel)}
{badge(d)}
{d.titlu}
)}
))}
);
}
/* ── 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) => (
{badgeFolder(lista)}
{nume}
{lista.length > 0
? {lista.length} {lista.length === 1 ? "material" : "materiale"}
: în pregătire la Dr. Petre }
);
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) => (
Folder Tip Materiale Stare
{randuri}
);
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}
setAudioActiv(null)}>Închide
)}
);
}
window.BibliotecaView = BibliotecaView;