import React, { useEffect, useMemo, useState } from "react"; import { createRoot } from "react-dom/client"; import { BarChart3, Beef, Check, ChevronLeft, ClipboardList, ExternalLink, Globe, Instagram, LayoutDashboard, Lock, MapPin, Menu, MessageCircle, Minus, Plus, Search, ShoppingBag, Sparkles, Trash2, Users, X } from "lucide-react"; import { loadDashboard, loadPublicPage, loginAdmin, postLead, postOrder } from "./services/api"; import { flush, setupCoreTracking, track, trackClickAndOpen, trackingIdentity } from "./tracking/tracker"; import type { CartItem, DashboardPayload, LinkItem, Product, ProductOptionItem, PublicPayload } from "./types"; import "./styles.css"; const brl = new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }); function iconFor(link: LinkItem) { const props = { size: 20, strokeWidth: 2.4 }; if (link.icon === "whatsapp" || link.link_type === "whatsapp") return ; if (link.icon === "instagram") return ; if (link.icon === "map-pin" || link.link_type === "map") return ; if (link.icon === "menu" || link.link_type === "menu") return ; if (link.icon === "sparkles") return ; return ; } function useRoute() { const [path, setPath] = useState(location.pathname); useEffect(() => { const update = () => setPath(location.pathname); window.addEventListener("popstate", update); return () => window.removeEventListener("popstate", update); }, []); return path; } function App() { const path = useRoute(); const [payload, setPayload] = useState(null); useEffect(() => { const slug = path.split("/").filter(Boolean)[0] || "sabornabrasa"; loadPublicPage(slug).then(setPayload); }, [path]); useEffect(() => { if (!payload) return; return setupCoreTracking(payload.page.id); }, [payload, path]); if (!payload) return
Carregando PulseLink...
; if (path.startsWith("/admin")) return ; if (path.includes("/cardapio")) return ; return ; } function LinksPage({ payload }: { payload: PublicPayload }) { const menuLink = payload.links.find((link) => link.link_type === "menu"); return (
9:41
{payload.account.name} grill

{payload.page.slogan}

{payload.links.map((link) => ( ))}
Qualidade Sabor Tradição
); } function MenuPage({ payload }: { payload: PublicPayload }) { const [query, setQuery] = useState(""); const [category, setCategory] = useState(0); const [selected, setSelected] = useState(null); const [cart, setCart] = useState([]); const [showCart, setShowCart] = useState(false); const [checkout, setCheckout] = useState(false); const [success, setSuccess] = useState(false); const [leadModal, setLeadModal] = useState(false); const [viewedProducts, setViewedProducts] = useState(() => new Set()); const products = useMemo(() => { return payload.products.filter((product) => { const matchesCategory = category === 0 || Number(product.category_id) === category; const matchesQuery = product.name.toLowerCase().includes(query.toLowerCase()); return matchesCategory && matchesQuery; }); }, [payload.products, category, query]); const featured = products.filter((product) => Number(product.is_featured) === 1); const total = cart.reduce((sum, item) => sum + item.quantity * (item.unit_price + item.options.reduce((a, option) => a + option.price, 0)), 0); function openProduct(product: Product, source: string) { setSelected(product); setViewedProducts((current) => { const next = new Set(current); next.add(product.id); if (next.size >= 2 && !localStorage.getItem("pl_lead_modal_skip")) { window.setTimeout(() => { track({ event_name: "lead_modal_view", page_id: payload.page.id, metadata: { trigger: "two_products" } }, "lead_modal:two_products"); setLeadModal(true); }, 400); } return next; }); track({ event_name: "product_view", page_id: payload.page.id, product_id: product.id, metadata: { source_section: source, price: product.price } }); } function addItem(item: CartItem) { setCart((current) => [...current, item]); setSelected(null); setShowCart(true); track({ event_name: "cart_add", page_id: payload.page.id, product_id: item.product_id, metadata: { total: item.unit_price } }); } return (
{payload.account.name} online

O melhor sabor da nossa região

Carnes | Petiscos | Bebidas | Combos

{payload.categories.map((cat) => ( ))}
{featured.map((product) => ( openProduct(product, "featured")} /> ))}
{products.map((product) => ( ))}
{selected && setSelected(null)} onAdd={addItem} />} {showCart && setShowCart(false)} onRemove={(id) => setCart((items) => items.filter((item) => item.cartId !== id))} onCheckout={() => { setShowCart(false); setCheckout(true); track({ event_name: "order_start", page_id: payload.page.id }); }} />} {checkout && ( setCheckout(false)} onSuccess={() => { setCheckout(false); setSuccess(true); }} /> )} {leadModal && setLeadModal(false)} />} {success && setSuccess(false)} />}
); } function SectionTitle({ title }: { title: string }) { return

{title}

; } function ProductCard({ product, onOpen }: { product: Product; onOpen: () => void }) { useEffect(() => { const node = document.querySelector(`[data-product-id="${product.id}"]`); if (!node) return; let timer = 0; const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting && entry.intersectionRatio >= 0.5) { timer = window.setTimeout(() => { track({ event_name: "product_impression", product_id: product.id, metadata: { position: product.position } }, `impression:${product.id}`); }, 1000); } else { window.clearTimeout(timer); } }, { threshold: [0.5] } ); observer.observe(node); return () => { window.clearTimeout(timer); observer.disconnect(); }; }, [product.id, product.position]); return ( ); } function ProductSheet({ product, onClose, onAdd }: { product: Product; onClose: () => void; onAdd: (item: CartItem) => void }) { const [quantity, setQuantity] = useState(1); const [selected, setSelected] = useState>([]); const [observation, setObservation] = useState(""); const unitPrice = Number(product.price ?? 0); const optionsTotal = selected.reduce((sum, item) => sum + item.price, 0); const total = (unitPrice + optionsTotal) * quantity; function toggle(option: ProductOptionItem, group: string) { setSelected((current) => { const exists = current.some((item) => item.id === option.id); const next = exists ? current.filter((item) => item.id !== option.id) : [...current, { id: option.id, group, name: option.name, price: Number(option.price) }]; track({ event_name: exists ? "addon_remove" : "addon_select", product_id: product.id, metadata: { option_selected: option.name, price: option.price } }); return next; }); } return (

{product.name}

{unitPrice ? brl.format(unitPrice) : "Consultar"}

{product.description}

{product.options.map((option) => (

{option.name}

{option.items.map((item) => ( ))}
))}