[object Object]

← back to Dw Sarah Rowland Searchfix

TK-10037: add Sarah Rowland designer photo+bio block to collection page (self-hosted theme asset); find full Koroseal designer roster (13)

26fabe458a8ba64f7d581d4d1f4d907f37c6570e · 2026-07-30 09:27:05 -0700 · Steve Abrams

Files touched

Diff

commit 26fabe458a8ba64f7d581d4d1f4d907f37c6570e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 30 09:27:05 2026 -0700

    TK-10037: add Sarah Rowland designer photo+bio block to collection page (self-hosted theme asset); find full Koroseal designer roster (13)
---
 apply-bio.mjs                               | 17 +++++++++++++++++
 asset-url.txt                               |  1 +
 coll-backups/sarah-rowland.body.AFTER.html  | 10 ++++++++++
 coll-backups/sarah-rowland.body.BEFORE.html |  3 +++
 designers.tsv                               | 13 +++++++++++++
 read-coll-and-designers.mjs                 | 16 ++++++++++++++++
 upload-asset.mjs                            | 16 ++++++++++++++++
 verify-imgs.mjs                             | 11 +++++++++++
 8 files changed, 87 insertions(+)

diff --git a/apply-bio.mjs b/apply-bio.mjs
new file mode 100644
index 0000000..471c312
--- /dev/null
+++ b/apply-bio.mjs
@@ -0,0 +1,17 @@
+import { gql } from './lib.mjs'; import fs from 'node:fs';
+const bio=fs.readFileSync(new URL('./coll-backups/sarah-rowland.body.BEFORE.html',import.meta.url),'utf8').trim();
+let cdn=fs.readFileSync(new URL('./asset-url.txt',import.meta.url),'utf8').trim();
+const imgSrc=cdn+(cdn.includes('?')?'&':'?')+'width=600';
+const block=`<div class="dw-designer-bio" style="display:flex;gap:28px;align-items:flex-start;flex-wrap:wrap;margin:0 0 8px;">
+<img src="${imgSrc}" alt="Sarah Rowland, wallcovering and textile designer" width="200" style="width:200px;height:auto;border-radius:8px;flex:0 0 auto;" loading="lazy">
+<div style="flex:1 1 300px;min-width:260px;">
+<h2 style="margin:0 0 2px;font-size:1.35rem;letter-spacing:.02em;">Sarah Rowland</h2>
+<p style="margin:0 0 14px;font-style:italic;color:#777;">Wallcovering &amp; Textile Designer &middot; Richmond, Virginia</p>
+${bio}
+</div>
+</div>`;
+fs.writeFileSync(new URL('./coll-backups/sarah-rowland.body.AFTER.html',import.meta.url), block);
+const M=`mutation($id:ID!,$html:String!){ collectionUpdate(input:{id:$id, descriptionHtml:$html}){ collection{ id } userErrors{ field message } } }`;
+const d=await gql(M,{id:'gid://shopify/Collection/291337273395', html:block});
+const ue=d.collectionUpdate.userErrors;
+console.log(ue.length?('userErrors: '+JSON.stringify(ue)):'✓ collection description updated');
diff --git a/asset-url.txt b/asset-url.txt
new file mode 100644
index 0000000..5f18ed6
--- /dev/null
+++ b/asset-url.txt
@@ -0,0 +1 @@
+https://cdn.shopify.com/s/files/1/0015/4117/7456/t/182/assets/designer-sarah-rowland.png?v=1785428762
\ No newline at end of file
diff --git a/coll-backups/sarah-rowland.body.AFTER.html b/coll-backups/sarah-rowland.body.AFTER.html
new file mode 100644
index 0000000..8e9af91
--- /dev/null
+++ b/coll-backups/sarah-rowland.body.AFTER.html
@@ -0,0 +1,10 @@
+<div class="dw-designer-bio" style="display:flex;gap:28px;align-items:flex-start;flex-wrap:wrap;margin:0 0 8px;">
+<img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/t/182/assets/designer-sarah-rowland.png?v=1785428762&width=600" alt="Sarah Rowland, wallcovering and textile designer" width="200" style="width:200px;height:auto;border-radius:8px;flex:0 0 auto;" loading="lazy">
+<div style="flex:1 1 300px;min-width:260px;">
+<h2 style="margin:0 0 2px;font-size:1.35rem;letter-spacing:.02em;">Sarah Rowland</h2>
+<p style="margin:0 0 14px;font-style:italic;color:#777;">Wallcovering &amp; Textile Designer &middot; Richmond, Virginia</p>
+<p>Sarah’s rich experience and varied cultural influences are seen in every inch of her creations. Art, color, music, fashion, and most of all, nature, fuse together in hand-drawn, bespoke patterns that transform hospitality settings and residential spaces alike.</p>
+<p>Nature truly is Sarah’s guide, mentor, and muse. Or as she likes to say: “Mother Nature gives me so many design ideas. She really is the best designer and colorist out there. She also happens to be very generous with her advice!”</p>
+<p>Sarah’s work has been showcased and honored multiple times. In 2022, her patterns for Koroseal were nominated by Interior Design Magazine in the Contract Wallcovering category.</p>
+</div>
+</div>
\ No newline at end of file
diff --git a/coll-backups/sarah-rowland.body.BEFORE.html b/coll-backups/sarah-rowland.body.BEFORE.html
new file mode 100644
index 0000000..cd5441c
--- /dev/null
+++ b/coll-backups/sarah-rowland.body.BEFORE.html
@@ -0,0 +1,3 @@
+<p>Sarah’s rich experience and varied cultural influences are seen in every inch of her creations. Art, color, music, fashion, and most of all, nature, fuse together in hand-drawn, bespoke patterns that transform hospitality settings and residential spaces alike.</p>
+<p>Nature truly is Sarah’s guide, mentor, and muse. Or as she likes to say: “Mother Nature gives me so many design ideas. She really is the best designer and colorist out there. She also happens to be very generous with her advice!”</p>
+<p>Sarah’s work has been showcased and honored multiple times. In 2022, her patterns for Koroseal were nominated by Interior Design Magazine in the Contract Wallcovering category.</p>
\ No newline at end of file
diff --git a/designers.tsv b/designers.tsv
new file mode 100644
index 0000000..876c3f4
--- /dev/null
+++ b/designers.tsv
@@ -0,0 +1,13 @@
+Sarah Rowland	person	/getattachment/redefine-design/2024-June/Meet-the-Creator-Sarah-Rowland/BLOG-BODY-IMAGE-PORTRAIT.png
+Clodagh	person	/getmedia/cc0732e6-27fc-49f8-947d-e746a6e43620/clodagh-bio.jpg
+Roger Thomas	person	/getmedia/5af4ef81-86b2-468e-a8af-d288665ccaf9/ROGER-THOMAS-HEADSHOT.png
+Chris Collacott	person	/getmedia/23fdea8d-d86c-499f-b13d-34887d62928f/Chris-Collacott.jpg
+Kurt Johnson	person	/getmedia/2e90966d-6a92-43de-bbfe-fb124ac022a0/thumbnail_kurt-johnson-sm.jpg
+Mandy Budan	person	/getmedia/b5961500-fbb2-4e92-a626-ba41e7697694/budan-bio-pic-2024.jpg
+Patty Madden	person	/getmedia/604df33d-2b04-49a9-96cb-94276d51ecc5/Patty-waist-up-headshot-2.jpg
+Sondra Alexander	person	/getmedia/267d930d-7e40-433e-ba81-e2bf6561416d/sondra.jpg
+Republic of II BY IV	studio-people	/getmedia/a9debf6d-69b6-4c85-bd57-a2bbed7e4dc4/Dan_Keith_Headshot_Left.jpg
+Evans and Brown	studio-people	/getmedia/d7e3970f-dee4-49ca-ac62-961ecfed86b3/evansbrown.jpg
+Stacy Garcia	studio-asset	/getmedia/c033aac4-5cbd-4715-815b-35a3f0d38e2e/SGNY-2025-3.jpg
+Arte	studio	/getmedia/a825f71c-8c59-4f7c-80c0-2ac3e09ecabb/Arte.jpg
+Alpha Workshops	studio	/getmedia/31cb6b87-6573-4002-80db-1abed53a06b2/alphaworkshops2.jpg
diff --git a/read-coll-and-designers.mjs b/read-coll-and-designers.mjs
new file mode 100644
index 0000000..4cf65a3
--- /dev/null
+++ b/read-coll-and-designers.mjs
@@ -0,0 +1,16 @@
+import { gql } from './lib.mjs'; import fs from 'node:fs';
+// 1) current Sarah Rowland collection body (backup)
+const c=(await gql(`{ collection(id:"gid://shopify/Collection/291337273395"){ id title handle templateSuffix descriptionHtml } }`)).collection;
+fs.mkdirSync(new URL('./coll-backups/',import.meta.url),{recursive:true});
+fs.writeFileSync(new URL('./coll-backups/sarah-rowland.body.BEFORE.html',import.meta.url), c.descriptionHtml||'');
+console.log(`Collection: ${c.title} templateSuffix=${c.templateSuffix||'(default)'} bodyLen=${(c.descriptionHtml||'').length}`);
+console.log('body preview:', (c.descriptionHtml||'(empty)').replace(/\s+/g,' ').slice(0,200));
+// 2) which of the 13 Koroseal designers have Koroseal-vendor products in the DW store?
+const names=['Sarah Rowland','Clodagh','Roger Thomas','Chris Collacott','Kurt Johnson','Mandy Budan','Patty Madden','Sondra Alexander','Stacy Garcia','Republic of II BY IV','Evans and Brown','Arte','Alpha Workshops'];
+console.log('\nKoroseal designers present in DW store (vendor=Koroseal + name in title):');
+for(const n of names){
+  const q=`vendor:Koroseal AND title:*${n.split(' ')[0]}*`;
+  const d=await gql(`query($q:String!){ products(first:5, query:$q){ nodes{ title } } }`,{q});
+  const hits=d.products.nodes.filter(p=>new RegExp(n.split(' ')[0],'i').test(p.title));
+  console.log(`  ${hits.length? '✓ '+hits.length : '·  0'}  ${n}`);
+}
diff --git a/upload-asset.mjs b/upload-asset.mjs
new file mode 100644
index 0000000..9ef27c4
--- /dev/null
+++ b/upload-asset.mjs
@@ -0,0 +1,16 @@
+import fs from 'node:fs'; import os from 'node:os';
+const env=fs.readFileSync(`${os.homedir()}/Projects/secrets-manager/.env`,'utf8');
+const tok=(k)=>(env.match(new RegExp('^'+k+'=(.+)$','m'))||[])[1]?.trim();
+const T=tok('SHOPIFY_THEME_TOKEN')||tok('SHOPIFY_CONTENT_TOKEN')||tok('SHOPIFY_ADMIN_TOKEN');
+const S='designer-laboratory-sandbox.myshopify.com',A='2024-10',ID='144396058675';
+const IMG='https://koroseal.com/getattachment/redefine-design/2024-June/Meet-the-Creator-Sarah-Rowland/BLOG-BODY-IMAGE-PORTRAIT.png';
+const img=await fetch(IMG,{headers:{'User-Agent':'Mozilla/5.0'}});
+const buf=Buffer.from(await img.arrayBuffer());
+console.log('downloaded portrait:', buf.length, 'bytes', img.headers.get('content-type'));
+const put=await fetch(`https://${S}/admin/api/${A}/themes/${ID}/assets.json`,{method:'PUT',
+  headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json'},
+  body:JSON.stringify({asset:{key:'assets/designer-sarah-rowland.png', attachment: buf.toString('base64')}})});
+const j=await put.json();
+if(put.status!==200){ console.log('PUT FAILED', put.status, JSON.stringify(j).slice(0,300)); process.exit(1); }
+console.log('theme asset saved. public_url:', j.asset?.public_url || '(none)');
+fs.writeFileSync(new URL('./asset-url.txt',import.meta.url), j.asset?.public_url||'');
diff --git a/verify-imgs.mjs b/verify-imgs.mjs
new file mode 100644
index 0000000..57f01b4
--- /dev/null
+++ b/verify-imgs.mjs
@@ -0,0 +1,11 @@
+import fs from 'node:fs';
+const rows=fs.readFileSync(new URL('./designers.tsv',import.meta.url),'utf8').trim().split('\n').map(l=>l.split('\t'));
+for(const [name,type,path] of rows){
+  const url='https://koroseal.com'+path;
+  try{
+    const r=await fetch(url,{headers:{'User-Agent':'Mozilla/5.0'}});
+    const ct=r.headers.get('content-type')||'';
+    const ok=r.ok && /image\//.test(ct);
+    console.log(`${ok?'✓':'✗'} ${name.padEnd(22)} ${String(r.status).padEnd(4)} ${ct.split(';')[0].padEnd(12)} ${type}`);
+  }catch(e){ console.log(`✗ ${name.padEnd(22)} ERR ${e.message}`); }
+}

← 4f76e34 auto-save: 2026-07-30T09:16:33 (8 files) — build-titles.mjs  ·  back to Dw Sarah Rowland Searchfix  ·  TK-10037: complete Koroseal designer content library (13 des aa8201e →