Fixing issue when someone click on "add to cart" the cart value no update

Hi Team,

I’m facing issue with cart value not updated at real time,
Please help to fix this

<section id="ProductShowcaseSplit-{{ section.id }}" class="pss" style="
  --pss-container-pad: {{ section.settings.container_side_padding }}%;
  --pss-row-gap: {{ section.settings.row_gap }}px;
  --pss-left-bg: {{ section.settings.left_bg }};
  --pss-text: {{ section.settings.text_color }};
  --pss-line: {{ section.settings.line_color }};
  --pss-btn-bg: {{ section.settings.button_bg }};
  --pss-btn-text: {{ section.settings.button_text }};
  --pss-sep-width: {{ section.settings.separator_width }}%;
  --pss-desc-lines: {{ section.settings.desc_lines }};
  --pss-title-size: {{ section.settings.title_size }}px;
  --pss-subtitle-size: {{ section.settings.subtitle_size }}px;
  --pss-desc-size: {{ section.settings.desc_size }}px;
">
  <div class="pss__container">
    {% if section.blocks.size == 0 %}
      <div class="pss__empty">Add product blocks in this section.</div>
    {% else %}
      <product-showcase-split class="pss__list" data-section-id="{{ section.id }}">
        {% for block in section.blocks %}
          {% liquid
            assign product = block.settings.product
            if product != blank
              assign variant = product.selected_or_first_available_variant
              assign subtitle = block.settings.subtitle
              assign subtitle_2 = block.settings.subtitle_2
              
              assign img = block.settings.image
              if img == blank
                assign img = product.featured_image
              endif

              assign img_hover = null
              if product.images.size > 1
                assign img_hover = product.images[1]
              endif

              assign current_price = variant.price
              assign compare_price = variant.compare_at_price
              assign has_discount = false
              if compare_price and compare_price > current_price
                assign has_discount = true
                assign discount = compare_price | minus: current_price | times: 100.0 | divided_by: compare_price | round
              endif

              assign override_desc = block.settings.override_description
            endif
          %}

          {% if product != blank and variant != blank %}
            <article class="pss__row" {{ block.shopify_attributes }} data-row data-variant="{{ variant.id }}">
              <div class="pss__col pss__col--left">
                <div class="pss__content">
                  
                  <a href="{{ product.url }}" class="pss__title-link">
                    <h3 class="pss__title">{{ product.title }}</h3>
                  </a>
                  <hr class="pss__hr">

                  {% if subtitle != blank %}
                    <div class="pss__subtitle">{{ subtitle }}</div>
                    <hr class="pss__hr">
                  {% endif %}

                  {% if subtitle_2 != blank %}
                    <div class="pss__subtitle pss__subtitle--secondary">{{ subtitle_2 }}</div>
                    <hr class="pss__hr">
                  {% endif %}

                  <div class="pss__meta">
                    {% if block.settings.weight != blank %}
                      <span class="pss__weight">{{ block.settings.weight }}</span>
                      <span class="pss__divider">|</span>
                    {% endif %}

                    {% if has_discount %}
                      <span class="pss__price--compare">{{ compare_price | money }}</span>
                      <span class="pss__price--current">{{ current_price | money }}</span>
                      <span class="pss__discount">{{ discount }}% off</span>
                    {% else %}
                      <span class="pss__price--current">{{ current_price | money }}</span>
                    {% endif %}
                  </div>
                  <hr class="pss__hr">

                  {% assign final_desc = '' %}
                  {% if override_desc != blank %}
                    {% assign final_desc = override_desc %}
                  {% elsif product.description != blank %}
                    {% assign final_desc = product.description %}
                  {% endif %}

                  {% if final_desc != blank %}
                    <div class="pss__desc">{{ final_desc | strip_html }}</div>
                  {% endif %}

                  <div class="pss__actions">
                    <button
                      type="button"
                      class="pss__btn pss__btn--add"
                      data-add
                      data-variant-id="{{ variant.id }}"
                      {% unless variant.available %}disabled{% endunless %}
                    >
                      {% if variant.available %}Quick add{% else %}Out of stock{% endif %}
                    </button>

                    <div class="pss__qty" data-qty hidden>
                      <button type="button" class="pss__qty-btn" data-dec aria-label="Decrease">−</button>
                      <span class="pss__qty-val" data-val>1</span>
                      <button type="button" class="pss__qty-btn" data-inc aria-label="Increase">+</button>
                    </div>
                  </div>

                  <div class="pss__status" data-status aria-live="polite"></div>
                </div>
              </div>

              <div class="pss__col pss__col--right">
                <a href="{{ product.url }}" class="pss__image-link">
                  <div class="pss__image {% if img_hover %}pss__image--has-hover{% endif %}">
                    {% if img %}
                      {{ img | image_url: width: 1200 | image_tag:
                        class: 'pss__img pss__img--primary',
                        loading: 'lazy',
                        alt: img.alt | default: product.title
                      }}
                      
                      {% if img_hover %}
                        {{ img_hover | image_url: width: 1200 | image_tag:
                          class: 'pss__img pss__img--hover',
                          loading: 'lazy',
                          alt: img_hover.alt | default: product.title
                        }}
                      {% endif %}
                    {% else %}
                      <div class="pss__img-placeholder">
                        {{ 'product-1' | placeholder_svg_tag }}
                      </div>
                    {% endif %}
                  </div>
                </a>
              </div>
            </article>
          {% endif %}
        {% endfor %}
      </product-showcase-split>

      {% if section.settings.show_view_all and section.settings.view_all_url != blank %}
        <div class="pss__view-all-wrapper">
          <a href="{{ section.settings.view_all_url }}" class="pss__btn pss__btn--view-all">
            {{ section.settings.view_all_text }}
          </a>
        </div>
      {% endif %}
    {% endif %}
  </div>
</section>

<style>
  .pss { width: 100%; }

  .pss__container {
    width: 100%;
    padding-left: var(--pss-container-pad);
    padding-right: var(--pss-container-pad);
    box-sizing: border-box;
  }

  .pss__row {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin-bottom: var(--pss-row-gap);
    overflow: hidden;
  }
  
  .pss__row:last-child { margin-bottom: 0; }
  .pss__col { flex: 1; min-width: 0; }
  .pss [hidden] { display: none !important; }

  .pss__col--left { 
    background: var(--pss-left-bg); 
    display: flex; 
  }
  
  .pss__content {
    width: 100%;
    padding: 60px 75px;
    box-sizing: border-box;
    color: var(--pss-text);
    display: flex;
    flex-direction: column;
  }

  .pss__title-link {
    text-decoration: none;
    color: inherit;
    display: inline-block;
    transition: opacity 0.3s ease;
  }

  .pss__title-link:hover {
    opacity: 0.7;
  }

  .pss__title {
    margin: 0 0 10px 0;
    font-size: var(--pss-title-size);
    font-weight: 700;
    line-height: 1;
    color: var(--pss-text);
    letter-spacing: 0;
  }

  .pss__subtitle {
    margin: 0;
    padding: 6px 0;
    font-size: var(--pss-subtitle-size);
    font-weight: 500;
    letter-spacing: 0;
    color: var(--pss-text);
    line-height: 100%;
  }

  .pss__hr {
    height: 1px;
    background: var(--pss-line);
    width: var(--pss-sep-width);
    margin: 0;
    border: none;
    opacity: 1;
  }

  .pss__meta {
    display: flex;
    align-items: center;
    gap: 3px;
    flex-wrap: wrap;
    font-size: 18px;
    font-weight: 500;
    color: var(--pss-text);
    letter-spacing: 0;
  }

  .pss__price--compare {
    text-decoration: line-through;
    opacity: 0.6;
    font-weight: 500;
  }

  .pss__price--current { font-weight: 700; }
  
  .pss__discount {
    padding-left: 5px;
    background: #000;
    color: #fff;
    font-size: 14px;
    font-style: italic;
    padding: 2px;
  }

  .pss__desc {
    margin: 6px 0;
    font-size: var(--pss-desc-size);
    line-height: 1;
    color: var(--pss-text);
    display: -webkit-box;
    -webkit-line-clamp: var(--pss-desc-lines);
    -webkit-box-orient: vertical;
    overflow: hidden;
    letter-spacing: 0;
    font-weight: 300;
  }

  .pss__actions {
    margin-top: 5px;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
  }

  .pss__btn {
    background: var(--pss-btn-bg);
    color: var(--pss-btn-text);
    border: 0;
    padding: 6px 10px;
    font-size: 18px;
    font-weight: 500;
    cursor: pointer;
    text-transform: capitalize;
    letter-spacing: 0.05em;
    transition: opacity 0.3s ease, background 0.3s ease;
    text-decoration: none;
    display: inline-block;
    text-align: center;
  }
  
  .pss__btn:hover { 
    opacity: 0.85;
    background: #22BD77 !important; 
  }
  
  .pss__btn:disabled { 
    opacity: 0.55; 
    cursor: not-allowed; 
  }

  .pss__qty {
    display: flex;
    align-items: center;
    gap: 12px;
    border: 1px solid var(--pss-line);
    padding: 5px;
  }
  
  .pss__qty-btn {
    width: 35px;
    height: 35px;
    background: transparent;
    color: var(--pss-text);
    border: 0;
    font-size: 18px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    transition: opacity 0.3s ease;
  }
  
  .pss__qty-btn:hover { opacity: 0.7; }
  
  .pss__qty-val {
    min-width: 30px;
    text-align: center;
    font-weight: 700;
    font-size: 16px;
    color: var(--pss-text);
  }

  .pss__status {
    margin-top: 10px;
    min-height: 18px;
    font-size: 13px;
    opacity: 0.8;
  }

  .pss__col--right { 
    background: #fff; 
    display: flex; 
  }

  .pss__image-link {
    width: 100%;
    height: 100%;
    display: block;
    text-decoration: none;
  }

  .pss__image { 
    width: 100%; 
    height: 100%; 
    overflow: hidden;
    position: relative;
  }

  .pss__img { 
    width: 100%; 
    height: 100%; 
    object-fit: cover; 
    display: block;
    transition: opacity 0.4s ease;
  }

  .pss__img--primary {
    position: relative;
    z-index: 2;
  }

  .pss__img--hover {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    opacity: 0;
  }

  .pss__image--has-hover:hover .pss__img--primary {
    opacity: 0;
  }

  .pss__image--has-hover:hover .pss__img--hover {
    opacity: 1;
  }
  
  .pss__img-placeholder {
    width: 100%;
    min-height: 450px;
    background: #f0f0f0;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .pss__view-all-wrapper {
    display: flex;
    margin-top: 20px;
    padding: 5px 0;
  }

  .pss__btn--view-all {
    padding: 6px 10px;
    font-size: 18px;
  }

  @media (max-width: 900px) {
    .pss__container { padding: 0 10px; }
    .pss__row { flex-direction: column-reverse; }
    .pss__content { padding: 40px 20px; }
    .pss__hr { width: 100%; }
    .pss__title { font-size: {{ section.settings.title_size | times: 0.8 }}px; }
    .pss__btn--view-all { width: 100%; max-width: 300px; }
  }

  @media (max-width: 600px) {
    .pss__content { padding: 30px 15px; }
    .pss__title { font-size: {{ section.settings.title_size | times: 0.7 }}px; }
    .pss__subtitle { font-size: {{ section.settings.subtitle_size | times: 0.9 }}px; }
  }
</style>

<script>
(function(){
  class ProductShowcaseSplit extends HTMLElement {
    constructor(){
      super();
      this.cart = new Map();         // variantId -> qty
      this.updateQueue = new Map();  // variantId -> timeoutId
    }

    connectedCallback(){
      this.init();
    }

    async init(){
      await this.loadCart();
      this.addEventListener('click', this.handleClick.bind(this));
      this.updateAllRows();
      // Ensure badge is correct on load too
      this.updateCartCount();
    }

    handleClick(e){
      const addBtn = e.target.closest('[data-add]');
      const decBtn = e.target.closest('[data-dec]');
      const incBtn = e.target.closest('[data-inc]');

      if(addBtn) {
        e.preventDefault();
        this.quickAdd(addBtn);
      } else if(decBtn) {
        e.preventDefault();
        this.decrease(decBtn);
      } else if(incBtn) {
        e.preventDefault();
        this.increase(incBtn);
      }
    }

    async quickAdd(btn){
      const row = btn.closest('[data-row]');
      const vid = btn.dataset.variantId;
      if(!vid || btn.disabled) return;

      const current = this.cart.get(String(vid)) || 0;
      const next = current + 1;

      // optimistic UI
      this.cart.set(String(vid), next);
      this.updateRow(row, next);

      btn.disabled = true;
      const originalText = btn.textContent;
      btn.textContent = 'Adding…';

      try {
        await this.addToCart(String(vid), 1);

        // hard sync after server confirms
        await this.loadCart();
        this.updateAllRows();
        this.updateCartCount();

      } catch(err) {
        // rollback UI
        this.cart.set(String(vid), current);
        this.updateRow(row, current);
        this.showStatus(row, 'Failed to add');
        console.error(err);
      } finally {
        btn.disabled = false;
        btn.textContent = originalText;
      }
    }

    decrease(btn){
      const row = btn.closest('[data-row]');
      const vid = String(row.dataset.variant);

      const current = this.cart.get(vid) || 1;
      const next = Math.max(0, current - 1);

      this.cart.set(vid, next);
      this.updateRow(row, next);
      this.debouncedUpdate(vid, next);
    }

    increase(btn){
      const row = btn.closest('[data-row]');
      const vid = String(row.dataset.variant);

      const current = this.cart.get(vid) || 1;
      const next = current + 1;

      this.cart.set(vid, next);
      this.updateRow(row, next);
      this.debouncedUpdate(vid, next);
    }

    debouncedUpdate(vid, qty){
      if(this.updateQueue.has(vid)){
        clearTimeout(this.updateQueue.get(vid));
      }

      const timeout = setTimeout(async () => {
        await this.updateCartQty(vid, qty);

        // hard sync after update (handles cart rules / limits)
        await this.loadCart();
        this.updateAllRows();
        this.updateCartCount();

        this.updateQueue.delete(vid);
      }, 450);

      this.updateQueue.set(vid, timeout);
    }

    updateRow(row, qty){
      const addBtn = row.querySelector('[data-add]');
      const qtyWrap = row.querySelector('[data-qty]');
      const val = row.querySelector('[data-val]');

      if(!addBtn || !qtyWrap || !val) return;

      if(qty > 0){
        addBtn.hidden = true;
        qtyWrap.hidden = false;
        val.textContent = qty;
      } else {
        addBtn.hidden = false;
        qtyWrap.hidden = true;
        val.textContent = 1;
      }
    }

    updateAllRows(){
      this.querySelectorAll('[data-row]').forEach(row => {
        const vid = String(row.dataset.variant);
        const qty = this.cart.get(vid) || 0;
        this.updateRow(row, qty);
      });
    }

    showStatus(row, msg){
      const el = row.querySelector('[data-status]');
      if(el){
        el.textContent = msg;
        setTimeout(() => el.textContent = '', 3000);
      }
    }

    async loadCart(){
      try {
        const res = await fetch('/cart.js', { credentials: 'same-origin' });
        const data = await res.json();

        this.cart.clear();
        (data.items || []).forEach(item => {
          this.cart.set(String(item.variant_id), Number(item.quantity || 0));
        });

        return data;
      } catch(err) {
        console.error('Cart load failed:', err);
        return null;
      }
    }

    async addToCart(vid, qty){
      const res = await fetch('/cart/add.js', {
        method: 'POST',
        credentials: 'same-origin',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({ id: vid, quantity: qty })
      });

      if(!res.ok) throw new Error('Add failed');

      const data = await res.json();
      this.dispatchCartEvent(data);
      return data;
    }

    async updateCartQty(vid, qty){
      try {
        const res = await fetch('/cart/update.js', {
          method: 'POST',
          credentials: 'same-origin',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({ updates: { [vid]: qty } })
        });

        if(!res.ok) throw new Error('Update failed');

        const data = await res.json();
        this.dispatchCartEvent(data);
        return data;
      } catch(err) {
        console.error('Cart update failed:', err);
        return null;
      }
    }

    // ✅ FIXED: never overwrite .cart-count-bubble wrapper HTML
    async updateCartCount(){
      try {
        const res = await fetch('/cart.js', { credentials: 'same-origin' });
        const cart = await res.json();
        const count = Number(cart.item_count || 0);

        // Dawn/Sense: update bubble spans safely
        document.querySelectorAll('.cart-count-bubble').forEach((bubble) => {
          const visible = bubble.querySelector('span[aria-hidden="true"]');
          const srOnly  = bubble.querySelector('span.visually-hidden');

          if (visible) visible.textContent = String(count);
          if (srOnly)  srOnly.textContent = `${count} item${count === 1 ? '' : 's'}`;

          if (count > 0) {
            bubble.classList.remove('hidden');
            bubble.style.display = '';
          } else {
            bubble.classList.add('hidden');
            bubble.style.display = 'none';
          }
        });

        // Extra compatibility (if theme uses custom badge)
        document.querySelectorAll('[data-cart-count]').forEach((el) => {
          el.textContent = String(count);
        });

        // Notify listeners (cart drawer etc.)
        document.dispatchEvent(new CustomEvent('cart:refresh', {
          detail: { cart },
          bubbles: true
        }));

      } catch(err) {
        console.error('Cart count update failed:', err);
      }
    }

    dispatchCartEvent(data){
      document.dispatchEvent(new CustomEvent('cart:updated', {
        detail: data,
        bubbles: true
      }));

      // Some themes expose theme.cart
      try {
        if(typeof theme !== 'undefined' && theme.cart && typeof theme.cart.getCart === 'function'){
          theme.cart.getCart();
        }
      } catch(e) {}
    }
  }

  if(!customElements.get('product-showcase-split')){
    customElements.define('product-showcase-split', ProductShowcaseSplit);
  }
})();
</script>


{% schema %}
{
  "name": "Split Product Feature",
  "settings": [
    { "type": "header", "content": "Layout" },
    { "type": "range", "id": "container_side_padding", "min": 0, "max": 20, "step": 1, "unit": "%", "label": "Side padding", "default": 10 },
    { "type": "range", "id": "row_gap", "min": 0, "max": 100, "step": 5, "unit": "px", "label": "Gap between rows", "default": 40 },
    { "type": "range", "id": "separator_width", "min": 50, "max": 100, "step": 5, "unit": "%", "label": "Separator width", "default": 80 },
    { "type": "range", "id": "desc_lines", "min": 3, "max": 15, "step": 1, "label": "Description line clamp", "default": 6 },

    { "type": "header", "content": "Typography" },
    { "type": "range", "id": "title_size", "min": 20, "max": 80, "step": 2, "unit": "px", "label": "Title font size", "default": 34 },
    { "type": "range", "id": "subtitle_size", "min": 12, "max": 40, "step": 1, "unit": "px", "label": "Subtitle font size", "default": 18 },
    { "type": "range", "id": "desc_size", "min": 12, "max": 30, "step": 1, "unit": "px", "label": "Description font size", "default": 15 },

    { "type": "header", "content": "Colors" },
    { "type": "color", "id": "left_bg", "label": "Left column background", "default": "#F9F9F9" },
    { "type": "color", "id": "text_color", "label": "Text color", "default": "#121212" },
    { "type": "color", "id": "line_color", "label": "Separator line color", "default": "#DDDDDD" },
    { "type": "color", "id": "button_bg", "label": "Button background", "default": "#121212" },
    { "type": "color", "id": "button_text", "label": "Button text", "default": "#FFFFFF" },

    { "type": "header", "content": "View All Products" },
    { "type": "checkbox", "id": "show_view_all", "label": "Show 'View All' button", "default": true },
    { "type": "text", "id": "view_all_text", "label": "Button text", "default": "View All Products" },
    { "type": "url", "id": "view_all_url", "label": "Button link", "info": "Link to your products collection or shop page" }
  ],
  "blocks": [
    {
      "type": "row",
      "name": "Product row",
      "settings": [
        { "type": "product", "id": "product", "label": "Product" },
        { "type": "text", "id": "subtitle", "label": "Subtitle 1", "default": "Add a subtitle here" },
        { "type": "text", "id": "subtitle_2", "label": "Subtitle 2", "default": "Soothing · Refreshing · Mild" },
        { "type": "text", "id": "weight", "label": "Weight/Volume", "default": "100 ml" },
        { "type": "image_picker", "id": "image", "label": "Override image (optional)" },
        {
          "type": "textarea",
          "id": "override_description",
          "label": "Override description (optional)",
          "info": "If empty, it will use the product description."
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Split Product Feature",
      "blocks": [{ "type": "row" }]
    }
  ]
}
{% endschema %}```


Full section code

Hi @Goodness-Michael ,

Using Sense theme