/* global React, ReactDOM, Header, Footer */ const { useState: useStateB, useEffect: useEffectB } = React; /* ============================================================ Tiny markdown parser (safe-ish: escapes HTML first) ============================================================ */ function escapeHtml(str) { return str.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]); } function md(src) { if (!src) return ""; let s = src; // Code blocks const blocks = []; s = s.replace(/```([\s\S]*?)```/g, (_, code) => { blocks.push(`
${escapeHtml(code.trim())}
`); return `\u0000BLOCK${blocks.length - 1}\u0000`; }); // Images const imgs = []; s = s.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src2) => { imgs.push(`
${escapeHtml(alt)}${alt ? `
${escapeHtml(alt)}
` : ""}
`); return `\u0000IMG${imgs.length - 1}\u0000`; }); s = escapeHtml(s); s = s.replace(/^### (.+)$/gm, "

$1

"); s = s.replace(/^## (.+)$/gm, "

$1

"); s = s.replace(/^# (.+)$/gm, "

$1

"); s = s.replace(/^> (.+)$/gm, "
$1
"); s = s.replace(/(^|\n)((?:- [^\n]+(?:\n|$))+)/g, (m, pre, list) => { const items = list.trim().split(/\n/).map(l => l.replace(/^- /, "")).map(t => `
  • ${t}
  • `).join(""); return `${pre}`; }); s = s.replace(/\*\*(.+?)\*\*/g, "$1"); s = s.replace(/\*(.+?)\*/g, "$1"); s = s.replace(/`([^`]+)`/g, "$1"); s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); s = s.split(/\n{2,}/).map(block => { const trimmed = block.trim(); if (!trimmed) return ""; if (/^<(h\d|ul|ol|pre|blockquote)/.test(trimmed) || /\u0000(IMG|BLOCK)\d+\u0000/.test(trimmed)) return trimmed; return `

    ${trimmed.replace(/\n/g, "
    ")}

    `; }).join("\n"); s = s.replace(/\u0000IMG(\d+)\u0000/g, (_, i) => imgs[+i]); s = s.replace(/\u0000BLOCK(\d+)\u0000/g, (_, i) => blocks[+i]); return s; } /* ============================================================ Cover renderer — image if available, fallback SVG ============================================================ */ function CoverImg({ post, big }) { const c = post.cover; // Handle old format (number) and new (object) if (typeof c === "number") { return ; } if (c && c.image) { return {c.alt; } return ; } function CoverSVG({ kind }) { if (kind === 1) { return ( ); } if (kind === 2) { return ( ); } return ( ); } /* ============================================================ SEO meta tag injection ============================================================ */ function setMetaTag(attr, key, value) { let el = document.querySelector(`meta[${attr}="${key}"]`); if (!value) { if (el) el.remove(); return; } if (!el) { el = document.createElement("meta"); el.setAttribute(attr, key); document.head.appendChild(el); } el.setAttribute("content", value); } function setLinkCanonical(href) { let el = document.querySelector('link[rel="canonical"]'); if (!href) { if (el) el.remove(); return; } if (!el) { el = document.createElement("link"); el.setAttribute("rel", "canonical"); document.head.appendChild(el); } el.setAttribute("href", href); } function setJsonLd(data) { let el = document.getElementById("ms-jsonld"); if (el) el.remove(); if (!data) return; el = document.createElement("script"); el.type = "application/ld+json"; el.id = "ms-jsonld"; el.textContent = JSON.stringify(data); document.head.appendChild(el); } function applyPostSEO(post, siteUrl) { if (!post) { document.title = "Blog · MinerSuite"; setMetaTag("name", "description", "Conteúdo técnico para consultorias em mineração."); setMetaTag("property", "og:title", "Blog · MinerSuite"); setMetaTag("property", "og:description", "Conteúdo técnico para consultorias em mineração."); setMetaTag("property", "og:type", "website"); setMetaTag("property", "og:image", ""); setMetaTag("name", "twitter:card", "summary_large_image"); setLinkCanonical(siteUrl + "/blog.html"); setJsonLd(null); return; } const seo = post.seo || {}; const url = siteUrl + "/blog.html#" + post.slug; const img = (post.cover && post.cover.image) ? (siteUrl + "/" + post.cover.image) : ""; const title = seo.metaTitle || post.title; const desc = seo.metaDescription || post.excerpt; document.title = title; setMetaTag("name", "description", desc); setMetaTag("name", "keywords", (post.tags || []).join(", ")); setMetaTag("name", "author", post.author); setMetaTag("property", "og:type", "article"); setMetaTag("property", "og:title", title); setMetaTag("property", "og:description", desc); setMetaTag("property", "og:url", url); setMetaTag("property", "og:image", img); setMetaTag("property", "article:published_time", post.date); setMetaTag("property", "article:section", post.category); setMetaTag("name", "twitter:card", "summary_large_image"); setMetaTag("name", "twitter:title", title); setMetaTag("name", "twitter:description", desc); setMetaTag("name", "twitter:image", img); setLinkCanonical(url); setJsonLd({ "@context": "https://schema.org", "@type": "Article", "headline": post.title, "description": desc, "image": img || undefined, "datePublished": post.date, "dateModified": post.date, "author": { "@type": "Organization", "name": post.author }, "publisher": { "@type": "Organization", "name": "MinerSuite", "logo": { "@type": "ImageObject", "url": siteUrl + "/assets/marca/favicon.svg" } }, "mainEntityOfPage": url, "keywords": (post.tags || []).join(", ") }); } function formatDate(iso) { const d = new Date(iso + "T00:00:00"); const MES = ["JAN","FEV","MAR","ABR","MAI","JUN","JUL","AGO","SET","OUT","NOV","DEZ"]; return `${String(d.getDate()).padStart(2,"0")} ${MES[d.getMonth()]} · ${d.getFullYear()}`; } /* ============================================================ Hero ============================================================ */ function BlogHero({ count }) { return (
    BLOG · MINERSUITE

    Conteúdo técnico para consultorias em mineração.

    Artigos sobre gestão operacional, propostas, contratos, projetos técnicos e organização de carteira minerária — escritos a partir da rotina real do setor.

    ARTIGOS {count} Publicados até hoje.
    CATEGORIAS Gestão · Operação · Dados Conteúdo técnico do setor mineral.
    ); } /* ============================================================ Post list ============================================================ */ function PostList({ posts }) { if (!posts.length) { return (
    ARQUIVO VAZIO

    Nenhum artigo publicado ainda.

    Os primeiros conteúdos serão publicados em breve.

    ); } const [featured, ...rest] = posts; return (
    {featured.category}
    DESTAQUE

    {featured.title}

    {featured.excerpt}

    {formatDate(featured.date)} · {featured.readTime} min de leitura · {featured.author}
    Ler artigo
    {rest.length > 0 && ( <>
    DEMAIS ARTIGOS
    {rest.map((p) => (
    {p.category}
    {formatDate(p.date)} · {p.readTime} MIN

    {p.title}

    {p.excerpt}

    Ler artigo
    ))}
    )}
    ); } /* ============================================================ Single post view ============================================================ */ function PostView({ post, allPosts }) { const others = allPosts.filter((p) => p.id !== post.id && !p.draft).slice(0, 2); return (
    Voltar para o blog {post.category}

    {post.title}

    {post.excerpt}

    {formatDate(post.date)} · {post.readTime} min de leitura · {post.author}
    {(post.tags && post.tags.length > 0) && (
    {post.tags.map((t) => {t})}
    )}
    CONTINUE LENDO

    Outros artigos do MinerSuite.

    PRÓXIMO PASSO

    Coloque o método em prática com uma plataforma feita para mineração.

    Centralize clientes, propostas, contratos, financeiro e projetos técnicos em uma única plataforma.

    ); } /* ============================================================ Page ============================================================ */ function BlogPage() { const [posts, setPosts] = useStateB(null); const [siteUrl, setSiteUrl] = useStateB(""); const [route, setRoute] = useStateB(window.location.hash.slice(1)); const [error, setError] = useStateB(null); useEffectB(() => { fetch("assets/posts.json?v=" + Date.now()) .then((r) => { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); }) .then((data) => { setPosts((data.posts || []).filter((p) => !p.draft).sort((a, b) => b.date.localeCompare(a.date))); setSiteUrl((data.config && data.config.siteUrl) || window.location.origin); }) .catch((e) => { console.warn("posts.json não encontrado.", e); setPosts([]); setError(true); }); }, []); useEffectB(() => { function onHash() { setRoute(window.location.hash.slice(1)); window.scrollTo({ top: 0, behavior: "instant" }); } window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); // Inject SEO meta tags whenever route or posts change useEffectB(() => { if (posts === null) return; const current = route ? posts.find((p) => p.slug === route) : null; applyPostSEO(current, siteUrl || window.location.origin); }, [posts, route, siteUrl]); if (posts === null) { return ( <>
    CARREGANDO ARTIGOS…