← back to Customdigitalmurals
add /api/facets returning FILTERED counts (vendors + product_types); honors q + vendor query params
7905e4b5ffeb96633855c9bab7a95af31547c072 · 2026-05-25 21:01:33 -0700 · Steve Abrams
Replaces the all-counts behavior with FILTERED counts so chip-style facet UIs
reflect what selecting each option will actually return. Legacy /api/vendors
kept for back-compat. Vendor names remain admin-only metadata — must not be
rendered on customer-facing UI.
Files touched
Diff
commit 7905e4b5ffeb96633855c9bab7a95af31547c072
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon May 25 21:01:33 2026 -0700
add /api/facets returning FILTERED counts (vendors + product_types); honors q + vendor query params
Replaces the all-counts behavior with FILTERED counts so chip-style facet UIs
reflect what selecting each option will actually return. Legacy /api/vendors
kept for back-compat. Vendor names remain admin-only metadata — must not be
rendered on customer-facing UI.
---
server.js | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/server.js b/server.js
index 028e54c..9d8863c 100644
--- a/server.js
+++ b/server.js
@@ -100,7 +100,8 @@ app.get('/api/products', (req, res) => {
res.json({ total, pages, page, per, products: slice });
});
-// Vendor list for filter dropdown
+// Vendor list (LEGACY — kept for back-compat; new code should use /api/facets).
+// Vendor names are admin-only metadata and must NOT render on customer-facing UI.
app.get('/api/vendors', (_req, res) => {
const counts = {};
ALL.forEach(p => { counts[p.vendor] = (counts[p.vendor] || 0) + 1; });
@@ -110,6 +111,45 @@ app.get('/api/vendors', (_req, res) => {
res.json(list);
});
+// Facets — counts reflect the CURRENT FILTERED result set (q + vendor), so the
+// numbers shown next to each facet option match what will actually be returned
+// if the user selects it. NOTE: `vendors` here is admin-only metadata; never
+// render vendor names on customer-facing UI (DW rule).
+app.get('/api/facets', (req, res) => {
+ const q = (req.query.q || '').toLowerCase().trim();
+ const vendor = (req.query.vendor || '').toLowerCase().trim();
+
+ let filtered = ALL;
+ if (q) {
+ filtered = filtered.filter(p =>
+ p.title.toLowerCase().includes(q) ||
+ (p.vendor || '').toLowerCase().includes(q) ||
+ (p.product_type || '').toLowerCase().includes(q) ||
+ (p.tags || []).some(t => t.toLowerCase().includes(q))
+ );
+ }
+ if (vendor) {
+ filtered = filtered.filter(p => (p.vendor || '').toLowerCase() === vendor);
+ }
+
+ const vendors = {};
+ const productTypes = {};
+ filtered.forEach(p => {
+ if (p.vendor) vendors[p.vendor] = (vendors[p.vendor] || 0) + 1;
+ if (p.product_type) productTypes[p.product_type] = (productTypes[p.product_type] || 0) + 1;
+ });
+
+ const toList = obj => Object.entries(obj)
+ .sort((a, b) => b[1] - a[1])
+ .map(([name, count]) => ({ name, count }));
+
+ res.json({
+ total: filtered.length,
+ vendors: toList(vendors),
+ product_types: toList(productTypes),
+ });
+});
+
// Single product page
app.get('/product/:handle', (req, res) => {
const p = ALL.find(x => x.handle === req.params.handle);
← 27a4699 retag aesthetic from PG: 3663 rows updated
·
back to Customdigitalmurals
·
remove vendor names from customer UI — replace with 'Designe f60aec2 →