{
  "version": 3,
  "sources": ["/cdn/shop/t/4/assets/custom-ofertas-grid.js?nocache=11918"],
  "sourcesContent": ["/**\n * MOXIE \u2014 <ofertas-grid-component>\n *\n * P\u00E1gina /pages/descuentos: lista los productos con el descuento B2B que la API de Dyna\n * devuelve para el NIT del cliente (hoy hardcodeado \u2014 ver sections/custom_ofertas_grid.liquid).\n *\n * Flujo:\n *   1. Fetch a la API de Dyna -> [{ producto_codigo, producto_unidad, descuento }, ...]\n *   2. producto_codigo == variant.sku (confirmado con el negocio) -> se arma un Map sku -> % descuento\n *   3. Como Liquid no puede recorrer eficientemente los ~13k productos de la tienda para\n *      encontrar esos SKUs, se reutiliza el buscador nativo de Shopify: se pide (por lotes,\n *      v\u00EDa Section Rendering API) la secci\u00F3n standalone sections/custom_ofertas_results.liquid\n *      contra /search?type=product&q=variants.sku:\"X\" OR variants.sku:\"Y\"... \u2014 mismo mecanismo\n *      que ya usa assets/predictive-search.js con @theme/section-renderer.\n *   4. Cada card ya trae data-sku/data-price-cents/data-product-type/data-mox-brand/\n *      data-mox-subcategory/data-mox-line (snippets/custom_product_card.liquid) para poder\n *      inyectar el precio con descuento, filtrar por categor\u00EDa/marca/subcategor\u00EDa/l\u00EDnea/precio\n *      y ordenar sin volver a tocar el backend.\n *   5. Filtros (categor\u00EDa, marca, subcategor\u00EDa, l\u00EDnea, rango de precio \u2014 todos AND entre s\u00ED) +\n *      orden + paginaci\u00F3n (24/p\u00E1gina) se resuelven client-side sobre el arreglo ya resuelto\n *      (this._allItems) \u2014 no vuelven a pegarle a Shopify ni a Dyna.\n *\n * MOXIE: un solo descuento por producto (el mayor entre todas sus producto_unidad del mock) \u2014\n * no hay todav\u00EDa un mapeo confirmado producto_unidad -> tipo_empaque (bundle picker) para\n * repartir el descuento por presentaci\u00F3n. Ver docs/viabilidad/b2b-pricing-viabilidad.md.\n */\nimport { sectionRenderer } from '@theme/section-renderer';\n\n// MOXIE: va por el App Proxy de la app Dyna; el Bearer vive en el backend, no en el theme.\nconst DYNA_API_URL = `${window.DynaPriceCore.PROXY_BASE}mis-descuentos`;\n\n// MOXIE: secci\u00F3n standalone (no est\u00E1 en ning\u00FAn template) creada solo para este cruce de SKUs.\nconst SEARCH_SECTION_ID = 'custom_ofertas_results';\n\n// MOXIE: SKUs por query de b\u00FAsqueda. Conservador para no pasarse de una URL razonable\n// (~20 t\u00E9rminos \"variants.sku:\"X\" OR \" es un margen seguro). Verificar en vivo si Shopify\n// soporta m\u00E1s t\u00E9rminos por query de forma confiable antes de subir este n\u00FAmero.\nconst SKU_CHUNK_SIZE = 20;\n\n// MOXIE: mismo string y espera que custom-product-discount-flag.js.\nconst DYNA_PRICING_READY_EVENT = 'dyna-pricing:ready';\nconst DYNA_PRICING_READY_TIMEOUT_MS = 8000;\n\n// MOXIE: productos por p\u00E1gina del paginador (pedido expl\u00EDcito).\nconst PAGE_SIZE = 24;\n\n// MOXIE: tope de p\u00E1ginas al paginar mis-descuentos (s\u00ED pagina, confirmado con curl).\nconst DYNA_PAGE_LIMIT = 50;\n\n// MOXIE: mismo breakpoint que usa _custom_product_card.scss para decidir entre el layout de\n// lista \"zoom-out\" (desktop) y \"mobile-single\" (mobile) \u2014 ver secci\u00F3n \"VISTA LISTA\" ah\u00ED.\nconst DESKTOP_QUERY = '(min-width: 1024px)';\n\n// MOXIE TEMPORAL \u2014 MODO MOCK: en true, se salta el fetch a Dyna y el match por SKU, y en su\n// lugar pinta 6 productos reales de la tienda (sections/custom_ofertas_mock.liquid) con\n// descuentos inventados en el propio navegador, solo para ver la card + el precio con\n// descuento funcionando end-to-end sin depender del endpoint real ni de que los SKUs\n// matcheen. En false: usa el fetch real a Dyna + match por SKU (ver flujo arriba). El precio\n// solo se ve si customer est\u00E1 logueado \u2014 eso lo controla snippets/custom_product_card.liquid,\n// no este archivo (B2B: nunca se muestra precio a un visitante sin sesi\u00F3n).\nconst MOCK_MODE = false;\nconst MOCK_SECTION_ID = 'custom_ofertas_mock';\nconst MOCK_DISCOUNTS = [10, 15, 20, 25, 30, 35];\n\n// MOXIE: facetas tipo checklist del sidebar de filtros. `attr` es la propiedad de OfertaItem\n// (ver _buildItems) que alimenta cada una. Agregar una faceta nueva = agregar una entrada ac\u00E1 +\n// su data-attribute correspondiente en snippets/custom_product_card.liquid.\nconst FACET_DEFS = [\n  { key: 'brand', label: 'Marca', attr: 'brand' },\n  { key: 'category', label: 'Categor\u00EDa', attr: 'productType' },\n  { key: 'subcategory', label: 'Subcategor\u00EDa', attr: 'subcategory' },\n  { key: 'line', label: 'L\u00EDnea', attr: 'line' },\n];\n\n/**\n * @typedef {object} OfertaItem\n * @property {HTMLLIElement} li\n * @property {string} sku\n * @property {string} productType\n * @property {string} brand\n * @property {string} subcategory\n * @property {string} line\n * @property {number} priceCents\n * @property {number} discountPct\n * @property {number} discountedCents\n */\n\nclass OfertasGridComponent extends HTMLElement {\n  /** @type {string} */\n  nit = '';\n\n  /** @type {string} */\n  currency = 'COP';\n\n  /** @type {boolean} si hay customer con sesi\u00F3n iniciada (independiente de si es B2B/tiene NIT) */\n  loggedIn = false;\n\n  /** @type {boolean} */\n  _isMock = false;\n\n  /**\n   * Fuente de verdad: un item por producto que s\u00ED matche\u00F3 descuento. El filtro/orden/\n   * paginaci\u00F3n se calculan a partir de esto, sin volver a tocar la red.\n   * @type {OfertaItem[]}\n   */\n  _allItems = [];\n\n  /** @type {Record<string, Set<string>>} valores marcados por faceta \u2014 ver FACET_DEFS */\n  _selectedFacets = Object.fromEntries(FACET_DEFS.map((facet) => [facet.key, new Set()]));\n\n  /** @type {{min: number|null, max: number|null}} rango de precio aplicado (en centavos) */\n  _priceRange = { min: null, max: null };\n\n  /** @type {string} */\n  _sortKey = 'sku-asc';\n\n  /** @type {number} cu\u00E1ntos productos mostrar (crece de a PAGE_SIZE con \"Ver m\u00E1s\") */\n  _visibleCount = PAGE_SIZE;\n\n  /** @type {boolean} */\n  _listView = false;\n\n  /** @type {boolean} evita enganchar listeners m\u00E1s de una vez */\n  _filtersBound = false;\n  _sortBound = false;\n  _viewToggleBound = false;\n\n  connectedCallback() {\n    this.nit = this.dataset.nit ?? '';\n    this.currency = this.dataset.currency || 'COP';\n    this.loggedIn = this.dataset.loggedIn === 'true';\n\n    // MOXIE: debug \u2014 para confirmar r\u00E1pido en consola si el customer actual resuelve como\n    // B2B (tiene NIT) o no, sin tener que inspeccionar el DOM. Quitar cuando ya no haga falta.\n    // rawLoggedInAttr: si esto NO es \"true\" para un customer logueado, el problema est\u00E1 en\n    // c\u00F3mo se renderiza data-logged-in en sections/custom_ofertas_grid.liquid (o la p\u00E1gina\n    // qued\u00F3 con HTML de ANTES de iniciar sesi\u00F3n \u2014 recargar con hard refresh despu\u00E9s de loguearse).\n    console.log('[ofertas-grid] debug sesi\u00F3n', {\n      loggedIn: this.loggedIn,\n      rawLoggedInAttr: this.dataset.loggedIn,\n      nit: this.nit || null,\n      isB2B: Boolean(this.nit),\n      currency: this.currency,\n      // MOXIE: debug temporal \u2014 de d\u00F3nde (o por qu\u00E9 no) resuelve el NIT. Si companyId/\n      // locationId est\u00E1n vac\u00EDos, el customer no tiene current_company/current_location\n      // asignada. Si est\u00E1n llenos pero taxRegistrationId Y externalId est\u00E1n vac\u00EDos, la\n      // sucursal existe pero no tiene ninguno de esos dos campos cargado en Shopify Admin\n      // (Clientes > Empresas > [empresa] > [sucursal]) \u2014 ah\u00ED es donde debe cargarse el NIT.\n      companyId: this.dataset.debugCompanyId || null,\n      companyName: this.dataset.debugCompanyName || null,\n      companyExternalId: this.dataset.debugCompanyExternalId || null,\n      locationId: this.dataset.debugLocationId || null,\n      locationName: this.dataset.debugLocationName || null,\n      taxRegistrationId: this.dataset.debugTaxRegistrationId || null,\n      externalId: this.dataset.debugExternalId || null,\n    });\n\n    this._render();\n  }\n\n  get _resultsList() {\n    return this.querySelector('[data-ofertas-results]');\n  }\n\n  get _statusEl() {\n    return this.querySelector('[data-ofertas-status]');\n  }\n\n  get _filtersEl() {\n    return this.querySelector('[data-ofertas-filters]');\n  }\n\n  /**\n   * @param {'filter'|'caret'|'checkmark'} name\n   * @returns {DocumentFragment} el <svg> clonado de <template data-mox-icon-{name}>, sin envolver\n   */\n  _cloneIconFragment(name) {\n    const template = this.querySelector(`[data-mox-icon-${name}]`);\n    return template instanceof HTMLTemplateElement\n      ? template.content.cloneNode(true)\n      : document.createDocumentFragment();\n  }\n\n  /**\n   * Clona el <svg> de una <template data-mox-icon-*> (ver sections/custom_ofertas_grid.liquid)\n   * envuelto en un span.svg-wrapper \u2014 mismo patr\u00F3n que usa snippets/list-filter.liquid para sus\n   * \u00EDconos inline, as\u00ED el filtro reusa exactamente los mismos \u00EDcono/estilos nativos sin duplicar\n   * su markup a mano en este archivo JS.\n   * @param {'filter'|'caret'} name\n   * @param {string} [extraClass]\n   * @returns {HTMLSpanElement}\n   */\n  _cloneIcon(name, extraClass = '') {\n    const wrapper = document.createElement('span');\n    wrapper.className = `svg-wrapper icon-${name}${extraClass ? ` ${extraClass}` : ''}`;\n    wrapper.appendChild(this._cloneIconFragment(name));\n    return wrapper;\n  }\n\n  get _sortEl() {\n    return this.querySelector('[data-ofertas-sort]');\n  }\n\n  get _viewToggleEl() {\n    return this.querySelector('[data-ofertas-view-toggle]');\n  }\n\n  get _paginationEl() {\n    return this.querySelector('[data-ofertas-pagination]');\n  }\n\n  /**\n   * @param {string} message\n   */\n  _setStatus(message) {\n    const statusEl = this._statusEl;\n    if (!statusEl) return;\n    statusEl.textContent = message;\n    statusEl.hidden = !message;\n  }\n\n  get _loadingPillEl() {\n    return this.querySelector('[data-ofertas-loading-pill]');\n  }\n\n  get _loadingPillTextEl() {\n    return this.querySelector('[data-ofertas-loading-pill-text]');\n  }\n\n  // MOXIE: pill \"Cargando tus descuentos...\" -> check verde -> fade out, independiente de _setStatus.\n  _showLoadingPill(text) {\n    const pill = this._loadingPillEl;\n    const textEl = this._loadingPillTextEl;\n    if (!pill || !textEl) return;\n    clearTimeout(this._loadingPillHideTimer);\n    pill.hidden = false;\n    pill.classList.remove('mox-ofertas__loading-pill--success', 'mox-ofertas__loading-pill--leaving');\n    textEl.textContent = text;\n  }\n\n  _showLoadingPillSuccess(text) {\n    const pill = this._loadingPillEl;\n    const textEl = this._loadingPillTextEl;\n    if (!pill || !textEl) return;\n    pill.classList.add('mox-ofertas__loading-pill--success');\n    textEl.textContent = text;\n    this._loadingPillHideTimer = setTimeout(() => {\n      pill.classList.add('mox-ofertas__loading-pill--leaving');\n      setTimeout(() => {\n        pill.hidden = true;\n      }, 220);\n    }, 700);\n  }\n\n  _hideLoadingPill() {\n    clearTimeout(this._loadingPillHideTimer);\n    const pill = this._loadingPillEl;\n    if (pill) pill.hidden = true;\n  }\n\n  _clearSkeleton() {\n    this._resultsList?.replaceChildren();\n  }\n\n  async _render() {\n    if (MOCK_MODE) {\n      await this._renderMock();\n      return;\n    }\n\n    if (!this.nit) {\n      // MOXIE: distinguir \"sin sesi\u00F3n\" de \"logueado pero sin empresa B2B/NIT asociado\" \u2014\n      // pedirle iniciar sesi\u00F3n a alguien que ya inici\u00F3 sesi\u00F3n es confuso.\n      const message = this.loggedIn\n        ? 'Tu cuenta no tiene una empresa asociada con descuentos B2B. Contactanos.'\n        : 'Inicia sesi\u00F3n para ver tus descuentos.';\n      this._setStatus(message);\n      return;\n    }\n\n    this._showLoadingPill('Cargando tus descuentos\u2026');\n\n    /** @type {Array<{producto_codigo: string, producto_unidad: string, descuento: number}>} */\n    let discounts;\n    try {\n      discounts = await this._fetchDiscounts();\n    } catch (err) {\n      console.error('[ofertas-grid] ERROR de red/CORS al consultar descuentos', err);\n      this._hideLoadingPill();\n      this._setStatus('No pudimos cargar tus descuentos en este momento. Intenta de nuevo m\u00E1s tarde.');\n      this._clearSkeleton();\n      return;\n    }\n\n    if (!discounts.length) {\n      this._hideLoadingPill();\n      this._setStatus('No tienes descuentos disponibles por ahora.');\n      this._clearSkeleton();\n      return;\n    }\n\n    const discountMap = this._buildDiscountMap(discounts);\n    // MOXIE: se busca por producto_codigo \u2014 discountMap.keys() duplicar\u00EDa bare+compuesta.\n    const codigos = Array.from(new Set(discounts.map((entry) => String(entry?.producto_codigo ?? '').trim()).filter(Boolean)));\n    const chunks = this._chunk(codigos, SKU_CHUNK_SIZE);\n\n    const chunkResults = await Promise.all(chunks.map((chunk) => this._fetchProductsChunk(chunk)));\n    const cards = this._mergeCards(chunkResults);\n\n    if (!cards.length) {\n      this._hideLoadingPill();\n      this._setStatus('No encontramos productos disponibles para tu descuento.');\n      this._clearSkeleton();\n      return;\n    }\n\n    // MOXIE: la base es el precio del servicio Dyna \u2014 hay que esperar a dyna-pricing.js.\n    await this._waitForPricing();\n    this._allItems = this._buildItems(cards, discountMap);\n\n    if (!this._allItems.length) {\n      this._hideLoadingPill();\n      this._setStatus('No encontramos productos disponibles para tu descuento.');\n      this._clearSkeleton();\n      return;\n    }\n\n    this._isMock = false;\n    this._showLoadingPillSuccess('Descuentos cargados');\n    this._initListControls();\n  }\n\n  /**\n   * MOXIE TEMPORAL \u2014 ver MOCK_MODE arriba. Pinta 6 productos reales de la tienda con\n   * descuentos inventados en el navegador (no vienen de Dyna), solo para previsualizar la\n   * card + la inyecci\u00F3n de precio sin depender del endpoint real ni del match por SKU.\n   */\n  async _renderMock() {\n    this._showLoadingPill('Cargando tus descuentos\u2026 (modo mock \u2014 datos de prueba)');\n\n    /** @type {Element[]} */\n    let cards;\n    try {\n      const html = await sectionRenderer.getSectionHTML(MOCK_SECTION_ID, false, new URL('/', location.origin));\n      const doc = new DOMParser().parseFromString(html, 'text/html');\n      cards = Array.from(doc.querySelectorAll('.mox-product-card'));\n    } catch (err) {\n      console.error('[ofertas-grid][mock] ERROR cargando productos mock', err);\n      this._hideLoadingPill();\n      this._setStatus('No pudimos cargar el mock de productos.');\n      this._clearSkeleton();\n      return;\n    }\n\n    if (!cards.length) {\n      this._hideLoadingPill();\n      this._setStatus('El mock no encontr\u00F3 productos (\u00BFla tienda no tiene productos publicados?).');\n      this._clearSkeleton();\n      return;\n    }\n\n    // MOXIE: mock -> Map sku -> descuento inventado, para reusar exactamente el mismo\n    // _buildItems/_applyDiscount que usa el flujo real.\n    const discountMap = new Map();\n    cards.forEach((card, index) => {\n      const sku = (card.getAttribute('data-sku') ?? '').trim();\n      if (!sku) return;\n      discountMap.set(sku, MOCK_DISCOUNTS[index % MOCK_DISCOUNTS.length]);\n    });\n\n    // MOXIE: la base es el precio del servicio Dyna \u2014 hay que esperar a dyna-pricing.js.\n    await this._waitForPricing();\n    this._allItems = this._buildItems(cards, discountMap);\n\n    if (!this._allItems.length) {\n      this._hideLoadingPill();\n      this._setStatus('El mock no encontr\u00F3 productos con SKU.');\n      this._clearSkeleton();\n      return;\n    }\n\n    this._isMock = true;\n    this._showLoadingPillSuccess('Descuentos cargados');\n    this._initListControls();\n  }\n\n  /**\n   * Engancha filtros/orden/vista y pinta la primera p\u00E1gina. Se llama una sola vez, apenas\n   * this._allItems est\u00E1 listo (flujo real o mock).\n   */\n  _initListControls() {\n    this._setupFacetFilters();\n    this._setupSortControl();\n    this._setupViewToggle();\n    this._applyGridView();\n    this._visibleCount = PAGE_SIZE;\n    this._withFadeSwap(() => this._renderPage());\n  }\n\n  // MOXIE: cross-fade en vez de corte seco al reemplazar el contenido del <ul> (skeleton -> real, toggle vista).\n  _withFadeSwap(mutate) {\n    const list = this._resultsList;\n    if (!list) {\n      mutate();\n      return;\n    }\n    list.classList.add('mox-ofertas-results--swap');\n    requestAnimationFrame(() => {\n      mutate();\n      requestAnimationFrame(() => list.classList.remove('mox-ofertas-results--swap'));\n    });\n  }\n\n  /**\n   * @returns {Promise<Array<{producto_codigo: string, producto_unidad: string, descuento: number}>>}\n   */\n  async _fetchDiscounts() {\n    const url = new URL(DYNA_API_URL, location.origin);\n    // MOXIE: el proxy valida el NIT pelado (company.external_id) contra el cliente logueado; el prefijo NIT_ lo pone el backend.\n    const nitParam = this.nit.replace(/^NIT_/, '');\n    url.searchParams.set('nit', nitParam);\n\n    console.log('[ofertas-grid] consultando descuentos', { nitParam, url: url.toString() });\n\n    // MOXIE: sigue pages.next hasta agotar p\u00E1ginas.\n    const allData = [];\n    let nextUrl = url.toString();\n    let pageCount = 0;\n\n    while (nextUrl && pageCount < DYNA_PAGE_LIMIT) {\n      const response = await fetch(nextUrl, { headers: { Accept: 'application/json' } });\n\n      if (!response.ok) {\n        throw new Error(`HTTP ${response.status} ${response.statusText}`);\n      }\n\n      const json = await response.json();\n      if (Array.isArray(json?.data)) allData.push(...json.data);\n\n      nextUrl = window.DynaPriceCore.rebaseToProxy(json?.pages?.next) || null;\n      pageCount++;\n    }\n\n    console.log('[ofertas-grid] descuentos recibidos', { paginas: pageCount, entradas: allData.length });\n\n    return allData;\n  }\n\n  // MOXIE: guarda key pelada Y compuesta codigo-unidad \u2014 el SKU real puede ser cualquiera.\n  /**\n   * @param {Array<{producto_codigo: string, producto_unidad: string, descuento: number}>} discounts\n   * @returns {Map<string, number>} sku -> % descuento\n   */\n  _buildDiscountMap(discounts) {\n    const map = new Map();\n\n    for (const entry of discounts) {\n      const codigo = String(entry?.producto_codigo ?? '').trim();\n      const unidad = String(entry?.producto_unidad ?? '').trim();\n      const pct = Number(entry?.descuento);\n      if (!codigo || !Number.isFinite(pct)) continue;\n\n      if (unidad) {\n        const compoundSku = `${codigo}-${unidad}`;\n        const currentCompound = map.get(compoundSku);\n        if (currentCompound === undefined || pct > currentCompound) {\n          map.set(compoundSku, pct);\n        }\n      }\n\n      const currentBare = map.get(codigo);\n      if (currentBare === undefined || pct > currentBare) {\n        map.set(codigo, pct);\n      }\n    }\n\n    return map;\n  }\n\n  /**\n   * @param {string[]} items\n   * @param {number} size\n   * @returns {string[][]}\n   */\n  _chunk(items, size) {\n    const chunks = [];\n    for (let i = 0; i < items.length; i += size) {\n      chunks.push(items.slice(i, i + size));\n    }\n    return chunks;\n  }\n\n  /**\n   * MOXIE: el SKU vive en la variante, no en el producto \u2014 el campo de b\u00FAsqueda con scope de\n   * Shopify para eso es `variants.sku:`, no `sku:` (que no es un campo reconocido y hac\u00EDa que\n   * la b\u00FAsqueda no devolviera nada, aunque la API de Dyna respondiera bien).\n   *\n   * @param {string[]} skus\n   * @returns {Promise<Element[]>}\n   */\n  async _fetchProductsChunk(skus) {\n    const query = skus.map((sku) => `variants.sku:\"${sku}\"`).join(' OR ');\n    const url = new URL('/search', location.origin);\n    url.searchParams.set('type', 'product');\n    url.searchParams.set('q', query);\n\n    try {\n      const html = await sectionRenderer.getSectionHTML(SEARCH_SECTION_ID, false, url);\n      const doc = new DOMParser().parseFromString(html, 'text/html');\n      const cards = Array.from(doc.querySelectorAll('.mox-product-card'));\n\n      // MOXIE: log de diagn\u00F3stico \u2014 dejarlo mientras se termina de validar el cruce contra\n      // datos reales. Muestra qu\u00E9 SKUs se buscaron y cu\u00E1ntas cards volvieron para ese lote.\n      // console.log('[ofertas-grid] lote de SKUs', skus, '-> URL', url.toString(), '->', cards.length, 'card(s)');\n\n      return cards;\n    } catch (err) {\n      // console.error('[ofertas-grid] ERROR buscando productos para el lote', skus, err);\n      return [];\n    }\n  }\n\n  /**\n   * @param {Element[][]} chunkResults\n   * @returns {Element[]}\n   */\n  _mergeCards(chunkResults) {\n    const seen = new Set();\n    const merged = [];\n\n    for (const cards of chunkResults) {\n      for (const card of cards) {\n        const productId = card.getAttribute('data-product-id');\n        if (!productId || seen.has(productId)) continue;\n        seen.add(productId);\n        merged.push(card);\n      }\n    }\n\n    return merged;\n  }\n\n  /**\n   * Inyecta el precio con descuento en cada card y arma this._allItems. Las cards cuyo SKU\n   * no matchea el mapa de descuentos (red de seguridad \u2014 no deber\u00EDa pasar ya que solo se\n   * buscaron esos SKUs) se descartan.\n   *\n   * @param {Element[]} cards\n   * @param {Map<string, number>} discountMap\n   * @returns {OfertaItem[]}\n   */\n  _buildItems(cards, discountMap) {\n    const items = [];\n\n    for (const card of cards) {\n      const sku = (card.getAttribute('data-sku') ?? '').trim();\n      const discountPct = discountMap.get(sku);\n\n      if (discountPct === undefined) {\n        // MOXIE: log de diagn\u00F3stico \u2014 si esto aparece seguido, el SKU real de la variante no\n        // es exactamente igual a producto_codigo (revisar may\u00FAsculas, espacios, prefijos/sufijos).\n        console.warn('[ofertas-grid] card sin match de descuento \u2014 SKU real:', JSON.stringify(sku));\n        continue;\n      }\n\n      // MOXIE: precio del servicio Dyna (personalizado > gen\u00E9rico); si no hay, el cat\u00E1logo.\n      const service = window.dynaResolveServicePrice?.(sku) ?? null;\n      const priceCents = service ? service.cents : Number(card.getAttribute('data-price-cents'));\n      if (!Number.isFinite(priceCents) || priceCents <= 0) continue;\n\n      this._applyDiscount(card, priceCents, discountPct, service);\n\n      const li = document.createElement('li');\n      li.className = 'mox-ofertas-results__item';\n      li.appendChild(card);\n      // MOXIE: solo anima la primera vez que entra al viewport, no en cada re-render de filtro/orden.\n      li.addEventListener('animationend', () => li.classList.add('mox-ofertas-results__item--seen'), { once: true });\n\n      items.push({\n        li,\n        sku,\n        productType: card.getAttribute('data-product-type')?.trim() ?? '',\n        brand: card.getAttribute('data-mox-brand')?.trim() ?? '',\n        subcategory: card.getAttribute('data-mox-subcategory')?.trim() ?? '',\n        line: card.getAttribute('data-mox-line')?.trim() ?? '',\n        priceCents,\n        discountPct,\n        discountedCents: window.DynaPriceCore.computePrice({ baseCents: priceCents, discountPct }).finalCents,\n      });\n    }\n\n    return items;\n  }\n\n  /**\n   * Construye el sidebar de filtros (una faceta tipo checklist por cada entrada de FACET_DEFS,\n   * m\u00E1s un rango de precio) a partir de los productos que s\u00ED matchearon descuento\n   * (this._allItems), no de todo el cat\u00E1logo. Una faceta se omite por completo si ning\u00FAn item\n   * tiene valor para ella (ej. si \"l\u00EDnea\" no est\u00E1 cargada en ning\u00FAn producto con descuento).\n   */\n  _setupFacetFilters() {\n    const filtersEl = this._filtersEl;\n    if (!filtersEl) return;\n\n    this._selectedFacets = Object.fromEntries(FACET_DEFS.map((facet) => [facet.key, new Set()]));\n    this._priceRange = { min: null, max: null };\n\n    const groups = [];\n    for (const facet of FACET_DEFS) {\n      /** @type {Set<string>} */\n      const values = new Set();\n      for (const item of this._allItems) {\n        if (item[facet.attr]) values.add(item[facet.attr]);\n      }\n      if (values.size) {\n        groups.push(this._buildChecklistFacetGroup(facet, Array.from(values).sort((a, b) => a.localeCompare(b, 'es'))));\n      }\n    }\n\n    const priceGroup = this._buildPriceFacetGroup();\n    if (priceGroup) groups.push(priceGroup);\n\n    if (!groups.length) {\n      filtersEl.replaceChildren();\n      return;\n    }\n\n    const header = document.createElement('div');\n    header.className = 'mox-ofertas__filters-header';\n\n    const title = document.createElement('p');\n    title.className = 'mox-ofertas__filters-title';\n    title.append(this._cloneIcon('filter'), document.createTextNode('Filtrar'));\n\n    const clearButton = document.createElement('button');\n    clearButton.type = 'button';\n    clearButton.className = 'mox-ofertas__filters-clear';\n    clearButton.textContent = 'Limpiar filtros';\n    clearButton.addEventListener('click', this._handleClearFilters);\n\n    header.append(title, clearButton);\n\n    const fragment = document.createDocumentFragment();\n    fragment.appendChild(header);\n    for (const group of groups) fragment.appendChild(group);\n\n    filtersEl.replaceChildren(fragment);\n\n    if (!this._filtersBound) {\n      filtersEl.addEventListener('change', this._handleFilterChange);\n      this._filtersBound = true;\n    }\n  }\n\n  /**\n   * Acorde\u00F3n de una faceta \u2014 misma mec\u00E1nica nativa que <details>/<summary> usan en el resto del\n   * tema (ver base.css: `details[open] > summary .icon-animated > svg` rota el caret, `summary`\n   * ya viene con display:flex/cursor/list-style desde ah\u00ED), con el checkbox reusable de\n   * snippets/checkbox.liquid en vez de uno hecho a mano.\n   * @param {{key: string, label: string}} facet\n   * @param {string[]} values\n   * @returns {HTMLDetailsElement}\n   */\n  _buildChecklistFacetGroup(facet, values) {\n    const details = document.createElement('details');\n    details.className = 'mox-ofertas__filter-group';\n    details.open = true;\n\n    const summary = document.createElement('summary');\n    summary.className = 'mox-ofertas__filter-summary';\n\n    const label = document.createElement('span');\n    label.className = 'mox-ofertas__filter-label';\n    label.textContent = facet.label;\n\n    summary.append(label, this._cloneIcon('caret', 'icon-animated'));\n    details.appendChild(summary);\n\n    const options = document.createElement('div');\n    options.className = 'mox-ofertas__filter-options';\n\n    for (const value of values) {\n      options.appendChild(this._buildCheckboxOption(facet.key, value, this._selectedFacets[facet.key].has(value)));\n    }\n\n    details.appendChild(options);\n\n    return details;\n  }\n\n  /**\n   * Checkbox individual \u2014 mismo markup que snippets/checkbox.liquid (.checkbox/.checkbox__input/\n   * .checkbox__label) para heredar sus estilos ya definidos en base.css sin reescribirlos ac\u00E1.\n   * @param {string} facetKey\n   * @param {string} value\n   * @param {boolean} checked\n   * @returns {HTMLDivElement}\n   */\n  _buildCheckboxOption(facetKey, value, checked) {\n    const wrapper = document.createElement('div');\n    wrapper.className = 'checkbox';\n\n    const id = `OfertasFilter-${facetKey}-${value}`.replace(/\\s+/g, '-');\n\n    const input = document.createElement('input');\n    input.type = 'checkbox';\n    input.className = 'checkbox__input';\n    input.id = id;\n    input.value = value;\n    input.dataset.facet = facetKey;\n    input.checked = checked;\n\n    const optionLabel = document.createElement('label');\n    optionLabel.className = 'checkbox__label';\n    optionLabel.htmlFor = id;\n    optionLabel.setAttribute('role', 'checkbox');\n\n    const text = document.createElement('span');\n    text.className = 'checkbox__label-text';\n    text.textContent = value;\n\n    optionLabel.append(this._cloneIconFragment('checkmark'), text);\n    wrapper.append(input, optionLabel);\n\n    return wrapper;\n  }\n\n  /**\n   * Rango de precio (m\u00EDn/m\u00E1x) sobre this._allItems. Devuelve null si no hay al menos dos\n   * precios distintos (un solo valor posible no necesita filtro).\n   * @returns {HTMLDetailsElement|null}\n   */\n  _buildPriceFacetGroup() {\n    const prices = this._allItems.map((item) => item.priceCents).filter((cents) => Number.isFinite(cents));\n    if (!prices.length) return null;\n\n    const minBound = Math.min(...prices);\n    const maxBound = Math.max(...prices);\n    if (minBound >= maxBound) return null;\n\n    const details = document.createElement('details');\n    details.className = 'mox-ofertas__filter-group';\n    details.open = true;\n\n    const summary = document.createElement('summary');\n    summary.className = 'mox-ofertas__filter-summary';\n\n    const label = document.createElement('span');\n    label.className = 'mox-ofertas__filter-label';\n    label.textContent = 'Precio';\n\n    summary.append(label, this._cloneIcon('caret', 'icon-animated'));\n    details.appendChild(summary);\n\n    const row = document.createElement('div');\n    row.className = 'mox-ofertas__price-filter';\n    row.append(\n      this._buildPriceField('min', 'Precio m\u00EDnimo'),\n      Object.assign(document.createElement('span'), { className: 'mox-ofertas__price-separator', textContent: 'a' }),\n      this._buildPriceField('max', 'Precio m\u00E1ximo')\n    );\n    details.appendChild(row);\n\n    const hint = document.createElement('p');\n    hint.className = 'mox-ofertas__price-hint';\n    hint.textContent = `El precio m\u00E1s alto es ${this._formatMoney(maxBound)}`;\n    details.appendChild(hint);\n\n    return details;\n  }\n\n  /**\n   * @param {'min'|'max'} bound\n   * @param {string} ariaLabel\n   * @returns {HTMLDivElement}\n   */\n  _buildPriceField(bound, ariaLabel) {\n    const field = document.createElement('div');\n    field.className = 'mox-ofertas__price-field';\n\n    const currency = document.createElement('span');\n    currency.className = 'mox-ofertas__price-currency';\n    currency.textContent = '$';\n\n    const input = document.createElement('input');\n    input.type = 'number';\n    input.inputMode = 'decimal';\n    input.min = '0';\n    input.className = 'mox-ofertas__price-input';\n    input.dataset.priceBound = bound;\n    input.setAttribute('aria-label', ariaLabel);\n\n    field.append(currency, input);\n    return field;\n  }\n\n  /**\n   * Delegado en [data-ofertas-filters]: distingue checkboxes de faceta (data-facet) de los\n   * inputs num\u00E9ricos de precio (data-price-bound). 'change' en un <input type=\"number\"> dispara\n   * reci\u00E9n al perder foco o con Enter \u2014 no en cada tecla \u2014 as\u00ED que no hace falta debounce.\n   * @param {Event} event\n   */\n  _handleFilterChange = (event) => {\n    const input = event.target;\n    if (!(input instanceof HTMLInputElement)) return;\n\n    if (input.dataset.priceBound) {\n      const parsed = input.value === '' ? null : Math.round(Number(input.value) * 100);\n      const key = input.dataset.priceBound === 'min' ? 'min' : 'max';\n      this._priceRange[key] = Number.isFinite(parsed) ? parsed : null;\n    } else if (input.dataset.facet) {\n      const selected = this._selectedFacets[input.dataset.facet];\n      if (!selected) return;\n      if (input.checked) {\n        selected.add(input.value);\n      } else {\n        selected.delete(input.value);\n      }\n    } else {\n      return;\n    }\n\n    this._visibleCount = PAGE_SIZE;\n    this._renderPage();\n  };\n\n  /**\n   * Desmarca todas las facetas, limpia el rango de precio y vuelve a mostrar todos los\n   * productos.\n   */\n  _handleClearFilters = () => {\n    const hasActiveFacets = Object.values(this._selectedFacets).some((set) => set.size);\n    const hasActivePrice = this._priceRange.min != null || this._priceRange.max != null;\n    if (!hasActiveFacets && !hasActivePrice) return;\n\n    for (const selected of Object.values(this._selectedFacets)) selected.clear();\n    this._priceRange = { min: null, max: null };\n\n    const filtersEl = this._filtersEl;\n    if (filtersEl) {\n      for (const input of filtersEl.querySelectorAll('input[type=\"checkbox\"]')) {\n        if (input instanceof HTMLInputElement) input.checked = false;\n      }\n      for (const input of filtersEl.querySelectorAll('input[type=\"number\"]')) {\n        if (input instanceof HTMLInputElement) input.value = '';\n      }\n    }\n\n    this._visibleCount = PAGE_SIZE;\n    this._renderPage();\n  };\n\n  /**\n   * Engancha el <select> de orden (markup fijo en Liquid \u2014 solo hace falta el listener).\n   */\n  _setupSortControl() {\n    const sortEl = this._sortEl;\n    if (!(sortEl instanceof HTMLSelectElement) || this._sortBound) return;\n\n    sortEl.value = this._sortKey;\n    sortEl.addEventListener('change', () => {\n      this._sortKey = sortEl.value;\n      this._visibleCount = PAGE_SIZE;\n      this._renderPage();\n    });\n    this._sortBound = true;\n  }\n\n  /**\n   * Toggle de vista grilla/lista. Reusa el contrato que ya existe en\n   * _custom_product_card.scss (.product-grid[product-grid-view='zoom-out'|'mobile-single']),\n   * por eso el <ul> de resultados tambi\u00E9n lleva la clase `product-grid`.\n   */\n  _setupViewToggle() {\n    const container = this._viewToggleEl;\n    if (!container || this._viewToggleBound) return;\n\n    container.addEventListener('click', (event) => {\n      const target = event.target;\n      const button = target instanceof Element ? target.closest('[data-view]') : null;\n      if (!(button instanceof HTMLElement)) return;\n\n      this._listView = button.dataset.view === 'list';\n      this._withFadeSwap(() => this._applyGridView());\n\n      for (const btn of container.querySelectorAll('[data-view]')) {\n        btn.setAttribute('aria-pressed', String(btn === button));\n      }\n    });\n\n    // MOXIE: si la vista lista est\u00E1 activa y el viewport cruza el breakpoint 1024px, el\n    // valor correcto de product-grid-view cambia (zoom-out <-> mobile-single) \u2014 ver\n    // _custom_product_card.scss.\n    window.matchMedia(DESKTOP_QUERY).addEventListener('change', () => this._applyGridView());\n\n    this._viewToggleBound = true;\n  }\n\n  /**\n   * Aplica (o quita) la vista lista sobre el <ul> de resultados.\n   */\n  _applyGridView() {\n    const list = this._resultsList;\n    if (!list) return;\n\n    if (!this._listView) {\n      list.setAttribute('product-grid-view', 'default');\n      return;\n    }\n\n    const isDesktop = window.matchMedia(DESKTOP_QUERY).matches;\n    list.setAttribute('product-grid-view', isDesktop ? 'zoom-out' : 'mobile-single');\n  }\n\n  /**\n   * @returns {OfertaItem[]}\n   */\n  _computeVisibleItems() {\n    let items = this._allItems;\n\n    for (const facet of FACET_DEFS) {\n      const selected = this._selectedFacets[facet.key];\n      if (selected?.size) {\n        items = items.filter((item) => selected.has(item[facet.attr]));\n      }\n    }\n\n    const { min, max } = this._priceRange;\n    if (min != null) items = items.filter((item) => item.priceCents >= min);\n    if (max != null) items = items.filter((item) => item.priceCents <= max);\n\n    return this._sortItems(items);\n  }\n\n  /**\n   * @param {OfertaItem[]} items\n   * @returns {OfertaItem[]}\n   */\n  _sortItems(items) {\n    const sorted = [...items];\n\n    switch (this._sortKey) {\n      case 'sku-desc':\n        sorted.sort((a, b) => b.sku.localeCompare(a.sku, 'es', { numeric: true }));\n        break;\n      case 'price-asc':\n        sorted.sort((a, b) => a.discountedCents - b.discountedCents);\n        break;\n      case 'price-desc':\n        sorted.sort((a, b) => b.discountedCents - a.discountedCents);\n        break;\n      case 'discount-desc':\n        sorted.sort((a, b) => b.discountPct - a.discountPct);\n        break;\n      case 'discount-asc':\n        sorted.sort((a, b) => a.discountPct - b.discountPct);\n        break;\n      case 'sku-asc':\n      default:\n        sorted.sort((a, b) => a.sku.localeCompare(b.sku, 'es', { numeric: true }));\n        break;\n    }\n\n    return sorted;\n  }\n\n  /**\n   * Recalcula qu\u00E9 productos se muestran (seg\u00FAn filtro + orden vigentes) y los pinta, junto\n   * con la barra de progreso + bot\u00F3n \"Ver m\u00E1s\" y el mensaje de conteo.\n   */\n  _renderPage() {\n    const list = this._resultsList;\n    if (!list) return;\n\n    const visible = this._computeVisibleItems();\n    const totalItems = visible.length;\n    const shownCount = Math.min(this._visibleCount, totalItems);\n    const pageItems = visible.slice(0, shownCount);\n\n    list.replaceChildren(...pageItems.map((item) => item.li));\n    this._renderLoadMore(totalItems, shownCount);\n\n    const suffix = this._isMock ? ' (MOCK \u2014 datos de prueba, no reales)' : '';\n\n    if (!totalItems) {\n      this._setStatus(`No encontramos productos para este filtro.${suffix}`);\n      return;\n    }\n\n    this._setStatus(`Mostrando ${shownCount} de ${totalItems} producto${totalItems === 1 ? '' : 's'}${suffix}`);\n  }\n\n  /**\n   * Barra de progreso + conteo + bot\u00F3n \"Ver m\u00E1s\". Se oculta sola cuando ya se muestran\n   * todos los productos disponibles para el filtro actual.\n   *\n   * @param {number} totalItems\n   * @param {number} shownCount\n   */\n  _renderLoadMore(totalItems, shownCount) {\n    const container = this._paginationEl;\n    if (!container) return;\n\n    if (!totalItems || shownCount >= totalItems) {\n      container.replaceChildren();\n      return;\n    }\n\n    const percent = Math.round((shownCount / totalItems) * 100);\n\n    const wrapper = document.createElement('div');\n    wrapper.className = 'mox-ofertas__load-more';\n\n    const track = document.createElement('div');\n    track.className = 'mox-ofertas__load-more-track';\n    const fill = document.createElement('div');\n    fill.className = 'mox-ofertas__load-more-fill';\n    fill.style.width = `${percent}%`;\n    track.appendChild(fill);\n\n    const count = document.createElement('p');\n    count.className = 'mox-ofertas__load-more-count';\n    count.textContent = `Mostrando ${shownCount} de ${totalItems} productos`;\n\n    const button = document.createElement('button');\n    button.type = 'button';\n    button.className = 'mox-ofertas__load-more-btn';\n    button.textContent = 'Ver m\u00E1s';\n    button.addEventListener('click', () => this._showMore());\n\n    wrapper.append(track, count, button);\n    container.replaceChildren(wrapper);\n  }\n\n  /**\n   * Muestra PAGE_SIZE productos m\u00E1s (acumulativo, no reemplaza los ya visibles).\n   */\n  _showMore() {\n    this._visibleCount += PAGE_SIZE;\n    this._renderPage();\n  }\n\n  /**\n   * Reemplaza el contenido de los .mox-product-card__price-wrap \"de producto\" (no los de\n   * sub-cards de variante \u2014 bundle picker) por precio tachado + precio final, e inyecta el\n   * badge de descuento sobre la imagen (no junto al precio).\n   *\n   * @param {Element} card\n   * @param {number} priceCents\n   * @param {number} discountPct\n   * @param {{ cents: number, baseSku: string, unitId: string, source: string, tax: number | null } | null} service\n   */\n  _applyDiscount(card, priceCents, discountPct, service) {\n    // MOXIE: precio mostrado acumula encadenado con custom.descuento, igual que el checkout.\n    const totalPct = window.DynaPriceCore.combineDiscounts(discountPct, window.DynaPriceCore.getProductDiscount(card));\n    const { finalCents: discountedCents } = window.DynaPriceCore.computePrice({\n      baseCents: priceCents,\n      discountPct: totalPct,\n    });\n\n    // Solo el % de /mis-descuentos va al carrito; custom.descuento lo cobra el descuento nativo \"Descuento producto\".\n    card.setAttribute('data-client-discount', String(discountPct));\n\n    this._resolveVariantPrices(card);\n\n    const priceWraps = card.querySelectorAll('.mox-product-card__price-wrap');\n\n    for (const wrap of priceWraps) {\n      // MOXIE: los price-wrap dentro de .mox-product-card__variant son el precio de esa\n      // variante puntual (bundle picker) \u2014 fuera de alcance de esta primera versi\u00F3n.\n      if (wrap.closest('.mox-product-card__variant')) continue;\n\n      wrap.innerHTML = `\n        <s class=\"mox-product-card__price-compare\">${this._formatMoney(priceCents)}</s>\n        <div class=\"mox-product-card__price-row\">\n          <span class=\"mox-product-card__price\"${this._resolvedAttrs(service)}>${this._formatMoney(discountedCents)}</span>\n        </div>\n        <p class=\"mox-product-card__savings\">Ahorras ${this._formatMoney(priceCents - discountedCents)} hoy</p>\n        <p class=\"mox-product-card__iva\">Precio antes de IVA</p>\n      `;\n    }\n\n    // MOXIE: el badge de descuento se posiciona sobre la imagen (esquina superior izquierda),\n    // mismo contenedor que usan los flags Nuevo/Top ventas (snippets/custom_product_flags.liquid)\n    // \u2014 .mox-product-card__tags ya es display:flex; flex-direction:column, as\u00ED que se apila\n    // solo si el producto tambi\u00E9n tiene Nuevo/Top. Se crea el contenedor si no existe.\n    const imageWrap = card.querySelector('.mox-product-card__image-wrap');\n    if (imageWrap) {\n      let tags = imageWrap.querySelector('.mox-product-card__tags');\n      if (!tags) {\n        tags = document.createElement('div');\n        tags.className = 'mox-product-card__tags';\n        tags.setAttribute('aria-hidden', 'true');\n        imageWrap.appendChild(tags);\n      }\n\n      const badge = document.createElement('span');\n      badge.className = 'mox-product-card__discount-badge';\n      badge.setAttribute('aria-label', `Descuento ${discountPct}%`);\n      badge.textContent = (this.dataset.flagText || '[pct] %').replace('[pct]', String(discountPct));\n      tags.prepend(badge);\n    }\n  }\n\n  /**\n   * MOXIE: Intl con { style: 'currency', currency: 'COP' } mostraba \"COP\" en vez de \"$\" (el\n   * s\u00EDmbolo de COP no resuelve a \"$\" en todos los navegadores/locales). Como esta tienda es\n   * exclusivamente COP, se arma el \"$\" a mano en vez de depender de esa resoluci\u00F3n, para que\n   * coincida con el filtro `money` nativo de Liquid (ej. \"$3.149\").\n   *\n   * @param {number} cents\n   * @returns {string}\n   */\n  /**\n   * MOXIE: mismos data-dyna-resolved-* que deja dyna-pricing.js; los lee custom-product-card-cart.js.\n   * @param {{ cents: number, baseSku: string, unitId: string, source: string, tax: number | null } | null} service\n   * @returns {string}\n   */\n  _resolvedAttrs(service) {\n    if (!service) return '';\n    const esc = (value) => String(value).replace(/[&\"<>]/g, (c) => `&#${c.charCodeAt(0)};`);\n    return ` data-dyna-resolved-sku=\"${esc(service.baseSku)}\" data-dyna-resolved-unit=\"${esc(service.unitId)}\" data-dyna-resolved-price=\"${service.cents}\" data-dyna-resolved-source=\"${esc(service.source)}\"${service.tax === null || service.tax === undefined ? '' : ` data-dyna-resolved-tax=\"${esc(service.tax)}\"`}`;\n  }\n\n  /**\n   * MOXIE: sub-cards por presentaci\u00F3n (vista lista): precio del servicio + atributos para el add-to-cart.\n   * @param {Element} card\n   */\n  _resolveVariantPrices(card) {\n    for (const el of card.querySelectorAll('.mox-product-card__variant [data-dyna-precio][data-dyna-sku]')) {\n      // MOXIE: la grilla ya esper\u00F3 a dyna-pricing \u2014 el precio queda final, se revela.\n      el.classList.add('dyna-precio-listo');\n      const service = window.dynaResolveServicePrice?.(el.getAttribute('data-dyna-sku') ?? '');\n      if (!service) continue;\n      el.setAttribute('data-dyna-service-cents', String(service.cents));\n      el.setAttribute('data-dyna-resolved-sku', service.baseSku);\n      el.setAttribute('data-dyna-resolved-unit', service.unitId);\n      el.setAttribute('data-dyna-resolved-price', String(service.cents));\n      el.setAttribute('data-dyna-resolved-source', service.source);\n      if (service.tax !== null && service.tax !== undefined) el.setAttribute('data-dyna-resolved-tax', String(service.tax));\n      el.textContent = this._formatMoney(service.cents);\n    }\n  }\n\n  /** @returns {Promise<void>} */\n  _waitForPricing() {\n    if (window.dynaCache?.ready || !window.dynaCache) return Promise.resolve();\n    return new Promise((resolve) => {\n      const timeoutId = setTimeout(resolve, DYNA_PRICING_READY_TIMEOUT_MS);\n      document.addEventListener(\n        DYNA_PRICING_READY_EVENT,\n        () => {\n          clearTimeout(timeoutId);\n          resolve();\n        },\n        { once: true }\n      );\n    });\n  }\n\n  // MOXIE: formato \u00FAnico del theme, en dyna-price-core.js.\n  _formatMoney(cents) {\n    return window.DynaPriceCore.formatCOP(cents);\n  }\n}\n\nif (!customElements.get('ofertas-grid-component')) {\n  customElements.define('ofertas-grid-component', OfertasGridComponent);\n}\n"],
  "mappings": "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",
  "names": []
}
