/* global React */ /* =========================================================== VOLTLAB · INFRAESTRUTURA Quantitativo (BOM) de eletrocalhas, leitos, perfilados e eletrodutos a partir das distâncias do projeto. Adaptado para rodar dentro do shell VoltLab. =========================================================== */ const { useState: useStateInfra, useMemo: useMemoInfra } = React; const useInfraPersist = (key, initial) => window.useLocalStorage ? window.useLocalStorage(key, initial) : useStateInfra(initial); /* ── Cálculo (fórmulas da planilha) ───────────────────── */ const INFRA_CALC = { eletrocalha(m) { const d = parseFloat(m) || 0; if (d <= 0) return null; const u = Math.ceil(d / 3); return [ { item: "Eletrocalha (barra 3m)", qtd: u }, { item: "Emendas tipo tala/simples", qtd: u * 2 }, { item: 'Parafusos lentilha 1/4" × 3/4"', qtd: u * 8 }, { item: 'Porcas 1/4"', qtd: u * 8 }, { item: 'Arruelas lisas 1/4"', qtd: u * 8 }, { item: "Mão francesa (paraf. fix. 3/8)", qtd: u * 2 }, { item: "Fixação / bucha / parabolts", qtd: u * 4 }, ]; }, leito(m) { const d = parseFloat(m) || 0; if (d <= 0) return null; const u = Math.ceil(d / 3); return [ { item: "Leito portacabos (barra 3m)", qtd: u }, { item: "Emendas tipo tala/simples", qtd: u * 2 }, { item: 'Parafusos lentilha 3/8" × 3/4"', qtd: u * 8 }, { item: 'Porcas 3/8"', qtd: u * 8 }, { item: 'Arruelas lisas 3/8"', qtd: u * 8 }, { item: "Mão francesa (paraf. fix. 3/8)", qtd: u * 2 }, { item: "Fixação / bucha / parabolts", qtd: u * 4 }, ]; }, perfilado(m) { const d = parseFloat(m) || 0; if (d <= 0) return null; const u = Math.ceil(d / 6); return [ { item: "Perfilado 38×38 (barra 6m)", qtd: u }, { item: "Suporte / gancho perfilado 38×38", qtd: u * 4 }, { item: 'Parafuso lentilha 1/4" × 3/4"', qtd: u * 8 }, { item: 'Porca 1/4"', qtd: u * 8 }, { item: 'Arruela lisa 1/4"', qtd: u * 8 }, { item: "Mão francesa", qtd: u * 4 }, { item: "Fixação / bucha / parabolts", qtd: (u * 8) + 1 }, ]; }, eletroduto(m, ce) { const d = parseFloat(m) || 0; if (d <= 0) return null; const u = Math.ceil(d / 3); const c = parseInt(ce) || 0; return [ { item: "Eletroduto galvanizado (barra 3m)", qtd: u }, { item: "Luvas", qtd: Math.max(0, u - 1) }, { item: "Abraçadeiras", qtd: u * 2 }, { item: "Parafusos auto-atarraxante 4,8mm × 50mm", qtd: u * 2 }, { item: "Buchas nylon S8", qtd: u * 2 }, { item: "Conduletes (trechos retos/curvas)", qtd: c }, ]; }, }; const INFRA_TONE = { eletrocalha: "var(--accent)", leito: "var(--accent-2)", perfilado: "var(--ok)", eletroduto: "var(--warn)", }; const InfraIc = ({ n, s = 18 }) => { const p = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" }; const d = { tray: <>, rail: <>, bolt: , ring: <>, check: , info: <>, dl: <>, reset: <>, }; return {d[n]}; }; const InfraBanner = ({ kind, children }) => (
{children}
); const InfraNum = ({ label, hint, value, onChange, suffix }) => (
onChange(e.target.value)}/> {suffix && {suffix}}
); const InfraMatTable = ({ title, icon, tipo, items, dist, barra }) => { const cor = INFRA_TONE[tipo]; if (!items) return (
{title}
— informe a distância
); return (
{title.toUpperCase()}

{title}

{items[0].qtd}
BARRAS · {dist}m ÷ {barra}m
{items.map((row, i) => ( ))}
MATERIAL QTD.
{row.item} {row.qtd}
); }; const InfraDiagram = ({ dc, dl, dp, de }) => { const vals = [ { label: "Eletrocalha", m: parseFloat(dc) || 0, cor: INFRA_TONE.eletrocalha }, { label: "Leito", m: parseFloat(dl) || 0, cor: INFRA_TONE.leito }, { label: "Perfilado", m: parseFloat(dp) || 0, cor: INFRA_TONE.perfilado }, { label: "Eletroduto", m: parseFloat(de) || 0, cor: INFRA_TONE.eletroduto }, ].filter(v => v.m > 0); if (!vals.length) return null; const max = Math.max(...vals.map(v => v.m)); return (
ESCALA VISUAL
{vals.map((v, i) => (
{v.label} {v.m} m
))}
); }; const InfraBOM = ({ res }) => { const tipos = ["eletrocalha", "leito", "perfilado", "eletroduto"]; const labels = { eletrocalha: "Eletrocalha", leito: "Leito", perfilado: "Perfilado", eletroduto: "Eletroduto" }; const ativos = tipos.filter(t => res[t]); if (!ativos.length) return (
Informe ao menos uma distância na aba Dimensionamento.
); const mapa = {}; tipos.forEach(tipo => { (res[tipo] || []).forEach(r => { if (!mapa[r.item]) mapa[r.item] = { item: r.item, tipos: {} }; mapa[r.item].tipos[tipo] = r.qtd; }); }); const totais = {}; ativos.forEach(t => { totais[t] = (res[t] || []).reduce((s, r) => s + r.qtd, 0); }); const exportCSV = () => { const rows = [["MATERIAL", ...ativos.map(t => labels[t].toUpperCase())]]; Object.values(mapa).forEach(r => rows.push([r.item, ...ativos.map(t => r.tipos[t] ?? "")])); const csv = rows.map(r => r.join(";")).join("\n"); const a = document.createElement("a"); a.href = URL.createObjectURL(new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" })); a.download = "infraestrutura-bom.csv"; a.click(); if (window.vlToast) window.vlToast("✓ CSV exportado"); }; return (
{tipos.map(tipo => { const it = res[tipo]; const cor = INFRA_TONE[tipo]; return (
{labels[tipo].toUpperCase()}
{it ? it[0].qtd : "—"}
barras/peças
); })}
CONSOLIDADO · BILL OF MATERIALS

Quantitativo geral do projeto

{ativos.map(t => ())} {Object.values(mapa).map((row, i) => { const total = ativos.reduce((s, t) => s + (row.tipos[t] || 0), 0); return ( {ativos.map(t => ())} ); })} {ativos.map(t => ())}
MATERIAL{labels[t].toUpperCase()}TOTAL
{row.item}{row.tipos[t] || "—"}{total || "—"}
TOTAL DE ITENS{totais[t]}{ativos.reduce((s, t) => s + totais[t], 0)}
); }; /* ── Componente principal ─────────────────────────────── */ const Infraestrutura = () => { const [dc, setDc] = useInfraPersist("vl_infra_calha", ""); const [dl, setDl] = useInfraPersist("vl_infra_leito", ""); const [dp, setDp] = useInfraPersist("vl_infra_perf", ""); const [de, setDe] = useInfraPersist("vl_infra_eldt", ""); const [cond, setCond] = useInfraPersist("vl_infra_cond", "0"); const [tab, setTab] = useStateInfra("calc"); const res = useMemoInfra(() => ({ eletrocalha: INFRA_CALC.eletrocalha(dc), leito: INFRA_CALC.leito(dl), perfilado: INFRA_CALC.perfilado(dp), eletroduto: INFRA_CALC.eletroduto(de, cond), }), [dc, dl, dp, de, cond]); const ativos = Object.values(res).filter(Boolean).length; const totalItens = Object.values(res).filter(Boolean).flatMap(r => r).reduce((s, r) => s + r.qtd, 0); const totalDist = [dc, dl, dp, de].reduce((s, v) => s + (parseFloat(v) || 0), 0); const reset = () => { setDc(""); setDl(""); setDp(""); setDe(""); setCond("0"); if (window.vlToast) window.vlToast("✓ Campos limpos"); }; const Sync = window.VL_ProjetoSync; return (
{/* Abas */}
{[{ id: "calc", label: "Dimensionamento" }, { id: "resumo", label: "Resumo / BOM" }].map(t => ( ))}
{tab === "resumo" ? ( ) : (
ENTRADA · DISTÂNCIAS

Dados do projeto

AUTO
{Sync && ( { const c = s.get("comprimento"); if (c != null) setDe(String(c)); }} push={() => ({ comprimento: parseFloat(de) || undefined })} /> )}
FÓRMULAS DE REFERÊNCIA
{[ ["01", "Eletrocalha / Leito", "N = ⌈ distância ÷ 3 ⌉"], ["02", "Perfilado 38×38", "N = ⌈ distância ÷ 6 ⌉"], ["03", "Eletroduto galv.", "N = ⌈ distância ÷ 3 ⌉"], ].map(([n, title, formula]) => (
{n} {title}
{formula}
))}
{ativos === 0 && (
Informe uma distância para gerar o quantitativo.
)}
)}
); }; window.VL_Infraestrutura = Infraestrutura;