/* global React */ /* DocumentPreview — renders a SOP doc faithfully to the Google Doc reference. */ const { useMemo: useMemoDP } = React; /* ---- inline rich-text: **bold** + paragraphs + bullets ---- */ function renderRichText(text, key = "rt") { if (!text) return null; const lines = String(text).split(/\n/); const nodes = []; let buf = []; // paragraph buffer (string lines) let listBuf = []; // bullet buffer (string lines) let idx = 0; const flushPara = () => { if (!buf.length) return; const txt = buf.join(" ").trim(); if (txt) { nodes.push(

{renderInlineBold(txt)}

); } buf = []; }; const flushList = () => { if (!listBuf.length) return; nodes.push( ); listBuf = []; }; for (const raw of lines) { const line = raw.trim(); if (!line) { flushList(); flushPara(); continue; } const bullet = line.match(/^[•\-\*]\s+(.*)$/); if (bullet) { flushPara(); listBuf.push(bullet[1]); } else { flushList(); buf.push(line); } } flushList(); flushPara(); return nodes; } function renderInlineBold(s) { const parts = String(s).split(/(\*\*[^*]+\*\*)/g); return parts.map((p, i) => { const m = p.match(/^\*\*(.+)\*\*$/); if (m) return {m[1]}; return {p}; }); } /* ---- date formatter (preserves YYYY-MM-DD) ---- */ function fmtDate(d) { if (!d) return ""; // Already YYYY-MM-DD? keep as-is to match the reference. if (/^\d{4}-\d{2}-\d{2}$/.test(d)) return d; return d; } function SectionH2({ children, num }) { return (

{num != null && {num}.} {children}

); } function DocumentPreview({ doc }) { const hasIntro = !!(doc.intro && doc.intro.trim()); const hasObjective = !!(doc.objective && doc.objective.trim()); const hasScope = !!(doc.scope && doc.scope.trim()); const hasFlow = doc.flow && doc.flow.filter(s => s && s.trim()).length > 0; const hasRoles = doc.roles && doc.roles.some(r => r.name || r.desc); const hasResources = doc.resources && doc.resources.some(r => r.name || r.desc); const hasPhases = doc.phases && doc.phases.some(p => p.title || (p.steps && p.steps.some(s => s.title || s.body))); const hasDefs = doc.definitions && doc.definitions.some(d => d.term || d.meaning); const hasRefs = doc.references && doc.references.some(r => r.term || r.meaning); // Section auto-numbering (matches user's outline) let secNum = 0; const nextNum = () => ++secNum; return (
{/* ----- COVER ----- */}
Ads&Law

{doc.title || Sin título — añade un título arriba}

Versión {doc.version || "1.0"} · {fmtDate(doc.date) || "—"}

{/* ----- INTRO ----- */} {hasIntro &&
{renderRichText(doc.intro, "intro")}
} {/* ----- OBJECTIVE + SCOPE (inline lead-style, matches reference) ----- */} {(hasObjective || hasScope) && (
{hasObjective && (

Objetivo. {doc.objective}

)} {hasScope && (

Alcance. {doc.scope}

)}
)} {/* ----- RESUMEN DEL FLUJO ----- */} {hasFlow && ( <>

Resumen del flujo

{doc.flow.filter(Boolean).map((step, i, arr) => (
{i + 1}. {step}
{i < arr.length - 1 &&
}
))}
)} {/* ----- ROLES ----- */} {hasRoles && (() => { const n = nextNum(); return ( <> Roles y responsabilidades {doc.roles.filter(r => r.name || r.desc).map(r => (
{r.name}
{r.desc}
))} ); })()} {/* ----- RESOURCES ----- */} {hasResources && (() => { const n = nextNum(); return ( <> Recursos y herramientas {doc.resources.filter(r => r.name || r.desc).map(r => (
{r.name}
{r.desc}
))} ); })()} {/* ----- PROCEDURE ----- */} {hasPhases && (() => { const n = nextNum(); return ( <> Procedimiento paso a paso {doc.phases.map(phase => { const hasContent = phase.title || (phase.steps && phase.steps.some(s => s.title || s.body)); if (!hasContent) return null; return (
Fase {phase.num} {phase.title}
{(phase.steps || []).filter(s => s.title || s.body || (s.substeps && s.substeps.length)).map(step => (
{step.done && } Paso {step.num} {step.title} {step.time && ( · {step.time} )}
{step.body && (
{renderRichText(step.body, `step-${step.id}`)}
)} {step.image && (
)} {step.substeps && step.substeps.filter(ss => ss.text).map(ss => (
{ss.num}. {ss.text}
))}
))}
); })} ); })()} {/* ----- DEFINITIONS ----- */} {hasDefs && (() => { const n = nextNum(); return ( <> Definiciones {doc.definitions.filter(d => d.term || d.meaning).map(d => (
{d.term}
{d.meaning}
))} ); })()} {/* ----- REFERENCES ----- */} {hasRefs && (() => { const n = nextNum(); return ( <> Referencias {doc.references.filter(r => r.term || r.meaning).map(r => (
{r.term}
{r.meaning}
))} ); })()} {/* ----- empty state ----- */} {!hasIntro && !hasObjective && !hasScope && !hasFlow && !hasRoles && !hasResources && !hasPhases && !hasDefs && !hasRefs && (

Empieza rellenando los campos en el panel de la izquierda — el documento se actualizará aquí en tiempo real. Si tienes notas en bruto, pulsa Estructurar con IA y la app te lo organiza.

)} {/* ----- FOOTER ----- */}
Ads&Law SOP · {doc.title ? doc.title.slice(0, 60) : "Sin título"} · v{doc.version || "1.0"}
); } window.DocumentPreview = DocumentPreview;