const { useState, useEffect, useMemo, useRef } = React; /* ── date globale (data.js) ── */ const CREDINTE = window.PRP_CREDINTE; const MECANISME = window.PRP_MECANISME; const DICTIONAR = window.PRP_DICTIONAR; const FRECVENTE = ["Zilnic", "Săptămânal", "Lunar", "Trimestrial", "Anual", "Angajament (la nevoie)"]; const ZILE_RO = ["Lu", "Ma", "Mi", "Jo", "Vi", "Sâ", "Du"]; /* ── utilitare de timp (prototip client-only: fusul local al persoanei) ── */ const fmtISO = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; const startOfWeek = (d) => { const x = new Date(d); const day = (x.getDay() + 6) % 7; x.setDate(x.getDate() - day); x.setHours(0, 0, 0, 0); return x; }; const addDays = (d, n) => { const x = new Date(d); x.setDate(x.getDate() + n); return x; }; const sameDay = (a, b) => fmtISO(a) === fmtISO(b); /* cheia de inregistrare in jurnal, per frecventa */ function periodKey(frecventa, d) { if (frecventa === "Săptămânal") return "W" + fmtISO(startOfWeek(d)); if (frecventa === "Lunar") return fmtISO(d).slice(0, 7); if (frecventa === "Trimestrial") return d.getFullYear() + "-Q" + (Math.floor(d.getMonth() / 3) + 1); if (frecventa === "Anual") return String(d.getFullYear()); return fmtISO(d); // Zilnic + Angajament: pe zi } function periodLabel(frecventa, d) { if (frecventa === "Săptămânal") return "săptămâna asta"; if (frecventa === "Lunar") return d.toLocaleDateString("ro-RO", { month: "long" }); if (frecventa === "Trimestrial") return "trimestrul " + (Math.floor(d.getMonth() / 3) + 1); if (frecventa === "Anual") return String(d.getFullYear()); return fmtISO(d); } const getInitials = (n) => { const p = (n || "").trim().split(/\s+/).filter(Boolean); return p.length ? p.slice(0, 2).map((w) => w[0].toUpperCase()).join("") : "?"; }; /* frecventa sugerata din chiar textul actiunii lui Petre; intai frecventa explicita, apoi tiparele reactive (conditionate de eveniment) */ function detectFreq(text) { const t = text.toLowerCase(); if (t.includes("zilnic") || t.includes("în fiecare zi") || t.includes("in fiecare zi")) return "Zilnic"; if (t.includes("săptămân") || t.includes("saptaman")) return "Săptămânal"; if (t.includes("lunar")) return "Lunar"; if (t.includes("trimestrial")) return "Trimestrial"; if (t.includes("anual")) return "Anual"; if (/maximum \d+ (minute|ore|de ore)|în maximum|in maximum|la primul|după fiecare|dupa fiecare|fiecărei|in fiecare situa|în fiecare situa|de la aflarea|de la confirmarea|de la observare|când celălalt|imediat/.test(t)) { return "Angajament (la nevoie)"; } return null; } /* ── icons (stroke 1.7) ── */ const Ic = ({ d, size = 18, sw = 1.7, children }) => ( ); const IcHome = (p) => ; const IcUser = (p) => ; const IcBook = (p) => ; const IcGem = (p) => ; const IcList = (p) => ; const IcCal = (p) => ; const IcTarget = (p) => ; const IcPlus = (p) => ; const IcCheck = (p) => ; const IcX = (p) => ; const IcChev = (p) => ; const IcChevL = (p) => ; const IcSearch = (p) => ; const IcSun = (p) => ; const IcMoon = (p) => ; const IcArrow = (p) => ; const IcMenu = (p) => ; /* ── store persistat ── */ const LS_KEY = "prp-credinte-proto-v1"; const defaultStore = { theme: "light", view: "acasa", credinta: "bunatate", consola: { nume: "", dataStart: "", consultant: "Dr. Petre Nicolae", telefon: "", email: "", obs: "" }, plan: {}, /* care actiuni din plan au meniul de detalii deschis (cheie de plan -> true); persistat, ca sa nu se inchida singur la navigare/reload - cerinta Marian 25.07 */ detaliiDeschise: {}, log: {}, gantt: {}, anamneza: {}, valori: { sel: [], plan: {} }, chv: {}, viewMod: "lista", doarAleMele: false, sidebarInchis: false, planOrizont: 30, }; function loadStore() { try { const raw = localStorage.getItem(LS_KEY); if (!raw) return defaultStore; const s = { ...defaultStore, ...JSON.parse(raw) }; /* 25.07: variantele „(nou)" credinte+valori au inlocuit paginile vechi si au preluat slugurile originale. Un store salvat inainte poate tine „acasa-nou"/„calendar-nou"/ „plan-nou" sau vechiul „gantt" - se traduc, ca nimeni sa nu aterizeze pe un view inexistent (ar cadea pe Acasa si ar parea ca si-a pierdut pagina). */ const REDENUMIRI = { "acasa-nou": "acasa", "calendar-nou": "calendar", "plan-nou": "plan", gantt: "plan" }; if (REDENUMIRI[s.view]) s.view = REDENUMIRI[s.view]; if (s.view === "dashboard" || !VIEWURI.includes(s.view)) { s.view = "acasa"; } if (!s.log) s.log = {}; if (!s.anamneza) s.anamneza = {}; if (!s.valori || !Array.isArray(s.valori.sel)) s.valori = { sel: [], plan: {} }; if (!s.chv) s.chv = {}; if (!s.biblio) s.biblio = { view: "large", deschise: {} }; /* migrare aditiva chv: campul "anterior" (o singura completare) devine "istoric" (toate completarile arhivate - cerinta Marian 24.07: rezultatele trecute raman vizibile) */ Object.keys(s.chv).forEach((k) => { const e = s.chv[k]; if (e && !Array.isArray(e.istoric)) { e.istoric = e.anterior && e.anterior.finalizat ? [e.anterior] : []; delete e.anterior; } }); /* migrare aditiva: OA/OR devin CANTITATI per actiune (cerinta Marian 24.07); intrarile vechi primesc OA=1, iar OR mosteneste bifa "realizat" (1 sau 0); rutina = camp nou (antetul Excel 24.07: coloana Rutinele) */ if (s.plan) { Object.keys(s.plan).forEach((k) => { const e = s.plan[k]; if (typeof e.oa !== "number") e.oa = 1; if (typeof e.orNr !== "number") e.orNr = e.realizat ? 1 : 0; if (typeof e.rutina !== "string") e.rutina = ""; /* data asumarii (pt. saptamanile rosii din Planul strategic); intrarile vechi = data start proces */ if (typeof e.adaugat !== "string") e.adaugat = (s.consola && s.consola.dataStart) || ""; }); } /* meniurile de detalii deschise: normalizare + curatare de chei ramase fara actiune in plan (o actiune scoasa din plan nu trebuie sa reapara deschisa daca e re-adaugata) */ if (!s.detaliiDeschise || typeof s.detaliiDeschise !== "object") s.detaliiDeschise = {}; Object.keys(s.detaliiDeschise).forEach((k) => { if (!s.plan || !s.plan[k]) delete s.detaliiDeschise[k]; }); if (!["lista", "matrice"].includes(s.viewMod)) s.viewMod = "lista"; if (![30, 60, 90, 120].includes(s.planOrizont)) s.planOrizont = 30; s.doarAleMele = !!s.doarAleMele; s.sidebarInchis = !!s.sidebarInchis; migreazaIdValori(s); return s; } catch (e) { return defaultStore; } } const VIEWURI = ["acasa", "credinte", "credinta", "anamneza", "valori", "chestionare", "calendar", "plan", "dictionar", "biblioteca", "consola", "date-mele"]; /* Deep-link: daca URL-ul cere explicit o pagina, ea BATE ultima pagina salvata in store. Inainte, aplicatia rescria hash-ul din store la incarcare, deci un link trimis cuiva il ducea unde apucase el sa ramana ultima data (audit 25.07). Sub-navigarile (valori / anamneza / chestionare / biblioteca) isi citesc singure restul hash-ului. */ function viewDinHash(s) { const h = (location.hash || "").replace(/^#/, ""); if (!h || h === "reset") return s; const [v, sub] = h.split("/"); if (!VIEWURI.includes(v)) return s; const next = { ...s, view: v }; if (v === "credinta" && sub && CREDINTE.some((c) => c.slug === sub)) next.credinta = sub; return next; } /* Migrare aditiva 25.07: ID-urile valorilor au fost POZITIONALE (`morale-01`) si au devenit derivate din nume (`morale-onorabilitatea-personala`), fiindca ordinea a trecut la cea din masterul matricii lui Petre. Fara migrare, un plan salvat pe `morale-02` (Demnitatea) ar ateriza pe valoarea care ocupa acum poziția 2 (Probitatea) - date corecte, valoare gresita. Harta vine din date (window.PRP_VALORI.aliasId), nu e duplicata aici. Idempotenta: un ID care nu e in harta ramane neatins, deci o rulare a doua nu strica nimic. */ function migreazaIdValori(s) { const alias = (window.PRP_VALORI && window.PRP_VALORI.aliasId) || null; if (!alias || !s.valori) return; const nou = (id) => alias[id] || id; if (Array.isArray(s.valori.sel)) s.valori.sel = s.valori.sel.map(nou); const reScrieChei = (obj, cheieNoua) => { if (!obj) return obj; const out = {}; Object.keys(obj).forEach((k) => { out[cheieNoua(k)] = obj[k]; }); return out; }; s.valori.plan = reScrieChei(s.valori.plan, nou); /* itemii urmaribili ai valorilor din hibrid/calendar: cheia "val||" */ const cheieItem = (k) => { const p = k.split("|"); return p.length === 3 && p[0] === "val" ? "val|" + nou(p[1]) + "|" + p[2] : k; }; s.plan = reScrieChei(s.plan, cheieItem); s.log = reScrieChei(s.log, cheieItem); } /* consola indeplinita = poarta de acces la instrument si la export */ const consolaOk = (s) => s.consola.nume.trim() !== "" && String(s.consola.dataStart || "").trim() !== ""; const keyOf = (slug, ariaNr, indNr, actIdx) => `${slug}|${ariaNr}|${indNr}|${actIdx}`; /* perechile comportament <-> actiune ale unei arii (structura noua, zoom 2 din 24.07): comportamentul k se leaga de actiunea k in cadrul fiecarui indicator; numerotare continua pe arie */ function perechiAria(slug, aria) { const rez = []; let nr = 0; aria.indicatori.forEach((ind) => { const n = Math.min(ind.comportamente.length, ind.actiuni.length); for (let k = 0; k < n; k++) { nr += 1; rez.push({ nr, key: keyOf(slug, aria.nr, ind.nr, k), comp: ind.comportamente[k], act: ind.actiuni[k], ind }); } }); return rez; } /* Cheia unei actiuni asumate e POZITIONALA (`credinta|arie|indicator|index`). Cand Dr. Petre trimite un document nou si mecanismul se re-parseaza, aceeasi cheie poate ajunge pe ALTA actiune (dovedit la auditul din 25.07: `iubire|2|2|0` arata azi altceva decat ieri) sau poate ramane fara corespondent, si atunci actiunea dispare tacut din plan. De aceea, la asumare se retine si o AMPRENTA a textului: la citire se compara, iar nepotrivirea se semnaleaza. */ const AMPRENTA = 60; const amprenta = (t) => String(t || "").slice(0, AMPRENTA); function planEntries(plan) { return Object.entries(plan).map(([k, v]) => { const [slug, ariaNr, indNr, actIdx] = k.split("|"); const cred = CREDINTE.find((c) => c.slug === slug); const aria = (MECANISME[slug] || []).find((a) => a.nr === +ariaNr); const ind = aria && aria.indicatori.find((i) => i.nr === +indNr); const text = ind ? ind.actiuni[+actIdx] : ""; const e = { key: k, slug, cred, aria, ind, actIdx: +actIdx, text, vreau: "", nuVreau: "", ancora: "", rutina: "", oa: 1, orNr: 0, ...v }; /* intrarile de dinaintea amprentei (fara `sursa`) nu se semnaleaza - n-avem cu ce compara */ e.schimbat = !!(v.sursa && text && amprenta(text) !== v.sursa); return e; }).filter((e) => e.cred && e.aria && e.ind); } /* cheile care nu mai au corespondent in mecanismele curente (actiuni disparute la re-parsare) */ function planOrfane(plan) { return Object.keys(plan || {}).length - planEntries(plan || {}).length; } /* cate bifari are actiunea in perioada curenta a frecventei ei */ const isLogged = (log, key, pk) => !!(log[key] && log[key][pk]); /* ziua din care un item intra la socoteala: la credinte = ziua asumarii (`adaugat`), la valori = ziua in care campul a fost completat (`adaugat` pus de valori.jsx). Fara ea, o actiune asumata azi era judecata si pe zilele in care nici nu exista, iar cifrele scadeau exact cand omul isi asuma mai mult (audit 25.07: 100% -> 57% si sir 7 -> 1, dupa o singura adaugare). */ function dinZiua(e) { const d = e.adaugat ? new Date(e.adaugat + "T00:00:00") : null; return d && !isNaN(d) ? d : null; } const activaLa = (e, d) => { const de = dinZiua(e); return !de || de <= d; }; /* conformarea pe ultimele 7 zile (doar actiuni zilnice, doar din ziua asumarii incolo) */ function conform7(entries, log, refDate) { const daily = entries.filter((e) => e.frecventa === "Zilnic"); if (!daily.length) return null; let done = 0, expected = 0; for (let i = 6; i >= 0; i--) { const d = addDays(refDate, -i); const iso = fmtISO(d); daily.forEach((e) => { if (!activaLa(e, d)) return; expected += 1; if (isLogged(log, e.key, iso)) done += 1; }); } return expected ? { done, expected, pct: Math.round((done / expected) * 100) } : null; } /* zile consecutive cu TOATE zilnicele bifate (azi intra doar daca e complet); intr-o zi se judeca doar actiunile care existau atunci */ function streakZile(entries, log, refDate) { const daily = entries.filter((e) => e.frecventa === "Zilnic"); if (!daily.length) return 0; const complete = (d) => { const active = daily.filter((e) => activaLa(e, d)); return active.length > 0 && active.every((e) => isLogged(log, e.key, fmtISO(d))); }; let n = 0; let d = complete(refDate) ? refDate : addDays(refDate, -1); while (complete(d)) { n += 1; d = addDays(d, -1); if (n > 3650) break; } return n; } /* helperii expusi view-urilor hibride din hibrid.jsx (spec hibrid 24.07) - ancora ADITIVA; const-urile de mai sus raman sursa, aici doar referinte */ window.PRP_UTIL = { fmtISO, addDays, startOfWeek, sameDay, periodKey, periodLabel, isLogged, planEntries, conform7, streakZile, ZILE_RO, DICTIONAR, /* gettere: pkToDate si CREDINTE sunt declarate mai jos in fisier (TDZ) - se rezolva la acces */ get pkToDate() { return pkToDate; }, get CREDINTE() { return CREDINTE; }, IcCheck, IcArrow, IcChev, IcChevL }; /* ── shell ── */ function Sidebar({ store, set, drawer }) { const nav = (view, extra) => () => set((s) => ({ ...s, view, ...(extra || {}) })); /* doar intrarile valide (cheile orfane ale mecanismelor retrase nu se numara) */ const planCount = planEntries(store.plan).length; const nume = store.consola.nume.trim(); const gate = !consolaOk(store); const shownView = gate ? "consola" : store.view; const items = [ /* ordinea = cea data de Marian (25.07): intai unde LUCREZI zi de zi (Acasa, Calendarul, Planul), apoi la ce te UITI (Dictionar, Biblioteca), apoi perechile chestionar -> instrument, in ordinea metodologica (intai te evaluezi, apoi lucrezi), la final Consola */ ["acasa", "Acasă", IcHome], ["calendar", "Calendarul meu", IcCal], ["plan", "Plan strategic", IcTarget], ["dictionar", "Dicționar", IcBook], ["biblioteca", "Biblioteca metodologică", window.IcBiblio], ["anamneza", "Chestionare credințe", window.IcAnamneza], /* redenumit din "Anamneza" - zoom 5, aprobat Petre */ ["credinte", "Credințele mele", IcGem], ["chestionare", "Chestionare valori", window.IcChv], ["valori", "Valorile mele", window.IcValori], ["consola", "Consolă", IcUser], /* ce stie instrumentul despre el si ce poate controla - apare doar cand ruleaza pe web, fiindca doar acolo exista evidenta (pilot.jsx lipseste din standalone-ul offline) */ ...(window.PilotDateleMele ? [["date-mele", "Datele mele", window.IcScut || IcUser]] : []), ]; const active = (v) => shownView === v || (v === "credinte" && shownView === "credinta"); return ( ); } /* meniul orizontal cu toate credintele - saritul direct la oricare */ function CredinteTabs({ store, set }) { const activ = store.view === "credinta" ? store.credinta : null; return (
{CREDINTE.map((c) => ( ))}
); } /* ── Credintele mele (index) ── */ function CredinteIndexView({ store, set }) { const entries = planEntries(store.plan); return (
{CREDINTE.map((c) => { const arii = MECANISME[c.slug] || []; const nPerechi = arii.reduce((n, a) => n + a.indicatori.reduce((m, i) => m + Math.min(i.comportamente.length, i.actiuni.length), 0), 0); const inPlan = entries.filter((e) => e.slug === c.slug); return (
set((s) => ({ ...s, view: "credinta", credinta: c.slug })) : undefined} onKeyDown={c.gata ? (ev) => { if (ev.key === "Enter" || ev.key === " ") { ev.preventDefault(); set((s) => ({ ...s, view: "credinta", credinta: c.slug })); } } : undefined}>
{c.titlu}
{c.gata ? `${arii.length} arii · ${nPerechi} comportamente cu acțiuni` : "mecanism în lucru la Dr. Petre"}
{inPlan.length > 0 ? {inPlan.length} în plan : {c.gata ? "nicio acțiune aleasă" : "în lucru"}} {c.gata && ( )}
); })}
); } /* ── picker frecventa ── */ function FreqPicker({ onPick, onClose, suggested, sus }) { const ref = useRef(null); useEffect(() => { const h = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); }; document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h); }, [onClose]); return (
{FRECVENTE.map((f) => ( ))}
); } /* ── Credinta (mecanismul) ── */ /* Matricea credintei - tabelul lui Petre cu input pe randurile alocate (spec 23.07) */ function MatriceCredinta({ cred, arii, store, set, addToPlan, setNr, setRep, picker, setPicker }) { const [pickerSus, setPickerSus] = useState(false); const grupuri = []; arii.forEach((aria) => { const perechi = perechiAria(cred.slug, aria).map((p) => ({ ...p, e: store.plan[p.key] })); const vizibile = store.doarAleMele ? perechi.filter((p) => p.e) : perechi; if (vizibile.length) grupuri.push({ aria, perechi: vizibile }); }); return (

Matricea credinței - {cred.titlu}

{grupuri.length === 0 ? (
Nicio acțiune alocată încă - debifează „doar ale mele” sau alocă din coloana Acțiunea.
) : (
{grupuri.map(({ aria, perechi }) => ( {perechi.map((p) => ( {p.e ? (
# Comportamentul observabil Acțiunea concretă Reperele de viață personală Status Principiul de Viață / Ancora de Integrare Rutina
Cum vreau să fac asta Cum nu trebuie să fac OA OR
{String(aria.nr).padStart(2, "0")} - {aria.nume}
{p.nr} {p.comp}
{p.act} {p.e ? ( {picker === p.key && ( { set((s) => ({ ...s, plan: { ...s.plan, [p.key]: { ...s.plan[p.key], frecventa: f } } })); setPicker(null); }} onClose={() => setPicker(null)} /> )} ) : ( {picker === p.key && ( addToPlan(p.key, f, p.act)} onClose={() => setPicker(null)} /> )} )}