[object Object]

← back to Shopify Sample Shipping

TK-11333: stage cart-page shipping notice (live-theme write is classifier-gated)

f0f218bf008caa47a416a010ff58ef3721848d33 · 2026-09-09 15:26:08 -0700 · Steve Abrams

Prepared inject-cart-notice.mjs (adds a static 'samples over 10 units incur
shipping' notice to sections/cart.liquid on the main theme carnegie-color-swatch),
backup of original saved, restore-cart-notice.mjs for one-command revert. The live
theme PUT is blocked by the auto-mode classifier (customer-facing publish gate) —
Steve runs it via `!`. Also created 8-distinct free order #33049 earlier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit f0f218bf008caa47a416a010ff58ef3721848d33
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 15:26:08 2026 -0700

    TK-11333: stage cart-page shipping notice (live-theme write is classifier-gated)
    
    Prepared inject-cart-notice.mjs (adds a static 'samples over 10 units incur
    shipping' notice to sections/cart.liquid on the main theme carnegie-color-swatch),
    backup of original saved, restore-cart-notice.mjs for one-command revert. The live
    theme PUT is blocked by the auto-mode classifier (customer-facing publish gate) —
    Steve runs it via `!`. Also created 8-distinct free order #33049 earlier.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 inject-cart-notice.mjs          |  24 +++++++
 read-cart-section.mjs           |   9 +++
 restore-cart-notice.mjs         |   7 ++
 theme-inspect.mjs               |  25 +++++++
 verification/cart.liquid.backup | 145 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 210 insertions(+)

diff --git a/inject-cart-notice.mjs b/inject-cart-notice.mjs
new file mode 100644
index 0000000..b9307b8
--- /dev/null
+++ b/inject-cart-notice.mjs
@@ -0,0 +1,24 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const THEME='145121607731', KEY='sections/cart.liquid';
+const base=`https://${SHOP}/admin/api/2026-07/themes/${THEME}/assets.json`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+let val=fs.readFileSync('verification/cart.liquid.backup','utf8'); // original backup
+const anchor=`<h1 class="page-title">{{ 'cart.general.header' | t }}</h1>`;
+if(!val.includes(anchor)){console.log('ABORT: anchor not found');process.exit(1);}
+if(val.includes('tk-ship-notice')){console.log('notice already present — skipping insert');}
+else{
+  const notice=`\n  {% comment %}TK-11333 sample shipping notice{% endcomment %}\n  <p class="tk-ship-notice" style="margin:12px 0;padding:10px 14px;background:#f6f6f4;border:1px solid #e2e2dd;border-radius:6px;font-size:14px;line-height:1.4;color:#333;">\n    <strong>Sample shipping:</strong> orders of <strong>10 samples or fewer ship free</strong> (no tracking). Sample orders <strong>over 10 units</strong> include a shipping charge.\n  </p>`;
+  val=val.replace(anchor, anchor+notice);
+}
+const put=await fetch(base,{method:'PUT',headers:H,body:JSON.stringify({asset:{key:KEY,value:val}})});
+const pj=await put.json();
+console.log('PUT status',put.status,'| errors:',JSON.stringify(pj.errors||pj.asset?.key||'ok'));
+if(!put.ok){console.log('PUT FAILED — live theme unchanged (backup intact)');process.exit(1);}
+// verify asset now contains the notice
+const chk=await (await fetch(`${base}?asset[key]=${encodeURIComponent(KEY)}`,{headers:H})).json();
+console.log('asset contains notice?', (chk.asset?.value||'').includes('tk-ship-notice'));
+// verify the live cart page renders + shows it
+for(const url of [`https://designerwallcoverings.com/cart`,`https://${SHOP}/cart`]){
+  try{const r=await fetch(url,{redirect:'follow'});const html=await r.text();console.log(`GET ${url} -> ${r.status} | notice visible: ${html.includes('over 10 units')}`);}catch(e){console.log(`GET ${url} err`,e.message);}
+}
diff --git a/read-cart-section.mjs b/read-cart-section.mjs
new file mode 100644
index 0000000..87b75f9
--- /dev/null
+++ b/read-cart-section.mjs
@@ -0,0 +1,9 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const r=await fetch(`https://${SHOP}/admin/api/2026-07/themes/145121607731/assets.json?asset[key]=sections/cart.liquid`,{headers:{'X-Shopify-Access-Token':TOKEN}});
+const v=(await r.json()).asset?.value||'';
+fs.mkdirSync('verification',{recursive:true});
+fs.writeFileSync('verification/cart.liquid.backup',v);
+console.log('bytes:',v.length,'| saved backup -> verification/cart.liquid.backup');
+console.log('=== FIRST 40 LINES ===');
+console.log(v.split('\n').slice(0,40).map((l,i)=>(i+1)+': '+l).join('\n'));
diff --git a/restore-cart-notice.mjs b/restore-cart-notice.mjs
new file mode 100644
index 0000000..68acb97
--- /dev/null
+++ b/restore-cart-notice.mjs
@@ -0,0 +1,7 @@
+// UNDO: restore the original cart.liquid from backup (removes the TK-11333 notice).
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const THEME='145121607731', KEY='sections/cart.liquid';
+const val=fs.readFileSync('verification/cart.liquid.backup','utf8');
+const r=await fetch(`https://${SHOP}/admin/api/2026-07/themes/${THEME}/assets.json`,{method:'PUT',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({asset:{key:KEY,value:val}})});
+console.log('restore status',r.status, (await r.json()).asset?.key||'');
diff --git a/theme-inspect.mjs b/theme-inspect.mjs
new file mode 100644
index 0000000..50c5b60
--- /dev/null
+++ b/theme-inspect.mjs
@@ -0,0 +1,25 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+const base=`https://${SHOP}/admin/api/2026-07`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+const get=async p=>{const r=await fetch(base+p,{headers:H});return {status:r.status,json:await r.json().catch(()=>null)};};
+// 1) themes
+const t=await get('/themes.json');
+const themes=(t.json?.themes)||[];
+const main=themes.find(x=>x.role==='main');
+console.log('themes:',themes.map(x=>`${x.id} "${x.name}" [${x.role}]`).join(' | '));
+if(!main){console.log('no main theme / status',t.status);process.exit(0);}
+console.log('\nMAIN theme id=',main.id,'name=',main.name);
+// 2) list cart-related assets
+const a=await get(`/themes/${main.id}/assets.json`);
+const keys=(a.json?.assets||[]).map(x=>x.key);
+const cartKeys=keys.filter(k=>/cart/i.test(k));
+console.log('\ncart-related assets:');
+cartKeys.forEach(k=>console.log('  '+k));
+// 3) peek at the cart template JSON (OS 2.0) if present
+for(const key of ['templates/cart.json','templates/cart.liquid']){
+  if(!keys.includes(key))continue;
+  const as=await get(`/themes/${main.id}/assets.json?asset[key]=${encodeURIComponent(key)}`);
+  const val=as.json?.asset?.value||'';
+  console.log(`\n--- ${key} (first 800 chars) ---`);
+  console.log(val.slice(0,800));
+}
diff --git a/verification/cart.liquid.backup b/verification/cart.liquid.backup
new file mode 100644
index 0000000..9d32ef2
--- /dev/null
+++ b/verification/cart.liquid.backup
@@ -0,0 +1,145 @@
+{%- capture taxes_shipping_checkout -%}
+  {%- if cart.taxes_included and shop.shipping_policy.body != blank -%}
+    {{ 'cart.general.taxes_included_and_shipping_policy_html' | t: link: shop.shipping_policy.url }}
+  {%- elsif cart.taxes_included -%}
+    {{ 'cart.general.taxes_included_but_shipping_at_checkout' | t }}
+  {%- elsif shop.shipping_policy.body != blank -%}
+    {{ 'cart.general.taxes_and_shipping_policy_at_checkout_html' | t: link: shop.shipping_policy.url }}
+  {%- else -%}
+    {{ 'cart.general.tax_and_shipping' | t }}
+  {%- endif -%}
+{%- endcapture -%}
+
+<script
+  type="application/json"
+  data-section-type="static-cart"
+  data-section-id="{{ section.id }}"
+  data-section-data
+>
+  {
+    "hasShippingCalculator": {{ section.settings.shipping-calculator }}
+  }
+</script>
+
+<section
+  class="cart"
+  {% if section.settings.shipping-calculator %}data-shipping-calculator{% endif %}
+  data-section-id="{{ section.id }}"
+  data-section-type="cart">
+  <h1 class="page-title">{{ 'cart.general.header' | t }}</h1>
+
+  {% render 'breadcrumbs' %}
+
+  {% if cart.item_count > 0 %}
+
+    <form class="cart-form" action="{{ routes.cart_url }}" method="post">
+
+      {% render 'cart-table' %}
+      <div class="cart-tools {% if section.settings.shipping-calculator %}has-shipping-calculator{% endif %} {% if section.settings.special-instructions %}has-special-instructions{% endif %}">
+
+        {% if section.settings.special-instructions %}
+          <div class="cart-instructions">
+            <h2>{{ 'cart.general.special_instructions' | t }}</h2>
+            <textarea class="textarea" placeholder="{{ section.settings.special-instructions-placeholder | escape }}" name="note">{{ cart.note }}</textarea>
+          </div>
+        {% endif %}
+
+        <div class="cart-totals">
+          {% render 'cart-discounts' %}
+          <p class="cart-price"><span class="money" data-total-price>{{ cart.total_price | money }}</span></p>
+          <p class="cart-message">{{ taxes_shipping_checkout }}</p>
+          <div class="cart-buttons-container">
+            <noscript>
+              <input class="cart-update button secondary input" type="submit" name="update" value="{{ 'general.general.update' | t }}">
+            </noscript>
+            <button
+              class="cart-checkout button"
+              type="submit"
+              name="checkout"
+              value="{{ 'cart.general.submit' | t }}"
+            >
+              {% if section.settings.enable_checkout_lock_icon %}
+                {% render 'icons',
+                  id: 'checkout-lock',
+                %}
+              {% endif %}
+              <span>{{ 'cart.general.submit' | t }}</span>
+            </button>
+          </div>
+          {% if additional_checkout_buttons %}
+            <div class="additional-checkout-buttons">{{ content_for_additional_checkout_buttons }}</div>
+          {% endif %}
+        </div>
+
+      </div>
+
+    </form>
+
+    {% if section.settings.shipping-calculator %}
+      {% render 'shipping-calculator' %}
+    {% endif %}
+
+  {% else %}
+
+    {%- capture continueLink -%}
+      {% assign continue_href = section.settings.continue_shopping_link | default: routes.all_products_collection_url %}
+      <a href="{{ continue_href }}">{{ 'cart.general.continue_link' | t }}</a>
+    {%- endcapture -%}
+    <p class="empty">{{ 'cart.general.empty_html' | t: continue_link: continueLink }}</p>
+
+  {% endif %}
+</section>
+
+{% schema %}
+{
+  "name": "Cart",
+  "settings": [
+    {
+      "type": "checkbox",
+      "id": "enable_checkout_lock_icon",
+      "label": "Show lock icon on checkout button",
+      "default": false
+    },
+    {
+      "type": "header",
+      "content": "Order Notes"
+    },
+    {
+      "type": "checkbox",
+      "id": "special-instructions",
+      "label": "Enable"
+    },
+    {
+      "type": "text",
+      "id": "special-instructions-placeholder",
+      "label": "Placeholder text",
+      "default": "Write any special instructions for your shipment here."
+    },
+    {
+      "type": "header",
+      "content": "Shipping rate calculator"
+    },
+    {
+      "type": "checkbox",
+      "id": "shipping-calculator",
+      "label": "Enable"
+    },
+    {
+      "type": "text",
+      "id": "shipping_calculator_default_country",
+      "label": "Default country",
+      "default": "United States"
+    },
+    {
+      "type": "header",
+      "content": "Continue shopping button"
+    },
+    {
+      "type": "url",
+      "id": "continue_shopping_link",
+      "label": "Link"
+    }
+  ]
+}
+
+{% endschema %}
\ No newline at end of file

← f124f7e TK-11333: raise free band to $45 (10 samples free) + 8-disti  ·  back to Shopify Sample Shipping  ·  TK-11333: dedicated-profile re-pilot WORKS + 300-variant pil 0498d46 →