/* eslint-disable */ // Godoy Engenharia — shared components (icons, nav, footer, whatsapp, form, placeholders, diagrams) const { useState, useEffect, useRef } = React; /* ───────────────────────────────────────────── ICONS — simple, technical, single-stroke ───────────────────────────────────────────── */ const GEIcon = ({ name, size = 24, stroke = 1.6 }) => { const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" }; const paths = { panel: , power: , bolt: , expand: , retrofit: , lightning: , warehouse: , logistics: , arrow: , arrowUpRight: , flask: , car: , truck: , tower: , rail: , cabine: , laudo: , check: , whatsapp: , linkedin: , instagram: , mail: , close: , menu: }; return {paths[name] || null}; }; /* ───────────────────────────────────────────── PLACEHOLDER MEDIA ───────────────────────────────────────────── */ function GEPlaceholder({ label, corner, dark = false, children, style, img, imgAlt, imgTitle, imgW, imgH, eager = false }) { // If a real image basename is provided, render an SEO tag. // Files are expected at /img/{hero,servicos,mobile,thumbs}/{img}.webp // If the file doesn't exist yet (placeholder photos), fall back to a clean label. const [imgFailed, setImgFailed] = useState(false); // Retry transient load failures (server hiccups under concurrent load) // with a small backoff before showing the text fallback. const [retry, setRetry] = useState(0); const MAX_RETRIES = 4; const handleError = () => { if (retry < MAX_RETRIES) { const next = retry + 1; setTimeout(() => setRetry(next), 400 * next); } else { setImgFailed(true); } }; const bust = retry > 0 ? `?r=${retry}` : ""; if (img && !imgFailed) { return (
{corner ?
{corner}
: null} {imgAlt {imgTitle ?
{imgTitle}
: null}
); } return (
{corner ?
{corner}
: null} {children}
[ {label} ]
); } /* ───────────────────────────────────────────── TECHNICAL DIAGRAMS (SVG) — used in hero / scope ───────────────────────────────────────────── */ function GEDiagramUnifilar() { // Single-line diagram: utility → meter → main breaker → busbar → loads return ( {/* Utility source */} ~ Concessionária · 13,8 kV {/* Meter */} kWh Medição MT {/* Main breaker */} Disjuntor geral {/* Transformer */} Trafo 1.000 kVA 13,8 kV / 380 V {/* Busbar */} Barramento principal — 380 V {/* Loads */} {[100, 180, 260, 340].map((x, i) => {["Q1", "Q2", "Q3", "Q4"][i]} {["Força", "Ilum.", "CCM", "Reserva"][i]} {["250 A", "100 A", "400 A", "150 A"][i]} )} {/* Side legends */} DIAGRAMA UNIFILAR PROJ. GE-2026/14 · REV. 02 ENG. RESP. CREA-SP · ART QUANDO PREVISTA NBR 14039 NBR 5410 NR-10 ); } function GEDiagramGalpao() { // Top-down warehouse layout with electrical zones return ( {/* outer building */} PLANTA — GALPÃO INDUSTRIAL 4.500 m² {/* grid */} {[120, 200, 280, 360, 440].map((x) => )} {[140, 220, 300, 380, 460].map((y) => )} {/* Sala elétrica */} SALA ELÉTRICA CCM · QGBT · UPS {/* Production bays */} {[0, 1, 2].map((i) => BAY {String(i + 1).padStart(2, "0")} )} {/* Bandejamento horizontal (green line) */} EC-01 · BANDEJAMENTO ALIMENTADORES {/* Vertical risers */} {[120, 240, 360].map((x, i) => Q{i + 1} )} {/* Doca / acesso */} DOCA {/* SPDA marker */} SPDA ATERRAMENTO · R < 10 Ω {/* Footer */} ESCALA 1:200 · PROJ. GE-2026/22 REV. 03 · 14/05/2026 ); } function GEDiagramSpda() { return ( SPDA — GAIOLA DE FARADAY NBR 5419 · NÍVEL DE PROTEÇÃO III {/* Building silhouette */} {/* Captors */} {[120, 180, 240, 300, 360].map((x) => )} {/* Mesh */} {/* Descidas */} {/* Caixa de inspeção */} CX. INSP. CX. INSP. {/* Aterramento */} MALHA DE ATERRAMENTO PERIMETRAL {/* Side annotations */} CAPTORES FRANKLIN · h=300 mm MALHA CU NU 35 mm² DESCIDAS CU NU 50 mm² RESISTÊNCIA < 5 Ω ); } /* ───────────────────────────────────────────── NAV ───────────────────────────────────────────── */ function GENav({ route, go, services, brand }) { const [openMenu, setOpenMenu] = useState(false); const [openMobile, setOpenMobile] = useState(false); const [openMobileSvc, setOpenMobileSvc] = useState(false); const ref = useRef(null); const isService = route.startsWith("servico/"); useEffect(() => { const onClickOutside = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpenMenu(false); }; document.addEventListener("mousedown", onClickOutside); return () => document.removeEventListener("mousedown", onClickOutside); }, []); // Lock body scroll when mobile menu open useEffect(() => { document.body.style.overflow = openMobile ? "hidden" : ""; return () => {document.body.style.overflow = "";}; }, [openMobile]); const navigate = (path) => { setOpenMenu(false); setOpenMobile(false); setOpenMobileSvc(false); go(path); }; return ( ); } /* ───────────────────────────────────────────── FOOTER ───────────────────────────────────────────── */ function GEFooter({ go, services, brand }) { return ( ); } /* ───────────────────────────────────────────── WHATSAPP FLOATING ───────────────────────────────────────────── */ function GEWhatsApp({ brand }) { const [open, setOpen] = useState(false); const [copied, setCopied] = useState(false); const msg = encodeURIComponent("Olá, gostaria de falar com a engenharia sobre uma demanda elétrica."); const isMobile = typeof navigator !== "undefined" && /android|iphone|ipad|ipod|mobile/i.test(navigator.userAgent || ""); // wa.me on mobile (opens app), web.whatsapp.com on desktop (works even without app) const link = isMobile ? `https://wa.me/${brand.phoneRaw}?text=${msg}` : `https://web.whatsapp.com/send?phone=${brand.phoneRaw}&text=${msg}`; const fallbackLink = `https://wa.me/${brand.phoneRaw}?text=${msg}`; const copyPhone = async (e) => { e.preventDefault(); try { await navigator.clipboard.writeText(brand.phone); } catch (err) { // fallback for older browsers const ta = document.createElement("textarea"); ta.value = brand.phone; document.body.appendChild(ta); ta.select(); try {document.execCommand("copy");} catch (_) {} document.body.removeChild(ta); } setCopied(true); setTimeout(() => setCopied(false), 2200); }; const mailHref = `mailto:${brand.email}?subject=${encodeURIComponent("Envio de projeto para avaliação — Godoy Engenharia")}&body=${encodeURIComponent("Olá, equipe da Godoy Engenharia.\n\nSegue em anexo o projeto/escopo para avaliação técnica.\n\nEmpresa:\nTelefone:\nCidade da obra:\nTipo de serviço:\n\nObrigado.")}`; return (
{open &&
GE
Godoy Engenharia
Engenharia · atende em horário comercial
Olá! Aqui é da Godoy Engenharia. Conte um pouco da sua demanda elétrica — escopo, porte e localização — que a engenharia retorna com a análise técnica inicial. agora
Abrir WhatsApp
Se o WhatsApp não abrir no seu navegador, copie o número ou envie um e-mail.
Enviar projeto por e-mail wa.me direto
}
); } /* ───────────────────────────────────────────── FORM — qualifier lead ───────────────────────────────────────────── */ function GEForm({ onSent, go, compact = false }) { const [data, setData] = useState({ company: "", phone: "", email: "", message: "", file: null, consent: true }); const [errors, setErrors] = useState({}); const [sending, setSending] = useState(false); // Endpoint real de envio. Default: /send-form.php (PHP na Hostinger). // Pode ser sobrescrito por window.GE_FORM_ENDPOINT no index.html. const FORM_ENDPOINT = typeof window !== "undefined" && window.GE_FORM_ENDPOINT || "/send-form.php"; // Honeypot anti-spam (campo oculto; preenchido apenas por bots) const hpRef = useRef(null); const update = (k) => (e) => { if (k === "file") setData({ ...data, file: e.target.files[0] || null });else if (e.target.type === "checkbox") setData({ ...data, [k]: e.target.checked });else setData({ ...data, [k]: e.target.value }); }; const validate = () => { const e = {}; if (!data.company.trim()) e.company = "obrigatório"; if (!data.phone.trim()) e.phone = "obrigatório"; if (!data.email.trim() || !/^[^@]+@[^@]+\.[^@]+$/.test(data.email)) e.email = "e-mail inválido"; if (!data.message.trim()) e.message = "obrigatório"; if (!data.consent) e.consent = "necessário"; setErrors(e); return Object.keys(e).length === 0; }; const submit = async (ev) => { ev.preventDefault(); if (!validate()) return; setSending(true); // Monta payload (FormData — compatível com send-form.php) const fd = new FormData(); fd.append("Nome da empresa", data.company); fd.append("Telefone / WhatsApp", data.phone); fd.append("E-mail", data.email); fd.append("Mensagem", data.message); fd.append("Origem", typeof location !== "undefined" ? location.href : ""); // honeypot (vazio para humanos) fd.append("website", hpRef.current ? hpRef.current.value : ""); if (data.file) fd.append("anexo", data.file, data.file.name); let ok = false; try { const res = await fetch(FORM_ENDPOINT, { method: "POST", body: fd, headers: { "Accept": "application/json" } }); // Considera sucesso somente se o backend confirmar { ok: true } let json = null; try {json = await res.json();} catch (_) {json = null;} ok = res.ok && json && json.ok === true; } catch (err) { ok = false; } // dataLayer form_submit dispara SOMENTE após envio real confirmado if (ok) { try { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "form_submit", form_name: "contato_godoy", page_url: location.href, page_title: document.title }); } catch (err) {} // Google Ads — conversão de Envio de Formulário try { if (typeof window.gtag === "function") { window.gtag("event", "conversion", { send_to: "AW-736387313/36TbCIPcpbUcEPHBkd8C" }); } } catch (err) {} } setSending(false); if (ok) { onSent && onSent(data); setData({ company: "", phone: "", email: "", message: "", file: null, consent: true }); } else { setErrors({ submit: "Não foi possível enviar sua solicitação agora. Tente novamente ou envie e-mail para contato@godoyeng.com.br." }); } }; return (
{errors.company && {errors.company}}
{errors.phone && {errors.phone}}
{errors.email && {errors.email}}
{errors.message && {errors.message}}
{errors.submit &&
{errors.submit}
}
{/* Honeypot anti-spam — invisível para humanos */}
); } /* ───────────────────────────────────────────── TOAST ───────────────────────────────────────────── */ function GEToast({ message, onDone }) { useEffect(() => { if (!message) return; const t = setTimeout(() => onDone(), 4000); return () => clearTimeout(t); }, [message]); if (!message) return null; return (
{message}
); } /* ───────────────────────────────────────────── ACCORDION ───────────────────────────────────────────── */ function GEAccordion({ label, children, defaultOpen = false }) { const [open, setOpen] = useState(defaultOpen); return (
); } /* expose */ Object.assign(window, { GEIcon, GEPlaceholder, GEDiagramUnifilar, GEDiagramGalpao, GEDiagramSpda, GENav, GEFooter, GEWhatsApp, GEForm, GEToast, GEAccordion });