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>APP | Farmácias Brava </title> 14 <meta name="description" content="APP | Farmácias Brava"> 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/app"> 19 20 <!-- Open Graph / Facebook --> 21 <meta property="og:type" content="website"> 22 <meta property="og:url" content="https://www.farmaciasbrava.com.br/app"> 23 <meta property="og:title" content="APP | Farmácias Brava "> 24 <meta property="og:description" content="APP | Farmácias Brava"> 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/app"> 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="95b6b4c15eb4e50ce46ccbcd-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: 'KZtQTClVMYpoRszLTs5AobThvJeBVf3fzXRegiio7Gq0zLRr2AbtBGEESBZslfqg', 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="95b6b4c15eb4e50ce46ccbcd-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="95b6b4c15eb4e50ce46ccbcd-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="95b6b4c15eb4e50ce46ccbcd-text/javascript"></script> 204 205 206
206<script type="95b6b4c15eb4e50ce46ccbcd-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="95b6b4c15eb4e50ce46ccbcd-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-95b6b4c15eb4e50ce46ccbcd-=""> 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-95b6b4c15eb4e50ce46ccbcd-=""> 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-95b6b4c15eb4e50ce46ccbcd-=""> 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-95b6b4c15eb4e50ce46ccbcd-=""> 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="95b6b4c15eb4e50ce46ccbcd-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="95b6b4c15eb4e50ce46ccbcd-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-95b6b4c15eb4e50ce46ccbcd-=""></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 1665 1666 <a 1667 href="https://play.google.com/store/apps/details?id=com.inteliger.brava&pcampaignid=web_share" 1668 target="_blank" 1669 rel="noopener noreferrer" 1670 style="display:flex;align-items:center;gap:12px;background:#002aff;color:#ffffff;text-decoration:none;border-radius:12px;padding:14px 28px;font-weight:600;width:100%;max-width:320px;box-sizing:border-box;justify-content:center;font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;" 1671> 1672 <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="24" height="24" style="width:24px;height:24px;flex-shrink:0;"> 1673 <path d="M3.18 1.24C2.74 1.48 2.5 1.97 2.5 2.6v18.8c0 .63.24 1.12.68 1.36l.1.06 10.54-10.54v-.25L3.28 1.18l-.1.06z" fill="#EA4335"/> 1674 <path d="M17.32 15.53l-3.5-3.5v-.26l3.5-3.5.08.05 4.15 2.36c1.18.67 1.18 1.77 0 2.44l-4.15 2.36-.08.05z" fill="#FBBC04"/> 1675 <path d="M17.4 15.48L13.82 12 3.18 22.64c.39.42.98.47 1.66.09l12.56-7.25" fill="#34A853"/> 1676 <path d="M17.4 8.52L4.84 1.27C4.16.89 3.57.94 3.18 1.36L13.82 12l3.58-3.48z" fill="#4285F4"/> 1677 </svg> 1678 <div style="display:flex;flex-direction:column;align-items:flex-start;line-height:1.2;"> 1679 <small style="font-size:11px;font-weight:400;opacity:0.8;">DisponÃvel no</small> 1680 <span style="font-size:16px;">Google Play</span> 1681 </div> 1682</a> 1683 1684<a 1685 href="https://apps.apple.com/us/app/farm%C3%A1cias-brava/id6792831315" 1686 target="_blank" 1687 rel="noopener noreferrer" 1688 style="display:flex;align-items:center;gap:12px;background:#ff006c;color:#ffffff;text-decoration:none;border-radius:12px;padding:14px 28px;font-weight:600;width:100%;max-width:320px;box-sizing:border-box;justify-content:center;font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;margin-top:12px;" 1689 1690 <svg viewBox="0 0 24 24" fill="#ffffff" xmlns="http://www.w3.org/2000/svg" width="24" height="24" style="width:24px;height:24px;flex-shrink:0;"> 1691 <path d="M16.365 1.43c0 1.14-.463 2.083-1.39 2.868-.938.79-2.007 1.222-3.208 1.13a3.06 3.06 0 0 1-.023-.35c0-1.09.5-2.037 1.398-2.797.83-.7 1.86-1.15 2.94-1.24.02.13.283.288.283.389zm4.32 15.31c-.44 1.02-.65 1.47-1.22 2.37-.79 1.25-1.9 2.81-3.28 2.82-1.23.02-1.55-.8-3.22-.79-1.67.01-2.02.81-3.25.79-1.38-.02-2.43-1.42-3.22-2.67-2.21-3.5-2.44-7.6-1.08-9.79.97-1.56 2.5-2.48 3.94-2.48 1.47 0 2.39.81 3.6.81 1.18 0 1.9-.81 3.6-.81 1.29 0 2.66.7 3.63 1.92-3.19 1.75-2.67 6.32.5 7.85z"/> 1692 </svg> 1693 <div style="display:flex;flex-direction:column;align-items:flex-start;line-height:1.2;"> 1694 <small style="font-size:11px;font-weight:400;opacity:0.8;">Baixe na</small> 1695 <span style="font-size:16px;">App Store</span> 1696 </div> 1697</a> 1698 1699 1700</div> 1701 1702 </main> 1703 1704 1705 <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"> 1706 <div id="checkout-template"></div> 1707 </aside> 1708 1709 1710 </div> 1711 1712 1713 1714 <div id="app_footer"> 1715 1716 1717 <footer class="mt-4 flex flex-col "> 1718 <div class="w-full bg-[var(--c-footer-topbar-bg)] text-[var(--c-footer-mid-text)]"> 1719 <div class="container mx-auto p-4"> 1720 <!-- Newsletter --> 1721 <div class="grid grid-cols-3 py-6 flex items-center w-full justify-between"> 1722 <div class="col-span-1 flex justify-between"> 1723 <p class="text-xl font-medium flex items-center justify-center gap-2 text-nowrap"> 1724 <img src="/staticfiles/img/client2/icon_ofertas.webp" alt="Enviar" class="w-8 h-8" /> 1725 Receba as melhores ofertas! 1726 </p> 1727 </div> 1728 <form id="form-newsletter" action="" novalidate class="col-span-2 flex items-center w-full border-[1px] border-white rounded-full pl-[30px]"> 1729 1730 <input type="email" name="email" id="email-newsletter" placeholder="Digite o seu e-mail" autocomplete="off" maxlength="200" 1731 class="text-xs text-white bg-transparent flex-1 min-w-0 outline-none placeholder-white" /> 1732 <button type="submit" class="bg-white text-[#0044ff] rounded-full px-[6px] py-[6px] m-[8px] curs
1732or-pointer transition duration-200 ease-in-out hover:scale-110"> 1733 <img src="/staticfiles/img/client2/arrow-right.svg" alt="Enviar" class="w-5 h-5" /> 1734 </button> 1735 </form> 1736 </div> 1737 </div> 1738 </div> 1739 1740 1741 <div class="w-full bg-[var(--c-footer-bottombar-bg)] text-[var(--c-footer-mid-text)]"> 1742 <div class="container mx-auto md:flex-row justify-between items-center p-4"> 1743 <!-- Contato e Links --> 1744 <div class="flex py-5 grid-cols-2 gap-8 justify-between text-left"> 1745 <!-- Contato --> 1746 <div class="space-y-6"> 1747 <p class="text-lg font-semibold">Não encontrou o que precisava? Fale com a gente!</p> 1748 <div class="flex flex-col md:flex-row gap-4 justify-center md:justify-start"> 1749 <a href="/fale-conosco" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 1750 <img src="/staticfiles/img/client2/icon_operador.webp" alt="Enviar" class="w-5 h-5"/> 1751 Fale Conosco 1752 </a> 1753 <a href="/nossas-lojas" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 1754 <img src="/staticfiles/img/client2/icon_loja.png" alt="Enviar" class="w-5 h-5"/> 1755 Nossas lojas 1756 </a> 1757 <a href="/aplicativos" class="border border-white hover:bg-[#1b34d3] px-5 py-3 rounded-lg flex items-center justify-center gap-2"> 1758 <img src="https://storage.googleapis.com/retailon-publico/brava/admin/icon-phone_17055.svg" alt="Enviar" class="w-auto h-6 img-filter-fff"/> 1759 Intranet 1760 </a> 1761 </div> 1762 <div class="text-sm mt-4 space-y-2"> 1763 <div class="flex items-center gap-5"> 1764 <div class="flex items-center gap-1 hover:underline"> 1765 <a href="/pagina/quem-somos" class="">Nossa história</a> 1766 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 1767 </div> 1768 <div class="flex items-center gap-1 hover:underline"> 1769 <a href="/trabalhe-conosco" class="">Trabalhe conosco</a> 1770 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 1771 </div> 1772 1773 <div class="flex items-center gap-1 hover:underline"> 1774 <a href="/pagina/notinha-amiga" class="">Brava Vantagens e Notinha Amiga</a> 1775 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 1776 </div> 1777 </div> 1778 <div class="flex items-center gap-5"> 1779 1780 1781 <div class="flex items-center gap-1 hover:underline"> 1782 <a href="/pagina/pedido-patrocinio" class="">Pedidos de patrocÃnios e projetos</a> 1783 <img src="/staticfiles/img/client2/icon_seta_diagonal_white.png" alt="Seta" class="inline w-2 h-2"/> 1784 </div> 1785 </div> 1786 1787 </div> 1788 </div> 1789 1790 <!-- Franquia e QR Code --> 1791 <div class="flex justify-between"> 1792 <div class="gap-4"> 1793 <div class="flex items-center gap-8 p-2"> 1794 <div class="flex flex-col items-center gap-4"> 1795 <a href="/franquias" class="border border-white hover:bg-[#1b34d3] px-4 py-2 rounded-lg">Seja um franqueado</a> 1796 </div> 1797 1798 </div> 1799 <div class="flex gap-2 p-2"> 1800 <div class="bg-white rounded-md p-1"> 1801 <a href="https://www.facebook.com/farmaciasbrava1?locale=pt_BR" target="_blank" rel="noopener noreferrer"> 1802 <img src="/staticfiles/img/client2/icon_facebook.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="Facebook" /> 1803 </a> 1804 </div> 1805 <div class="bg-white rounded-md p-1"> 1806 <a href="https://www.instagram.com/farmaciasbrava/" target="_blank" rel="noopener noreferrer"> 1807 <img src="/staticfiles/img/client2/icon_instagram.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="Instagram" /> 1808 </a> 1809 </div> 1810 <div class="bg-white rounded-md p-1"> 1811 <a href="https://www.linkedin.com/company/farmaciasbrava/" target="_blank" rel="noopener noreferrer"> 1812 <img src="/staticfiles/img/client2/icon_linkedin.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="LinkedIn" /> 1813 </a> 1814 </div> 1815 <div class="bg-white rounded-md p-1"> 1816 <a href="https://www.youtube.com/@redefarmaciasbrava" target="_blank" rel="noopener noreferrer">
1817 <img src="/staticfiles/img/client2/icon_youtube.png" class="w-7 h-7 transition duration-200 ease-in-out hover:scale-105" alt="YouTube" /> 1818 </a> 1819 </div> 1820 </div> 1821 1822 </div> 1823 1824 </div> 1825 1826 </div> 1827 <!-- Encartes --> 1828 <div class="flex justify-center py-4"> 1829 <div class="flex w-full gap-5"> 1830 <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"> 1831 <img src="/staticfiles/img/client2/icon_revista.png" alt="Encarte" class="w-5 h-5 mr-2" /> 1832 Encarte Paraná 1833 </a> 1834 <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"> 1835 <img src="/staticfiles/img/client2/icon_revista.png" alt="Encarte" class="w-5 h-5 mr-2" /> 1836 Encarte Santa Catarina 1837 </a> 1838 </div> 1839 </div> 1840 1841 <!-- PolÃticas --> 1842 <div class="text-xs text-center pt-4 pb-6 space-x-2"> 1843 <a href="/pagina/politica-guia-seguranca" class="hover:underline">Guia de segurança</a> 1844 <span>|</span> 1845 <a href="/pagina/politica-privacidade" class="hover:underline">PolÃtica de privacidade</a> 1846 <span>|</span> 1847 <a href="/pagina/politica-dados-pessoais" class="hover:underline">Solicitação sobre dados pessoais</a> 1848 <span>|</span> 1849 <a href="/pagina/politica-entrega" class="hover:underline">PolÃtica de entrega</a> 1850 <span>|</span> 1851 <a href="/pagina/politica-troca-devolucao" class="hover:underline">Troca e devolução</a> 1852 </div> 1853 1854 </div> 1855 </div> 1856 1857 <div class="w-full flex justify-center items-center py-1 bg-[#eeeeee] shadow-[0_-2px_5px_rgba(0,0,0,0.06)]"> 1858 <div class="flex flex-col items-center justify-around my-3"> 1859 <span class="text-[#333] text-sm">Desenvolvido por:</span> 1860 <a href="https://retailon.com.br/?utm_source=brava&utm_medium=rodape&utm_id=inteliger" target="_blank"> 1861 <img loading="lazy" src="https://storage.googleapis.com/retailon-publico/inteliger/logos/logo-pequena.webp" alt="Logo RetailON" 1862 class="mx-auto w-[130px]"> 1863 </a> 1864 </div> 1865 </div> 1866 </footer> 1867 1868
1868<script type="95b6b4c15eb4e50ce46ccbcd-text/javascript"> 1869 async function cadastrarEmailNewsletter() { 1870 const email = document.getElementById('email-newsletter').value; 1871 if (!email) { 1872 toastr.error('Por favor, preencha o e-mail!'); 1873 return; 1874 } 1875 try { 1876 const response = await fetch('cliente/promocao/criar', { 1877 method: 'POST', 1878 headers: { 1879 'Content-Type': 'application/json', 1880 'X-CSRFToken': window.CSRF_TOKEN(), 1881 }, 1882 body: JSON.stringify({ email }), 1883 }); 1884 1885 if (response.ok) { 1886 toastr.success('E-mail cadastrado com sucesso!'); 1887 } else { 1888 toastr.error('Erro ao cadastrar e-mail!'); 1889 } 1890 } catch (error) { 1891 toastr.error('Erro ao cadastrar e-mail!'); 1892 } 1893 } 1894 1895 document.getElementById('form-newsletter').addEventListener('submit', (e) => { 1896 e.preventDefault(); 1897 e.stopPropagation(); 1898 cadastrarEmailNewsletter(); 1899 }); 1900 </script>
1900 1901 1902 </div> 1903 1904 1905 1906
1906<script type="95b6b4c15eb4e50ce46ccbcd-text/javascript" src="/staticfiles/js/defer.js" defer></script>
1906 1907
1907<script type="95b6b4c15eb4e50ce46ccbcd-text/javascript" src="/staticfiles/js/carrosseis.js" defer></script>
1907 1908
1908<script type="95b6b4c15eb4e50ce46ccbcd-text/javascript" src="/staticfiles/js/pbm.js" defer></script>
1908 1909 1910 1911 1912
1912<script defer type="95b6b4c15eb4e50ce46ccbcd-text/javascript"> 1913 1914 1915document.addEventListener('DOMContentLoaded', async () => { 1916 buscarProdutosAvaliados() 1917 buscarProdutosFavoritos() 1918}) 1919 1920async function buscarProdutosAvaliados(produtos_id = []) { 1921 let produtoId = produtos_id?.length ? produtos_id : []; 1922 1923 if (!produtoId?.length) { 1924 const produtosComAvaliacao = document.querySelectorAll('[data-target="avaliacao-produto"]'); 1925 if (!produtosComAvaliacao?.length) return; 1926 produtosComAvaliacao.forEach(produto => { 1927 const cdProduto = produto?.closest('[data-cd-produto]')?.getAttribute('data-cd-produto'); 1928 if (cdProduto) produtoId.push(cdProduto); 1929 }); 1930 if (produtoId?.length) { 1931 produtoId = Array.from(new Set(produtoId)); 1932 } else { 1933 return; 1934 } 1935 } else { 1936 produtoId = Array.from(new Set(produtoId)); 1937 } 1938 1939 const request = await fetch(`/produto/buscar/avaliacoes`, { 1940 method: "POST", 1941 headers: { 1942 "X-CSRFToken": window.CSRF_TOKEN(), 1943 "Content-Type": "application/json", 1944 }, 1945 body: JSON.stringify({ 1946 produtos_id: produtoId 1947 }) 1948 }) 1949 1950 for (const cdProduto of produtoId) { 1951 const htmlDefault = ` 1952 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1953 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1954 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1955 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1956 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1957 <span class="text-[10px] font-semibold text-[#444] ml-1">(0)</span> 1958 ` 1959 const produtoElementos = document.querySelectorAll(`[data-cd-produto="${cdProduto}"]`) 1960 if (produtoElementos?.length) { 1961 produtoElementos.forEach(produtoElemento => { 1962 if(produtoElemento.querySelector('[data-target="avaliacao-produto"]')) 1963 produtoElemento.querySelector('[data-target="avaliacao-produto"]').innerHTML = htmlDefault 1964 }) 1965 } 1966 } 1967 1968 const response = await request.json() 1969 1970 if (!request.ok || !response?.status || !Object.keys(response?.avaliacoes_produtos)?.length) 1971 return 1972 1973 const avaliacoes = response?.avaliacoes_produtos 1974 1975 Object.keys(avaliacoes).forEach((produto_id) => { 1976 1977 const listaAvaliacoes = avaliacoes[produto_id] 1978 const produtoElementosComAvaliacoes = document.querySelectorAll(`[data-cd-produto="${produto_id}"]`) 1979 1980 if (produtoElementosComAvaliacoes?.length && listaAvaliacoes?.length) { 1981 1982 const mediaAvaliacoes = listaAvaliacoes?.reduce((acc, avaliacao) => acc + avaliacao?.nota_avaliacao, 0) / listaAvaliacoes?.length 1983 1984 let htmlContainerAvaliacoes = `` 1985 1986 if(mediaAvaliacoes >= 1) { 1987 htmlContainerAvaliacoes += ` 1988 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 1989 ` 1990 } else { 1991 htmlContainerAvaliacoes += ` 1992 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 1993 ` 1994 } 1995 if(mediaAvaliacoes >= 2) { 1996 htmlContainerAvaliacoes += ` 1997 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 1998 ` 1999 } else { 2000 htmlContainerAvaliacoes += ` 2001 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2002 ` 2003 } 2004 if(mediaAvaliacoes >= 3) { 2005 htmlContainerAvaliacoes += ` 2006 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2007 ` 2008 } else { 2009 htmlContainerAvaliacoes += ` 2010 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2011 ` 2012 } 2013 if(mediaAvaliacoes >= 4) { 2014 htmlContainerAvaliacoes += ` 2015 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2016 ` 2017 } else { 2018 htmlContainerAvaliacoes += ` 2019 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2020 ` 2021 } 2022 if(mediaAvaliacoes >= 5) { 2023 htmlContainerAvaliacoes += ` 2024 <img src="/staticfiles/img/client2/icon-avaliacao-fill.svg" alt="Estrela" width="15" height="15"> 2025 ` 2026 } else { 2027 htmlContainerAvaliacoes += ` 2028 <img src="/staticfiles/img/client2/icon-avaliacao.svg" alt="Estrela" width="15" height="15"> 2029 ` 2030 } 2031 2032 htmlContainerAvaliacoes += `
2033 <span class="text-[10px] font-semibold text-[#444] ml-1">(${listaAvaliacoes?.length})</span> 2034 ` 2035 2036 produtoElementosComAvaliacoes.forEach(produtoElementoComAvaliacoes => { 2037 if(produtoElementoComAvaliacoes.querySelector('[data-target="avaliacao-produto"]')) 2038 produtoElementoComAvaliacoes.querySelector('[data-target="avaliacao-produto"]').innerHTML = htmlContainerAvaliacoes 2039 }) 2040 } 2041 }) 2042 2043} 2044 2045async function buscarProdutosFavoritos() { 2046 2047 if(!document.querySelectorAll('[data-favoritar]')?.length) 2048 return 2049 2050 const request = await fetch(`/produto/buscar/favoritos`, { 2051 method: "GET", 2052 headers: { 2053 "X-CSRFToken": window.CSRF_TOKEN(), 2054 } 2055 }) 2056 2057 const response = await request.json() 2058 2059 if(!request.ok || !response?.status || !response?.produtos?.length) 2060 return 2061 2062 const favoritos = response?.produtos 2063 for(const favorito of favoritos){ 2064 const els = document.querySelectorAll(`[data-favoritar="${favorito?.id}"]`) 2065 els.forEach(el => { 2066 el.setAttribute('data-is-favoritado', 'true') 2067 el.innerHTML = ` 2068 <img src="/staticfiles/img/client2/icon_wishlist_fill.svg" class="cursor-pointer" alt="Remover dos favoritos" width="35" height="35"> 2069 ` 2070 }) 2071 } 2072} 2073 2074async function favoritarProduto(el, produtoId) { 2075 2076 if(!produtoId) 2077 return 2078 2079 const isFavoritado = el?.getAttribute('data-is-favoritado') === 'true' 2080 2081 const elPaginaProduto = el?.closest('[data-target="pagina-produto"]') 2082 const elLoader = elPaginaProduto 2083 ? document.body 2084 : (el?.closest('[data-target="carrossel-produto"]') || el) 2085 2086 load('on', [elLoader]) 2087 2088 const request = await fetch(`/produto/atualizar/favorito`, { 2089 method: "POST", 2090 headers: { 2091 "Content-Type": "application/json", 2092 "X-CSRFToken": window.CSRF_TOKEN(), 2093 }, 2094 body: JSON.stringify({ 2095 produto_id: produtoId 2096 }) 2097 }) 2098 2099 const response = await request.json() 2100 2101 if(!request.ok || !response?.status){ 2102 toastr.error(response?.descricao || 'Não foi possÃvel executar a ação. Tente novamente.') 2103 if(el){ 2104 el.setAttribute('data-is-favoritado', 'false') 2105 el.innerHTML = ` 2106 <img src="/staticfiles/img/client2/icon_wishlist.svg" 2107 class="cursor-pointer" 2108 width="35" height="35" 2109 alt="Adicionar aos favoritos" 2110 > 2111 `; 2112 } 2113 load('off', [elLoader]) 2114 return 2115 } 2116 2117 if(el){ 2118 el.setAttribute('data-is-favoritado', !isFavoritado) 2119 el.innerHTML = ` 2120 <img src="/staticfiles/img/client2/icon_${!isFavoritado ? 'wishlist_fill' : 'wishlist'}.svg" 2121 class="cursor-pointer" 2122 width="35" height="35" 2123 alt="${!isFavoritado ? 'Adicionar aos favoritos' : 'Remover dos favoritos'}" 2124 > 2125 `; 2126 } 2127 load('off', [elLoader]) 2128} 2129 2130function visualizarMenuCategorias() { 2131 const menuCategoriaContainer = document.getElementById("menu-categorias-mobile"); 2132 menuCategoriaContainer?.classList?.toggle("hidden"); 2133} 2134_logicaMenuCategoriasCarregada = true; 2135 2136/* 2137* Pesquisa mobile 2138*/ 2139// function sidebarPesquisa(toggle) { 2140// if (toggle === "open") { 2141// document.getElementById("pesquisa-sidebar")?.classList?.remove("hidden"); 2142// setTimeout(() => { 2143// document.getElementById("pesquisa-sidebar-input")?.focus(); 2144// }, 150); 2145// const debouncedPesquisa = debounce(buscarProdutosSidebarPesquisa, 750); 2146// const input = document.getElementById("pesquisa-sidebar-input"); 2147// input.addEventListener("input", debouncedPesquisa); 2148// return; 2149// } 2150// if (toggle === "close") { 2151// document.getElementById("pesquisa-sidebar")?.classList?.add("hidden"); 2152// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2153// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2154// ""; 2155// return; 2156// } 2157// if (!toggle) 2158// document.getElementById("pesquisa-sidebar")?.classList?.toggle("hidden"); 2159// } 2160 2161function sidebarPesquisa(state){ 2162 const aside = document.getElementById('pesquisa-sidebar'); 2163 if(!aside) return; 2164 2165 const input = document.getElementById('pesquisa-sidebar-input'); 2166 2167 if(state === 'open'){ 2168 document.body.classList.add('overflow-hidden'); 2169 aside.classList.remove('hidden'); 2170 inicializarSidebarPesquisa() 2171 input.focus(); 2172 } else { 2173 document.body.classList.remove('overflow-hidden'); 2174 aside.classList.add('hidden'); 2175 } 2176} 2177 2178_sidebarPesquisaCarregada = true; 2179 2180function clearSidebarPesquisa() { 2181 if (document.getElementById("pesquisa-sidebar-input")) 2182 document.getElementById("pesquisa-sidebar-input").value = ""; 2183 document.getElementById("pesquisa-sidebar-input")?.focus(); 2184} 2185 2186// function buscarProdutosSidebarPesquisa() { 2187// const termo = document.getElementById("pesquisa-sidebar-input")?.value; 2188// if (!termo) { 2189// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2190// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2191// ""; 2192// return; 2193// } 2194 2195// load("on", document.getElementById("pesquisa-sidebar")); 2196 2197// const produtos = [ 2198// { 2199// imagem_produto: "produtos/1001815_200x200.webp", 2200// cd_produto: "1001815", 2201// categoria: "ZERO AÃÃCAR", 2202// parceiro: "", 2203// avaliacao: null,
2204// tags_nomes: 2205// ";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;", 2206// marca: "PINATI", 2207// nm_produto: "Doce Pinati Sweet Mini Cocadinha Queimada Sem Açúcar 14g", 2208// tags_agrupamentos: 2209// ";ate-10-reais;td-consumidor-50;desc-semana-consumidor;", 2210// is_servico: false, 2211// fabricante: "SUPER SAUDE NUTRICIONAL", 2212// filtros: { 2213// selo: "", 2214// marca: "PINATI", 2215// tipo: "", 2216// categoria: "ZERO AÃÃCAR", 2217// fabricante: "SUPER SAUDE NUTRICIONAL", 2218// avaliacao: null, 2219// palavras: {}, 2220// }, 2221// url_produto: "doce-pinati-sweet-mini-cocadinha-queimada-sem-acucar-14g", 2222// is_disponivel: true, 2223// }, 2224// { 2225// imagem_produto: "produtos/1002811_200x200.webp", 2226// cd_produto: "1002811", 2227// categoria: "REFRIGERANTE", 2228// parceiro: "", 2229// avaliacao: null, 2230// tags_nomes: 2231// ";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;", 2232// marca: "COCA COLA", 2233// nm_produto: "Refrigerante Coca-Cola Lata 310ml", 2234// tags_agrupamentos: ";mosaico-refrigerante;mosaico-bebidas;", 2235// is_servico: false, 2236// fabricante: "COCA COLA", 2237// filtros: { 2238// selo: "", 2239// marca: "COCA COLA", 2240// tipo: "", 2241// categoria: "REFRIGERANTE", 2242// fabricante: "COCA COLA", 2243// avaliacao: null, 2244// palavras: {}, 2245// }, 2246// url_produto: "refrigerante-coca-cola-lata-310ml", 2247// is_disponivel: true, 2248// }, 2249// { 2250// imagem_produto: "produtos/69625_5tL4aHL.jpg", 2251// cd_produto: "69625", 2252// categoria: "REFRIGERANTE", 2253// parceiro: "", 2254// avaliacao: null, 2255// tags_nomes: 2256// ";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;", 2257// marca: "COCA COLA", 2258// nm_produto: "Refrigerante Coca-Cola Lata 350ml", 2259// tags_agrupamentos: 2260// ";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;", 2261// is_servico: false, 2262// fabricante: "COCA COLA", 2263// filtros: { 2264// selo: "produtos/selos/GA - SELO 5_modelo 1.png", 2265// marca: "COCA COLA", 2266// tipo: "", 2267// categoria: "REFRIGERANTE", 2268// fabricante: "COCA COLA", 2269// avaliacao: null, 2270// palavras: {}, 2271// }, 2272// url_produto: "refrigerante-coca-cola-lata-350ml", 2273// is_disponivel: true, 2274// }, 2275// ]; 2276 2277// setTimeout(() => { 2278// construirTemplateProdutosSidebarPesquisa(produtos); 2279// load("off", document.getElementById("pesquisa-sidebar")); 2280// // ViewItemList + SelectItem base para resultados simulados 2281// try { 2282// if (typeof filtroParaDadosAnaliticos === 'function') { 2283// const items = (produtos || []).map((p, i) => ({ 2284// id: String(p?.cd_produto || ''), 2285// item_name: p?.nm_produto || '', 2286// price: +p?.valor || 0, 2287// quantidade: 1, 2288// index: i + 1, 2289// item_list_name: 'Sidebar Pesquisa', 2290// item_list_id: 'sidebar_pesquisa' 2291// })).filter(x => x.id);
2292// if (items.length) filtroParaDadosAnaliticos('ViewItemList', { items }); 2293// } 2294// } catch(_){} 2295// }, 1500); 2296// } 2297 2298// function construirTemplateProdutosSidebarPesquisa(produtos) { 2299// if (!produtos?.length) return; 2300// let html = ` 2301// <p class="text-sm font-bold text-[#444]">Resultados</p> 2302// `; 2303// for (const produto of produtos) { 2304// html += ` 2305// <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="/${ 2306// produto?.url_produto 2307// }"> 2308// <div class="flex items-center gap-x-4"> 2309// <img src="/media/${ 2310// produto?.imagem_produto 2311// }" 2312// class="p-1 border-1 rounded-lg border-[#E6E6E6]" loading="lazy" alt="${ 2313// produto?.cd_produto 2314// }" width="50" height="50"> 2315// <p class="text-xs text-[#444] line-clamp-2">${produto?.nm_produto}</p> 2316// </div> 2317// <p class="text-xs text-[#3F84C7] font-bold flex">${formatFloat( 2318// produto?.preco || 0, 2319// { currency: true } 2320// )}</p> 2321// </a> 2322// `; 2323// } 2324// if (document.getElementById("template-produtos-sidebar-pesquisa")) 2325// document.getElementById("template-produtos-sidebar-pesquisa").innerHTML = 2326// html; 2327// } 2328 2329// --- pesquisa --- 2330 2331const HEADER_SEARCH_CATALOGO = [ 2332 // { 2333 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2334 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2335 // produto__imagem: "produtos/55520_200x200.jpg", 2336 // valor_ini: 9.99, 2337 // valor: 7.99, 2338 // }, 2339 // { 2340 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2341 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2342 // produto__imagem: "produtos/55520_200x200.jpg", 2343 // valor_ini: 9.99, 2344 // valor: 7.99, 2345 // }, 2346 // { 2347 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2348 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2349 // produto__imagem: "produtos/55520_200x200.jpg", 2350 // valor_ini: 9.99, 2351 // valor: 7.99, 2352 // }, 2353 // { 2354 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2355 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2356 // produto__imagem: "produtos/55520_200x200.jpg", 2357 // valor_ini: 9.99, 2358 // valor: 7.99, 2359 // }, 2360 // { 2361 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2362 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2363 // produto__imagem: "produtos/55520_200x200.jpg", 2364 // valor_ini: 9.99, 2365 // valor: 7.99, 2366 // }, 2367 // { 2368 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2369 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2370 // produto__imagem: "produtos/55520_200x200.jpg", 2371 // valor_ini: 9.99, 2372 // valor: 7.99, 2373 // }, 2374 // { 2375 // produto__nm_ecommerce: "Algodão Multiuso 50g - Marca Alfa", 2376 // produto__nm_url_ecommerce: "algodao-multiuso-50g-marca-alfa", 2377 // produto__imagem: "produtos/55520_200x200.jpg", 2378 // valor_ini: 9.99, 2379 // valor: 7.99, 2380 // }, 2381 // { 2382 // produto__nm_ecommerce: "Algodão Bola 50 Unidades - Marca Beta", 2383 // produto__nm_url_ecommerce: "algodao-bola-50-un-beta", 2384 // produto__imagem: "produtos/55520_200x200.jpg", 2385 // valor_ini: 12.9, 2386 // valor: 9.9, 2387 // }, 2388 // { 2389 // produto__nm_ecommerce: "Discos de Algodão 70 Unidades - Marca Gama", 2390 // produto__nm_url_ecommerce: "discos-algodao-70un-gama", 2391 // produto__imagem: "produtos/55520_200x200.jpg", 2392 // valor_ini: 14.9, 2393 // valor: 14.9, 2394 // }, 2395 // { 2396 // produto__nm_ecommerce: "Hidratante Corporal 400ml", 2397 // produto__nm_url_ecommerce: "hidratante-corporal-400ml", 2398 // produto__imagem: "produtos/55520_200x200.jpg", 2399 // valor_ini: 29.9, 2400 // valor: 19.9, 2401 // }, 2402 // { 2403 // produto__nm_ecommerce: "Shampoo Nutritivo 300ml", 2404 // produto__nm_url_ecommerce: "shampoo-nutritivo-300ml", 2405 // produto__imagem: "produtos/55520_200x200.jpg",
2406 // valor_ini: 24.9, 2407 // valor: 22.9, 2408 // }, 2409]; 2410 2411window.HeaderSearchCore = { 2412 buscarSugestoes, 2413 buscarProdutosPorTermo, 2414 getSearchData, 2415 getHistoricoPesquisa, 2416 setHistoricoPesquisa, 2417 addHistoricoPesquisa, 2418 removeHistoricoPesquisa, 2419 clearHistoricoPesquisa, 2420 escapeHtml, 2421 normalizeText, 2422 uniqueArray, 2423 templateLoading, 2424}; 2425 2426async function buscarSugestoes(termo) { 2427 termo = (termo || "").trim(); 2428 if (!termo) return []; 2429 2430 const termoNorm = normalizeText(termo); 2431 const lenSemEspaco = termoNorm.replace(/\s+/g, "").length; 2432 2433 //const base = ['shampoo', 'condicionador', 'cerave', 'vitamina c', 'fralda', 'hidratante', 'algodão']; 2434 const base = []; 2435 const baseFiltrada = 2436 lenSemEspaco >= 3 2437 ? base.filter((t) => normalizeText(t).includes(termoNorm)) 2438 : []; 2439 2440 const tokensProdutos = []; 2441 try { 2442 for (const p of HEADER_SEARCH_CATALOGO || []) { 2443 const nome = p?.nm_produto || ""; 2444 const partes = String(nome) 2445 .split(/[^A-Za-zÃ-ÃÃ-öø-ÿ0-9]+/) 2446 .filter(Boolean); 2447 for (const tk of partes) { 2448 const tkNorm = normalizeText(tk); 2449 if (tkNorm.length >= 3 && tkNorm.includes(termoNorm)) { 2450 tokensProdutos.push(tk.toLowerCase()); 2451 } 2452 } 2453 } 2454 } catch {} 2455 2456 const combinadas = uniqueArray([...baseFiltrada, ...tokensProdutos]); 2457 return combinadas.slice(0, 5); 2458} 2459 2460const _cachePesquisa = new Map(); 2461let _abortPesquisaCtrl = null; 2462 2463/** 2464 * Busca sugestões de produtos no backend, reaproveitando o POST da pesquisa. 2465 * Retorna até 6 itens ou []. 2466 */ 2467async function buscarProdutosPorTermo(termo) { 2468 termo = (termo || "").trim(); 2469 if (!termo) return []; 2470 2471 const termoNorm = normalizeText(termo); 2472 if (termoNorm.length < 3) return []; 2473 2474 if (_cachePesquisa.has(termo)) { 2475 return _cachePesquisa.get(termo); 2476 } 2477 2478 // Cancela a requisição anterior 2479 if (_abortPesquisaCtrl) { 2480 _abortPesquisaCtrl.abort(); 2481 } 2482 _abortPesquisaCtrl = new AbortController(); 2483 2484 const body = new URLSearchParams(); 2485 body.set("pesquisa", termo); 2486 const csrf = window.CSRF_TOKEN(); 2487 if (csrf) { 2488 body.set("csrfmiddlewaretoken", csrf); 2489 } 2490 2491 try { 2492 const resp = await fetch("/sugestoes", { 2493 method: "POST", 2494 headers: { 2495 "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", 2496 "X-CSRFToken": csrf || "", 2497 }, 2498 body, 2499 credentials: "same-origin", 2500 signal: _abortPesquisaCtrl.signal, 2501 }); 2502 2503 if (!resp.ok) { 2504 return []; 2505 } 2506 2507 const data = await resp.json(); 2508 const items = Array.isArray(data?.dados.produtos) 2509 ? data.dados.produtos 2510 : []; 2511 2512 const resultados = items.slice(0, 6); 2513 2514 _cachePesquisa.set(termo, resultados); 2515 2516 return resultados; 2517 } catch (err) { 2518 if (err?.name === "AbortError") return []; 2519 console.error("Erro ao buscar sugestões:", err); 2520 return []; 2521 } 2522} 2523 2524let precosTimer = null; 2525 2526// Pega dados de busca em paralelo (reúso no desk e mobile) 2527async function getSearchData(termo) { 2528 termo = (termo || "").trim(); 2529 if (!termo || termo.length < 3) return { termos: [], produtos: [] }; 2530 const [termos, produtos] = await Promise.all([ 2531 buscarSugestoes(termo), 2532 buscarProdutosPorTermo(termo), 2533 ]); 2534 // dispara 1 vez, substituindo o anterior 2535 clearTimeout(precosTimer); 2536 precosTimer = setTimeout(() => { 2537 try { carregarPrecos(); } catch(_){} 2538 }, 300); 2539 2540 // Search analytics 2541 try { if (typeof filtroParaDadosAnaliticos === 'function') filtroParaDadosAnaliticos('Search', { search_term: termo }); } catch(_){} 2542 2543 return { termos, produtos }; 2544} 2545 2546function getHistoricoPesquisa() { 2547 try { 2548 const raw = localStorage.getItem("header_search_history"); 2549 const lista = raw ? JSON.parse(raw) : []; 2550 return Array.isArray(lista) ? lista : []; 2551 } catch { 2552 return []; 2553 } 2554} 2555function setHistoricoPesquisa(lista) { 2556 try { 2557 localStorage.setItem( 2558 "header_search_history", 2559 JSON.stringify(lista.slice(0, 8)) 2560 ); 2561 } catch {} 2562} 2563function addHistoricoPesquisa(termo) { 2564 termo = (termo || "").trim(); 2565 if (!termo) return; 2566 const lista = getHistoricoPesquisa().filter( 2567 (t) => t.toLowerCase() !== termo.toLowerCase() 2568 ); 2569 lista.unshift(termo); 2570 setHistoricoPesquisa(lista); 2571} 2572function removeHistoricoPesquisa(termo) { 2573 const lista = getHistoricoPesquisa().filter( 2574 (t) => t.toLowerCase() !== (termo || "").toLowerCase() 2575 ); 2576 setHistoricoPesquisa(lista); 2577 atualizarDropdownComHistoricoESugestoes(); 2578} 2579function clearHistoricoPesquisa() { 2580 setHistoricoPesquisa([]); 2581 atualizarDropdownComHistoricoESugestoes(); 2582} 2583 2584function escapeHtml(str) { 2585 return String(str) 2586 .replace(/&/g, "&") 2587 .replace(/</g, "<") 2588 .replace(/>/g, ">") 2589 .replace(/"/g, """) 2590 .replace(/'/g, "'"); 2591} 2592 2593function normalizeText(str) { 2594 return String(str || "") 2595 .toLowerCase() 2596 .normalize("NFD") 2597 .replace(/[\u0300-\u036f]/g, ""); 2598} 2599 2600function uniqueArray(arr) { 2601 const set = new Set(); 2602 const out = []; 2603 for (const v of arr) {
2604 const key = normalizeText(v); 2605 if (!set.has(key)) { 2606 set.add(key); 2607 out.push(v); 2608 } 2609 } 2610 return out; 2611} 2612 2613function templateLoading() { 2614 return `<div class="p-3 text-[12px] text-[#666]">Carregando...</div>`; 2615} 2616 2617// --- fim pesquisa --- 2618 2619 2620 2621 2622 2623// --- Avise-me --- 2624(function () { 2625 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"/>'; 2626 2627 let timer_avise_me = null; 2628 let avise_me_pos_login_agendado = false; 2629 2630 const dict_cores_mensagem = { 2631 success: 'bg-[#0C670F] border-[#9FFCAC] text-white', 2632 error: 'bg-[#B42318] border-[#FECDCA] text-white', 2633 warning: 'bg-[#B54708] border-[#FEDF89] text-white', 2634 }; 2635 2636 function removerMensagensInlineAviseMe(btn) { 2637 const bloco = btn?.closest('[data-target="comprar-produto"]') 2638 || btn?.closest('[data-notificar-disponibilidade="true"]'); 2639 bloco?.querySelectorAll('[data-target="mensagem-avise-me"]').forEach((el) => el.remove()); 2640 } 2641 2642 function exibirMensagemAviseMe(tipo, msg) { 2643 if (window.toastr?.[tipo]) { 2644 window.toastr[tipo](msg); 2645 } else { 2646 exibirToastAviseMe(tipo, msg); 2647 } 2648 } 2649 2650 function garantirEstilosAviseMe() { 2651 let style = document.getElementById('avise-me-estilos'); 2652 if (!style) { 2653 style = document.createElement('style'); 2654 style.id = 'avise-me-estilos'; 2655 document.head.appendChild(style); 2656 } 2657 2658 style.textContent = ` 2659 [data-target="btn-notificar-disponibilidade"] { 2660 background-color: var(--dark-gray, #838383) !important; 2661 border-color: var(--dark-gray, #838383) !important; 2662 color: #fff !important; 2663 transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease; 2664 opacity: 0.6; 2665 } 2666 [data-target="btn-notificar-disponibilidade"] span, 2667 [data-target="btn-notificar-disponibilidade"] svg { 2668 color: inherit !important; 2669 } 2670 [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado):hover, 2671 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 2672 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 2673 [data-target="pesquisa-produto-linear"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 2674 [data-target="pesquisa-produto-colunar"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado), 2675 [data-target="carrossel-produto"]:hover [data-target="btn-notificar-disponibilidade"]:not(.is-avise-me-solicitado) { 2676 background-color: var(--c-btn-padrao-comprar-bg-hover, var(--c-btn-padrao-comprar-bg)) !important; 2677 border-color: var(--c-btn-padrao-comprar-border) !important; 2678 color: var(--c-btn-padrao-comprar-text-hover, var(--c-btn-padrao-comprar-text, #fff)) !important; 2679 opacity: 1; 2680 } 2681 [data-target="pesquisa-header"] [data-target="comprar-produto"], 2682 [data-target="pesquisa-header-mobile"] [data-target="comprar-produto"], 2683 [data-target="pesquisa-header"] > div, 2684 [data-target="pesquisa-header-mobile"] > div { 2685 overflow: visible; 2686 } 2687 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto, 2688 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto { 2689 width: 2.5rem !important; 2690 max-width: 2.5rem !important; 2691 overflow: hidden; 2692 } 2693 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto .avise-me-texto, 2694 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto .avise-me-texto { 2695 display: none; 2696 } 2697 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado), 2698 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado), 2699 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover, 2700 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover { 2701 width: auto !important; 2702 max-width: none !important; 2703 padding-inline: 0.5rem !important; 2704 } 2705 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) .avise-me-texto, 2706 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) .avise-me-texto, 2707 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover .avise-me-texto, 2708 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover .avise-me-texto { 2709 display: inline; 2710 } 2711 [data-target="pesquisa-header"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) svg, 2712 [data-target="pesquisa-header-mobile"]:hover [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado) svg, 2713 [data-target="pesquisa-header"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover svg, 2714 [data-target="pesquisa-header-mobile"] [data-target="btn-notificar-disponibilidade"].is-avise-me-compacto:not(.is-avise-me-solicitado):hover svg { 2715 display: none; 2716 } 2717 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado, 2718 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado span, 2719 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado svg { 2720 cursor: default !important; 2721 } 2722 [data-target="btn-notificar-disponibilidade"].is-avise-me-solicitado { 2723 background-color: var(--dark-gray, #838383) !important; 2724 border-color: var(--dark-gray, #838383) !important; 2725 opacity: 0.6; 2726 } 2727 [data-target="comprar-produto"].is-avise-me-solicitado-bloco, 2728 [data-target="comprar-produto"].is-avise-me-solicitado-bloco * { 2729 cursor: default !important; 2730 } 2731 `; 2732 } 2733 2734 function aplicarGrayscaleIndisponivelAviseMe(produto_el) { 2735 if (!produto_el) return; 2736 2737 const target = produto_el.dataset?.target || ''; 2738 if (target === 'pagina-produto') return; 2739 2740 produto_el.style.filter = ''; 2741 produto_el.querySelectorAll('img').forEach(function (img) { 2742 img.style.filter = 'grayscale(1)'; 2743 });
2744 } 2745 2746 function garantirContainerMensagemAviseMe() { 2747 let container = document.getElementById('avise-me-mensagem-container'); 2748 if (container) return container; 2749 2750 container = document.createElement('div'); 2751 container.id = 'avise-me-mensagem-container'; 2752 container.className = 'fixed top-4 right-4 z-[9999] flex flex-col gap-2 w-[min(100%,22rem)] pointer-events-none px-4'; 2753 container.setAttribute('aria-live', 'polite'); 2754 container.setAttribute('aria-atomic', 'true'); 2755 document.body.appendChild(container); 2756 return container; 2757 } 2758 2759 function exibirToastAviseMe(tipo, msg) { 2760 const container = garantirContainerMensagemAviseMe(); 2761 const toast = document.createElement('div'); 2762 const classe_cor = dict_cores_mensagem[tipo] || dict_cores_mensagem.success; 2763 2764 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}`; 2765 toast.setAttribute('role', 'status'); 2766 toast.textContent = msg; 2767 container.appendChild(toast); 2768 2769 window.setTimeout(function () { 2770 toast.classList.replace('opacity-100', 'opacity-0'); 2771 window.setTimeout(function () { 2772 toast.remove(); 2773 if (!container.childElementCount) container.remove(); 2774 }, 300); 2775 }, 4500); 2776 } 2777 2778 function isContextoCompactoAviseMe(btn) { 2779 const produto_el = btn?.closest?.('[data-notificar-disponibilidade="true"]'); 2780 const target = produto_el?.dataset?.target || ''; 2781 return target === 'pesquisa-header' || target === 'pesquisa-header-mobile'; 2782 } 2783 2784 function compactarLayoutAviseMe(comprar_el, produto_el) { 2785 const target = produto_el?.dataset?.target || ''; 2786 if (target === 'pesquisa-header' || target === 'pesquisa-header-mobile') return; 2787 } 2788 2789 function desabilitarBotaoAviseMeSolicitado(btn) { 2790 if (!btn) return; 2791 2792 btn.removeAttribute('disabled'); 2793 btn.setAttribute('aria-disabled', 'true'); 2794 btn.tabIndex = -1; 2795 btn.classList.remove('cursor-pointer'); 2796 btn.classList.add('is-avise-me-solicitado', 'cursor-default'); 2797 2798 const comprar_el = btn.closest('[data-target="comprar-produto"]'); 2799 if (comprar_el) comprar_el.classList.add('is-avise-me-solicitado-bloco'); 2800 } 2801 2802 const CHAVE_AVISE_ME_PENDENTE = 'avise_me_pendente'; 2803 2804 function obterUrlRetornoAviseMe() { 2805 return window.location.pathname + window.location.search + window.location.hash; 2806 } 2807 2808 function montarUrlRetornoAviseMe(produto_id) { 2809 const url = new URL(window.location.href); 2810 url.searchParams.delete('avise_me_produto'); 2811 url.searchParams.set('avise_me_produto', String(produto_id)); 2812 return url.pathname + url.search + url.hash; 2813 } 2814 2815 function salvarPendenciaAviseMe(produto_id) { 2816 try { 2817 sessionStorage.setItem(CHAVE_AVISE_ME_PENDENTE, JSON.stringify({ 2818 produto_id: String(produto_id), 2819 url: obterUrlRetornoAviseMe(), 2820 })); 2821 } catch (_) {} 2822 } 2823 2824 function limparPendenciaAviseMe() { 2825 try { 2826 sessionStorage.removeItem(CHAVE_AVISE_ME_PENDENTE); 2827 } catch (_) {} 2828 2829 try { 2830 const url = new URL(window.location.href); 2831 if (!url.searchParams.has('avise_me_produto')) return; 2832 url.searchParams.delete('avise_me_produto'); 2833 window.history.replaceState(null, '', url.pathname + url.search + url.hash); 2834 } catch (_) {} 2835 } 2836 2837 function obterProdutoIdAviseMePendente() { 2838 try { 2839 const da_url = new URLSearchParams(window.location.search).get('avise_me_produto'); 2840 if (da_url) return da_url; 2841 } catch (_) {} 2842 2843 try { 2844 const pendente = sessionStorage.getItem(CHAVE_AVISE_ME_PENDENTE); 2845 if (pendente) return JSON.parse(pendente).produto_id || null; 2846 } catch (_) {} 2847 2848 return null; 2849 } 2850 2851 function isUsuarioAutenticadoAviseMe() { 2852 if (typeof window.IS_AUTENTICADO === 'function') { 2853 return window.IS_AUTENTICADO(); 2854 } 2855 if (typeof window.APP_CONTEXT === 'function') { 2856 return !!window.APP_CONTEXT()?.auth?.is_autenticado; 2857 } 2858 return false; 2859 } 2860 2861 function redirecionarLoginAviseMe(produto_id) { 2862 if (produto_id) { 2863 salvarPendenciaAviseMe(produto_id); 2864 } 2865 exibirMensagemAviseMe('error', 'Faça login para ser avisado quando o produto estiver disponÃvel.'); 2866 window.setTimeout(function () { 2867 const url_retorno = produto_id 2868 ? montarUrlRetornoAviseMe(produto_id) 2869 : obterUrlRetornoAviseMe(); 2870 window.location.href = '/login?next=' + encodeURIComponent(url_retorno); 2871 }, 1200); 2872 } 2873 2874 function redirecionarSeAviseMePendentePosLogin() {
2875 if (!isUsuarioAutenticadoAviseMe()) return false; 2876 if (obterProdutoIdAviseMePendente()) return false; 2877 2878 let pendente = null; 2879 try { 2880 const raw = sessionStorage.getItem(CHAVE_AVISE_ME_PENDENTE); 2881 if (raw) pendente = JSON.parse(raw); 2882 } catch (_) {} 2883 2884 if (!pendente?.produto_id) return false; 2885 2886 const url_alvo = new URL(pendente.url || '/', window.location.origin); 2887 url_alvo.searchParams.set('avise_me_produto', String(pendente.produto_id)); 2888 const destino = url_alvo.pathname + url_alvo.search + url_alvo.hash; 2889 const atual = obterUrlRetornoAviseMe(); 2890 2891 if (destino !== atual) { 2892 window.location.href = destino; 2893 return true; 2894 } 2895 2896 return false; 2897 } 2898 2899 function encontrarBotaoAviseMeProduto(produto_id) { 2900 return document.querySelector( 2901 `[data-notificar-disponibilidade="true"][data-cd-produto="${produto_id}"] [data-target="btn-notificar-disponibilidade"]` 2902 ); 2903 } 2904 2905 function marcarBotaoAviseMeSolicitado(btn, produto_id) { 2906 const btn_alvo = btn || encontrarBotaoAviseMeProduto(produto_id); 2907 if (!btn_alvo) return; 2908 2909 removerMensagensInlineAviseMe(btn_alvo); 2910 2911 const comprar_el = btn_alvo.closest('[data-target="comprar-produto"]'); 2912 if (comprar_el) comprar_el.dataset.aviseMeSolicitado = 'true'; 2913 2914 const is_compacto = isContextoCompactoAviseMe(btn_alvo); 2915 2916 btn_alvo.innerHTML = ` 2917 <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"> 2918 ${ICONE_SINO} 2919 </svg> 2920 ${is_compacto 2921 ? '<span class="avise-me-texto font-bold text-[13px] text-white whitespace-nowrap">Avise-me solicitado</span>' 2922 : '<span class="font-bold text-[13px] text-white">Avise-me solicitado</span>'} 2923 `; 2924 if (is_compacto) btn_alvo.classList.add('is-avise-me-compacto'); 2925 desabilitarBotaoAviseMeSolicitado(btn_alvo); 2926 } 2927 2928 function agendarProcessarAviseMePosLogin() { 2929 const produto_id = obterProdutoIdAviseMePendente(); 2930 if (!produto_id || !isUsuarioAutenticadoAviseMe()) return; 2931 if (avise_me_pos_login_agendado) return; 2932 avise_me_pos_login_agendado = true; 2933 2934 let tentativas = 0; 2935 const max_tentativas = 30; 2936 2937 const tentar = function () { 2938 tentativas += 1; 2939 const btn = encontrarBotaoAviseMeProduto(produto_id); 2940 if (btn && btn.getAttribute('aria-disabled') !== 'true') { 2941 notificarDisponibilidadeProduto(produto_id, btn, true); 2942 return; 2943 } 2944 if (tentativas < max_tentativas) { 2945 window.setTimeout(tentar, 200); 2946 return; 2947 } 2948 notificarDisponibilidadeProduto(produto_id, null, true); 2949 }; 2950 2951 window.setTimeout(tentar, 400); 2952 } 2953 2954 async function notificarDisponibilidadeProduto(produto_id, btn, is_pos_login) { 2955 if (!produto_id) return; 2956 2957 if (btn?.getAttribute('aria-disabled') === 'true') return; 2958 2959 if (!isUsuarioAutenticadoAviseMe()) { 2960 redirecionarLoginAviseMe(produto_id); 2961 return; 2962 } 2963 2964 try { 2965 const request = await fetch('/produto/notificar-disponibilidade', { 2966 method: 'POST', 2967 headers: { 2968 'Content-Type': 'application/json', 2969 'X-CSRFToken': window.CSRF_TOKEN?.() || '', 2970 }, 2971 body: JSON.stringify({ produto_id: produto_id }), 2972 }); 2973 2974 const response = await request.json(); 2975 2976 if (!request.ok || !response?.status) { 2977 const descricao_erro = response?.descricao || ''; 2978 if ( 2979 request.status === 401 2980 || request.status === 403 2981 || /identificado|login|autentic/i.test(descricao_erro) 2982 ) { 2983 redirecionarLoginAviseMe(produto_id); 2984 return; 2985 } 2986 exibirMensagemAviseMe('error', descricao_erro || 'Não foi possÃvel cadastrar a notificação. Tente novamente.'); 2987 return; 2988 } 2989 2990 const msg_toast = response?.descricao || 'Notificação cadastrada com sucesso'; 2991 2992 marcarBotaoAviseMeSolicitado(btn, produto_id); 2993 2994 if (is_pos_login || obterProdutoIdAviseMePendente()) { 2995 limparPendenciaAviseMe(); 2996 } 2997 2998 exibirMensagemAviseMe('success', msg_toast); 2999 } catch (e) { 3000 exibirMensagemAviseMe('error', 'Não foi possÃvel cadastrar a notificação. Tente novamente.'); 3001 } 3002 } 3003 3004 function montarIconeSinoAviseMe(tamanho) { 3005 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>`; 3006 } 3007 3008 function montarHtmlBotaoAviseMe(produto_id, produto_el) { 3009 const target = produto_el?.dataset?.target || ''; 3010 const is_compacto = target === 'pesquisa-header' || target === 'pesquisa-header-mobile'; 3011 const is_pagina_produto = target === 'pagina-produto'; 3012 const classe_botao = is_pagina_produto ? 'rounded-[var(--r-btn-comprar)]' : 'rounded-[var(--r-cta-radius)]'; 3013 const tamanho_icone = 17.5; 3014 const texto_avise_me = is_compacto 3015 ? '<span class="avise-me-texto font-bold text-[13px] text-white whitespace-nowrap"> IndisponÃvel, Avise-me </span>' 3016 : '<span class="font-bold text-[13px] text-white">Avise-me</span>'; 3017 const classe_tamanho = is_compacto 3018 ? 'relative w-10 is-avise-me-compacto h-[var(--h-btn-comprar)] p-1 shrink-0 gap-x-1.5' 3019 : 'w-full h-[var(--h-btn-comprar)] p-0.5'; 3020 3021 const html_botao = ` 3022 <button type="button" 3023 class="flex flex-row justify-center items-center cursor-pointer border-1 3024 ${classe_tamanho} ${classe_botao}" 3025 data-target="btn-notificar-disponibilidade" 3026 data-cd-produto="${produto_id}" 3027 ${is_compacto ? 'aria-label=" IndisponÃvel, Avise-me "' : ''} 3028 onclick="event.preventDefault(); event.stopPropagation(); window.notificarDisponibilidadeProduto('${produto_id}', this); return false;"> 3029 ${is_compacto ? texto_avise_me : ''} 3030 ${montarIconeSinoAviseMe(tamanho_icone)} 3031 ${is_compacto ? '' : texto_avise_me} 3032 </button> 3033 `; 3034 3035 if (is_pagina_produto) { 3036 return ` 3037 <div class="flex flex-col gap-2 w-full" data-target="bloco-avise-me">
3038 <span class="font-bold text-red-600 bg-white whitespace-nowrap text-md text-center p-0.5">Produto indisponÃvel</span> 3039 ${html_botao} 3040 </div> 3041 `; 3042 } 3043 3044 return html_botao; 3045 } 3046 3047 function isConteudoIndisponivelAviseMe(comprar_el) { 3048 if (!comprar_el || comprar_el.dataset.aviseMeSolicitado === 'true') { 3049 return false; 3050 } 3051 if (comprar_el.querySelector('[data-target="btn-notificar-disponibilidade"]')) { 3052 return false; 3053 } 3054 return /indispon/i.test(comprar_el.textContent || ''); 3055 } 3056 3057 function aplicarBotoesAviseMe() { 3058 document.querySelectorAll('[data-notificar-disponibilidade="true"]').forEach((produto_el) => { 3059 const produto_id = produto_el.getAttribute('data-cd-produto'); 3060 if (!produto_id) return; 3061 3062 produto_el.querySelectorAll('[data-target="comprar-produto"]').forEach((comprar_el) => { 3063 if (comprar_el.dataset.aviseMeSolicitado === 'true') return; 3064 if (!isConteudoIndisponivelAviseMe(comprar_el)) return; 3065 compactarLayoutAviseMe(comprar_el, produto_el); 3066 comprar_el.innerHTML = montarHtmlBotaoAviseMe(produto_id, produto_el); 3067 aplicarGrayscaleIndisponivelAviseMe(produto_el); 3068 }); 3069 }); 3070 } 3071 3072 function observarComprarProdutoAviseMe(comprar_el) { 3073 if (!comprar_el || comprar_el.dataset.aviseMeObservado === 'true') return; 3074 comprar_el.dataset.aviseMeObservado = 'true'; 3075 new MutationObserver(agendarAplicarBotoesAviseMe).observe(comprar_el, { 3076 childList: true, 3077 subtree: true, 3078 characterData: true, 3079 }); 3080 } 3081 3082 function iniciarObservadoresAviseMe() { 3083 document.querySelectorAll('[data-notificar-disponibilidade="true"] [data-target="comprar-produto"]').forEach(observarComprarProdutoAviseMe); 3084 } 3085 3086 function agendarAplicarBotoesAviseMe() { 3087 clearTimeout(timer_avise_me); 3088 timer_avise_me = setTimeout(function () { 3089 aplicarBotoesAviseMe(); 3090 iniciarObservadoresAviseMe(); 3091 }, 80); 3092 } 3093 3094 function iniciarAviseMe() { 3095 garantirEstilosAviseMe(); 3096 aplicarBotoesAviseMe(); 3097 iniciarObservadoresAviseMe(); 3098 } 3099 3100 window.notificarDisponibilidadeProduto = notificarDisponibilidadeProduto; 3101 window.iniciarAviseMe = iniciarAviseMe; 3102 3103 window.addEventListener('load', function () { 3104 if (redirecionarSeAviseMePendentePosLogin()) return; 3105 3106 iniciarAviseMe(); 3107 new MutationObserver(agendarAplicarBotoesAviseMe).observe(document.body, { 3108 childList: true, 3109 subtree: true, 3110 }); 3111 agendarProcessarAviseMePosLogin(); 3112 }); 3113})(); 3114// fim avise-me 3115 3116 3117 3118</script>
vendor: 159 bytes, lines 3118-3127
3118 3119 3120 3121 3122 3123 3124 3125 3126 3127<script src="/cdn-cgi/scripts/7d0fa10a/cloudflare-static/rocket-loader.min.js" data-cf-settings="95b6b4c15eb4e50ce46ccbcd-|49" defer></script>
3127<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>
3127 3128</body> 3129 3130</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.