1<!DOCTYPE html> 2<html lang="pt-br"> 3 4 5 6 7<head> 8 9 <meta charset="UTF-8"> 10 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 11 12 <!-- SEO Meta Tags --> 13 <title>Encontre uma Farmácia Brava perto de você | Farmácias Brava </title> 14 <meta name="description" content="Encontre a Farmácia Brava mais próxima. Consulte endereços, horários de atendimento e telefones das nossas lojas."> 15 <meta name="keywords" content="ecommerce, compras online, ofertas, produtos"> 16 <meta name="author" content="RetailOn"> 17 <meta name="robots" content="index, follow"> 18 <link rel="canonical" href="https://www.farmaciasbrava.com.br/nossas-lojas"> 19 20 <!-- Open Graph / Facebook --> 21 <meta property="og:type" content="website"> 22 <meta property="og:url" content="https://www.farmaciasbrava.com.br/nossas-lojas"> 23 <meta property="og:title" content="Encontre uma Farmácia Brava perto de você | Farmácias Brava "> 24 <meta property="og:description" content="Encontre a Farmácia Brava mais próxima. Consulte endereços, horários de atendimento e telefones das nossas lojas."> 25 <meta property="og:image" content="https://storage.googleapis.com/retailon-publico/brava/admin/icone-perfil.png"> 26 <meta property="og:image:secure_url" content="https://storage.googleapis.com/retailon-publico/brava/admin/icone-perfil.png"> 27 <meta property="og:image:width" content="1200"> 28 <meta property="og:image:height" content="630"> 29 <meta property="og:image:type" content="image/jpeg"> 30 31 <!-- Twitter --> 32 <meta property="twitter:card" content="summary_large_image"> 33 <meta property="twitter:url" content="https://www.farmaciasbrava.com.br/nossas-lojas"> 34 <meta property="twitter:title" content="Farmácias Brava "> 35 <meta property="twitter:description" content="Aproveite nossas ofertas no site ou pelo app e receba em casa ou retire seus pedidos na loja."> 36 <meta property="twitter:image" content="https://storage.googleapis.com/retailon-publico/brava/admin/icone-perfil.png"> 37 38 <!-- Mobile Web App --> 39 <meta name="theme-color" content="#ffffff"> 40 <meta name="mobile-web-app-capable" content="yes"> 41 <meta name="apple-mobile-web-app-capable" content="yes"> 42 <meta name="apple-mobile-web-app-status-bar-style" content="default"> 43 <meta name="apple-mobile-web-app-title" content="Farmácias Brava "> 44 45 46 47 <!-- Favicon --> 48 49 <link rel="icon" type="image/x-icon" href="https://storage.googleapis.com/retailon-publico/brava/imagens/favicon.png"> 50 <link rel="apple-touch-icon" sizes="180x180" href="https://storage.googleapis.com/retailon-publico/brava/imagens/favicon.png"> 51 <link rel="icon" type="image/png" sizes="16x16" href="https://storage.googleapis.com/retailon-publico/brava/imagens/favicon.png"> 52 <link rel="icon" type="image/png" sizes="32x32" href="https://storage.googleapis.com/retailon-publico/brava/imagens/favicon.png"> 53 54 55
55<script type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 56 /* 57 * Tags do Django que serão utilizadas dentro dos scripts da aplicação 58 * Lógica feita através da inserção de uma função na API 'window' do navegador que retorna o valor desejado e que foi configurada como read-only, assim nao sendo possÃvel alterar via console do navegador 59 * Toda a lógica é executada após o carregamento da página para nao impactar na performace inicial 60 * ATENÃÃO: DEVIDO A ISSO, TOME CUIDADO COM O USO DA VARIÃVEL, POIS Sà SERà POSSÃVEL REALIZAR AÃÃES APÃS O CARREGAMENTO DA PÃGINA 61 */ 62 const APP_CONTEXT_VALUE = Object.freeze({ 63 auth: Object.freeze({ 64 is_autenticado: false, 65 nm_primeiro: ``, 66 }), 67 ambiente: Object.freeze({ 68 is_app: /true/.test(`${false}`), 69 is_pagina_produto: /true/.test(`${false}`), 70 }), 71 app: Object.freeze({ 72 link_android: '', 73 link_ios: '', 74 play_store: '', 75 app_store: '', 76 deep_link: '', 77 deep_link_android: '', 78 deep_link_ios: '', 79 }), 80 seguranca: Object.freeze({ 81 csrf_token: 'OVFTj6iZUaoYaiuN12wRuhMzbo7fwr0GG8qNrhXRXeVzuizy7rszjfRVVKazkHx7', 82 session_uuid: '', 83 }), 84 pagamento: Object.freeze({ 85 integrador_antifraude: 'getnet', 86 adquirente: 'getnet', 87 public_token: '', 88 url_antifraude: 'https://api.globalgetnet.com/dpm/digital\u002Dplatform/antifraud/afdf.js', 89 }), 90 integracoes: Object.freeze({ 91 recaptcha_site_key: '6LeTEYgsAAAAAAS\u002DrSySSWZyRiNHzHcfExsfIjqY', 92 }), 93 preco: Object.freeze({ 94 estado: '', 95 estado_padrao: '', 96 filial_id: '', 97 }), 98 }); 99 100 Object.defineProperty(window, "APP_CONTEXT", { 101 value: () => APP_CONTEXT_VALUE, 102 writable: false, 103 configurable: false, 104 }); 105 106 // Aliases de compatibilidade para scripts legados. 107 Object.defineProperty(window, "IS_AUTENTICADO", { 108 value: () => window.APP_CONTEXT().auth.is_autenticado, 109 writable: false, 110 configurable: false, 111 }); 112 Object.defineProperty(window, "NM_PRIMEIRO", { 113 value: () => window.APP_CONTEXT().auth.nm_primeiro, 114 writable: false, 115 configurable: false, 116 }); 117 118 const IS_APP = /true/.test(`${false}`); 119 Object.defineProperty(window, "IS_APP", { 120 value: () => window.APP_CONTEXT().ambiente.is_app, 121 writable: false, 122 configurable: false, 123 }); 124 Object.defineProperty(window, "CSRF_TOKEN", { 125 value: () => window.APP_CONTEXT().seguranca.csrf_token, 126 writable: false, 127 configurable: false, 128 }); 129 Object.defineProperty(window, "INTEGRADOR_ANTIFRAUDE", { 130 value: () => window.APP_CONTEXT().pagamento.integrador_antifraude, 131 writable: false, 132 configurable: false, 133 }); 134 Object.defineProperty(window, "ADQUIRENTE", { 135 value: () => window.APP_CONTEXT().pagamento.adquirente, 136 writable: false, 137 configurable: false, 138 }); 139 Object.defineProperty(window, "PUBLIC_TOKEN", { 140 value: () => window.APP_CONTEXT().pagamento.public_token, 141 writable: false, 142 configurable: false, 143 }); 144 Object.defineProperty(window, "__ANTIFRAUDE_AFDF_URL", { 145 value: () => window.APP_CONTEXT().pagamento.url_antifraude, 146 writable: false, 147 configurable: false, 148 }); 149 Object.defineProperty(window, "SESSION_UUID", { 150 value: () => window.APP_CONTEXT().seguranca.session_uuid, 151 writable: false, 152 configurable: false, 153 }); 154 Object.defineProperty(window, "recaptcha_site_key", { 155 value: () => window.APP_CONTEXT().integracoes.recaptcha_site_key, 156 writable: false, 157 configurable: false, 158 }); 159 </script>
159 160 161 <!-- CRÃTICOS (preconnect) --> 162 <link rel="preconnect" href="https://www.googletagmanager.com" crossorigin> 163 <link rel="preconnect" href="https://static.hotjar.com" crossorigin> 164 <link rel="preconnect" href="https://www.clarity.ms" crossorigin> 165 <link rel="preconnect" href="https://connect.facebook.net" crossorigin> 166 <link rel="preconnect" href="https://analytics.tiktok.com" crossorigin> 167 <link rel="preconnect" href="https://bat.bing.com" crossorigin> 168 <link rel="preconnect" href="https://tag.navdmp.com" crossorigin> 169 170 <!-- BAIXA PRIORIDADE (dns-prefetch) --> 171 <link rel="dns-prefetch" href="//www.google-analytics.com"> 172 <link rel="dns-prefetch" href="//googleads.g.doubleclick.net"> 173 <link rel="dns-prefetch" href="//ad.doubleclick.net"> 174 175
175<script type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 176 window.__AN_CFG = { 177 gtm: "GTM-THG4PHZ", // se tiver GTM, deixe GA4/Meta pelo GTM, exceto para o RetailON 178 ga4: "", // use só se NÃO tiver GTM 179 ga4_retailon: "", // chave secundária para o RetailON G-RQCDM9M3X2 180 tenant_name: "brava", // nome do tenant 181 tenant_id: "1", // id do tenant 182 tenant_segment: "None", // segmento do tenant 183 price_zone: "", // zona de preço do tenant 184 185 hotjar: { hjid: "6549243", hjsv: "6" }, 186 fb_pixel: "780780368411618", 187 tiktok: "", 188 bing_uet: "", 189 clarity: "", 190 navegg_acc: "", 191 streamshop_ads: "true", // 192 // flags de controle 193 metaAutoPageView: false, // GTM já dispara? então false 194 gaAutoPageView: false, // deixamos Analytics.page() cuidar 195 tiktokAutoPage: false // chamamos ttq.page() via Analytics.page() 196 }; 197 </script>
197 198 199 200 201
201<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/sha256.min.js" async defer type="ff2c2aaab21707ab2ba08cc2-text/javascript"></script>
vendor: 73 bytes, lines 201-203
201 202 203 <script src="https://www.google.com/recaptcha/enterprise.js?render=
2036LeTEYgsAAAAAAS-rSySSWZyRiNHzHcfExsfIjqY
vendor: 66 bytes, lines 203-206
203" type="ff2c2aaab21707ab2ba08cc2-text/javascript"></script> 204 205 206
206<script type="ff2c2aaab21707ab2ba08cc2-text/javascript" src="/staticfiles/js/analytics-loader.js" defer></script>
206 207 208 <style> 209 210 @font-face { 211 font-family: 'Gilroy'; 212 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Thin.woff2') format('woff2'); 213 font-weight: 100; 214 font-style: normal; 215 font-display: swap; 216} 217 218@font-face { 219 font-family: 'Gilroy'; 220 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-UltraLight.woff2') format('woff2'); 221 font-weight: 200; 222 font-style: normal; 223 font-display: swap; 224} 225 226@font-face { 227 font-family: 'Gilroy'; 228 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Light.woff2') format('woff2'); 229 font-weight: 300; 230 font-style: normal; 231 font-display: swap; 232} 233 234@font-face { 235 font-family: 'Gilroy'; 236 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Regular.woff2') format('woff2'); 237 font-weight: 400; 238 font-style: normal; 239 font-display: swap; 240} 241 242@font-face { 243 font-family: 'Gilroy'; 244 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-RegularItalic.woff2') format('woff2'); 245 font-weight: 400; 246 font-style: italic; 247 font-display: swap; 248} 249 250@font-face { 251 font-family: 'Gilroy'; 252 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Medium.woff2') format('woff2'); 253 font-weight: 500; 254 font-style: normal; 255 font-display: swap; 256} 257 258@font-face { 259 font-family: 'Gilroy'; 260 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-MediumItalic.woff2') format('woff2'); 261 font-weight: 500; 262 font-style: italic; 263 font-display: swap; 264} 265 266@font-face { 267 font-family: 'Gilroy'; 268 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-SemiBold.woff2') format('woff2'); 269 font-weight: 600; 270 font-style: normal; 271 font-display: swap; 272} 273 274@font-face { 275 font-family: 'Gilroy'; 276 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-SemiBoldItalic.woff2') format('woff2'); 277 font-weight: 600; 278 font-style: italic; 279 font-display: swap; 280} 281 282@font-face { 283 font-family: 'Gilroy'; 284 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Bold.woff2') format('woff2'); 285 font-weight: 700; 286 font-style: normal; 287 font-display: swap; 288} 289 290@font-face { 291 font-family: 'Gilroy'; 292 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-BoldItalic.woff2') format('woff2'); 293 font-weight: 700; 294 font-style: italic; 295 font-display: swap; 296} 297 298@font-face { 299 font-family: 'Gilroy'; 300 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-ExtraBold.woff2') format('woff2'); 301 font-weight: 800; 302 font-style: normal; 303 font-display: swap; 304} 305 306@font-face { 307 font-family: 'Gilroy'; 308 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Black.woff2') format('woff2'); 309 font-weight: 900; 310 font-style: normal; 311 font-display: swap; 312} 313 314@font-face { 315 font-family: 'Gilroy'; 316 src: url('/staticfiles/css/fonts/fonte-gilroy/Gilroy-Heavy.woff2') format('woff2'); 317 font-weight: 950; 318 font-style: normal; 319 font-display: swap; 320} 321 322 :root { 323 324 --f-base: 'Gilroy'; 325 326 327 --c-header-btn-bg: var(--c-brand-primary); 328--c-header-btn-border: var(--c-brand-primary); 329--c-header-btn-bg-hover: #00235a; 330--c-header-btn-text: var(--c-brand-on); 331--c-header-btn-text-hover: var(--c-brand-on-hover); 332--c-footer-topbar-bg: #001ccd; 333--c-footer-topbar-text: #fff; 334--c-footer-topbar-link: #fff; 335--c-footer-mid-bg: #001ccd; 336--c-footer-mid-text: #fff; 337--c-footer-mid-link-bg: #fff; 338--c-footer-bottombar-bg: #001ccd; 339--c-footer-bottombar-text: var(--c-brand-on); 340--c-footer-btn-bg: rgba(170, 170, 238, 0); 341--c-footer-btn-text: #FFF; 342--c-footer-btn-bg-hover: #1b34d3; 343--c-footer-btn-text-hover: #FFF; 344--c-footer-btn-border: #FFF; 345--c-footer-menu-mobile-text: var(--c-brand-primary); 346--c-header-topbar-bg: var(--c-brand-secondary); 347--c-header-topbar-text: var(--c-brand-on); 348--c-header-topbar-link: var(--c-brand-on); 349--c-header-mid-bg: var(--c-brand-primary); 350--c-header-mid-text: var(--c-brand-on); 351--c-header-carrinho-bg: #5D90FF; 352--c-header-bottombar-bg: var(--c-white); 353--c-header-bottombar-text: #1b2985; 354--c-header-bottombar-categorias-bg: var(--c-brand-primary); 355--c-header-bottombar-categorias-text: var(--c-brand-on); 356--c-categorias-hover: #1b2985; 357--c-brand-primary: #002aff; 358--c-brand-secondary: #ff006c; 359--c-base-fundo: #fff; 360--c-base-texto: #444; 361--c-brand-accent: #ff006c; 362--c-brand-on: #fff; 363--c-brand-primary-hover: #003CFF; 364--c-brand-secondary-hover: #FF3384; 365--c-brand-accent-hover: #FF3384; 366--c-brand-on-hover: #FFF; 367--c-white: #fff; 368--loader-tenant: url("https://storage.googleapis.com/retailon-publico/brava/admin/loader-brava.gif"); 369--c-text-primary: #444; 370--c-text-secondary: #616161; 371--c-text-muted: #8a9297; 372--c-text-subtle: #a3a3a3; 373--c-text-inverse: #000; 374--c-text-link: #002aff; 375--c-text-link-hover: #002aff; 376--c-text-brand-primary: #002aff; 377--c-text-brand-secondary: #ff006c; 378--c-cookiebanner-bg: var(--c-brand-secondary); 379--c-cookiebanner-border: var(--c-brand-primary); 380--c-cookiebanner-text: var(--c-brand-on); 381--c-cookiebanner-link: var(--c-brand-accent); 382--r-cookiebanner-radius: 12px; 383--c-cookiebanner-btn-bg: var(--c-brand-primary); 384--c-cookiebanner-btn-text: var(--c-brand-on); 385--c-cookiebanner-btn-border: var(--c-brand-primary); 386--c-cookiebanner-btn-bg-hover: color-mix(in srgb, var(--c-cookiebanner-btn-bg) 82%, black); 387--r-cookiebanner-btn-radius: 20px; 388--filter-icon-primary: brightness(0) saturate(100%) invert(12%) sepia(100%) saturate(4818%) hue-rotate(235deg) brightness(100%) contrast(127%); 389--filter-icon-secondary: brightness(0) saturate(100%) invert(14%) sepia(91%) saturate(6564%) hue-rotate(332deg) brightness(112%) contrast(109%);
390--filter-icon-wishlist: brightness(0) saturate(100%) invert(14%) sepia(66%) saturate(7307%) hue-rotate(329deg) brightness(102%) contrast(107%); 391--filter-icon-gray: brightness(0) saturate(100%) invert(75%) sepia(4%) saturate(0%) hue-rotate(172deg) brightness(92%) contrast(90%); 392--client-swiper-scrollbar-background: #002aff2b; 393--client-swiper-scrollbar-color: #002aff; 394--client-swiper-scrollbar-size: 7px; 395--client-swiper-pagination-bullet-border-radius: 50px; 396--client-swiper-pagination-bullet-size: 12px; 397--client-swiper-pagination-bullet-active-size: 12px; 398--client-swiper-pagination-bullets-space: 4px; 399--client-swiper-pagination-background: #002aff; 400--client-swiper-pagination-color: #002aff; 401--client-opacity-button-next-prev: 0; 402--client-opacity-mobile-next-prev: 1; 403--client-bg-button-next-prev: #FFF; 404--h-swipper-button: 30px; 405--w-swipper-button: 30px; 406--s-arrow-image: 20px; 407--c-border-default: #002aff; 408--c-border-muted: #E9F2F9; 409--c-border-strong: #E9F2F9; 410--r-rounded-default: 0.5rem; 411--c-divider-muted: #E9F2F9; 412--container-max-width: 1300px; 413--c-page-bg: #fff; 414--c-section-bg: #fff; 415--c-card-bg: #fff; 416--c-card-border: var(--c-border-muted); 417--c-divider: var(--c-border-muted); 418--c-surface-base: #E9F2F9; 419--c-surface-muted: #E9F2F9; 420--c-surface-soft: #E9F2F9; 421--c-surface-subtle: #E9F2F9; 422--c-surface-inverse: #001d4b; 423--c-surface-default: var(--c-white); 424--c-surface-brand: #EFF5FF; 425--c-surface-brand-hover: #d9e6fc; 426--c-pdp-h1-text: #242424; 427--f-pdp-h1-mobile: 18px; 428--f-pdp-h1-desktop: 25px; 429--c-pdp-avaliacoes-text: var(--c-brand-primary); 430--c-pdp-sem-avaliacoes-text: #808080; 431--c-pdp-controlado-border: #ffc9c9; 432--c-pdp-controlado-bg: #fef2f2; 433--c-pdp-controlado-titulo-text: #e7000b; 434--c-pdp-controlado-descricao-text: #4a5565; 435--c-pdp-termolabil-border: #c6c3fa; 436--c-pdp-termolabil-bg: #e8edfe; 437--c-pdp-termolabil-titulo-text: #0300b4; 438--c-pdp-termolabil-descricao-text: #4a5565; 439--c-pdp-galeria-border: #FFF; 440--r-pdp-galeria: 0px; 441--c-pdp-vendido-entregue-text: #8a9297; 442--slide-thumb-active: #002aff; 443--slide-thumb-not-active: #92969b; 444--c-pdp-breadcrumb-text: #8a9297; 445--c-pdp-breadcrumb-link-text: #444; 446--c-pdp-apresentacao-descricao-bullets-text: #8a9297; 447--c-pdp-apresentacao-descricao-text: #444; 448--r-pdp-apresentacoes: 50px; 449--c-pdp-apresentacoes-titulo-text: #444; 450--c-pdp-apresentacoes-ativa: var(--c-brand-secondary); 451--c-pdp-apresentacoes-ativa-text: var(--c-text-brand-secondary); 452--c-pdp-resumo-text: #8a9297; 453--c-pdp-resumo-aviso-border: #ffc9c9; 454--c-pdp-resumo-aviso-bg: #fef2f2; 455--c-pdp-resumo-aviso-text: #4a5565; 456--c-pdp-descricao-completa-text: var(--c-brand-primary); 457--c-pdp-btn-avaliacao-bg: var(--c-brand-secondary); 458--c-pdp-avaliacao-accent: var(--c-brand-primary); 459--c-pdp-avaliacoes-border: #ebe6e7; 460--r-pdp-avaliacoes: 20px; 461--c-pdp-pbm-accent: var(--c-brand-primary); 462--c-pdp-especificacoes-border: #d1d5dc; 463--r-pdp-especificacoes: 20px; 464--c-pdp-especificacoes-divisor: #f6f3f4; 465--c-pdp-especificacoes-link: var(--c-brand-primary); 466--c-pdp-divisor: #d1d5dc; 467--c-pdp-bg: #fff; 468--f-pdp-titulo: 700; 469--c-pdp-texto: #8a9297; 470--f-pdp-cta: 18px; 471--c-pdp-qtd-menos-icon: var(--c-brand-primary); 472--c-pdp-qtd-mais-icon: var(--c-brand-primary); 473--c-pdp-oferta-formas-pagamento-text: var(--c-brand-primary); 474--c-pdp-oferta-pbm-border: #ebe6e7; 475--c-pdp-oferta-pbm-bg: #F5FFF6; 476--r-pdp-oferta-pbm: 10px; 477--c-pdp-oferta-pbm-text: #00691D; 478--c-pdp-oferta-border: #d1d5dc; 479--r-pdp-oferta: 20px; 480--c-pdp-oferta-bg: #fff; 481--c-pdp-oferta-text: #444; 482--c-pdp-oferta-qtd-border: #ebe6e7; 483--r-pdp-oferta-qtd: 10px; 484--c-pdp-oferta-qtd-bg: #fff; 485--c-pdp-entrega-border: #d1d5dc; 486--r-pdp-entrega: 10px; 487--c-pdp-entrega-bg: #fff; 488--c-pdp-entrega-titulo: var(--c-brand-primary); 489--c-pdp-entrega-expandida-bg: #fff; 490--r-pdp-tab: 50px; 491--py-pdp-tab: 8px; 492--c-pdp-tab-inativa-text: #444; 493--c-pdp-tab-inativa-border: var(--c-brand-primary); 494--c-pdp-atalho-tab-bg: var(--c-brand-primary); 495--c-pdp-atalho-tab-text: #FFF; 496--r-cta-radius: 20px; 497--r-btn-comprar: 20px; 498--h-btn-comprar: 38px; 499--c-btn-padrao-comprar-bg: var(--c-brand-secondary); 500--c-btn-padrao-comprar-text: var(--c-brand-on); 501--c-btn-padrao-comprar-bg-hover: color-mix(in srgb, var(--c-btn-padrao-comprar-bg) 82%, black); 502--c-btn-padrao-comprar-text-hover: var(--c-brand-on); 503--c-btn-padrao-comprar-border: ; 504--c-btn-desconto-comprar-bg: var(--c-brand-primary); 505--c-btn-desconto-comprar-text: var(--c-brand-on); 506--c-btn-desconto-comprar-bg-hover: color-mix(in srgb, var(--c-btn-desconto-comprar-bg) 82%, black); 507--c-btn-desconto-comprar-text-hover: var(--c-brand-on); 508--c-btn-desconto-comprar-border: var(--c-brand-primary); 509--c-btn-pbm-comprar-bg: #00691d; 510--c-btn-pbm-comprar-text: var(--c-brand-on); 511--c-btn-pbm-comprar-bg-hover: color-mix(in srgb, var(--c-btn-pbm-comprar-bg) 82%, black); 512--c-btn-pbm-comprar-text-hover: var(--c-brand-on); 513--c-btn-pbm-comprar-border: ; 514--r-badge-tags: 20px; 515--c-badge-pbm-bg: #DEF5EB; 516--c-badge-pbm-text: #00AF66; 517--c-badge-desconto-bg: var(--c-brand-secondary); 518--c-badge-desconto-text: #FFF; 519--r-badge-percentual: 2px; 520--c-badge-percentual-bg: #00A66F; 521--c-badge-percentual-text: #FFF; 522--r-badge-secundaria: 2px; 523--c-badge-secundaria-bg: var(--c-brand-primary); 524--c-badge-secundaria-text: #FFF; 525--r-badge-receita: 0px; 526--c-badge-receita-bg: #e62d36; 527--c-badge-receita-text: #FFF; 528--c-emphasis: #1b2985; 529--c-bg-fundo-soft: #dbe2ff; 530--c-bg-pesquisa-soft: #fff; 531--c-bg-nossas-lojas: #fff; 532--c-btn-nossas-lojas: #fff; 533--c-checkbox-nossas-lojas: #fff; 534--c-carrinho: #fff; 535--c-comprar-produto-bg: #FFF; 536--c-breadcrumb-nav-text: #6b7280; 537--c-breadcrumb-link: var(--c-brand-primary); 538--c-breadcrumb-atual-text: #1f2937; 539--c-page-inst-h1-text: var(--c-brand-primary); 540--c-page-inst-subtitulo-text: var(--c-brand-secondary); 541--c-page-inst-hero-bg: #fff; 542--c-page-inst-texto-muted: #374151; 543--c-page-inst-secao-bg: var(--c-surface-muted); 544--c-page-inst-link: #007bff; 545--c-page-inst-link-hover: #3d85c6; 546--c-page-inst-accent-text: var(--c-brand-accent);
547--c-page-fc-titulo-text: var(--c-brand-primary); 548--c-page-fc-banner-bg: var(--c-brand-primary); 549--c-page-fc-form-bg: var(--c-surface-subtle); 550--c-page-fc-btn-bg: var(--c-brand-secondary); 551--c-page-fc-btn-text: #fff; 552--c-page-fc-input-bg: #fff; 553--c-page-lojas-titulo-text: var(--c-brand-primary); 554--c-page-lojas-subtitulo-text: #374151; 555--c-page-lojas-select-text: var(--c-brand-primary); 556--c-page-lojas-select-bg: #fff; 557--c-page-lojas-btn-outline-hover-bg: #f3f4f6; 558--c-checkout-aside-bg: #fff; 559--c-checkout-header-bg: #fff; 560--c-checkout-header-text: #444; 561--c-checkout-endereco-bg: #fff; 562--c-checkout-endereco-text: #444; 563--c-checkout-aside-footer-bg: #fff; 564--c-checkout-cupom-bg: var(--c-brand-primary); 565--c-checkout-radio-button-bg: var(--c-brand-primary); 566--c-checkout-button-bg: var(--c-brand-primary); 567--c-checkout-default-text: var(--c-text-primary); 568--c-checkout-primary-text: var(--c-brand-primary); 569--c-checkout-secondary-text: var(--c-brand-secondary); 570--c-checkout-accent-text: var(--c-brand-accent); 571--c-checkout-surface-muted: var(--c-surface-muted); 572--c-checkout-surface-selected: var(--c-surface-soft); 573--c-checkout-muted-border: #e4e7ea; 574--c-checkout-accent-border: var(--c-brand-secondary); 575--c-checkout-divider: var(--c-border-muted); 576--c-checkout-primary-border: var(--c-brand-primary); 577--c-checkout-icon: var(--c-brand-primary); 578--c-checkout-voltar-icon: var(--c-brand-primary); 579--c-checkout-cta-bg: var(--c-brand-secondary); 580--c-checkout-cta-text: var(--c-brand-on); 581--c-checkout-cta-border: var(--c-brand-secondary); 582--r-checkout-cta: 50px; 583--c-checkout-active-step-bg: var(--c-brand-primary); 584--c-checkout-active-step-icon: #fff; 585--c-checkout-inative-step-bg: #95A0AF; 586--c-checkout-inative-step-icon: #fff; 587--c-checkout-line-step-bg: #E0E4EA; 588--r-checkout-step: 50px; 589--c-popup-checkout-bg: #fff; 590--c-popup-checkout-border: var(--c-border-default); 591--c-popup-checkout-default-text: var(--c-text-primary); 592--c-popup-checkout-accent-text: var(--c-text-brand-primary); 593--r-popup-checkout-border-radius-top-right: 30px; 594--r-popup-checkout-border-radius-top-left: 30px; 595--c-checkout-input-cabecalho-bg: #FFF; 596--c-checkout-input-cabecalho-h: 35px; 597--c-checkout-input-cabecalho-radius: 2rem 0 0 2rem; 598--c-checkout-input-cabecalho-mr: 0; 599--c-checkout-input-cabecalho-btn-radius: 0 2rem 2rem 0; 600--c-checkout-endereco-icon: #444; 601--p-pesquisa-toggle: 0.5rem 0.875rem; 602--r-pesquisa-toggle-inicio: 9999px 0 0 9999px; 603--r-pesquisa-toggle-fim: 0 9999px 9999px 0; 604--gap-pesquisa-toggle: 0px; 605--w-pesquisa-toggle: 25px; 606--c-pesquisa-primary: var(--c-brand-primary); 607--c-pesquisa-primary-on: var(--c-brand-on); 608--c-pesquisa-secondary: var(--c-brand-secondary); 609--c-pesquisa-secondary-on: #fff; 610--c-pesquisa-bg-soft: var(--c-bg-pesquisa-soft); 611--c-pesquisa-surface-muted: var(--c-surface-muted); 612--c-pesquisa-toggle-inativo-bg: #eee; 613--c-pesquisa-toolbar-text: #000; 614--c-pesquisa-termo-text: #9ca3af; 615--c-pesquisa-texto: #444; 616--c-pesquisa-texto-ordenacao: #1C1B1B; 617--c-pesquisa-limpar-filtros: #E62D36; 618--c-pesquisa-filtro-chip-bg: color-mix(in srgb, var(--c-pesquisa-primary) 15%, transparent); 619--c-pesquisa-popup-bg: #fff; 620--c-overlay-soft: rgba(0, 0, 0, 0.3); 621--c-overlay-default: rgba(0, 0, 0, 0.5); 622--c-overlay-strong: rgba(0, 0, 0, 0.7); 623--c-success: #C6FF99; 624--c-warning: #FFF9B3; 625--c-danger: #E60005; 626--c-danger-soft: #ef4444; 627--c-info: #0000ff; 628--c-badge-bg: var(--c-surface-brand); 629--c-badge-text: var(--c-text-brand-primary); 630--c-badge-accent-bg: var(--c-brand-accent); 631--c-badge-accent-text: var(--c-brand-on); 632--c-pulse: rgba(0, 42, 255, 0.40); 633--c-btn-primary-bg: var(--c-brand-primary); 634--c-btn-primary-text: var(--c-brand-on); 635--c-btn-primary-border: var(--c-brand-primary); 636--c-btn-primary-hover-bg: var(--c-brand-primary-hover); 637--c-btn-primary-hover-text: var(--c-brand-on-hover); 638--c-btn-secondary-bg: var(--c-brand-secondary); 639--c-btn-secondary-text: var(--c-brand-on); 640--c-btn-secondary-border: var(--c-brand-secondary); 641--c-btn-secondary-hover-bg: var(--c-brand-secondary-hover); 642--c-btn-secondary-hover-text: var(--c-brand-on-hover); 643--c-btn-ghost-bg: rgba(255, 255, 255, 0); 644--c-btn-ghost-text: #1e2939; 645--c-btn-ghost-hover-bg: #fbf9fa; 646--c-btn-ghost-hover-text: #1e2939; 647--c-btn-ghost-border: #d1d5dc; 648--c-btn-muted-bg: #ebe6e7; 649--c-btn-muted-text: #1e2939; 650--c-btn-muted-hover-bg: rgba(255, 255, 255, 0); 651--c-btn-muted-hover-text: #1e2939; 652--c-btn-muted-border: rgba(255, 255, 255, 0); 653--c-btn-danger-bg: #ef4444; 654--c-btn-danger-text: #fff; 655--c-btn-danger-hover-bg: #e7000b; 656--c-btn-danger-hover-text: #fff; 657--c-btn-danger-border: rgba(255, 255, 255, 0); 658--c-menu-aside-bg: #fff; 659--c-menu-aside-coluna-esq-bg: #F5F5F5; 660--c-menu-aside-coluna-esq-border: #E5E5E5; 661--c-menu-aside-item-ativo-bg: #fff; 662--c-menu-aside-categoria-text: #333; 663--c-menu-aside-categoria-ativo-text: var(--c-brand-secondary); 664--c-menu-aside-barra-lateral-bg: var(--c-brand-secondary); 665--c-menu-aside-subtitulo-text: #111; 666--c-menu-aside-voltar-text: var(--c-brand-secondary); 667--c-menu-aside-pill-bg: var(--c-surface-subtle); 668--c-menu-aside-pill-text: #333; 669--c-menu-aside-vazio-text: #6b7280; 670--c-menu-cat-mobile-text: #fff; 671--c-menu-cat-mobile-barra-ativa-bg: #fff; 672--c-text-itens: #444; 673--c-text-itens-pilulas: 000; 674--c-background-itens-pilulas: #FFDE21; 675--c-background-destaque-pilulas: #FFDE21; 676--c-text-destaque-pilulas: #000; 677--f-title-carrossel-produtos-mobile: 12px; 678--f-title-carrossel-produtos-desktop: 16px; 679--c-title-carrossel-produtos: #444; 680--c-vermais-text-carrossel-produtos: #444; 681--c-border-vermais-carrossel-produtos: #E9E9E9; 682--c-bg-vermais-carrossel-produtos: #E9E9E9; 683--c-bg-hover-vermais-carrossel-produtos: #E9E9E9; 684--c-text-hover-vermais-carrossel-produtos: #444; 685--c-border-hover-vermais-carrossel-produtos: #E9E9E9; 686--c-border-card-carrossel-produtos: #E9E9E9; 687--c-bg-card-carrossel-produtos: #FFF; 688--c-text-nome-produto-carrossel-produtos: #444; 689--c-text-preco-carrossel-produtos: #444;
690--c-background-informacoes: #E9E9E9; 691--c-text-informacoes: #444; 692--c-background-item-informacoes: #E9E9E9; 693--c-border-item-informacoes: #E9E9E9; 694--c-border-imagens-redondas: #D9D9D9; 695--c-text-imagens-redondas: #444; 696 697 } 698 </style> 699 700 701 702 703 <style> 704 705</style> 706 707 708 <link rel="stylesheet" href="/staticfiles/css/output.css"> 709 710
710<script type="ff2c2aaab21707ab2ba08cc2-text/javascript" src="/staticfiles/js/index.js"></script>
710 711 712 <!-- Structured Data - Schema.org --> 713
713<script type="application/ld+json"> 714 { 715 "@context": "https://schema.org", 716 "@type": "WebSite", 717 "name": "Farmácias Brava ", 718 "url": "https://www.farmaciasbrava.com.br", 719 "description": "Aproveite nossas ofertas no site ou pelo app e receba em casa ou retire seus pedidos na loja.", 720 "potentialAction": { 721 "@type": "SearchAction", 722 "target": { 723 "@type": "EntryPoint", 724 "urlTemplate": "https://www.farmaciasbrava.com.br/pesquisa/{search_term_string}" 725 }, 726 "query-input": "required name=search_term_string" 727 } 728 } 729 </script>
729 730 731
731<script type="application/ld+json"> 732 { 733 "@context": "https://schema.org", 734 "@type": "Organization", 735 "name": "Farmácias Brava ", 736 "url": "https://www.farmaciasbrava.com.br", 737 "logo": "https://storage.googleapis.com/retailon-publico/brava/imagens/favicon.png", 738 "contactPoint": { 739 "@type": "ContactPoint", 740 "contactType": "Customer Service", 741 "availableLanguage": "Portuguese" 742 }, 743 "sameAs": [] 744 } 745 </script>
745 746 747 748 749 750 751 752</head> 753 754<body class="min-h-screen flex flex-col">
vendor: 83 bytes, lines 754-760
754 755 756 757 758 759 <noscript> 760 <iframe src="https://www.googletagmanager.com/ns.html?id=
760GTM-THG4PHZ
vendor: 104 bytes, lines 760-767
760" 761 height="0" width="0" style="display:none;visibility:hidden"></iframe> 762 </noscript> 763 764 765 766 767
767<div id="app_aviso"> 768 769 </div> 770 771 772 773 774 775 <div id="app_header"> 776 777 778 779 780 <header class="w-full fixed z-50"> 781 <div class="w-full bg-[var(--c-header-topbar-bg)] px-4 py-2 gap-y-2"> 782 <p class="text-[var(--c-header-topbar-text)] font-semibold text-center">Frete grátis acima de R$ 100,00 | 6x sem juros</p> 783 </div> 784 785 <div class="w-full bg-[var(--c-header-mid-bg)] py-5 gap-y-2"> 786 <div class="container mx-auto flex-1"> 787 <div class="flex flex-row justify-between items-center gap-x-4 px-4"> 788 <div class="flex items-center"> 789 <!-- Menu --> 790 791 <!-- Logo --> 792 <a href="/" class="flex items-center shrink-0"> 793 <img loading="lazy" src="/staticfiles/img/client2/logo_branca.png" alt="Logo" width="180" height="auto" class="px-3"> 794 </a> 795 </div> 796 <!-- Barra de pesquisa e localização --> 797 <div class="flex flex-col gap-y-3 flex-1 min-w-0"> 798 <div class="bg-white rounded-full flex items-center px-3 relative"> 799 <input id="header-search-input" autocomplete="off" type="text" placeholder="Buscar na Brava" class="flex-1 ml-2 outline-none text-sm"> 800 <button type="button" class="flex items-center justify-center cursor-pointer rounded-full border-1 border-[var(--c-brand-primary)] w-6 h-6 hover:shadow-md transition" onclick="if (!window.__cfRLUnblockHandlers) return false; submitHeaderSearch()" data-cf-modified-ff2c2aaab21707ab2ba08cc2-=""> 801 <img loading="lazy" src="/staticfiles/img/client2/icon_search.png" alt="Pesquisa" class="w-3 h-3" /> 802 </button> 803 804 <div id="header-search-dropdown" class="absolute top-full left-0 mt-2 w-full bg-white rounded-2xl shadow-[0_1px_8px_rgba(0,0,0,0.15)] border border-[#E6E6E6] z-50 hidden"> 805 <div id="header-search-dropdown-content" class="p-3"></div> 806 </div> 807 808 809 </div> 810 811 </div> 812 813 <div class="flex items-center gap-8 text-[var(--c-header-mid-text)] shrink-0"> 814 815 <div class="flex items-center gap-2"> 816 <!-- Bloco 1: Login --> 817 <img loading="lazy" src="/staticfiles/img/client2/user_perfil.png" alt="Ãcone usuário" class="w-8 h-8" /> 818 819 <p class="text-sm leading-tight"> 820 Olá!<br/> 821 822 <a href="/login"> 823 <span class="font-bold">Entre ou cadastre-se</span> 824 </a> 825 826 </p> 827 </div> 828 829 <!-- Bloco 2: Localização --> 830 831 832 <!-- Bloco: Ultimo pedido --> 833 <div class="relative inline-block"> 834 <!-- BOTÃO DE ABERTURA --> 835 <div id="btn-dropdown" 836 class="flex items-center gap-2 px-3 py-2 cursor-pointer justify-center rounded-xl hover:bg-[#003CFF] hover:shadow transition"> 837 <div class="relative" data-target="notification"> 838 <img loading="lazy" id="img-ultimo-pedido" src="/staticfiles/img/client2/box_white.png" alt="Ãcone localização" class="w-10 h-10" /> 839 </div> 840 <p class="text-sm leading-tight"> 841 Ãltimo<br /> 842 <span class="font-bold">pedido</span> 843 </p> 844 </div> 845 846 <!-- DROPDOWN --> 847 <div id="dropdown-menu" class="py-[20px] px-[20px] absolute left-[-40px] z-50 bg-white border border-gray-200 rounded-xl shadow-lg w-100 hidden"> 848 849 <!-- TOGGLE DE ENTREGA --> 850 <div class="w-full max-w-md mx-auto"> 851 852 853 <div class="grid grid-cols-1 gap-4"> 854 <div class="flex justify-center items-center"> 855 <img loading="lazy" src="/staticfiles/img/client2/cart-pedido.png" class="w-30 h-30 p-1" alt="Atendimento"> 856 </div> 857 <p class="mb-2 text-[14px] font-semibold text-gray-600 text-center">Necessário estar logado para visualizar o último pedido</p> 858 <a href="/login" class="text-center"> 859 <div class="flex justify-center items-center w-full h-10 bg-[var(--c-brand-secondary)] hover:opacity-90 text-white rounded-lg " id="verificar-cpf"> 860 Entre ou cadastre-se 861 </div> 862 </a> 863 </div> 864 865 866 </div> 867 </div> 868 </div> 869 870 <!-- Bloco 3: Cesta --> 871 <div class="flex items-center cursor-pointer qtd-checkout gap-2 px-3 py-2 bg-[var(--c-header-carrinho-bg)] rounded-xl" data-content="" onclick="if (!window.__cfRLUnblockHandlers) return false; toggleCarrinho('open')" data-cf-modified-ff2c2aaab21707ab2ba08cc2-=""> 872 <div class="relative"> 873 <img loading="lazy" src="/staticfiles/img/client2/cesta.png" alt="Ãcone cesta" class="w-8 h-8 " /> 874 <!-- Badge de quantidade -->
875 <span data-target="badge-qtd-itens" 876 class="absolute -bottom-1 -left-1 bg-[var(--c-brand-secondary)] text-white text-[xx-small] w-4 h-4 rounded-full flex items-center justify-center font-bold"> 877 0 878 </span> 879 </div> 880 <div class="text-sm leading-tight text-nowrap"> 881 Minha cesta<br/> 882 <span class="font-bold" data-target="badge-total-cesta">R$ 0,00</span> 883 </div> 884 </div> 885 </div> 886 </div> 887 </div> 888 </div> 889 <div class="w-full bg-[var(--c-header-bottombar-bg)] shadow-[9px_-2px_10px_rgb(0,0,0,0.38)] gap-y-2 "> 890 <div class="container mx-auto flex-1"> 891 <div class="flex flex-row items-center gap-x-4 relative"> 892 <div class="flex items-stretch w-full"> 893 <ul class="grid grid-cols-5 flex-1 divide-x divide-white/40"> 894 895 <!-- Menu desktop --> 896 897 <li class="list-none relative flex bg-[var(--c-brand-primary)] rounded-b-[20px] hover:bg-[#1b2985] hover:shadow-sm items-center justify-center px-10 transition" data-target="menu-principal" onmouseenter="if (!window.__cfRLUnblockHandlers) return false; toggleContainerMenuCategorias('open')" onmouseleave="if (!window.__cfRLUnblockHandlers) return false; closeMenuCategoriasSeNaoEstiverFocado()" data-cf-modified-ff2c2aaab21707ab2ba08cc2-=""> 898 <a href="/" class="flex items-center justify-center"> 899 <img loading="lazy" loading="lazy"src="/staticfiles/img/client2/menu-bar.png" alt="Menu" width="18" height="18" class="img-filter-fff"> 900 <span class="text-[#FFF] text-base font-semibold pl-2">Categorias</span> 901 </a> 902 </li> 903 904 <!--<li> 905 <a href="/categorias/saldao" class="group block text-center px-10 py-1.5 text-[var(--c-header-bottombar-text)] font-semibold text-[15px] hover:bg-white/15 whitespace-nowrap"> 906 <span class="relative inline-block"> 907 Saldão 908 <span class="absolute bottom-0 left-0 h-[1.3px] bg-current w-0 group-hover:w-full transition-all duration-300 ease-out"></span> 909 </span> 910 </a> 911 </li>--> 912 <li> 913 <a href="/nossas-lojas" class="group block text-center px-10 py-1.5 text-[var(--c-header-bottombar-text)] font-semibold text-[15px] hover:bg-white/15 whitespace-nowrap"> 914 <span class="relative inline-block"> 915 Nossas Lojas 916 <span class="absolute bottom-0 left-0 h-[1.3px] bg-current w-0 group-hover:w-full transition-all duration-300 ease-out"></span> 917 </span> 918 </a> 919 </li> 920 <li> 921 <a href="/trabalhe-conosco" class="group block text-center px-10 py-1.5 text-[var(--c-header-bottombar-text)] font-semibold text-[15px] hover:bg-white/15 whitespace-nowrap"> 922 <span class="relative inline-block"> 923 Trabalhe Conosco 924 <span class="absolute bottom-0 left-0 h-[1.3px] bg-current w-0 group-hover:w-full transition-all duration-300 ease-out"></span> 925 </span> 926 </a> 927 </li> 928 <li class="bg-[var(--c-brand-secondary)]"> 929 <a href="/franquias" class="group block text-center px-10 py-1.5 text-white font-semibold text-[15px] hover:bg-white/15 whitespace-nowrap"> 930 <span class="relative inline-block"> 931 Seja um franqueado 932 <span class="absolute bottom-0 left-0 h-[1.3px] bg-current w-0 group-hover:w-full transition-all duration-300 ease-out"></span> 933 </span> 934 </a> 935 </li> 936 937 <!-- Menu desktop --> 938 <div class="absolute left-0 top-full w-full z-40"> 939 <div class="container mx-auto flex-1"> 940 941 <div class="container mx-auto flex absolute h-[520px] border-1 border-[var(--c-brand-primary)] w-full bg-[#fff] rounded-[15p
941x] hidden" data-target="container-menu-principal" onmouseenter="if (!window.__cfRLUnblockHandlers) return false; toggleContainerMenuCategoriasEmFoco('open')" onmouseleave="if (!window.__cfRLUnblockHandlers) return false; toggleContainerMenuCategorias('close');" data-cf-modified-ff2c2aaab21707ab2ba08cc2-=""> 942 <div class="h-full w-[230px] bg-[#dee3ff] rounded-[15px] flex flex-col"> 943 944 <!-- LOGO --> 945 <div class="flex items-center px-4 py-2 pt-[30px] pb-2"> 946 <img loading="lazy" src="/staticfiles/img/client2/logo_azul_rosa.png" alt="Logo" class="w-[120px]"> 947 </div> 948 949 <a href="/login" class="flex items-center px-4 py-2 cursor-pointer"> 950 <img loading="lazy" src="/staticfiles/img/client2/fav_icon.png" alt="Medicamentos" class="img-filter-002aff w-6 h-6"> 951 <span class="pl-3 text-[var(--c-brand-primary)] font-semibold text-[14px]">Lista de Desejos</span> 952 </a> 953 <a href="/login" class="flex items-center px-4 py-2 cursor-pointer"> 954 <img loading="lazy" src="/staticfiles/img/client2/box.png" alt="Medicamentos" class="img-filter-002aff w-6 h-6"> 955 <span class="pl-3 text-[var(--c-brand-primary)] font-semibold text-[14px]">Minhas compras</span> 956 </a> 957 <div class="flex items-center px-4 py-3"> 958 <span class="pr-3 text-[var(--c-brand-primary)] font-semibold text-[18px]">Categorias</span> 959 </div> 960 961 <!-- LISTA DE CATEGORIAS --> 962 <div class="" id="menu-categorias-esquerda"></div> 963 964 </div> 965 <div class="h-full w-full min-h-0 overflow-hidden px-6 py-4 flex flex-col" id="menu-categorias-direita"></div> 966 967 </div> 968 969 </div> 970 </div> 971 972 </ul> 973 974 975 976 977 </div> 978 </div> 979 </div> 980 </div> 981 </header> 982 983 984
984<script type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 985 986 // Controle do dropdown de localização 987 const btnDropdown = document.getElementById('btn-dropdown'); 988 const dropdownMenu = document.getElementById('dropdown-menu'); 989 990 // Expor ID do usuário para chaves de storage 991 window.APP_USER_ID = null; 992 window.ULTIMO_PEDIDO_STATUS_KEY = null; 993 const STORAGE_SEEN_KEY = (userId) => `ultimoPedidoStatusSeen:${userId||'anon'}`; 994 995 btnDropdown.addEventListener('click', (e) => { 996 e.stopPropagation(); 997 dropdownMenu.classList.toggle('hidden'); 998 // Se abriu, marca como visualizado e remove badge 999 if (!dropdownMenu.classList.contains('hidden')) { 1000 marcarUltimoPedidoComoVisto(); 1001 ensureNotificationBadge(false); 1002 } 1003 }); 1004 1005 document.addEventListener('click', () => { 1006 dropdownMenu.classList.add('hidden'); 1007 }); 1008 1009 dropdownMenu.addEventListener('click', (e) => { 1010 e.stopPropagation(); 1011 }); 1012 1013 1014 1015 1016 1017 1018 1019 function ensureNotificationBadge(show){ 1020 try { 1021 const target = document.querySelector('[data-target="notification"]'); 1022 if(!target) return; 1023 let badge = target.querySelector('[data-notification-badge]'); 1024 if (show) { 1025 if (!badge){ 1026 badge = document.createElement('span'); 1027 badge.setAttribute('data-notification-badge','1'); 1028 badge.className = 'absolute bottom-0 -left-1 bg-[var(--c-brand-secondary)] text-white text-[xx-small] w-4 h-4 rounded-full flex items-center justify-center font-bold'; 1029 badge.textContent = '!'; 1030 target.appendChild(badge); 1031 } 1032 } else if (badge) { 1033 badge.remove(); 1034 } 1035 } catch(err){ console.error(err); } 1036 } 1037 1038 function marcarUltimoPedidoComoVisto(){ 1039 try { 1040 const userId = window.APP_USER_ID; 1041 if (!window.ULTIMO_PEDIDO_STATUS_KEY) return; 1042 localStorage.setItem(STORAGE_SEEN_KEY(userId), window.ULTIMO_PEDIDO_STATUS_KEY); 1043 } catch(err){ console.error(err); } 1044 } 1045 1046 function getPedidoFromResponse(payload){ 1047 // Tenta localizar o objeto do pedido em diferentes chaves comuns 1048 const obj = payload?.pedido || payload?.data || payload?.result || payload; 1049 return obj || null; 1050 } 1051 1052 function renderUltimoPedidoStatusCard(p){ 1053 // Campos defensivos 1054 const numero = p?.id || 'â'; 1055 const status = p?.nm_descritivo || 'â'; 1056 const tipo_entrega = p?.entrega_descricao || 'â'; 1057 const endereco = `${p?.endereco_destino}, ${p?.numero_destino}, ${p?.bairro_destino} | ${p?.cidade_destino} - ${p?.estado_destino}`; 1058 const hash = p?.hash || ''; 1059 const img = p?.entrega_imagem || '/staticfiles/img/client2/icon-delivery-white.png'; 1060 const status_imagem = p?.status_imagem || '/staticfiles/img/client2/box_white.png'; 1061 1062 (()=>{ 1063 const el = document.getElementById('img-ultimo-pedido'); 1064 if (el) { 1065 el.src = img; 1066 el.alt = 'Status do último pedido'; 1067 el.onerror = function(){ this.onerror=null; this.src=status_imagem; }; 1068 } 1069 })(); 1070 1071 return ` 1072 <a href="/cliente/pedido/${hash}" class="flex items-center w-full p-2 rounded-lg border border-gray-200 hover:bg-[#dee3ff] cursor-pointer transition"> 1073 <div class="grid grid-cols-1 gap-1 flex-1 min-w-0"> 1074 <div class="flex justify-between items-center"> 1075 <div class="flex items-center"> 1076 <div class="img-filter-002aff pr-2 flex-shrink-0"> 1077 <img loading="lazy" src="${img}" class="w-7 h-7" alt="Ãcone entrega" /> 1078 </div> 1079 <p class="font-semibold text-sm text-gray-900">Pedido nº ${numero}</p> 1080 </div> 1081 <p class="text-xs text-gray-600 leading-snug break-words">${tipo_entrega}</p> 1082 </div> 1083 <p class="text-sm text-gray-600"> 1084 Status do Pedido: <span class="text-gray-600 pl-2">${status}</span> 1085 </p> 1086 ${endereco ? ` 1087 <div class="text-sm text-gray-400"> 1088 <p class="text-sm text-gray-400 leading-snug break-words">${endereco}</p> 1089 </div>` : ''} 1090 </div> 1091 </a> 1092 `; 1093 } 1094 1095 async function atualizarUltimoPedido(){ 1096 try { 1097 const container = document.getElementById('ultimo-pedido-status'); 1098 if(!container) return; 1099 container.classList.remove('hidden'); 1100 1101 const resp = await fetch('/cliente/buscar/componente/ultimo/pedido', { 1102 method: 'GET', 1103 headers: { 1104 'Content-Type': 'application/json', 1105 'X-CSRFToken': window.CSRF_TOKEN(), 1106 }, 1107 }); 1108 1109 const payload = await resp.json().catch(() => ({})); 1110 const ok = resp.ok && (payload?.status ?? true); 1111 if (ok) { 1112 const pedido = getPedidoFromResponse(payload); 1113 if (pedido) { 1114 container.innerHTML = renderUltimoPedidoStatusCard(pedido); 1115 // Determina chave de status atual 1116 const numero = pedido?.numero || pedido?.num || pedido?.id || pedido?.cd_pedido || ''; 1117 const status = pedido?.status_atual_id || ''; 1118 window.ULTIMO_PEDIDO_STATUS_KEY = `${numero}|${status}`; 1119 1120 // Badge de notificação: mostrar somente se status mudou desde o último visto 1121 const seenKey = localStorage.getItem(STORAGE_SEEN_KEY(window.APP_USER_ID)); 1122 const precisaAlertar = !!window.ULTIMO_PEDIDO_STATUS_KEY && window.ULTIMO_PEDIDO_STATUS_KEY !== seenKey; 1123 ensureNotificationBadge(precisaAlertar); 1124 } else { 1125 container.innerHTML = 1126 `<div class="w-full text-center bg-white"> 1127 <div class="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-[var(--c-brand-primary)]/10"> 1128 <svg class="h-7 w-7 text-[var(--c-brand-primary)]" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> 1129 <path d="M12.1 21.35a1 1 0 0 1-1.2 0C5.6 17.3 2 14.13 2 10.27A5.27 5.27 0 0 1 7.27 5a5.5 5.5 0 0 1 4.73 2.73A5.5 5.5 0 0 1 16.73 5 5.27 5.27 0 0 1 22 10.27c0 3.86-3.6 7.03-8.9 11.08Z"/> 1130 </svg> 1131 </div> 1132 <p class="text-lg font-semibold text-gray-900" id="empty-title">Nada por aqui⦠ainda</p> 1133 1134 <div class="mt-5 flex flex-col items-center gap-2 sm:flex-row sm:justify-center"> 1135 <a href="/login" class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"> 1136 Entre ou cadastre-se 1137 </a> 1138 </div> 1139 1140 </div>`; 1141 ensureNotificationBadge(false); 1142 } 1143 } else { 1144 container.innerHTML = 1145 `<div class="w-full text-center bg-white"> 1146 <div class="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-[var(--c-brand-primary)]/10"> 1147 <svg class="h-7 w-7 text-[var(--c-brand-primary)]" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> 1148 <path d="M12.1 21.35a1 1 0 0 1-1.2 0C5.6 17.3 2 14.13 2 10.27A5.27 5.27 0 0 1 7.27 5a5.5 5.5 0 0 1 4.73 2.73A5.5 5.5 0 0 1 16.73 5 5.27 5.27 0 0 1 22 10.27c0 3.86-3.6 7.03-8.9 11.08Z"/> 1149 </svg> 1150 </div> 1151 <p class="text-lg font-semibold text-gray-900" id="empty-title">Nada por aqui⦠ainda</p> 1152 1153 <div class="mt-5 flex flex-col items-center gap-2 sm:flex-row sm:justify-center"> 1154 <a href="/login" class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"> 1155 Entre ou cadastre-se 1156 </a> 1157 </div> 1158 1159 </div>`; 1160 ensureNotificationBadge(false); 1161 } 1162 } catch (error) { 1163 console.error(error); 1164 const container = document.getElementById('ultimo-pedido-status'); 1165 if (container) container.innerHTML = 'Não foi possÃvel carregar o último pedido'; 1166 ensureNotificationBadge(false); 1167 } 1168 } 1169 1170 document.addEventListener('DOMContentLoaded', atualizarUltimoPedido); 1171 1172 </script>
1172 1173 1174
1174<script type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 1175 //navbar menu 1176 1177 containerMenuCategoriasAtivo = false 1178 function toggleContainerMenuCategoriasEmFoco(status){ 1179 containerMenuCategoriasAtivo = status == 'open' ? true : false 1180 } 1181 function closeMenuCategoriasSeNaoEstiverFocado(){ 1182 if(!document.querySelector('[data-target="container-menu-principal"]').classList.contains('hidden')){ 1183 setTimeout(() => { 1184 if(!containerMenuCategoriasAtivo) 1185 toggleContainerMenuCategorias('close') 1186 }); 1187 } 1188 } 1189 1190 function toggleContainerMenuCategorias(status){ 1191 const containerMenuPrincipal = document.querySelector('[data-target="container-menu-principal"]') 1192 const menuPrincipal = document.querySelector('[data-target="menu-principal"]') 1193 if(containerMenuPrincipal){ 1194 if(status === 'open'){ 1195 containerMenuPrincipal.classList.remove('hidden') 1196 if (menuPrincipal) { 1197 menuPrincipal.style.backgroundColor = '#1b2985'; 1198 //menuPrincipal.classList.add('bg-[var(--c-categorias-hover)]') 1199 } 1200 // Evento: abriu menu de categorias 1201 trackMenuCategoriaClick({ 1202 event: 'open_menu_categorias', 1203 content_type: 'menu', 1204 content_id: 'menu_categorias_header', 1205 }); 1206 } else { 1207 if (menuPrincipal) { 1208 menuPrincipal.style.backgroundColor = ''; 1209 //menuPrincipal.classList.remove('bg-[var(--c-categorias-hover)]') 1210 } 1211 containerMenuPrincipal.classList.add('hidden') 1212 containerMenuCategoriasAtivo = false 1213 1214 // Evento: fechou menu de categorias 1215 trackMenuCategoriaClick({ 1216 event: 'close_menu_categorias', 1217 content_type: 'menu', 1218 content_id: 'menu_categorias_header', 1219 }); 1220 } 1221 } 1222 } 1223 function exibirMenuCategorias(el) { 1224 const categoriaHover = el?.getAttribute('data-categoria') 1225 if(!categoriaHover) 1226 return 1227 const menuCategorias = document.querySelector(`[data-sub-categoria="${categoriaHover}"]`) 1228 if(!menuCategorias || !menuCategorias?.classList?.contains('hidden')) 1229 return 1230 const todosMenusCategorias = document.querySelectorAll('[data-sub-categoria]') 1231 todosMenusCategorias.forEach(menu => { 1232 menu?.classList?.add('hidden') 1233 }) 1234 menuCategorias?.classList?.remove('hidden') 1235 } 1236 1237 function trackMenuCategoriaClick(data) { 1238 try { 1239 if (typeof filtroParaDadosAnaliticos === 'function') { 1240 filtroParaDadosAnaliticos('MenuAndCallToAction', { 1241 section: 'menu_categorias', 1242 ...data, 1243 }); 1244 } 1245 } catch (e) { 1246 console.error('Erro ao disparar evento de menu categorias:', e); 1247 } 1248 } 1249 1250 function montarMenu(arvore) { 1251 1252 const toNode = (n) => { 1253 const children = Array.isArray(n?.filhos) ? n.filhos : []; 1254 1255 return { 1256 id: n?.id, 1257 url: n?.url || '#', 1258 label: n?.nome || '', 1259 order: typeof n?.ordem === 'number' ? n.ordem : Number.MAX_SAFE_INTEGER, 1260 1261 // mantém o que seu render já espera 1262 children: children.map(toNode), 1263 1264 // opcionais (se quiser manter compatibilidade com banner) 1265 banner: n?.imagem || null, 1266 banner_url: n?.url || null, 1267 }; 1268 }; 1269 arvore = arvore.map(toNode); 1270 1271 const left = document.getElementById('menu-categorias-esquerda'); 1272 const right = document.getElementById('menu-categorias-direita'); 1273 if (!left || !right) return; 1274 1275 left.innerHTML = ''; 1276 right.innerHTML = ''; 1277 1278 const ordenadas = [...(arvore || [])].sort((a, b) => { 1279 const ao = typeof a.order === 'number' ? a.order : Number.MAX_SAFE_INTEGER; 1280 const bo = typeof b.order === 'number' ? b.order : Number.MAX_SAFE_INTEGER; 1281 return ao - bo; 1282 }); 1283 1284 // Helper 1285 const toSentenceCase = (str) => { 1286 const s = (str || '').toLowerCase(); 1287 return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; 1288 }; 1289 1290 // Monta os itens do menu esquerdo e os painéis da direita 1291 ordenadas.forEach((cat, idx) => { 1292 // Item esquerda: <a> clicável com seta ao lado, data-categoria + onmouseenter para exibirMenuCategorias 1293 const item = document.createElement('a'); 1294 item.href = cat.url; 1295 item.className = 'flex items-center px-4 py-2 w-full cursor-pointer group'; 1296 item.setAttribute('data-target', 'item-categorias'); 1297 item.setAttribute('data-categoria', String(cat.id)); 1298 item.setAttribute('onmouseenter', 'exibirMenuCategorias(this)'); 1299 1300 item.innerHTML = `
1301 <span class="text-[#444] font-semibold group-hover:font-bold text-[15px] pr-3">${cat.label}</span> 1302 <img loading="lazy" src="/staticfiles/img/client2/arrow-right.svg" alt="Seta" width="21" height="21"> 1303 `; 1304 1305 item.addEventListener('click', () => { 1306 trackMenuCategoriaClick({ 1307 event: 'click_categoria_principal', 1308 content_type: 'categoria', 1309 content_id: cat.id, 1310 label: cat.label, 1311 }); 1312 }); 1313 1314 left.appendChild(item); 1315 1316 // Painel direita: pré-renderizado com data-sub-categoria (estrutura SEO-friendly) 1317 const panel = document.createElement('div'); 1318 panel.className = `flex-1 flex flex-col overflow-hidden${idx === 0 ? '' : ' hidden'}`; 1319 panel.setAttribute('data-sub-categoria', String(cat.id)); 1320 1321 panel.innerHTML = ` 1322 <div class="flex justify-between shrink-0"> 1323 <p class="text-[16px] text-[var(--c-brand-primary)] font-bold uppercase">${cat.label}</p> 1324 <a href="${cat.url}" class="text-[#1b2985] font-medium text-[12px]">Ver todos</a> 1325 </div> 1326 `; 1327 1328 const categoriesWrap = document.createElement('div'); 1329 categoriesWrap.className = 'flex-1 min-h-0 overflow-hidden pt-2'; 1330 1331 const grid = document.createElement('div'); 1332 grid.className = 'w-full h-full overflow-hidden'; 1333 grid.style.cssText = 'columns: 6; column-fill: auto;'; 1334 1335 (cat.children || []).forEach(sec => { 1336 const block = document.createElement('div'); 1337 block.className = ' mb-3 flex flex-col gap-y-0.5'; 1338 1339 const tituloSec = document.createElement('a'); 1340 tituloSec.href = sec.url; 1341 tituloSec.className = 'pb-0.5 text-[16px] font-semibold text-[var(--c-brand-primary)] leading-tight'; 1342 tituloSec.textContent = sec.label; 1343 1344 tituloSec.addEventListener('click', () => { 1345 trackMenuCategoriaClick({ 1346 event: 'click_subcategoria', 1347 content_type: 'subcategoria', 1348 content_id: sec.id, 1349 label: sec.label, 1350 parent_id: cat.id, 1351 parent_label: cat.label, 1352 }); 1353 }); 1354 1355 block.appendChild(tituloSec); 1356 1357 if (Array.isArray(sec.children)) { 1358 sec.children.forEach(ter => { 1359 const linkTer = document.createElement('a'); 1360 linkTer.href = ter.url; 1361 linkTer.className = 'text-[14px] text-[#444] hover:underline leading-snug'; 1362 linkTer.textContent = toSentenceCase(ter.label); 1363 1364 linkTer.addEventListener('click', () => { 1365 trackMenuCategoriaClick({ 1366 event: 'click_subcategoria_nivel_2', 1367 content_type: 'subcategoria_2', 1368 content_id: ter.id, 1369 label: ter.label, 1370 parent_id: sec.id, 1371 parent_label: sec.label, 1372 root_id: cat.id, 1373 root_label: cat.label, 1374 }); 1375 }); 1376 1377 block.appendChild(linkTer); 1378 }); 1379 } 1380 1381 grid.appendChild(block); 1382 }); 1383 1384 const banner = document.createElement('div'); 1385 banner.className = 'flex items-center w-full shrink-0 pt-2'; 1386 const bHref = cat.banner_url || cat.url || '#'; 1387 const bImg = cat.banner ? `<img loading="lazy" src="${cat.banner}" alt="Banner ${cat.label}" class="w-full rounded-[15px] object-cover" style="height: 90px; object-position: center;">` : '<img loading="lazy" src="https://placehold.co/1000x90" alt="Banner ${cat.label}" class="w-full rounded-[15px] object-cover" style="height: 90px; object-position: center;">'; 1388 banner.innerHTML = `<a href="${bHref}" class="cursor-pointer w-full block" target="_blank" rel="noopener noreferrer">${bImg}</a>`; 1389 1390 categoriesWrap.appendChild(grid); 1391 panel.appendChild(categoriesWrap); 1392 panel.appendChild(banner); 1393 right.appendChild(panel); 1394 }); 1395 } 1396 1397 // CARREGAMENTO DE PESQUISA 1398 1399 function inicializarHeaderSearch(){ 1400 try { 1401 const input = document.getElementById('header-search-input'); 1402 const dropdown = document.getElementById('header-search-dropdown'); 1403 const content = document.getElementById('header-search-dropdown-content'); 1404 if(!input || !dropdown || !content) return; 1405 1406 // Delegação: termos/histórico (bind uma vez) 1407 bindHeaderSearchDelegates(); 1408 1409 const debounced = debounce(renderHeaderSearch, 200); 1410 1411 input.addEventListener('focus', () => { 1412 abrirDropdown(); 1413 renderHeaderSearch(); // decide histórico x resultados 1414 }); 1415 1416 input.addEventListener('input', () => { 1417 abrirDropdown(); 1418 debounced(); 1419 }); 1420 1421 input.addEventListener('keydown', (e) => { 1422 if (e.key === 'Enter') submitHeaderSearch(); 1423 if (e.key === 'Escape') fecharDropdown(); 1424 }); 1425 1426 // Fecha clicando fora 1427 document.addEventListener('mousedown', (e) => { 1428 const t = e.target; 1429 if (t === input || input.contains(t)) return; 1430 if (dropdown && (t === dropdown || dropdown.contains(t))) return; 1431 fecharDropdown(); 1432 }); 1433 } catch (error) { 1434 console.error(error); 1435 } 1436 } 1437 1438 function bindHeaderSearchDelegates(){ 1439 const content = document.getElementById('header-search-dropdown-content'); 1440 if (!content || content.dataset.bound) return; 1441 1442 content.addEventListener('click', (e) => { 1443 const termBtn = e.target.closest('[data-term]'); 1444 if (termBtn) { 1445 const termo = decodeURIComponent(termBtn.getAttribute('data-term') || ''); 1446 preencherBuscaCom(termo); 1447 submitHeaderSearch(); 1448 return; 1449 } 1450 const rmBtn = e.target.closest('[data-hist-remove]'); 1451 if (rmBtn) { 1452 const termo = decodeURIComponent(rmBtn.getAttribute('data-hist-remove') || ''); 1453 HeaderSearchCore.removeHistoricoPesquisa(termo); 1454 atualizarDropdownComHistoricoESugestoes(); 1455 return; 1456 } 1457 const clrBtn = e.target.closest('[data-hist-clear]'); 1458 if (clrBtn) { 1459 HeaderSearchCore.clearHistoricoPesquisa(); 1460 atualizarDropdownComHistoricoESugestoes(); 1461 return; 1462 } 1463 }); 1464 1465 content.dataset.bound = '1'; 1466 } 1467 1468 function abrirDropdown(){ 1469 const dd = document.getElementById('header-search-dropdown'); 1470 if(dd) dd.classList.remove('hidden'); 1471 } 1472 function fecharDropdown(){ 1473 const dd = document.getElementById('header-search-dropdown'); 1474 if(dd) dd.classList.add('hidden'); 1475 } 1476 1477 async function renderHeaderSearch(){ 1478 const input = document.getElementById('header-search-input'); 1479 const content = document.getElementById('header-search-dropdown-content'); 1480 if(!input || !content) return; 1481 1482 const termo = (input.value || '').trim(); 1483 if(!termo){ 1484 atualizarDropdownComHistoricoESugestoes(); 1485 return; 1486 } 1487 1488 content.innerHTML = HeaderSearchCore.templateLoading(); 1489 1490 const { termos, produtos } = await HeaderSearchCore.getSearchData(termo); 1491 1492 let html = ''; 1493 if (Array.isArray(termos) && termos.length) { 1494 html += `<p class="text-[12px] text-[#777] px-1 mb-1">Termos sugeridos</p>`; 1495 html += termos.map(s => ` 1496 <button type="button" 1497 class="w-full text-left px-2 py-1.5 hover:bg-[#f6f7ff] rounded cursor-pointer" 1498 data-term="${encodeURIComponent(s)}"> 1499 ${HeaderSearchCore.escapeHtml(s)} 1500 </button> 1501 `).join(''); 1502 if (produtos?.length) html += `<hr class="my-2 border-[#eee]">`; 1503 } 1504 1505 if (produtos?.length){ 1506 html += `<p class="text-[12px] text-[#777] px-1 mb-2">Produtos sugeridos</p>`;
1507 html += renderDropdownProdutos2Cols(produtos); // mantém 2 colunas no desktop 1508 html += ` 1509 <div class="mt-4 flex justify-center"> 1510 <a class="block text-[var(--c-brand-primary)] font-semibold px-2 py-2 hover:underline" 1511 href="/pesquisa/${encodeURIComponent(termo)}">Ver todos os produtos</a> 1512 </div>`; 1513 } else { 1514 html += ` 1515 <a class="block text-[var(--c-brand-primary)] font-semibold px-2 py-2 hover:underline" 1516 href="/pesquisa/${encodeURIComponent(termo)}"> 1517 Ver resultados para "${HeaderSearchCore.escapeHtml(termo)}" 1518 </a>`; 1519 } 1520 1521 content.innerHTML = html; 1522 } 1523 1524 function atualizarDropdownComHistoricoESugestoes(){ 1525 const content = document.getElementById('header-search-dropdown-content'); 1526 const input = document.getElementById('header-search-input'); 1527 if(!content || !input) return; 1528 1529 const historico = HeaderSearchCore.getHistoricoPesquisa(); 1530 let html = ''; 1531 if (historico.length){ 1532 html += ` 1533 <div class="flex items-center justify-between px-1 mb-1"> 1534 <p class="text-[12px] text-[#777]">Histórico</p> 1535 <button type="button" class="text-[12px] text-[#888] hover:underline" data-hist-clear> 1536 Limpar 1537 </button> 1538 </div> 1539 `; 1540 html += historico.map(h => ` 1541 <div class="flex items-center justify-between px-2 py-1.5 hover:bg-[#f6f7ff] rounded"> 1542 <button type="button" class="text-left flex-1" data-term="${encodeURIComponent(h)}"> 1543 ${HeaderSearchCore.escapeHtml(h)} 1544 </button> 1545 <button type="button" class="text-[#999] ml-2" aria-label="Remover" 1546 data-hist-remove="${encodeURIComponent(h)}">â</button> 1547 </div> 1548 `).join(''); 1549 } else { 1550 html += `<p class="text-[12px] text-[#777] px-1">Sem histórico recente</p>`; 1551 } 1552 1553 content.innerHTML = html; 1554 } 1555 1556 function preencherBuscaCom(termo){ 1557 const input = document.getElementById('header-search-input'); 1558 if(!input) return; 1559 input.value = termo; 1560 abrirDropdown(); 1561 renderHeaderSearch(); 1562 } 1563 1564 function submitHeaderSearch(){ 1565 const input = document.getElementById('header-search-input'); 1566 if(!input) return; 1567 const termo = (input.value || '').trim(); 1568 if(!termo){ 1569 abrirDropdown(); 1570 atualizarDropdownComHistoricoESugestoes(); 1571 return; 1572 } 1573 HeaderSearchCore.addHistoricoPesquisa(termo); 1574 window.location.href = `/pesquisa/${encodeURIComponent(termo)}`; 1575 } 1576 1577 document.addEventListener('DOMContentLoaded', () => { 1578 inicializarHeaderSearch(); 1579 }); 1580 1581 function renderDropdownProdutos2Cols(listaProdutos = []) { 1582 const itens = listaProdutos.map(templateCardProdutoDropdown).join(''); 1583 return ` 1584 <div class="grid grid-cols-2 gap-2 p-2"> 1585 ${itens} 1586 </div> 1587 `; 1588 } 1589 1590 function templateCardProdutoDropdown(produto){ 1591 const produto_id = produto?.produto_id || ''; 1592 const imagem = produto?.imagem_produto || ''; 1593 const nome = produto?.nm_produto || ''; 1594 const url = `/${produto?.url_produto || ''}`; 1595 const perc = '' 1596 //const perc = temDesc ? Math.round(((valorIni - valor) / valorIni) * 100) : 0; 1597 1598 return ` 1599 <a class="block group" href="${url}" aria-label="${escapeHtml(nome)}" data-target="pesquisa-header" data-invisible="true" data-cd-produto="${produto_id}" data-notificar-disponibilidade="true"> 1600 <div class="relative border border-gray-200 rounded-[16px] p-3 w-full h-full flex gap-x-3 hover:shadow-sm transition"> 1601 1602 ${perc ? ` 1603 <div class="absolute p-0.5 top-2 left-2 z-[3] flex items-center justify-center rounded-lg bg-[#9FFCAC] text-[#0C670F] font-semibold"> 1604 <div class="text-[8px]/[11px] font-bold flex flex-col items-center leading-none">
1605 <span>-${perc}%</span><span>OFF</span> 1606 </div> 1607 <img loading="lazy" src="/staticfiles/img/client2/icon-arrow-down-desc2.svg" alt="Selo de Oferta" 1608 class="w-[14px] h-[18px] ml-1"> 1609 </div>` : ''} 1610 1611 <!-- Imagem --> 1612 <div class="flex-none"> 1613 <img 1614 loading="lazy" 1615 class="w-[70px] h-[70px] object-contain" 1616 src="${imagem}" 1617 onerror="this.onerror=null;this.src='https://placehold.co/190x190';" 1618 alt="${escapeHtml(nome)}"> 1619 </div> 1620 1621 <!-- Conteúdo --> 1622 <div class="flex flex-col flex-1 min-w-0 relative"> 1623 <span class="text-[12px] text-[#444] font-semibold line-clamp-2"> 1624 ${escapeHtml(nome)} 1625 </span> 1626 1627 <div class="text-[15px] text-[#444] font-black min-w-0 mt-1" data-target="preco-produto"> 1628 <div class="placeholder" style="height: 30px;"></div> 1629 </div> 1630 1631 <div class="absolute bottom-0 right-0 z-[3]" data-target="comprar-produto"> 1632 <div class="placeholder h-[24px]"></div> 1633 </div> 1634 </div> 1635 </div> 1636 </a> 1637 `; 1638 } 1639 </script>
1639 1640 1641 1642 1643 </div> 1644 1645 1646 1647 1648 1649 <div id="app_menu"> 1650 1651 </div> 1652 1653 1654 1655 <div id="checkout-desfoco" class="fixed top-0 right-0 bottom-0 left-0 bg-[#000] opacity-[.4] z-[12] closed" data-seletor="" data-popup-checkout="" onclick="if (!window.__cfRLUnblockHandlers) return false; toggleCarrinhoDesfoco(this)" data-cf-modified-ff2c2aaab21707ab2ba08cc2-=""></div> 1656 1657 <div id="pagina-header" class="flex flex-1 pb-8 md:pb-8 mt-35"> 1658 <main class="container mx-auto flex-1" > 1659 1660 1661<div class="flex flex-col gap-y-7.5 md:gap-y-10 px-0 lg:px-4"> 1662 1663 1664 <h1 class="sr-only">Encontre uma Loja das Farmácias Brava</h1> 1665 1666 1667 1668 <link 1669rel="stylesheet" 1670href="https://unpkg.com/[email protected]/dist/leaflet.css" 1671integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" 1672crossorigin="" 1673/>
1674<script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin="" defer type="ff2c2aaab21707ab2ba08cc2-text/javascript"></script>
1674 1675 1676<div aria-labelledby="nossas-lojas" class="container mx-auto px-2 md:px-4 py-3 md:py-6"> 1677 <!-- Hero/banner --> 1678 <section class="w-full"> 1679 <div class="container mx-auto grid grid-cols-12 gap-5 items-center"> 1680 <div class="col-span-12 py-5"> 1681 <div class="w-full rounded-lg"> 1682 <img src="/staticfiles/img/client2/institucional/banner_nossas_lojas.png" alt="Banner" class="w-full h-full object-cover"> 1683 </div> 1684 </div> 1685 </div> 1686 </section> 1687 1688 <!-- Seção busca + mapa --> 1689 <section class="w-full bg-[var(--c-brand-secondary)] py-10 rounded-xl"> 1690 <div class="container mx-auto grid grid-cols-12 gap-8 px-2 md:px-10"> 1691 <!-- Coluna filtros --> 1692 <div class="col-span-12 md:col-span-6"> 1693 <div class="max-w-xl"> 1694 <h2 class="text-[#87f0fe] text-3xl font-bold">Encontre a<br> Brava mais próxima</h2> 1695 <p class="text-white text-xl mt-4"> 1696 Há sempre uma Brava perto de você! Informe seu estado para 1697 encontrar a unidade mais próxima. 1698 </p> 1699 1700 1701 1702 <!-- Filtro 24h --> 1703 <div class="mt-2 flex items-center gap-2"> 1704 <input id="chk-24h" type="checkbox" class="form-checkbox cursor-pointer peer relative h-5 w-5 mr-2 shrink-0 appearance-none rounded-sm 1705 bg-white after:absolute after:left-0 after:top-0 after:h-full after:w-full after:bg-[url('/staticfiles/img/icon_check.svg')] 1706 after:bg-[length:40px] after:bg-center after:bg-no-repeat after:content-[''] checked:bg-[var(--c-brand-primary)] 1707 checked:border-[var(--c-brand-primary)] hover:ring hover:ring-gray-200 focus:outline-none"> 1708 <label for="chk-24h" class="text-white cursor-pointer">Somente unidades 24h</label> 1709 </div> 1710 1711 1712 <div class="mt-6 flex flex-col gap-3"> 1713 <button id="btn-geolocate" type="button" class="w-full inline-flex justify-center cursor-pointer items-center gap-2 rounded-lg bg-[#eae0ff] px-4 py-3 font-semibold shadow"> 1714 <img src="/staticfiles/img/client2/institucional/gps.webp" alt="Localização" class="w-5 h-5"> 1715 Usar minha localização 1716 </button> 1717 1718 <div class="relative"> 1719 <select id="select-estado" aria-label="Estado" 1720 class="appearance-none justify-between w-full rounded-lg px-4 py-3 pr-10 border-0 shadow bg-white text-gray-400 font-semibold 1721 focus:outline-none focus:ring-2 focus:ring-[var(--c-brand-secondary)]"> 1722 <option selected>Estado</option> 1723 </select> 1724 <!-- seta --> 1725 <img src="/staticfiles/img/client2/icon-arrow-down-pink.svg" alt="Seta" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4"> 1726 </div> 1727 <div class="relative"> 1728 <select id="select-filiais" aria-label="Filiais" 1729 class="appearance-none justify-between w-full rounded-lg px-4 py-3 pr-10 border-0 shadow bg-white text-gray-400 font-semibold 1730 focus:outline-none focus:ring-2 focus:ring-[var(--c-brand-secondary)]"> 1731 <option selected disabled>Filiais</option> 1732 </select> 1733 <!-- seta --> 1734 <img src="/staticfiles/img/client2/icon-arrow-down-pink.svg" alt="Seta" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4"> 1735 </div> 1736 1737 </div> 1738 </div> 1739 </div> 1740 1741 <!-- Coluna mapa --> 1742 <div class="col-span-12 md:col-span-6"> 1743 <div class="rounded-xl overflow-hidden bg-white shadow-xl"> 1744 <div id="lojas-map" class="w-full h-[380px] lg:h-[360px] z-0"></div> 1745 </div> 1746 </div> 1747 1748 <!-- Card loja selecionada --> 1749 <div class="col-span-12"> 1750 <div class="bg-white/95 rounded-xl shadow-xl p-5"> 1751 <p id="loja-endereco" class="font-semibold text-center uppercase text-lg text-gray-800" aria-l
1751ive="polite"> 1752 Selecione uma unidade no mapa para ver o endereço. 1753 </p> 1754 <div class="mt-5 grid grid-cols-12 gap-3"> 1755 1756 <a id="btn-whats" 1757 class="col-span-12 md:col-span-4 button bg-[#eae0ff] text-[#000] opacity-60 pointer-events-none" 1758 target="_blank" rel="noopener" aria-disabled="true"> 1759 <div class="flex items-center justify-center gap-3 py-1.5"> 1760 <img src="/staticfiles/img/client2/institucional/whats.svg" alt="Whatsapp" class="w-5 h-5"> 1761 Whatsapp 1762 </div> 1763 </a> 1764 1765 <a id="btn-conheca" 1766 class="col-span-12 md:col-span-4 button bg-[#eae0ff] text-[#000] opacity-60 pointer-events-none" 1767 target="_blank" rel="noopener" aria-disabled="true"> 1768 <div class="flex items-center justify-center gap-3 py-1.5"> 1769 <img src="/staticfiles/img/client2/institucional/loja.webp" alt="Conheça a loja" class="w-5 h-5"> 1770 Conheça a loja 1771 </div> 1772 </a> 1773 1774 <a id="btn-rota" 1775 class="col-span-12 md:col-span-4 button bg-[#eae0ff] text-[#000] opacity-60 pointer-events-none" 1776 target="_blank" rel="noopener" aria-disabled="true"> 1777 <div class="flex items-center justify-center gap-3 py-1.5"> 1778 <img src="/staticfiles/img/client2/institucional/mapa.webp" alt="Traçar rota" class="w-5 h-5"> 1779 Traçar rota 1780 </div> 1781 </a> 1782 </div> 1783 </div> 1784 </div> 1785 </div> 1786 </section> 1787 </div> 1788 1789
1789<script defer type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 1790 document.addEventListener('DOMContentLoaded', () => { 1791 // ----------------- MAPA ----------------- 1792 const defaultCoords = [-26.229, -52.671]; 1793 1794 const map = L.map('lojas-map', { scrollWheelZoom: false }).setView(defaultCoords, 12); 1795 1796 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 1797 maxZoom: 19, 1798 attribution: '© OpenStreetMap' 1799 }).addTo(map); 1800 1801 async function getLojas() { 1802 load('on', document.body); 1803 try { 1804 const resp = await fetch("/dados/filiais", { 1805 method: "POST", 1806 headers: { "Content-Type": "application/json", "X-CSRFToken": window.CSRF_TOKEN() }, 1807 body: JSON.stringify({}) 1808 }); 1809 const data = await resp.json(); 1810 1811 if (resp.ok && data.status) { 1812 const lista = Array.isArray(data.lista_filiais) ? data.lista_filiais : []; 1813 lojas = lista.map((f) => { 1814 const lat = parseFloat(f.latitude); 1815 const lng = parseFloat(f.longitude); 1816 const hasCoords = Number.isFinite(lat) && Number.isFinite(lng); 1817 const filiais = f?.nm_completo || ''; 1818 const enderecoFmt = [ 1819 [f?.endereco, f?.numero].filter(Boolean).join(', '), 1820 f?.bairro, 1821 [filiais, f?.nm_abrev].filter(Boolean).join(' - '), 1822 f?.cep_id ? `CEP ${f.cep_id}` : '' 1823 ].filter(Boolean).join(' | '); 1824 const telefoneDigits = (f?.telefone ? String(f.telefone) : '').replace(/\D/g, ''); 1825 const whatsapp = telefoneDigits ? `https://wa.me/55${telefoneDigits}` : null; 1826 const nomeLoja = (f?.nm_completo || f?.nm_descritivo || `Filial ${f?.codigo || ''}`).trim(); 1827 const coords = hasCoords ? [lat, lng] : null; 1828 return { 1829 id: String(f?.id ?? f?.codigo ?? cryptoRandomId()), 1830 uf: f?.nm_abrev || '', 1831 filiais, 1832 nome: nomeLoja, 1833 coords, 1834 endereco: enderecoFmt, 1835 whatsapp, 1836 url: buildGooglePlaceUrl(nomeLoja, enderecoFmt, coords), 1837 aberto24h: !!f?.is_24hr 1838 }; 1839 }); 1840 1841 // Reconstroi Ãndice por id 1842 lojaById.clear(); 1843 lojas.forEach(l => lojaById.set(l.id, l)); 1844 1845 // Popular selects dinâmicos 1846 populateEstadoSelect(); 1847 1848 // Renderiza mapa com as lojas 1849 renderMarkers(); 1850 1851 } else { 1852 toastr?.error(data.descricao || 'Não foi possÃvel encontrar as lojas.'); 1853 } 1854 } finally { 1855 load('off', document.body); 1856 } 1857 } 1858 1859 getLojas(); 1860 1861 1862 let lojas = []; 1863 const lojaById = new Map(); 1864 1865 // ----------------- ELEMENTOS UI ----------------- 1866 const enderecoEl = document.getElementById('loja-endereco'); 1867 const btnRota = document.getElementById('btn-rota'); 1868 const btnWhats = document.getElementById('btn-whats'); 1869 const btnConheca = document.getElementById('btn-conheca'); 1870 1871 const btnGeo = document.getElementById('btn-geolocate'); 1872 const inputEnd = document.getElementById('endereco-input'); 1873 const btnBuscarEnd = document.getElementById('btn-buscar-endereco'); 1874 const statusEnd = document.getElementById('endereco-status'); 1875 const chk24h = document.getElementById('chk-24h'); 1876 1877 const selectEstado = document.getElementById('select-estado'); 1878 const selectfiliais = document.getElementById('select-filiais'); 1879 const selectFranquia = document.getElementById('select-franquia') || null; 1880 1881 // ----------------- HELPERS ----------------- 1882 function norm(s){ 1883 return String(s || '') 1884 .toLowerCase() 1885 .normalize('NFD') 1886 .replace(/[\u0300-\u036f]/g, ''); 1887 } 1888 function cryptoRandomId(){ 1889 return Math.random().toString(36).slice(2) + Date.now().toString(36); 1890 } 1891 1892 function setLinkDisabled(a, disabled) { 1893 if (!a) return; 1894 if (disabled) { 1895 a.setAttribute('aria-disabled', 'true'); 1896 a.classList.add('opacity-60', 'pointer-events-none'); 1897 a.removeAttribute('href'); 1898 } else { 1899 a.setAttribute('aria-disabled', 'false'); 1900 a.classList.remove('opacity-60', 'pointer-events-none'); 1901 } 1902 } 1903 1904 function buildMapsDirUrl(coords){ 1905 const [lat, lng] = coords; 1906 const destination = encodeURIComponent(`${lat},${lng}`); 1907 return `https://www.google.com/maps/dir/?api=1&destination=${destination}`; 1908 } 1909 1910 function buildGooglePlaceUrl(nome, endereco, coords){ 1911 if (Array.isArray(coords) && coords.length === 2 && coords.every(Number.isFinite)) { 1912 return `https://www.google.com/maps?q=${coords[0]},${coords[1]}`; 1913 }
1914 const query = [nome, endereco].filter(Boolean).join(' ').trim(); 1915 if (!query) return ''; 1916 return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`; 1917 } 1918 1919 function selectLoja(loja){ 1920 if (!loja) return; 1921 1922 enderecoEl && (enderecoEl.textContent = loja.endereco); 1923 1924 setLinkDisabled(btnWhats, !loja.whatsapp); 1925 if (btnWhats && loja.whatsapp) btnWhats.href = loja.whatsapp; 1926 1927 setLinkDisabled(btnConheca, !loja.url || loja.url === '#'); 1928 if (btnConheca && loja.url) btnConheca.href = loja.url; 1929 1930 const hasCoords = Array.isArray(loja.coords) && loja.coords.length === 2 && loja.coords.every(Number.isFinite); 1931 setLinkDisabled(btnRota, !hasCoords); 1932 if (btnRota && hasCoords) btnRota.href = buildMapsDirUrl(loja.coords); 1933 } 1934 1935 // Estado inicial 1936 setLinkDisabled(btnWhats, true); 1937 setLinkDisabled(btnConheca, true); 1938 setLinkDisabled(btnRota, true); 1939 1940 const FranquiasAPI = { 1941 list({ uf, filiais, only24h = false }) { 1942 return new Promise((resolve) => { 1943 setTimeout(() => { 1944 const data = lojas 1945 .filter(l => 1946 (!uf || l.uf === uf) && 1947 (!filiais || l.filiais === filiais) && 1948 (!only24h || l.aberto24h) 1949 ) 1950 .map(l => ({ 1951 id: l.id, 1952 nome: l.nome, 1953 filiais: l.filiais, 1954 uf: l.uf, 1955 aberto24h: l.aberto24h 1956 })); 1957 resolve(data); 1958 }, 300); // simula latência 1959 }); 1960 } 1961 }; 1962 1963 function resetFranquiaSelect(texto = 'Franquia') { 1964 if (!selectFranquia) return; 1965 selectFranquia.innerHTML = `<option selected disabled>${texto}</option>`; 1966 selectFranquia.disabled = true; 1967 } 1968 1969 async function loadFranquias() { 1970 if (!selectEstado || !selectfiliais || !selectFranquia) return; 1971 1972 const uf = selectEstado.value; 1973 const filiais = selectfiliais.value; 1974 1975 resetFranquiaSelect('Carregandoâ¦'); 1976 1977 const lista = await FranquiasAPI.list({ 1978 uf, 1979 filiais, 1980 only24h: !!(chk24h && chk24h.checked) 1981 }); 1982 1983 resetFranquiaSelect('Franquia'); 1984 if (!lista.length) { 1985 const opt = new Option('Sem franquias nessa região', ''); 1986 opt.disabled = true; 1987 selectFranquia.add(opt); 1988 return; 1989 } 1990 1991 lista.forEach(f => selectFranquia.add(new Option(f.nome, f.id))); 1992 selectFranquia.disabled = false; 1993 } 1994 1995 // ----------------- MARCADORES ----------------- 1996 const markerLayer = L.layerGroup().addTo(map); 1997 let markersMap = new Map(); // loja -> marker 1998 1999 function getFiltrosSelecionados() { 2000 const uf = selectEstado && selectEstado.value !== 'Estado' ? selectEstado.value : ''; 2001 const filiaisVal = selectfiliais && !selectfiliais.disabled && selectfiliais.value !== 'Filiais' 2002 ? selectfiliais.value 2003 : ''; 2004 const somente24h = !!(chk24h && chk24h.checked); 2005 return { uf, filiais: filiaisVal, somente24h }; 2006 } 2007 2008 function getLojasFiltradas() { 2009 const { uf, filiais, somente24h } = getFiltrosSelecionados(); 2010 return lojas.filter((l) => { 2011 if (somente24h && !l.aberto24h) return false; 2012 if (uf && l.uf !== uf) return false; 2013 if (filiais && norm(l.filiais) !== norm(filiais)) return false; 2014 return Array.isArray(l.coords) && l.coords.length === 2; 2015 }); 2016 } 2017 2018 function trackStoreSearch(trigger) { 2019 try { 2020 if (typeof filtroParaDadosAnaliticos !== 'function') return; 2021 2022 const { uf, filiais, somente24h } = getFiltrosSelecionados(); 2023 const searchPieces = [uf, filiais].filter(Boolean); 2024 const searchTerm = searchPieces.join(' | ') || (trigger === 'geolocation' ? 'geolocalizacao' : 'todos'); 2025 2026 filtroParaDadosAnaliticos('Search', { 2027 search_term: searchTerm,
2028 search_type: 'store', 2029 results_count: getLojasFiltradas().length, 2030 state: uf || undefined, 2031 is_24h: somente24h, 2032 trigger 2033 }); 2034 } catch (_) {} 2035 } 2036 2037 function renderMarkers() { 2038 markerLayer.clearLayers(); 2039 markersMap = new Map(); 2040 2041 const filtradas = getLojasFiltradas(); 2042 2043 const created = []; 2044 2045 filtradas.forEach((loja) => { 2046 const marker = L.marker(loja.coords).addTo(markerLayer); 2047 const tag24h = loja.aberto24h ? ' <span style="color:#16a34a;font-weight:600;">(24h)</span>' : ''; 2048 marker.bindPopup(`<strong>${loja.nome}</strong>${tag24h}`); 2049 marker.on('click', () => selectLoja(loja)); 2050 markersMap.set(loja, marker); 2051 created.push(marker); 2052 }); 2053 2054 if (created.length) { 2055 const group = L.featureGroup(created); 2056 map.fitBounds(group.getBounds().pad(0.2)); 2057 } else { 2058 map.setView(defaultCoords, 12); 2059 if (enderecoEl) enderecoEl.textContent = 'Nenhuma unidade encontrada com o filtro aplicado.'; 2060 setLinkDisabled(btnRota, true); 2061 setLinkDisabled(btnWhats, true); 2062 setLinkDisabled(btnConheca, true); 2063 } 2064 } 2065 2066 // renderMarkers() será chamado após o carregamento da API 2067 2068 // ----------------- GEOLOCALIZAÃÃO ----------------- 2069 btnGeo && btnGeo.addEventListener('click', async () => { 2070 trackStoreSearch('geolocation'); 2071 if (!('geolocation' in navigator)) { 2072 toastr.error('Seu navegador não suporta geolocalização.', 'Erro'); 2073 return; 2074 } 2075 if (location.protocol !== 'https:' && location.hostname !== 'localhost') { 2076 toastr.error('Ative HTTPS para usar geolocalização.', 'Erro'); 2077 return; 2078 } 2079 2080 navigator.geolocation.getCurrentPosition( 2081 ({ coords }) => { 2082 const { latitude, longitude, accuracy } = coords;
2083 map.setView([latitude, longitude], 13); 2084 const userPane = map.createPane('userPane'); 2085 userPane.style.zIndex = 650; // acima de markers (600) 2086 L.circle([latitude, longitude], { 2087 pane: 'userPane', 2088 radius: Math.max(150, accuracy || 200), 2089 color: '#2563eb', 2090 fillColor: '#2563eb', 2091 fillOpacity: 0.2 2092 }).addTo(map); 2093 }, 2094 (err) => { 2095 if (err.code === 1) toastr.error('Permissão negada. Libere a localização e recarregue.', 'Erro'); 2096 else if (err.code === 2) toastr.error('Posição indisponÃvel. Tente novamente.', 'Erro'); 2097 else if (err.code === 3) toastr.error('Tempo esgotado. Tente de novo.', 'Erro'); 2098 }, 2099 { enableHighAccuracy: true, timeout: 10000, maximumAge: 30000 } 2100 ); 2101 }); 2102 2103 // ----------------- GEOCODING / BUSCA ----------------- 2104 let userSearchMarker = null; 2105 2106 async function geocodeAddress(q) { 2107 if (statusEnd) statusEnd.textContent = 'Buscando endereçoâ¦'; 2108 try { 2109 const url = `https://nominatim.openstreetmap.org/search?format=json&limit=1&countrycodes=br&q=${encodeURIComponent(q)}`; 2110 const res = await fetch(url); 2111 if (!res.ok) throw new Error('Falha no geocoding'); 2112 const data = await res.json(); 2113 if (!data.length) { 2114 if (statusEnd) statusEnd.textContent = 'Endereço não encontrado.'; 2115 return; 2116 } 2117 const { lat, lon, display_name } = data[0]; 2118 if (statusEnd) statusEnd.textContent = display_name; 2119 2120 const latNum = parseFloat(lat), lonNum = parseFloat(lon);
2121 map.setView([latNum, lonNum], 14); 2122 2123 if (userSearchMarker) map.removeLayer(userSearchMarker); 2124 userSearchMarker = L.marker([latNum, lonNum]).addTo(map).bindPopup('Endereço buscado').openPopup(); 2125 } catch (e) { 2126 if (statusEnd) statusEnd.textContent = 'Erro ao buscar endereço.'; 2127 console.error(e); 2128 } 2129 } 2130 2131 async function buscarEnderecoOuLoja(){ 2132 const q = (inputEnd && inputEnd.value || '').trim(); 2133 if(!q) return; 2134 2135 // 1) Tenta por nome/endereço nas lojas (respeitando o 24h) 2136 const qn = norm(q); 2137 const somente24h = !!(chk24h && chk24h.checked); 2138 2139 const matches = lojas.filter(l => 2140 (!somente24h || l.aberto24h) && 2141 (norm(l.nome).includes(qn) || norm(l.endereco).includes(qn)) 2142 ); 2143 2144 if (matches.length > 0) { 2145 if (statusEnd) statusEnd.textContent = matches.length === 1 2146 ? `Loja encontrada: ${matches[0].nome}` 2147 : `${matches.length} lojas encontradas`; 2148 2149 // atualiza markers conforme filtro atual 2150 renderMarkers(); 2151 2152 // pega os markers das lojas encontradas 2153 const ms = matches.map(l => markersMap.get(l)).filter(Boolean); 2154 2155 if (ms.length === 1) { 2156 const m = ms[0]; 2157 const { lat, lng } = m.getLatLng();
2158 map.setView([lat, lng], 15); 2159 m.openPopup(); 2160 selectLoja(matches[0]); 2161 } else if (ms.length > 1) { 2162 const group = L.featureGroup(ms); 2163 map.fitBounds(group.getBounds().pad(0.2)); 2164 ms[0].openPopup(); 2165 selectLoja(matches[0]); 2166 } 2167 return; // já resolveu localmente 2168 } 2169 2170 // 2) Fallback: geocoding (endereço) 2171 await geocodeAddress(q); 2172 } 2173 2174 btnBuscarEnd && btnBuscarEnd.addEventListener('click', buscarEnderecoOuLoja); 2175 inputEnd && inputEnd.addEventListener('keydown', (e) => { 2176 if (e.key === 'Enter') { e.preventDefault(); buscarEnderecoOuLoja(); } 2177 }); 2178 2179 // ----------------- ESTADOS / filiaisS (dinâmico da API) ----------------- 2180 function buildIndexPorUffiliais() { 2181 const mapUf = new Map(); 2182 lojas.forEach(l => { 2183 if (!l.uf) return; 2184 if (!mapUf.has(l.uf)) mapUf.set(l.uf, new Set()); 2185 if (l.filiais) mapUf.get(l.uf).add(l.filiais); 2186 }); 2187 return mapUf; 2188 } 2189 2190 function populateEstadoSelect() { 2191 if (!selectEstado) return; 2192 // limpa mantendo placeholder 2193 selectEstado.innerHTML = '<option selected>Estado</option>'; 2194 const mapUf = buildIndexPorUffiliais(); 2195 const ufs = Array.from(mapUf.keys()).sort((a,b) => a.localeCompare(b)); 2196 ufs.forEach(uf => selectEstado.add(new Option(uf, uf))); 2197 selectEstado.disabled = ufs.length === 0; 2198 // ao popular estados, reseta filiais/franquia 2199 if (selectfiliais) { 2200 selectfiliais.innerHTML = '<option selected disabled>Filiais</option>'; 2201 selectfiliais.disabled = true; 2202 } 2203 resetFranquiaSelect(); 2204 } 2205 2206 function populatefiliaisSelect(uf) { 2207 if (!selectfiliais) return; 2208 selectfiliais.innerHTML = '<option selected disabled>Filiais</option>'; 2209 const mapUf = buildIndexPorUffiliais(); 2210 const filiaissSet = mapUf.get(uf); 2211 if (!filiaissSet) { selectfiliais.disabled = true; resetFranquiaSelect(); return; } 2212 const filiaiss = Array.from(filiaissSet).sort((a,b) => a.localeCompare(b)); 2213 filiaiss.forEach(c => selectfiliais.add(new Option(c, c))); 2214 selectfiliais.disabled = filiaiss.length === 0; 2215 resetFranquiaSelect(); 2216 } 2217 2218 if (selectEstado) { 2219 selectEstado.addEventListener('change', () => { 2220 populatefiliaisSelect(selectEstado.value); 2221 renderMarkers(); 2222 trackStoreSearch('state_select'); 2223 }); 2224 selectfiliais && selectfiliais.addEventListener('change', () => { 2225 loadFranquias(); 2226 renderMarkers(); 2227 trackStoreSearch('secondary_select'); 2228 2229 const { uf, filiais, somente24h } = getFiltrosSelecionados(); 2230 const lojaSelecionada = lojas.find((l) => { 2231 if (somente24h && !l.aberto24h) return false; 2232 if (uf && l.uf !== uf) return false; 2233 if (filiais && norm(l.filiais) !== norm(filiais)) return false; 2234 return Array.isArray(l.coords) && l.coords.length === 2 && l.coords.every(Number.isFinite); 2235 }); 2236 2237 if (!lojaSelecionada) return; 2238 2239 const marker = markersMap.get(lojaSelecionada); 2240 if (!marker) return; 2241 2242 const { lat, lng } = marker.getLatLng();
2243 map.setView([lat, lng], 15); 2244 marker.openPopup(); 2245 selectLoja(lojaSelecionada); 2246 }); 2247 } 2248 2249 // ----------------- FILTRO 24h ----------------- 2250 chk24h && chk24h.addEventListener('change', () => { 2251 renderMarkers(); 2252 // se já tiver UF+filiais selecionados, recarrega a lista 2253 if (selectEstado?.value && selectfiliais?.value) loadFranquias(); 2254 }); 2255 2256 // ----------------- SELEÃÃO DE FRANQUIA ----------------- 2257 selectFranquia && selectFranquia.addEventListener('change', () => { 2258 const id = selectFranquia.value; 2259 const loja = lojaById.get(id); 2260 if (!loja) return; 2261 2262 // garante markers atualizados conforme filtro atual 2263 renderMarkers(); 2264 2265 // tenta achar o marker dessa loja 2266 let marker = markersMap.get(loja); 2267 const hasCoords = Array.isArray(loja.coords) && loja.coords.length === 2 && loja.coords.every(Number.isFinite); 2268 if (!hasCoords){ 2269 // sem coordenadas: não cria marker, apenas atualiza o card e desabilita rota 2270 setLinkDisabled(btnRota, true); 2271 selectLoja(loja); 2272 return; 2273 } 2274 if (!marker) { 2275 // se o filtro 24h ocultou, adiciona um marker avulso para focalizar 2276 marker = L.marker(loja.coords).addTo(markerLayer).bindPopup(`<strong>${loja.nome}</strong>`); 2277 } 2278 2279 const { lat, lng } = marker.getLatLng();
2280 map.setView([lat, lng], 15); 2281 marker.openPopup(); 2282 selectLoja(loja); 2283 }); 2284 2285 }); 2286 </script>
2286 2287 2288 2289</div> 2290 2291 </main> 2292 2293 2294 <aside id="checkout-asidebar" class="fixed top-0 right-0 w-screen lg:w-[35vw] h-screen duration-[0.3s] z-11 md:z-[12] hidden"> 2295 <div id="checkout-template"></div> 2296 </aside> 2297 2298 2299 </div> 2300 2301 2302 2303 <div id="app_footer"> 2304 2305 2306 <footer class="mt-4 flex flex-col "> 2307 <div class="w-full bg-[var(--c-footer-topbar-bg)] text-[var(--c-footer-mid-text)]"> 2308 <div class="container mx-auto p-4"> 2309 <!-- Newsletter --> 2310 <div class="grid grid-cols-3 py-6 flex items-center w-full justify-between"> 2311 <div class="col-span-1 flex justify-between"> 2312 <p class="text-xl font-medium flex items-center justify-center gap-2 text-nowrap"> 2313 <img src="/staticfiles/img/client2/icon_ofertas.webp" alt="Enviar" class="w-8 h-8" /> 2314 Receba as melhores ofertas! 2315 </p> 2316 </div> 2317 <form id="form-newsletter" action="" novalidate class="col-span-2 flex items-center w-full border-[1px] border-white rounded-full pl-[30px]"> 2318 2319 <input type="email" name="email" id="email-newsletter" placeholder="Digite o seu e-mail" autocomplete="off" maxlength="200" 2320 class="text-xs text-white bg-transparent flex-1 min-w-0 outline-none placeholder-white" /> 2321 <button type="submit" class="bg-white text-[#0044ff] rounded-full px-[6px] py-[6px] m-[8px] cursor-pointer transition duration-200 ease-in-out hover:scale-110"> 2322 <img src="/staticfiles/img/client2/arrow-right.svg" alt="Enviar" class="w-5 h-5" /> 2323 </button> 2324 </form> 2325 </div> 2326 </div> 2327 </div> 2328 2329 2330 <div class="w-full bg-[var(--c-footer-bottombar-bg)] text-[var(--c-footer-mid-text)]"> 2331 <div class="container mx-auto md:flex-row justify-between items-center p-4"> 2332 <!-- Contato e Links --> 2333 <div class="flex py-5 grid-cols-2 gap-8 justify-between text-left"> 2334 <!-- Contato --> 2335 <div class="space-y-6"> 2336 <p class="text-lg font-semibold">Não encontrou o que precisava? Fale com a gente!</p> 2337 <div class="flex flex-col md:flex-row gap-4 justify-center md:justify-start"> 2338 <a href="/fale-conosco" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 2339 <img src="/staticfiles/img/client2/icon_operador.webp" alt="Enviar" class="w-5 h-5"/> 2340 Fale Conosco 2341 </a> 2342 <a href="/nossas-lojas" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 2343 <img src="/staticfiles/img/client2/icon_loja.png" alt="Enviar" class="w-5 h-5"/> 2344 Nossas lojas 2345 </a> 2346 <a href="/aplicativos" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 2347 <img src="https://storage.googleapis.com/retailon-publico/brava/admin/icon-phone_17055.svg" alt="Enviar" class="w-auto h-6 img-filter-fff"/> 2348 Intranet 2349 </a> 2350 </div> 2351 <div class="text-sm mt-4 space-y-2"> 2352 <div class="flex items-center gap-5"> 2353 <div class="flex items-center gap-1 hover:underline"> 2354 <a href="/pagina/quem-somos" class="">Nossa história</a> 2355 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 2356 </div> 2357 <div class="flex items-center gap-1 hover:underline"> 2358 <a href="/trabalhe-conosco" class="">Trabalhe conosco</a> 2359 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 2360 </div> 2361 2362 <div class="flex items-center gap-1 hover:underline"> 2363 <a href="/pagina/notinha-amiga" class="">Brava Vantagens e Notinha Amiga</a> 2364 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 2365 </div> 2366 </div> 2367 <div class="flex items-center gap-5"> 2368 2369 2370 <div class="flex items-center gap-1 hover:underline"> 2371 <a href="/pagina/pedido-patrocinio" class="">Pedidos de patrocÃnios e projetos</a> 2372 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 2373 </div> 2374 </div> 2375 2376 </div> 2377 </div> 2378 2379 <!-- Franquia e QR Code --> 2380 <div class="flex justify-between"> 2381 <div class="gap-4"> 2382 <div class="flex items-center gap-8 p-2"> 2383 <div class="flex flex-col items-center gap-4"> 2384 <a href="/franquias" class="border border-white hover:bg-[#1b34d3] px-4 py-2 rounded-lg">Seja um franqueado</a> 2385 </div> 2386 2387 </div> 2388 <div class="flex gap-2 p-2"> 2389 <div class="bg-white rounded-md p-1"> 2390 <a href="https://www.facebook.com/farmaciasbrava1?locale=pt_BR" target="_blank" rel="noopener noreferrer"> 2391 <img src="/staticfiles/img/client2/icon_facebook.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="Facebook" /> 2392 </a> 2393 </div> 2394 <div class="bg-white rounded-md p-1"> 2395 <a href="https://www.instagram.com/farmaciasbrava/" target="_blank" rel="noopener noreferrer"> 2396 <img src="/staticfiles/img/client2/icon_instagram.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="Instagram" /> 2397 </a> 2398 </div> 2399 <div class="bg-white rounded-md p-1"> 2400 <a href="https://www.linkedin.com/company/farmaciasbrava/" target="_blank" rel="noopener noreferrer"> 2401 <img src="/staticfiles/img/client2/icon_linkedin.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="LinkedIn" /> 2402 </a> 2403 </div> 2404 <div class="bg-white rounded-md p-1"> 2405 <a href="https://www.youtube.com/@redefarmaciasbrava" target="_blank" rel="noopener noreferrer">
2406 <img src="/staticfiles/img/client2/icon_youtube.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="YouTube" /> 2407 </a> 2408 </div> 2409 </div> 2410 2411 </div> 2412 2413 </div> 2414 2415 </div> 2416 <!-- Encartes --> 2417 <div class="flex justify-center py-4"> 2418 <div class="flex w-full gap-5"> 2419 <a href="https://storage.googleapis.com/retailon-publico/brava/admin/flyer-setembro-pr-ajustado_5fc18.pdf" class="flex items-center justify-center border hover:bg-[#1b34d3] border-white px-4 py-3 w-full rounded-lg text-center" target="_blank"> 2420 <img src="/staticfiles/img/client2/icon_revista.png" alt="Encarte" class="w-5 h-5 mr-2" /> 2421 Encarte Paraná 2422 </a> 2423 <a href="https://storage.googleapis.com/retailon-publico/brava/admin/flyer-setembro-pr-ajustado.pdf" class="flex items-center justify-center border hover:bg-[#1b34d3] border-white px-4 py-3 w-full rounded-lg text-center" target="_blank"> 2424 <img src="/staticfiles/img/client2/icon_revista.png" alt="Encarte" class="w-5 h-5 mr-2" /> 2425 Encarte Santa Catarina 2426 </a> 2427 </div> 2428 </div> 2429 2430 <!-- PolÃticas --> 2431 <div class="text-xs text-center pt-4 pb-6 space-x-2"> 2432 <a href="/pagina/politica-guia-seguranca" class="hover:underline">Guia de segurança</a> 2433 <span>|</span> 2434 <a href="/pagina/politica-privacidade" class="hover:underline">PolÃtica de privacidade</a> 2435 <span>|</span> 2436 <a href="/pagina/politica-dados-pessoais" class="hover:underline">Solicitação sobre dados pessoais</a> 2437 <span>|</span> 2438 <a href="/pagina/politica-entrega" class="hover:underline">PolÃtica de entrega</a> 2439 <span>|</span> 2440 <a href="/pagina/politica-troca-devolucao" class="hover:underline">Troca e devolução</a> 2441 </div> 2442 2443 </div> 2444 </div> 2445 2446 <div class="w-full flex justify-center items-center py-1 bg-[#eeeeee] shadow-[0_-2px_5px_rgba(0,0,0,0.06)]"> 2447 <div class="flex flex-col items-center justify-around my-3"> 2448 <span class="text-[#333] text-sm">Desenvolvido por:</span> 2449 <a href="https://retailon.com.br/?utm_source=brava&utm_medium=rodape&utm_id=inteliger" target="_blank"> 2450 <img loading="lazy" src="https://storage.googleapis.com/retailon-publico/inteliger/logos/logo-pequena.webp" alt="Logo RetailON" 2451 class="mx-auto w-[130px]"> 2452 </a> 2453 </div> 2454 </div> 2455 </footer> 2456 2457
2457<script type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 2458 async function cadastrarEmailNewsletter() { 2459 const email = document.getElementById('email-newsletter').value; 2460 if (!email) { 2461 toastr.error('Por favor, preencha o e-mail!'); 2462 return; 2463 } 2464 try { 2465 const response = await fetch('cliente/promocao/criar', { 2466 method: 'POST', 2467 headers: { 2468 'Content-Type': 'application/json', 2469 'X-CSRFToken': window.CSRF_TOKEN(), 2470 }, 2471 body: JSON.stringify({ email }), 2472 }); 2473 2474 if (response.ok) { 2475 toastr.success('E-mail cadastrado com sucesso!'); 2476 } else { 2477 toastr.error('Erro ao cadastrar e-mail!'); 2478 } 2479 } catch (error) { 2480 toastr.error('Erro ao cadastrar e-mail!'); 2481 } 2482 } 2483 2484 document.getElementById('form-newsletter').addEventListener('submit', (e) => { 2485 e.preventDefault(); 2486 e.stopPropagation(); 2487 cadastrarEmailNewsletter(); 2488 }); 2489 </script>
2489 2490 2491 </div> 2492 2493 2494 2495
2495<script type="ff2c2aaab21707ab2ba08cc2-text/javascript" src="/staticfiles/js/defer.js" defer></script>
2495 2496
2496<script type="ff2c2aaab21707ab2ba08cc2-text/javascript" src="/staticfiles/js/carrosseis.js" defer></script>
2496 2497
2497<script type="ff2c2aaab21707ab2ba08cc2-text/javascript" src="/staticfiles/js/pbm.js" defer></script>
2497 2498 2499 2500 2501
2501<script defer type="ff2c2aaab21707ab2ba08cc2-text/javascript"> 2502 2503 2504document.addEventListener('DOMContentLoaded', async () => { 2505 buscarProdutosAvaliados() 2506 buscarProdutosFavoritos() 2507}) 2508 2509async function buscarProdutosAvaliados(produtos_id = []) { 2510 let produtoId = produtos_id?.length ? produtos_id : []; 2511 2512 if (!produtoId?.length) { 2513 const produtosComAvaliacao = document.querySelectorAll('[data-target="avaliacao-produto"]'); 2514 if (!produtosComAvaliacao?.length) return; 2515 produtosComAvaliacao.forEach(produto => { 2516 const cdProduto = produto?.closest('[data-cd-produto]')?.getAttribute('data-cd-produto'); 2517 if (cdProduto) produtoId.push(cdProduto); 2518 }); 2519 if (produtoId?.length) { 2520 produtoId = Array.from(new Set(produtoId)); 2521 } else { 2522 return; 2523 } 2524 } else { 2525 produtoId = Array.from(new Set(produtoId)); 2526 } 2527 2528 const request = await fetch(`/produto/buscar/avaliacoes`, { 2529 method: "POST", 2530 headers: { 2531 "X-CSRFToken": window.CSRF_TOKEN(), 2532 "Content-Type": "application/json", 2533 }, 2534 body: JSON.stringify({ 2535 produtos_id: produtoId 2536 }) 2537 }) 2538 2539 for (const cdProduto of produtoId) { 2540 const htmlDefault = ` 2541 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2542 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2543 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2544 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2545 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2546 <span class="text-[10px] font-semibold text-[#444] ml-1">(0)</span> 2547 ` 2548 const produtoElementos = document.querySelectorAll(`[data-cd-produto="${cdProduto}"]`) 2549 if (produtoElementos?.length) { 2550 produtoElementos.forEach(produtoElemento => { 2551 if(produtoElemento.querySelector('[data-target="avaliacao-produto"]')) 2552 produtoElemento.querySelector('[data-target="avaliacao-produto"]').innerHTML = htmlDefault 2553 }) 2554 } 2555 } 2556 2557 const response = await request.json() 2558 2559 if (!request.ok || !response?.status || !Object.keys(response?.avaliacoes_produtos)?.length) 2560 return 2561 2562 const avaliacoes = response?.avaliacoes_produtos 2563 2564 Object.keys(avaliacoes).forEach((produto_id) => { 2565 2566 const listaAvaliacoes = avaliacoes[produto_id] 2567 const produtoElementosComAvaliacoes = document.querySelectorAll(`[data-cd-produto="${produto_id}"]`) 2568 2569 if (produtoElementosComAvaliacoes?.length && listaAvaliacoes?.length) { 2570 2571 const mediaAvaliacoes = listaAvaliacoes?.reduce((acc, avaliacao) => acc + avaliacao?.nota_avaliacao, 0) / listaAvaliacoes?.length 2572 2573 let htmlContainerAvaliacoes = `` 2574 2575 if(mediaAvaliacoes >= 1) { 2576 htmlContainerAvaliacoes += ` 2577 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2578 ` 2579 } else { 2580 htmlContainerAvaliacoes += ` 2581 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2582 ` 2583 } 2584 if(mediaAvaliacoes >= 2) { 2585 htmlContainerAvaliacoes += ` 2586 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2587 ` 2588 } else { 2589 htmlContainerAvaliacoes += ` 2590 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2591 ` 2592 } 2593 if(mediaAvaliacoes >= 3) { 2594 htmlContainerAvaliacoes += ` 2595 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2596 ` 2597 } else { 2598 htmlContainerAvaliacoes += ` 2599 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2600 ` 2601 } 2602 if(mediaAvaliacoes >= 4) { 2603 htmlContainerAvaliacoes += ` 2604 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2605 ` 2606 } else { 2607 htmlContainerAvaliacoes += ` 2608 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2609 ` 2610 } 2611 if(mediaAvaliacoes >= 5) { 2612 htmlContainerAvaliacoes += ` 2613 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2614 ` 2615 } else { 2616 htmlContainerAvaliacoes += ` 2617 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2618 ` 2619 } 2620 2621 htmlContainerAvaliacoes += `
2622 <span class="text-[10px] font-semibold text-[#444] ml-1">(${listaAvaliacoes?.length})</span> 2623 ` 2624 2625 produtoElementosComAvaliacoes.forEach(produtoElementoComAvaliacoes => { 2626 if(produtoElementoComAvaliacoes.querySelector('[data-target="avaliacao-produto"]')) 2627 produtoElementoComAvaliacoes.querySelector('[data-target="avaliacao-produto"]').innerHTML = htmlContainerAvaliacoes 2628 }) 2629 } 2630 }) 2631 2632} 2633 2634async function buscarProdutosFavoritos() { 2635 2636 if(!document.querySelectorAll('[data-favoritar]')?.length) 2637 return 2638 2639 const request = await fetch(`/produto/buscar/favoritos`, { 2640 method: "GET", 2641 headers: { 2642 "X-CSRFToken": window.CSRF_TOKEN(), 2643 } 2644 }) 2645 2646 const response = await request.json() 2647 2648 if(!request.ok || !response?.status || !response?.produtos?.length) 2649 return 2650 2651 const favoritos = response?.produtos 2652 for(const favorito of favoritos){ 2653 const els = document.querySelectorAll(`[data-favoritar="${favorito?.id}"]`) 2654 els.forEach(el => { 2655 el.setAttribute('data-is-favoritado', 'true') 2656 el.innerHTML = ` 2657 <img src="/staticfiles/img/client2/icon_wishlist_fill.svg" class="cursor-pointer" alt="Remover dos favoritos" width="35" height="35"> 2658 ` 2659 }) 2660 } 2661} 2662 2663async function favoritarProduto(el, produtoId) { 2664 2665 if(!produtoId) 2666 return 2667 2668 const isFavoritado = el?.getAttribute('data-is-favoritado') === 'true' 2669 2670 const elPaginaProduto = el?.closest('[data-target="pagina-produto"]') 2671 const elLoader = elPaginaProduto 2672 ? document.body 2673 : (el?.closest('[data-target="carrossel-produto"]') || el) 2674 2675 load('on', [elLoader]) 2676 2677 const request = await fetch(`/produto/atualizar/favorito`, { 2678 method: "POST", 2679 headers: { 2680 "Content-Type": "application/json", 2681 "X-CSRFToken": window.CSRF_TOKEN(), 2682 }, 2683 body: JSON.stringify({ 2684 produto_id: produtoId 2685 }) 2686 }) 2687 2688 const response = await request.json() 2689 2690 if(!request.ok || !response?.status){ 2691 toastr.error(response?.descricao || 'Não foi possÃvel executar a ação. Tente novamente.') 2692 if(el){ 2693 el.setAttribute('data-is-favoritado', 'false') 2694 el.innerHTML = ` 2695 <img src="/staticfiles/img/client2/icon_wishlist.svg" 2696 class="cursor-pointer" 2697 width="35" height="35" 2698 alt="Adicionar aos favoritos" 2699 > 2700 `; 2701 } 2702 load('off', [elLoader]) 2703 return 2704 } 2705 2706 if(el){ 2707 el.setAttribute('data-is-favoritado', !isFavoritado) 2708 el.innerHTML = ` 2709 <img src="/staticfiles/img/client2/icon_${!isFavoritado ? 'wishlist_fill' : 'wishlist'}.svg" 2710 class="cursor-pointer" 2711 width="35" height="35" 2712 alt="${!isFavoritado ? 'Adicionar aos favoritos' : 'Remover dos favoritos'}" 2713 > 2714 `; 2715 } 2716 load('off', [elLoader]) 2717} 2718 2719function visualizarMenuCategorias() { 2720 const menuCategoriaContainer = document.getElementById("menu-categorias-mobile"); 2721 menuCategoriaContainer?.classList?.toggle("hidden"); 2722} 2723_logicaMenuCategoriasCarregada = true; 2724 2725/* 2726* Pesquisa mobile 2727*/ 2728// function sidebarPesquisa(toggle) { 2729// if (toggle === "open") { 2730// document.getElementById("pesquisa-sidebar")?.classList?.remove("hidden"); 2731// setTimeout(() => { 2732// document.getElementById("pesquisa-sidebar-input")?.focus(); 2733// }, 150); 2734// const debouncedPesquisa = debounce(buscarProdutosSidebarPesquisa, 750); 2735// const input = document.getElementById("pesquisa-sidebar-input"); 2736// input.addEventListener("input", debouncedPesquisa); 2737// return; 2738// } 2739// if (toggle === "close") { 2740// document.getElementById("pesquisa-sidebar")?.classList?.add("hidden"); 2741// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2742// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2743// ""; 2744// return; 2745// } 2746// if (!toggle) 2747// document.getElementById("pesquisa-sidebar")?.classList?.toggle("hidden"); 2748// } 2749 2750function sidebarPesquisa(state){ 2751 const aside = document.getElementById('pesquisa-sidebar'); 2752 if(!aside) return; 2753 2754 const input = document.getElementById('pesquisa-sidebar-input'); 2755 2756 if(state === 'open'){ 2757 document.body.classList.add('overflow-hidden'); 2758 aside.classList.remove('hidden'); 2759 inicializarSidebarPesquisa() 2760 input.focus(); 2761 } else { 2762 document.body.classList.remove('overflow-hidden'); 2763 aside.classList.add('hidden'); 2764 } 2765} 2766 2767_sidebarPesquisaCarregada = true; 2768 2769function clearSidebarPesquisa() { 2770 if (document.getElementById("pesquisa-sidebar-input")) 2771 document.getElementById("pesquisa-sidebar-input").value = ""; 2772 document.getElementById("pesquisa-sidebar-input")?.focus(); 2773} 2774 2775// function buscarProdutosSidebarPesquisa() { 2776// const termo = document.getElementById("pesquisa-sidebar-input")?.value; 2777// if (!termo) { 2778// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2779// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2780// ""; 2781// return; 2782// } 2783 2784// load("on", document.getElementById("pesquisa-sidebar")); 2785 2786// const produtos = [ 2787// { 2788// imagem_produto: "produtos/1001815_200x200.webp", 2789// cd_produto: "1001815", 2790// categoria: "ZERO AÃÃCAR", 2791// parceiro: "", 2792// avaliacao: null,
2793// tags_nomes: 2794// ";pinati swet mini cocadinha queimada sem acucar 14g;queimada;cocadinha;qooeimada;keimada;zem;sweet;açúxar;açúcar;doce pinati swet mini cocadinha queimada sem acucar;doce;doce pinati sweet mini cocadinha queimada sem acucar 14g;doce pinati swet mini cocadinha queimada acucar 14g;doce pinati swet mini cocadinha sem acucar 14g;zweet;doce pinati swet mini cocadinha queimada sem acucar 14g;doce swet mini cocadinha queimada sem acucar 14g;xoxadina;sem;doce pinati mini cocadinha queimada sem acucar 14g;pinati;xoxadinha;doxe;cocadina;doce pinati swet mini queimada sem acucar 14g;mini;14g;doce pinati swet mini cocadinha queimada sem 14g;doce pinati swet cocadinha queimada sem acucar 14g;", 2795// marca: "PINATI", 2796// nm_produto: "Doce Pinati Sweet Mini Cocadinha Queimada Sem Açúcar 14g", 2797// tags_agrupamentos: 2798// ";ate-10-reais;td-consumidor-50;desc-semana-consumidor;", 2799// is_servico: false, 2800// fabricante: "SUPER SAUDE NUTRICIONAL", 2801// filtros: { 2802// selo: "", 2803// marca: "PINATI", 2804// tipo: "", 2805// categoria: "ZERO AÃÃCAR", 2806// fabricante: "SUPER SAUDE NUTRICIONAL", 2807// avaliacao: null, 2808// palavras: {}, 2809// }, 2810// url_produto: "doce-pinati-sweet-mini-cocadinha-queimada-sem-acucar-14g", 2811// is_disponivel: true, 2812// }, 2813// { 2814// imagem_produto: "produtos/1002811_200x200.webp", 2815// cd_produto: "1002811", 2816// categoria: "REFRIGERANTE", 2817// parceiro: "", 2818// avaliacao: null, 2819// tags_nomes: 2820// ";refrigerante lata;coca-cola lata 310ml;lata;xoxa-xola;310ml;refrigerante lata 310ml;refrigerante coca-cola lata;refrijerante;refrigerante;coca-cola;refrigerante coca-cola lata 310ml;coca-cola lata;refrigerante 310ml;coca-cola 310ml;refrigerante coca-cola;refrigerante coca-cola 310ml;", 2821// marca: "COCA COLA", 2822// nm_produto: "Refrigerante Coca-Cola Lata 310ml", 2823// tags_agrupamentos: ";mosaico-refrigerante;mosaico-bebidas;", 2824// is_servico: false, 2825// fabricante: "COCA COLA", 2826// filtros: { 2827// selo: "", 2828// marca: "COCA COLA", 2829// tipo: "", 2830// categoria: "REFRIGERANTE", 2831// fabricante: "COCA COLA", 2832// avaliacao: null, 2833// palavras: {}, 2834// }, 2835// url_produto: "refrigerante-coca-cola-lata-310ml", 2836// is_disponivel: true, 2837// }, 2838// { 2839// imagem_produto: "produtos/69625_5tL4aHL.jpg", 2840// cd_produto: "69625", 2841// categoria: "REFRIGERANTE", 2842// parceiro: "", 2843// avaliacao: null, 2844// tags_nomes: 2845// ";coca-cola lata 350ml;refrigerante coca-cola lata 350ml;refrigerante lata;lata;xoxa-xola;refrigerante lata 350ml;refrigerante coca-cola 350ml;refrigerante coca-cola lata;refrijerante;refrigerante;coca-cola;refrigerante 350ml;coca-cola lata;350ml;refrigerante coca-cola;coca-cola 350ml;", 2846// marca: "COCA COLA", 2847// nm_produto: "Refrigerante Coca-Cola Lata 350ml", 2848// tags_agrupamentos: 2849// ";mercearia-leve-mais;dinheiro-de-volta-marco;lamina-coca-cola;coca-cola;conveniencia-almanaque;alm-jan25-mercearia;complete-sua-cesta;mosaico-halloween;mosaico-halloween;mosaico-halloween;mosaico-halloween;mosaico-halloween;mosaico-halloween;mosaico-refrigerante;mosaico-refrigerante;mosaico-refrigerante;mosaico-refrigerante;mosaico-refrigerante;mosaico-refrigerante;mosaico-refrigerante;mosaico-bebidas;mosaico-bebidas;mosaico-bebidas;mosaico-bebidas;mosaico-bebidas;mosaico-bebidas;mosaico-bebidas;ate-10-reais;", 2850// is_servico: false, 2851// fabricante: "COCA COLA", 2852// filtros: { 2853// selo: "produtos/selos/GA - SELO 5_modelo 1.png", 2854// marca: "COCA COLA", 2855// tipo: "", 2856// categoria: "REFRIGERANTE", 2857// fabricante: "COCA COLA", 2858// avaliacao: null, 2859// palavras: {}, 2860// }, 2861// url_produto: "refrigerante-coca-cola-lata-350ml", 2862// is_disponivel: true, 2863// }, 2864// ]; 2865 2866// setTimeout(() => { 2867// construirTemplateProdutosSidebarPesquisa(produtos); 2868// load("off", document.getElementById("pesquisa-sidebar")); 2869// // ViewItemList + SelectItem base para resultados simulados 2870// try { 2871// if (typeof filtroParaDadosAnaliticos === 'function') { 2872// const items = (produtos || []).map((p, i) => ({ 2873// id: String(p?.cd_produto || ''), 2874// item_name: p?.nm_produto || '', 2875// price: +p?.valor || 0, 2876// quantidade: 1, 2877// index: i + 1, 2878// item_list_name: 'Sidebar Pesquisa', 2879// item_list_id: 'sidebar_pesquisa' 2880// })).filter(x => x.id);
2881// if (items.length) filtroParaDadosAnaliticos('ViewItemList', { items }); 2882// } 2883// } catch(_){} 2884// }, 1500); 2885// } 2886 2887// function construirTemplateProdutosSidebarPesquisa(produtos) { 2888// if (!produtos?.length) return; 2889// let html = ` 2890// <p class="text-sm font-bold text-[#444]">Resultados</p> 2891// `; 2892// for (const produto of produtos) { 2893// html += ` 2894// <a class="flex justify-between items-center gap-x-4" onclick="(function(){ try { if (typeof filtroParaDadosAnaliticos === 'function') filtroParaDadosAnaliticos('SelectItem', { event:'click_card', item_list_name:'Sidebar Pesquisa', item_list_id:'sidebar_pesquisa' }); } catch(_){} })()" href="/${ 2895// produto?.url_produto 2896// }"> 2897// <div class="flex items-center gap-x-4"> 2898// <img src="/media/${ 2899// produto?.imagem_produto 2900// }" 2901// class="p-1 border-1 rounded-lg border-[#E6E6E6]" loading="lazy" alt="${ 2902// produto?.cd_produto 2903// }" width="50" height="50"> 2904// <p class="text-xs text-[#444] line-clamp-2">${produto?.nm_produto}</p> 2905// </div> 2906// <p class="text-xs text-[#3F84C7] font-bold flex">${formatFloat( 2907// produto?.preco || 0, 2908// { currency: true } 2909// )}</p> 2910// </a> 2911// `; 2912// } 2913// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2914// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2915// html; 2916// } 2917 2918// --- pesquisa --- 2919 2920const HEADER_SEARCH_CATALOGO = [ 2921 // { 2922 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2923 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2924 // produto__imagem: "produtos/55520_200x200.jpg", 2925 // valor_ini: 9.99, 2926 // valor: 7.99, 2927 // }, 2928 // { 2929 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2930 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2931 // produto__imagem: "produtos/55520_200x200.jpg", 2932 // valor_ini: 9.99, 2933 // valor: 7.99, 2934 // }, 2935 // { 2936 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2937 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2938 // produto__imagem: "produtos/55520_200x200.jpg", 2939 // valor_ini: 9.99, 2940 // valor: 7.99, 2941 // }, 2942 // { 2943 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2944 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2945 // produto__imagem: "produtos/55520_200x200.jpg", 2946 // valor_ini: 9.99, 2947 // valor: 7.99, 2948 // }, 2949 // { 2950 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2951 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2952 // produto__imagem: "produtos/55520_200x200.jpg", 2953 // valor_ini: 9.99, 2954 // valor: 7.99, 2955 // }, 2956 // { 2957 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2958 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2959 // produto__imagem: "produtos/55520_200x200.jpg", 2960 // valor_ini: 9.99, 2961 // valor: 7.99, 2962 // }, 2963 // { 2964 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2965 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2966 // produto__imagem: "produtos/55520_200x200.jpg", 2967 // valor_ini: 9.99, 2968 // valor: 7.99, 2969 // }, 2970 // { 2971 // produto__nm_ecommerce: "Algodão Bola 50 Unidades - Marca Beta", 2972 // produto__nm_url_ecommerce: "algodao-bola-50-un-beta", 2973 // produto__imagem: "produtos/55520_200x200.jpg", 2974 // valor_ini: 12.9, 2975 // valor: 9.9, 2976 // }, 2977 // { 2978 // produto__nm_ecommerce: "Discos de Algodão 70 Unidades - Marca Gama", 2979 // produto__nm_url_ecommerce: "discos-algodao-70un-gama", 2980 // produto__imagem: "produtos/55520_200x200.jpg", 2981 // valor_ini: 14.9, 2982 // valor: 14.9, 2983 // }, 2984 // { 2985 // produto__nm_ecommerce: "Hidratante Corporal 400ml", 2986 // produto__nm_url_ecommerce: "hidratante-corporal-400ml", 2987 // produto__imagem: "produtos/55520_200x200.jpg", 2988 // valor_ini: 29.9, 2989 // valor: 19.9, 2990 // }, 2991 // { 2992 // produto__nm_ecommerce: "Shampoo Nutritivo 300ml", 2993 // produto__nm_url_ecommerce: "shampoo-nutritivo-300ml", 2994 // produto__imagem: "produtos/55520_200x200.jpg",
2995 // valor_ini: 24.9, 2996 // valor: 22.9, 2997 // }, 2998]; 2999 3000window.HeaderSearchCore = { 3001 buscarSugestoes, 3002 buscarProdutosPorTermo, 3003 getSearchData, 3004 getHistoricoPesquisa, 3005 setHistoricoPesquisa, 3006 addHistoricoPesquisa, 3007 removeHistoricoPesquisa, 3008 clearHistoricoPesquisa, 3009 escapeHtml, 3010 normalizeText, 3011 uniqueArray, 3012 templateLoading, 3013}; 3014 3015async function buscarSugestoes(termo) { 3016 termo = (termo || "").trim(); 3017 if (!termo) return []; 3018 3019 const termoNorm = normalizeText(termo); 3020 const lenSemEspaco = termoNorm.replace(/\s+/g, "").length; 3021 3022 //const base = ['shampoo', 'condicionador', 'cerave', 'vitamina c', 'fralda', 'hidratante', 'algodão']; 3023 const base = []; 3024 const baseFiltrada = 3025 lenSemEspaco >= 3 3026 ? base.filter((t) => normalizeText(t).includes(termoNorm)) 3027 : []; 3028 3029 const tokensProdutos = []; 3030 try { 3031 for (const p of HEADER_SEARCH_CATALOGO || []) { 3032 const nome = p?.nm_produto || ""; 3033 const partes = String(nome) 3034 .split(/[^A-Za-zÃ-ÃÃ-öø-ÿ0-9]+/) 3035 .filter(Boolean); 3036 for (const tk of partes) { 3037 const tkNorm = normalizeText(tk); 3038 if (tkNorm.length >= 3 && tkNorm.includes(termoNorm)) { 3039 tokensProdutos.push(tk.toLowerCase()); 3040 } 3041 } 3042 } 3043 } catch {} 3044 3045 const combinadas = uniqueArray([...baseFiltrada, ...tokensProdutos]); 3046 return combinadas.slice(0, 5); 3047} 3048 3049const _cachePesquisa = new Map(); 3050let _abortPesquisaCtrl = null; 3051 3052/** 3053 * Busca sugestões de produtos no backend, reaproveitando o POST da pesquisa. 3054 * Retorna até 6 itens ou []. 3055 */ 3056async function buscarProdutosPorTermo(termo) { 3057 termo = (termo || "").trim(); 3058 if (!termo) return []; 3059 3060 const termoNorm = normalizeText(termo); 3061 if (termoNorm.length < 3) return []; 3062 3063 if (_cachePesquisa.has(termo)) { 3064 return _cachePesquisa.get(termo); 3065 } 3066 3067 // Cancela a requisição anterior 3068 if (_abortPesquisaCtrl) { 3069 _abortPesquisaCtrl.abort(); 3070 } 3071 _abortPesquisaCtrl = new AbortController(); 3072 3073 const body = new URLSearchParams(); 3074 body.set("pesquisa", termo); 3075 const csrf = window.CSRF_TOKEN(); 3076 if (csrf) { 3077 body.set("csrfmiddlewaretoken", csrf); 3078 } 3079 3080 try { 3081 const resp = await fetch("/sugestoes", { 3082 method: "POST", 3083 headers: { 3084 "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", 3085 "X-CSRFToken": csrf || "", 3086 }, 3087 body, 3088 credentials: "same-origin", 3089 signal: _abortPesquisaCtrl.signal, 3090 }); 3091 3092 if (!resp.ok) { 3093 return []; 3094 } 3095 3096 const data = await resp.json(); 3097 const items = Array.isArray(data?.dados.produtos) 3098 ? data.dados.produtos 3099 : []; 3100 3101 const resultados = items.slice(0, 6); 3102 3103 _cachePesquisa.set(termo, resultados); 3104 3105 return resultados; 3106 } catch (err) { 3107 if (err?.name === "AbortError") return []; 3108 console.error("Erro ao buscar sugestões:", err); 3109 return []; 3110 } 3111} 3112 3113let precosTimer = null; 3114 3115// Pega dados de busca em paralelo (reúso no desk e mobile) 3116async function getSearchData(termo) { 3117 termo = (termo || "").trim(); 3118 if (!termo || termo.length < 3) return { termos: [], produtos: [] }; 3119 const [termos, produtos] = await Promise.all([ 3120 buscarSugestoes(termo), 3121 buscarProdutosPorTermo(termo), 3122 ]); 3123 // dispara 1 vez, substituindo o anterior 3124 clearTimeout(precosTimer); 3125 precosTimer = setTimeout(() => { 3126 try { carregarPrecos(); } catch(_){} 3127 }, 300); 3128 3129 // Search analytics 3130 try { if (typeof filtroParaDadosAnaliticos === 'function') filtroParaDadosAnaliticos('Search', { search_term: termo }); } catch(_){} 3131 3132 return { termos, produtos }; 3133} 3134 3135function getHistoricoPesquisa() { 3136 try { 3137 const raw = localStorage.getItem("header_search_history"); 3138 const lista = raw ? JSON.parse(raw) : []; 3139 return Array.isArray(lista) ? lista : []; 3140 } catch { 3141 return []; 3142 } 3143} 3144function setHistoricoPesquisa(lista) { 3145 try { 3146 localStorage.setItem( 3147 "header_search_history", 3148 JSON.stringify(lista.slice(0, 8)) 3149 ); 3150 } catch {} 3151} 3152function addHistoricoPesquisa(termo) { 3153 termo = (termo || "").trim(); 3154 if (!termo) return; 3155 const lista = getHistoricoPesquisa().filter( 3156 (t) => t.toLowerCase() !== termo.toLowerCase() 3157 ); 3158 lista.unshift(termo); 3159 setHistoricoPesquisa(lista); 3160} 3161function removeHistoricoPesquisa(termo) { 3162 const lista = getHistoricoPesquisa().filter( 3163 (t) => t.toLowerCase() !== (termo || "").toLowerCase() 3164 ); 3165 setHistoricoPesquisa(lista); 3166 atualizarDropdownComHistoricoESugestoes(); 3167} 3168function clearHistoricoPesquisa() { 3169 setHistoricoPesquisa([]); 3170 atualizarDropdownComHistoricoESugestoes(); 3171} 3172 3173function escapeHtml(str) { 3174 return String(str) 3175 .replace(/&/g, "&") 3176 .replace(/</g, "<") 3177 .replace(/>/g, ">") 3178 .replace(/"/g, """) 3179 .replace(/'/g, "'"); 3180} 3181 3182function normalizeText(str) { 3183 return String(str || "") 3184 .toLowerCase() 3185 .normalize("NFD") 3186 .replace(/[\u0300-\u036f]/g, ""); 3187} 3188 3189function uniqueArray(arr) { 3190 const set = new Set(); 3191 const out = []; 3192 for (const v of arr) {
3193 const key = normalizeText(v); 3194 if (!set.has(key)) { 3195 set.add(key); 3196 out.push(v); 3197 } 3198 } 3199 return out; 3200} 3201 3202function templateLoading() { 3203 return `<div class="p-3 text-[12px] text-[#666]">Carregando...</div>`; 3204} 3205 3206// --- fim pesquisa --- 3207 3208 3209 3210 3211 3212// --- Avise-me --- 3213(function () { 3214 const ICONE_SINO = '<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z"/>'; 3215 3216 let timer_avise_me = null; 3217 let avise_me_pos_login_agendado = false; 3218 3219 const dict_cores_mensagem = { 3220 success: 'bg-[#0C670F] border-[#9FFCAC] text-white', 3221 error: 'bg-[#B42318] border-[#FECDCA] text-white', 3222 warning: 'bg-[#B54708] border-[#FEDF89] text-white', 3223 }; 3224 3225 function removerMensagensInlineAviseMe(btn) { 3226 const bloco = btn?.closest('[data-target="comprar-produto"]') 3227 || btn?.closest('[data-notificar-disponibilidade="true"]'); 3228 bloco?.querySelectorAll('[data-target="mensagem-avise-me"]').forEach((el) => el.remove()); 3229 } 3230 3231 function exibirMensagemAviseMe(tipo, msg) { 3232 if (window.toastr?.[tipo]) { 3233 window.toastr[tipo](msg); 3234 } else { 3235 exibirToastAviseMe(tipo, msg); 3236 } 3237 } 3238 3239 function garantirEstilosAviseMe() { 3240 let style = document.getElementById('avise-me-estilos'); 3241 if (!style) { 3242 style = document.createElement('style'); 3243 style.id = 'avise-me-estilos'; 3244 document.head.appendChild(style); 3245 } 3246 3247 style.textContent = ` 3248 [data-target="btn-notificar-disponibilidade"] { 3249 background-color: var(--dark-gray, #838383) !important; 3250 border-color: var(--dark-gray, #838383) !important; 3251 color: #fff !important; 3252 transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease; 3253 opacity: 0.6; 3254 } 3255 [data-target="btn-notificar-disponibilidade"] span, 3256 [data-target="btn-notificar-disponibilidade"] svg { 3257 color: inherit !important; 3258 } 3259 [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado):hover, 3260 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 3261 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 3262 [data-target="pesquisa-produto-linear"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 3263 [data-target="pesquisa-produto-colunar"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 3264 [data-target="carrossel-produto"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado) { 3265 background-color: var(--c-btn-padrao-comprar-bg-hover, var(--c-btn-padrao-comprar-bg)) !important; 3266 border-color: var(--c-btn-padrao-comprar-border) !important; 3267 color: var(--c-btn-padrao-comprar-text-hover, var(--c-btn-padrao-comprar-text, #fff)) !important; 3268 opacity: 1; 3269 } 3270 [data-target="pesquisa-header"] [data-target="comprar-produto"], 3271 [data-target="pesquisa-header-mobile"] [data-target="comprar-produto"], 3272 [data-target="pesquisa-header"] > div, 3273 [data-target="pesquisa-header-mobile"] > div { 3274 overflow: visible; 3275 } 3276 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto, 3277 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto { 3278 width: 2.5rem !important; 3279 max-width: 2.5rem !important; 3280 overflow: hidden; 3281 } 3282 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto .avise-me-texto, 3283 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto .avise-me-texto { 3284 display: none; 3285 } 3286 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado), 3287 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado), 3288 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover, 3289 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover { 3290 width: auto !important; 3291 max-width: none !important; 3292 padding-inline: 0.5rem !important; 3293 } 3294 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) .avise-me-texto, 3295 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) .avise-me-texto, 3296 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover .avise-me-texto, 3297 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover .avise-me-texto { 3298 display: inline; 3299 } 3300 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) svg, 3301 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) svg, 3302 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover svg, 3303 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover svg { 3304 display: none; 3305 } 3306 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado, 3307 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado span, 3308 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado svg { 3309 cursor: default !important; 3310 } 3311 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado { 3312 background-color: var(--dark-gray, #838383) !important; 3313 border-color: var(--dark-gray, #838383) !important; 3314 opacity: 0.6; 3315 } 3316 [data-target="comprar-produto"].is-avise-me-solicitado-bloco, 3317 [data-target="comprar-produto"].is-avise-me-solicitado-bloco * { 3318 cursor: default !important; 3319 } 3320 `; 3321 } 3322 3323 function aplicarGrayscaleIndisponivelAviseMe(produto_el) { 3324 if (!produto_el) return; 3325 3326 const target = produto_el.dataset?.target || ''; 3327 if (target === 'pagina-produto') return; 3328 3329 produto_el.style.filter = ''; 3330 produto_el.querySelectorAll('img').forEach(function (img) { 3331 img.style.filter = 'grayscale(1)'; 3332 });
3333 } 3334 3335 function garantirContainerMensagemAviseMe() { 3336 let container = document.getElementById('avise-me-mensagem-container'); 3337 if (container) return container; 3338 3339 container = document.createElement('div'); 3340 container.id = 'avise-me-mensagem-container'; 3341 container.className = 'fixed top-4 right-4 z-[9999] flex flex-col gap-2 w-[min(100%,22rem)] pointer-events-none px-4'; 3342 container.setAttribute('aria-live', 'polite'); 3343 container.setAttribute('aria-atomic', 'true'); 3344 document.body.appendChild(container); 3345 return container; 3346 } 3347 3348 function exibirToastAviseMe(tipo, msg) { 3349 const container = garantirContainerMensagemAviseMe(); 3350 const toast = document.createElement('div'); 3351 const classe_cor = dict_cores_mensagem[tipo] || dict_cores_mensagem.success; 3352 3353 toast.className = `pointer-events-auto px-4 py-3 rounded-[12px] border text-[13px] leading-snug font-semibold shadow-lg transition-opacity duration-300 opacity-100 ${classe_cor}`; 3354 toast.setAttribute('role', 'status'); 3355 toast.textContent = msg; 3356 container.appendChild(toast); 3357 3358 window.setTimeout(function () { 3359 toast.classList.replace('opacity-100', 'opacity-0'); 3360 window.setTimeout(function () { 3361 toast.remove(); 3362 if (!container.childElementCount) container.remove(); 3363 }, 300); 3364 }, 4500); 3365 } 3366 3367 function isContextoCompactoAviseMe(btn) { 3368 const produto_el = btn?.closest?.('[data-notificar-disponibilidade="true"]'); 3369 const target = produto_el?.dataset?.target || ''; 3370 return target === 'pesquisa-header' || target === 'pesquisa-header-mobile'; 3371 } 3372 3373 function compactarLayoutAviseMe(comprar_el, produto_el) { 3374 const target = produto_el?.dataset?.target || ''; 3375 if (target === 'pesquisa-header' || target === 'pesquisa-header-mobile') return; 3376 } 3377 3378 function desabilitarBotaoAviseMeSolicitado(btn) { 3379 if (!btn) return; 3380 3381 btn.removeAttribute('disabled'); 3382 btn.setAttribute('aria-disabled', 'true'); 3383 btn.tabIndex = -1; 3384 btn.classList.remove('cursor-pointer'); 3385 btn.classList.add('is-avise-me-solicitado', 'cursor-default'); 3386 3387 const comprar_el = btn.closest('[data-target="comprar-produto"]'); 3388 if (comprar_el) comprar_el.classList.add('is-avise-me-solicitado-bloco'); 3389 } 3390 3391 const CHAVE_AVISE_ME_PENDENTE = 'avise_me_pendente'; 3392 3393 function obterUrlRetornoAviseMe() { 3394 return window.location.pathname + window.location.search + window.location.hash; 3395 } 3396 3397 function montarUrlRetornoAviseMe(produto_id) { 3398 const url = new URL(window.location.href); 3399 url.searchParams.delete('avise_me_produto'); 3400 url.searchParams.set('avise_me_produto', String(produto_id)); 3401 return url.pathname + url.search + url.hash; 3402 } 3403 3404 function salvarPendenciaAviseMe(produto_id) { 3405 try { 3406 sessionStorage.setItem(CHAVE_AVISE_ME_PENDENTE, JSON.stringify({ 3407 produto_id: String(produto_id), 3408 url: obterUrlRetornoAviseMe(), 3409 })); 3410 } catch (_) {} 3411 } 3412 3413 function limparPendenciaAviseMe() { 3414 try { 3415 sessionStorage.removeItem(CHAVE_AVISE_ME_PENDENTE); 3416 } catch (_) {} 3417 3418 try { 3419 const url = new URL(window.location.href); 3420 if (!url.searchParams.has('avise_me_produto')) return; 3421 url.searchParams.delete('avise_me_produto'); 3422 window.history.replaceState(null, '', url.pathname + url.search + url.hash); 3423 } catch (_) {} 3424 } 3425 3426 function obterProdutoIdAviseMePendente() { 3427 try { 3428 const da_url = new URLSearchParams(window.location.search).get('avise_me_produto'); 3429 if (da_url) return da_url; 3430 } catch (_) {} 3431 3432 try { 3433 const pendente = sessionStorage.getItem(CHAVE_AVISE_ME_PENDENTE); 3434 if (pendente) return JSON.parse(pendente).produto_id || null; 3435 } catch (_) {} 3436 3437 return null; 3438 } 3439 3440 function isUsuarioAutenticadoAviseMe() { 3441 if (typeof window.IS_AUTENTICADO === 'function') { 3442 return window.IS_AUTENTICADO(); 3443 } 3444 if (typeof window.APP_CONTEXT === 'function') { 3445 return !!window.APP_CONTEXT()?.auth?.is_autenticado; 3446 } 3447 return false; 3448 } 3449 3450 function redirecionarLoginAviseMe(produto_id) { 3451 if (produto_id) { 3452 salvarPendenciaAviseMe(produto_id); 3453 } 3454 exibirMensagemAviseMe('error', 'Faça login para ser avisado quando o produto estiver disponÃvel.'); 3455 window.setTimeout(function () { 3456 const url_retorno = produto_id 3457 ? montarUrlRetornoAviseMe(produto_id) 3458 : obterUrlRetornoAviseMe(); 3459 window.location.href = '/login?next=' + encodeURIComponent(url_retorno); 3460 }, 1200); 3461 } 3462 3463 function redirecionarSeAviseMePendentePosLogin() {
3464 if (!isUsuarioAutenticadoAviseMe()) return false; 3465 if (obterProdutoIdAviseMePendente()) return false; 3466 3467 let pendente = null; 3468 try { 3469 const raw = sessionStorage.getItem(CHAVE_AVISE_ME_PENDENTE); 3470 if (raw) pendente = JSON.parse(raw); 3471 } catch (_) {} 3472 3473 if (!pendente?.produto_id) return false; 3474 3475 const url_alvo = new URL(pendente.url || '/', window.location.origin); 3476 url_alvo.searchParams.set('avise_me_produto', String(pendente.produto_id)); 3477 const destino = url_alvo.pathname + url_alvo.search + url_alvo.hash; 3478 const atual = obterUrlRetornoAviseMe(); 3479 3480 if (destino !== atual) { 3481 window.location.href = destino; 3482 return true; 3483 } 3484 3485 return false; 3486 } 3487 3488 function encontrarBotaoAviseMeProduto(produto_id) { 3489 return document.querySelector( 3490 `[data-notificar-disponibilidade="true"][data-cd-produto="${produto_id}"] [data-target="btn-notificar-disponibilidade"]` 3491 ); 3492 } 3493 3494 function marcarBotaoAviseMeSolicitado(btn, produto_id) { 3495 const btn_alvo = btn || encontrarBotaoAviseMeProduto(produto_id); 3496 if (!btn_alvo) return; 3497 3498 removerMensagensInlineAviseMe(btn_alvo); 3499 3500 const comprar_el = btn_alvo.closest('[data-target="comprar-produto"]'); 3501 if (comprar_el) comprar_el.dataset.aviseMeSolicitado = 'true'; 3502 3503 const is_compacto = isContextoCompactoAviseMe(btn_alvo); 3504 3505 btn_alvo.innerHTML = ` 3506 <svg xmlns="http://www.w3.org/2000/svg" width="17.5" height="17.5" viewBox="0 0 24 24" fill="currentColor" class="text-white shrink-0" aria-hidden="true"> 3507 ${ICONE_SINO} 3508 </svg> 3509 ${is_compacto 3510 ? '<span class="avise-me-texto font-bold text-[13px] text-white whitespace-nowrap">Avise-me solicitado</span>' 3511 : '<span class="font-bold text-[13px] text-white">Avise-me solicitado</span>'} 3512 `; 3513 if (is_compacto) btn_alvo.classList.add('is-avise-me-compacto'); 3514 desabilitarBotaoAviseMeSolicitado(btn_alvo); 3515 } 3516 3517 function agendarProcessarAviseMePosLogin() { 3518 const produto_id = obterProdutoIdAviseMePendente(); 3519 if (!produto_id || !isUsuarioAutenticadoAviseMe()) return; 3520 if (avise_me_pos_login_agendado) return; 3521 avise_me_pos_login_agendado = true; 3522 3523 let tentativas = 0; 3524 const max_tentativas = 30; 3525 3526 const tentar = function () { 3527 tentativas += 1; 3528 const btn = encontrarBotaoAviseMeProduto(produto_id); 3529 if (btn && btn.getAttribute('aria-disabled') !== 'true') { 3530 notificarDisponibilidadeProduto(produto_id, btn, true); 3531 return; 3532 } 3533 if (tentativas < max_tentativas) { 3534 window.setTimeout(tentar, 200); 3535 return; 3536 } 3537 notificarDisponibilidadeProduto(produto_id, null, true); 3538 }; 3539 3540 window.setTimeout(tentar, 400); 3541 } 3542 3543 async function notificarDisponibilidadeProduto(produto_id, btn, is_pos_login) { 3544 if (!produto_id) return; 3545 3546 if (btn?.getAttribute('aria-disabled') === 'true') return; 3547 3548 if (!isUsuarioAutenticadoAviseMe()) { 3549 redirecionarLoginAviseMe(produto_id); 3550 return; 3551 } 3552 3553 try { 3554 const request = await fetch('/produto/notificar-disponibilidade', { 3555 method: 'POST', 3556 headers: { 3557 'Content-Type': 'application/json', 3558 'X-CSRFToken': window.CSRF_TOKEN?.() || '', 3559 }, 3560 body: JSON.stringify({ produto_id: produto_id }), 3561 }); 3562 3563 const response = await request.json(); 3564 3565 if (!request.ok || !response?.status) { 3566 const descricao_erro = response?.descricao || ''; 3567 if ( 3568 request.status === 401 3569 || request.status === 403 3570 || /identificado|login|autentic/i.test(descricao_erro) 3571 ) { 3572 redirecionarLoginAviseMe(produto_id); 3573 return; 3574 } 3575 exibirMensagemAviseMe('error', descricao_erro || 'Não foi possÃvel cadastrar a notificação. Tente novamente.'); 3576 return; 3577 } 3578 3579 const msg_toast = response?.descricao || 'Notificação cadastrada com sucesso'; 3580 3581 marcarBotaoAviseMeSolicitado(btn, produto_id); 3582 3583 if (is_pos_login || obterProdutoIdAviseMePendente()) { 3584 limparPendenciaAviseMe(); 3585 } 3586 3587 exibirMensagemAviseMe('success', msg_toast); 3588 } catch (e) { 3589 exibirMensagemAviseMe('error', 'Não foi possÃvel cadastrar a notificação. Tente novamente.'); 3590 } 3591 } 3592 3593 function montarIconeSinoAviseMe(tamanho) { 3594 return `<svg xmlns="http://www.w3.org/2000/svg" width="${tamanho}" height="${tamanho}" viewBox="0 0 24 24" fill="currentColor" class="text-white shrink-0" aria-hidden="true">${ICONE_SINO}</svg>`; 3595 } 3596 3597 function montarHtmlBotaoAviseMe(produto_id, produto_el) { 3598 const target = produto_el?.dataset?.target || ''; 3599 const is_compacto = target === 'pesquisa-header' || target === 'pesquisa-header-mobile'; 3600 const is_pagina_produto = target === 'pagina-produto'; 3601 const classe_botao = is_pagina_produto ? 'rounded-[var(--r-btn-comprar)]' : 'rounded-[var(--r-cta-radius)]'; 3602 const tamanho_icone = 17.5; 3603 const texto_avise_me = is_compacto 3604 ? '<span class="avise-me-texto font-bold text-[13px] text-white whitespace-nowrap"> IndisponÃvel, Avise-me </span>' 3605 : '<span class="font-bold text-[13px] text-white">Avise-me</span>'; 3606 const classe_tamanho = is_compacto 3607 ? 'relative w-10 is-avise-me-compacto h-[var(--h-btn-comprar)] p-1 shrink-0 gap-x-1.5' 3608 : 'w-full h-[var(--h-btn-comprar)] p-0.5'; 3609 3610 const html_botao = ` 3611 <button type="button" 3612 class="flex flex-row justify-center items-center cursor-pointer border-1 3613 ${classe_tamanho} ${classe_botao}" 3614 data-target="btn-notificar-disponibilidade" 3615 data-cd-produto="${produto_id}" 3616 ${is_compacto ? 'aria-label=" IndisponÃvel, Avise-me "' : ''} 3617 onclick="event.preventDefault(); event.stopPropagation(); window.notificarDisponibilidadeProduto('${produto_id}', this); return false;"> 3618 ${is_compacto ? texto_avise_me : ''} 3619 ${montarIconeSinoAviseMe(tamanho_icone)} 3620 ${is_compacto ? '' : texto_avise_me} 3621 </button> 3622 `; 3623 3624 if (is_pagina_produto) { 3625 return ` 3626 <div class="flex flex-col gap-2 w-full" data-target="bloco-avise-me">
3627 <span class="font-bold text-red-600 bg-white whitespace-nowrap text-md text-center p-0.5">Produto indisponÃvel</span> 3628 ${html_botao} 3629 </div> 3630 `; 3631 } 3632 3633 return html_botao; 3634 } 3635 3636 function isConteudoIndisponivelAviseMe(comprar_el) { 3637 if (!comprar_el || comprar_el.dataset.aviseMeSolicitado === 'true') { 3638 return false; 3639 } 3640 if (comprar_el.querySelector('[data-target="btn-notificar-disponibilidade"]')) { 3641 return false; 3642 } 3643 return /indispon/i.test(comprar_el.textContent || ''); 3644 } 3645 3646 function aplicarBotoesAviseMe() { 3647 document.querySelectorAll('[data-notificar-disponibilidade="true"]').forEach((produto_el) => { 3648 const produto_id = produto_el.getAttribute('data-cd-produto'); 3649 if (!produto_id) return; 3650 3651 produto_el.querySelectorAll('[data-target="comprar-produto"]').forEach((comprar_el) => { 3652 if (comprar_el.dataset.aviseMeSolicitado === 'true') return; 3653 if (!isConteudoIndisponivelAviseMe(comprar_el)) return; 3654 compactarLayoutAviseMe(comprar_el, produto_el); 3655 comprar_el.innerHTML = montarHtmlBotaoAviseMe(produto_id, produto_el); 3656 aplicarGrayscaleIndisponivelAviseMe(produto_el); 3657 }); 3658 }); 3659 } 3660 3661 function observarComprarProdutoAviseMe(comprar_el) { 3662 if (!comprar_el || comprar_el.dataset.aviseMeObservado === 'true') return; 3663 comprar_el.dataset.aviseMeObservado = 'true'; 3664 new MutationObserver(agendarAplicarBotoesAviseMe).observe(comprar_el, { 3665 childList: true, 3666 subtree: true, 3667 characterData: true, 3668 }); 3669 } 3670 3671 function iniciarObservadoresAviseMe() { 3672 document.querySelectorAll('[data-notificar-disponibilidade="true"] [data-target="comprar-produto"]').forEach(observarComprarProdutoAviseMe); 3673 } 3674 3675 function agendarAplicarBotoesAviseMe() { 3676 clearTimeout(timer_avise_me); 3677 timer_avise_me = setTimeout(function () { 3678 aplicarBotoesAviseMe(); 3679 iniciarObservadoresAviseMe(); 3680 }, 80); 3681 } 3682 3683 function iniciarAviseMe() { 3684 garantirEstilosAviseMe(); 3685 aplicarBotoesAviseMe(); 3686 iniciarObservadoresAviseMe(); 3687 } 3688 3689 window.notificarDisponibilidadeProduto = notificarDisponibilidadeProduto; 3690 window.iniciarAviseMe = iniciarAviseMe; 3691 3692 window.addEventListener('load', function () { 3693 if (redirecionarSeAviseMePendentePosLogin()) return; 3694 3695 iniciarAviseMe(); 3696 new MutationObserver(agendarAplicarBotoesAviseMe).observe(document.body, { 3697 childList: true, 3698 subtree: true, 3699 }); 3700 agendarProcessarAviseMePosLogin(); 3701 }); 3702})(); 3703// fim avise-me 3704 3705 3706 3707</script>
vendor: 159 bytes, lines 3707-3716
3707 3708 3709 3710 3711 3712 3713 3714 3715 3716<script src="/cdn-cgi/scripts/7d0fa10a/cloudflare-static/rocket-loader.min.js" data-cf-settings="ff2c2aaab21707ab2ba08cc2-|49" defer></script>
3716<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"76d8126d89a14c5d9414af4de517c5fc","spa":2}' crossorigin="anonymous"></script>
3716 3717</body> 3718 3719</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.