/* global React */ /* =========================================================== VOLTLAB · CONFIGURAÇÕES - VL_PREFS: store global de preferências de engenharia, persistido em localStorage + pub/sub (módulos podem ler window.VL_PREFS.get("tensao") etc. como ponto de partida). - VL_Settings: página de configurações (Aparência, Padrões de cálculo, Identificação & Relatórios, Dados & privacidade). =========================================================== */ const VL_PREFS_KEY = "vl_prefs_v1"; const VL_PREFS_DEFAULTS = { // Padrões de cálculo tensao: 220, tensaoCustom: false, sistema: "trifasico", frequencia: 60, material: "Cu", tempAmbiente: 30, isolacao: "PVC", quedaMax: 4, fpAlvo: 0.92, // Identificação & relatórios empresa: "", responsavel: "", crea: "", exportFmt: "json", decimais: 2, }; window.VL_PREFS_DEFAULTS = VL_PREFS_DEFAULTS; /* ---------- store singleton ---------- */ const VL_PREFS = (() => { const subs = new Set(); let data = (() => { try { const r = localStorage.getItem(VL_PREFS_KEY); return r ? { ...VL_PREFS_DEFAULTS, ...JSON.parse(r) } : { ...VL_PREFS_DEFAULTS }; } catch { return { ...VL_PREFS_DEFAULTS }; } })(); const persist = () => { try { localStorage.setItem(VL_PREFS_KEY, JSON.stringify(data)); } catch {} }; const emit = () => { persist(); subs.forEach(f => f(data)); window.dispatchEvent(new CustomEvent("vl:prefs", { detail: data })); }; return { all: () => data, get: (k) => data[k], set(k, v) { data = { ...data, [k]: v }; emit(); }, setMany(obj) { data = { ...data, ...obj }; emit(); }, reset() { data = { ...VL_PREFS_DEFAULTS }; emit(); }, subscribe(f) { subs.add(f); return () => subs.delete(f); }, }; })(); window.VL_PREFS = VL_PREFS; function useVLPrefs() { const [, force] = React.useState(0); React.useEffect(() => VL_PREFS.subscribe(() => force(x => x + 1)), []); return VL_PREFS; } window.useVLPrefs = useVLPrefs; /* ---------- ícones locais ---------- */ const StIcon = ({ n, s = 16 }) => { const p = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" }; const d = { palette: <>, calc: <>, file: <>, shield: <>, check: , trash: <>, refresh: <>, warn: <>, company: <>, }; return {d[n] || null}; }; /* ---------- primitivos de linha de configuração ---------- */ const SetRow = ({ label, hint, children, full }) => (
{label} {hint && {hint}}
{children}
); const SetSeg = ({ value, onChange, options }) => (
{options.map(o => ( ))}
); const SetNum = ({ value, onChange, unit, step = 1, min, max, placeholder }) => ( onChange(e.target.value === "" ? "" : parseFloat(e.target.value))} /> {unit && {unit}} ); const SetText = ({ value, onChange, placeholder, icon }) => ( {icon && } onChange(e.target.value)} /> ); const SetToggle = ({ value, onChange, on = "Ativado", off = "Desativado" }) => ( ); /* swatches de cor de acento (mesmos hex do ACCENT_MAP do app) */ const ACCENT_SWATCHES = [ { hex: "#3D8BFF", name: "Azul", color: "oklch(72% 0.18 245)" }, { hex: "#3DD6E6", name: "Ciano", color: "oklch(82% 0.14 200)" }, { hex: "#5BE38C", name: "Verde", color: "oklch(84% 0.18 145)" }, { hex: "#F2B043", name: "Âmbar", color: "oklch(80% 0.16 75)" }, ]; /* =========================================================== SETTINGS PAGE =========================================================== */ const VL_Settings = ({ tweaks = {}, setTweak = () => {}, onNav = () => {} }) => { const prefs = useVLPrefs(); const P = prefs.all(); const [active, setActive] = React.useState("aparencia"); const scrollerRef = React.useRef(null); const SECTIONS = [ { id: "aparencia", label: "Aparência", icon: "palette" }, { id: "calculo", label: "Padrões de cálculo", icon: "calc" }, { id: "relatorios", label: "Identificação & Relatórios", icon: "file" }, { id: "dados", label: "Dados & privacidade", icon: "shield" }, ]; const toast = (m) => window.vlToast ? window.vlToast(m) : null; const set = (k, v) => prefs.set(k, v); /* navegação por âncoras dentro do scroller do app */ const goSection = (id) => { setActive(id); const root = scrollerRef.current; const target = root && root.querySelector(`#sec-${id}`); const scroller = root && root.closest(".main-scroll"); if (target && scroller) { const top = target.offsetTop - 14; scroller.scrollTo({ top, behavior: "smooth" }); } }; /* destaca seção ativa conforme rolagem */ React.useEffect(() => { const root = scrollerRef.current; const scroller = root && root.closest(".main-scroll"); if (!scroller) return; const onScroll = () => { const secs = SECTIONS.map(s => root.querySelector(`#sec-${s.id}`)).filter(Boolean); const y = scroller.scrollTop + 80; let cur = SECTIONS[0].id; for (const el of secs) { if (el.offsetTop <= y) cur = el.id.replace("sec-", "") || el.id; } const found = secs.reverse().find(el => el.offsetTop <= y); if (found) setActive(found.id.replace("sec-", "")); }; scroller.addEventListener("scroll", onScroll, { passive: true }); return () => scroller.removeEventListener("scroll", onScroll); }, []); const handleResetAppearance = () => { setTweak("accent", "#3D8BFF"); setTweak("density", "comfortable"); setTweak("showCircuitry", true); setTweak("monoReadouts", true); toast("Aparência restaurada ao padrão"); }; const handleResetPrefs = () => { prefs.reset(); toast("Padrões de cálculo restaurados"); }; const handleClearProjeto = () => { if (window.VL_PROJETO) { window.VL_PROJETO.clear(); toast("Barramento do projeto limpo"); } }; const handleWipe = () => { const ok = window.confirm( "Apagar TODOS os dados locais?\n\nIsto remove preferências, projeto compartilhado e o histórico salvo de todas as calculadoras neste navegador. Esta ação não pode ser desfeita." ); if (!ok) return; try { Object.keys(localStorage) .filter(k => k.startsWith("vl_") || k.startsWith("voltlab")) .forEach(k => localStorage.removeItem(k)); } catch {} prefs.reset(); if (window.VL_PROJETO) window.VL_PROJETO.clear(); toast("Dados locais apagados"); setTimeout(() => window.location.reload(), 600); }; const tensoesComuns = [127, 220, 380, 440]; return (
{/* RAIL */} {/* CONTENT */}
{/* ───────── APARÊNCIA ───────── */}
INTERFACE

Aparência

{ACCENT_SWATCHES.map(sw => ( ))}
setTweak("density", v)} options={[{ value: "comfortable", label: "Conforto" }, { value: "compact", label: "Compacta" }]} /> setTweak("showCircuitry", v)}/> setTweak("monoReadouts", v)}/>
{/* ───────── PADRÕES DE CÁLCULO ───────── */}
ENGENHARIA · VALORES INICIAIS

Padrões de cálculo

APLICADO AOS MÓDULOS

Estes valores são usados como ponto de partida ao abrir as calculadoras. Você pode sobrescrevê-los em cada módulo.

{ if (v === "custom") { set("tensaoCustom", true); } else { prefs.setMany({ tensaoCustom: false, tensao: parseFloat(v) }); } }} options={[...tensoesComuns.map(t => ({ value: String(t), label: t + " V" })), { value: "custom", label: "Outra" }]} /> {P.tensaoCustom && ( set("tensao", v)}/> )}
set("sistema", v)} options={[ { value: "monofasico", label: "Mono" }, { value: "bifasico", label: "Bi" }, { value: "trifasico", label: "Tri" }, ]} /> set("frequencia", parseFloat(v))} options={[{ value: "60", label: "60 Hz" }, { value: "50", label: "50 Hz" }]} />
set("material", v)} options={[{ value: "Cu", label: "Cobre" }, { value: "Al", label: "Alumínio" }]} /> set("isolacao", v)} options={[{ value: "PVC", label: "PVC" }, { value: "EPR", label: "EPR" }, { value: "XLPE", label: "XLPE" }]} />
set("tempAmbiente", v)}/> set("quedaMax", v)}/> set("fpAlvo", v)}/>
{/* ───────── IDENTIFICAÇÃO & RELATÓRIOS ───────── */}
EXPORTAÇÃO

Identificação & Relatórios

Dados que aparecem no cabeçalho dos relatórios e exportações geradas pelas calculadoras.

set("empresa", v)}/>
set("responsavel", v)}/> set("crea", v)}/>
set("exportFmt", v)} options={[{ value: "json", label: "JSON" }, { value: "csv", label: "CSV" }]} /> set("decimais", parseInt(v, 10))} options={[{ value: "0", label: "0" }, { value: "1", label: "1" }, { value: "2", label: "2" }, { value: "3", label: "3" }]} />
{/* ───────── DADOS & PRIVACIDADE ───────── */}
ARMAZENAMENTO LOCAL

Dados & privacidade

Suas preferências e o histórico das calculadoras ficam guardados apenas neste navegador.

Limpar barramento do projeto Remove todos os valores compartilhados entre calculadoras (tensão, corrente, seção…).
Restaurar todas as configurações Volta aparência e padrões de cálculo aos valores de fábrica.
Apagar todos os dados locais Exclui preferências, projeto e histórico de todas as calculadoras. Ação irreversível.
); }; window.VL_Settings = VL_Settings;