← back to Designer Wallcoverings
PDP qty-inline: move quantity stepper to the right of the price (staged on dev theme, live gated)
631ae2c3a68f2d6451a96ebe0e70e854929caedc · 2026-07-02 11:58:45 -0700 · Steve
Files touched
A shopify/push-qty-inline-to-theme.sh
Diff
commit 631ae2c3a68f2d6451a96ebe0e70e854929caedc
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 2 11:58:45 2026 -0700
PDP qty-inline: move quantity stepper to the right of the price (staged on dev theme, live gated)
---
shopify/push-qty-inline-to-theme.sh | 114 ++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/shopify/push-qty-inline-to-theme.sh b/shopify/push-qty-inline-to-theme.sh
new file mode 100644
index 00000000..1c5f081f
--- /dev/null
+++ b/shopify/push-qty-inline-to-theme.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# DW: move the quantity stepper to the RIGHT of the price on every PDP.
+# Injects an idempotent style+script block into layout/theme.liquid that
+# relocates .mm_quantity (+ its Quantity label) into .product__price as a
+# flex row. Both nodes stay inside the add-to-cart form, so submission,
+# the -/+ handlers, and mm_validate min/step enforcement are untouched.
+#
+# Drift-safe: pulls current theme.liquid, strips any prior dw-qty-inline
+# block, re-inserts, backs up the original, pushes, verifies.
+#
+# Usage:
+# bash push-qty-inline-to-theme.sh # dev theme (default)
+# TARGET=live bash push-qty-inline-to-theme.sh # LIVE theme (role=main)
+set -euo pipefail
+cd "$(dirname "$0")"
+
+STORE="designer-laboratory-sandbox.myshopify.com"
+API="2024-10"
+SECRETS="$HOME/Projects/secrets-manager/.env"
+DEV_NAME="Updated copy of NewWall Template [Dev]"
+TARGET="${TARGET:-dev}"
+
+TOK="${THEME_TOKEN:-$(grep -hE '^SHOPIFY_THEME_TOKEN=' "$SECRETS" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"'"'"' ')}"
+[ -z "$TOK" ] && { echo "No theme token in $SECRETS"; exit 1; }
+echo "token …${TOK: -4} target=$TARGET"
+
+mkdir -p theme-backups
+python3 - "$STORE" "$API" "$TOK" "$DEV_NAME" "theme-backups" "$TARGET" <<'PY'
+import sys, json, re, urllib.request, urllib.parse, datetime
+store, api, tok, dev_name, bkdir, target = sys.argv[1:7]
+H = {"X-Shopify-Access-Token": tok, "Content-Type": "application/json"}
+def get(url): return json.load(urllib.request.urlopen(urllib.request.Request(url, headers=H)))
+
+themes = get(f"https://{store}/admin/api/{api}/themes.json")["themes"]
+for t in themes: print(" ", t["id"], t["role"], "|", t["name"])
+if target == "live":
+ match = [t for t in themes if t["role"] == "main"]
+else:
+ match = [t for t in themes if t["name"] == dev_name] or \
+ [t for t in themes if t["role"] == "development"]
+ match = [t for t in match if t["role"] != "main"]
+if not match: print("target theme not found"); sys.exit(1)
+theme = match[0]; tid = theme["id"]
+print(f'=== target: {tid} "{theme["name"]}" (role={theme["role"]}) ===')
+
+key = "layout/theme.liquid"
+q = urllib.parse.urlencode({"asset[key]": key})
+cur = get(f"https://{store}/admin/api/{api}/themes/{tid}/assets.json?{q}")["asset"]["value"]
+stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
+bk = f"{bkdir}/theme.liquid.{target}-{tid}.{stamp}.bak"
+open(bk, "w").write(cur); print(f"backup: {bk} ({len(cur)} bytes)")
+
+BLOCK = r"""
+{% if template contains 'product' %}
+<!-- DW: qty-inline v1 — quantity stepper moved to the right of the price (2026-07-02) -->
+<style>
+ .dw-price-qty-row{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
+ .dw-price-qty-row .product-price{margin:0}
+ .dw-price-qty-row .product-price__unit-price{flex-basis:100%}
+ .dw-qty-inline{display:inline-flex;align-items:center;gap:8px}
+ .dw-qty-inline .product-option-quantity-label{margin:0;white-space:nowrap}
+ .dw-qty-inline .mm_quantity{margin:0}
+ .dw-qty-inline .product-option-quantity-label.hidden,
+ .dw-qty-inline .mm_quantity.hidden{display:none!important}
+</style>
+<script>
+(function(){
+ function relocate(){
+ if(document.querySelector('.dw-qty-inline')) return;
+ var qty=document.querySelector('form[action*="/cart/add"] .mm_quantity');
+ if(!qty) return;
+ var form=qty.closest('form');
+ var priceBox=(form&&form.querySelector('.product__price'))||document.querySelector('.product__price');
+ if(!priceBox) return;
+ var label=(form||document).querySelector('label.product-option-quantity-label');
+ var wrap=document.createElement('span'); wrap.className='dw-qty-inline';
+ if(label) wrap.appendChild(label);
+ wrap.appendChild(qty);
+ priceBox.classList.add('dw-price-qty-row');
+ priceBox.appendChild(wrap);
+ }
+ function init(){ relocate(); setTimeout(relocate,400); setTimeout(relocate,1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
+})();
+</script>
+<!-- /DW qty-inline -->
+{% endif %}
+"""
+
+src = cur
+# strip any prior injected qty-inline block (idempotent update in place)
+src = re.sub(r"\n*\{%-?\s*if template contains 'product'\s*-?%\}\s*\n?<!-- DW: qty-inline.*?<!-- /DW qty-inline -->\s*\{%-?\s*endif\s*-?%\}\n*",
+ "\n", src, flags=re.S)
+# IMPORTANT: this theme.liquid opens with a {% if dw_is_blocked %} China-block
+# branch that has its OWN </head> — inserting before the FIRST </head> lands in
+# that never-rendered branch. Insert before the LAST </head> (the real page).
+pos = src.rfind("</head>")
+if pos != -1:
+ src = src[:pos] + BLOCK + "\n" + src[pos:]
+else:
+ src = BLOCK + src
+
+if src == cur: print("no change needed. done."); sys.exit(0)
+
+body = json.dumps({"asset": {"key": key, "value": src}}).encode()
+r = urllib.request.urlopen(urllib.request.Request(
+ f"https://{store}/admin/api/{api}/themes/{tid}/assets.json", data=body, method="PUT", headers=H))
+print("PUT HTTP", r.status, "OK", f"({len(src)} bytes)")
+
+ver = get(f"https://{store}/admin/api/{api}/themes/{tid}/assets.json?{q}")["asset"]["value"]
+print("verify: dw-qty-inline x", ver.count("DW: qty-inline"))
+print(f"\nPREVIEW: https://www.designerwallcoverings.com/products/tate-trellis-green-anna-french?preview_theme_id={tid}")
+print(f"revert: PUT {bk} back to {key} on theme {tid}")
+PY
← 7568c2fa auto-save: 2026-07-02T11:47:40 (7 files) — pending-approval/
·
back to Designer Wallcoverings
·
limited-stock badge: fix dead-branch insertion (China-block 2f00d11c →