← back to Raffiawallcoverings
server.js mechanical fixes: (1) /api/facets drill-down — aesthetics counted over the q-filtered set (not the aesthetic-filtered one), vendors over q+aesthetic, total = fully filtered; (2) 301 redirect legacy /(history|vocabulary|sourcing|care|trade|index).html -> clean URLs before express.static (was serving both forms = duplicate content)
638b7220e50b5e4de6da47751fe794ae38ca0adf · 2026-06-01 07:41:35 -0700 · steve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 638b7220e50b5e4de6da47751fe794ae38ca0adf
Author: steve <steve@designerwallcoverings.com>
Date: Mon Jun 1 07:41:35 2026 -0700
server.js mechanical fixes: (1) /api/facets drill-down — aesthetics counted over the q-filtered set (not the aesthetic-filtered one), vendors over q+aesthetic, total = fully filtered; (2) 301 redirect legacy /(history|vocabulary|sourcing|care|trade|index).html -> clean URLs before express.static (was serving both forms = duplicate content)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
server.js | 40 +++++++++++++++++++++++++++++-----------
1 file changed, 29 insertions(+), 11 deletions(-)
diff --git a/server.js b/server.js
index 0bce6f8..0dcc8d6 100644
--- a/server.js
+++ b/server.js
@@ -147,6 +147,13 @@ app.use(express.json({ limit: '256kb' }));
require('./_universal-contact')(app, { siteName: "Raffia Wallcoverings", zdColor: "#a87830", zdPosition: 'right' });
require('./_universal-auth')(app, { siteName: "raffiawallcoverings" });
+// 301 legacy .html -> clean URL (must precede express.static so the .html form
+// isn't served raw = duplicate content). index.html stays at / via static.
+app.get(/^\/(history|vocabulary|sourcing|care|trade|index)\.html$/, (req, res) => {
+ const slug = req.path.slice(1).replace(/\.html$/, '');
+ res.redirect(301, slug === 'index' ? '/' : '/' + slug);
+});
+
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/products', (req, res) => {
@@ -176,21 +183,32 @@ app.get('/api/sliders', (req, res) => {
});
app.get('/api/facets', (req, res) => {
- // total counts the FILTERED list (same filters as /api/products) so the
- // header "N Patterns." reflects the current search/facet, not the catalog total.
+ // Drill-down counts: each facet dimension is counted over the set filtered by
+ // the OTHER active facets (so picking aesthetic=X still shows the counts of the
+ // other aesthetics you could switch to), and total = the fully-filtered set —
+ // matching the header "N Patterns." to the current search/facet, not the catalog.
const { q, aesthetic } = req.query;
- let list = PRODUCTS_NICHE;
- if (q) {
+ const byQ = (p) => {
+ if (!q) return true;
const needle = String(q).toLowerCase();
- list = list.filter(p => (p.title || '').toLowerCase().includes(needle) || (p.tags || []).some(t => t.toLowerCase().includes(needle)));
+ return (p.title || '').toLowerCase().includes(needle) || (p.tags || []).some(t => t.toLowerCase().includes(needle));
+ };
+ const byAesthetic = (p) => !aesthetic || aesthetic === 'all' || p.aesthetic === aesthetic;
+
+ // aesthetics dimension: count over everything EXCEPT the aesthetic filter
+ const aesthetics = {};
+ for (const p of PRODUCTS_NICHE) {
+ if (byQ(p)) aesthetics[p.aesthetic] = (aesthetics[p.aesthetic] || 0) + 1;
}
- if (aesthetic && aesthetic !== 'all') list = list.filter(p => p.aesthetic === aesthetic);
- const aesthetics = {}; const vendors = {};
- for (const p of list) {
- aesthetics[p.aesthetic] = (aesthetics[p.aesthetic] || 0) + 1;
- vendors[p.vendor] = (vendors[p.vendor] || 0) + 1;
+ // vendors dimension: count over q + aesthetic (vendor is its own dimension)
+ const vendors = {};
+ for (const p of PRODUCTS_NICHE) {
+ if (byQ(p) && byAesthetic(p)) vendors[p.vendor] = (vendors[p.vendor] || 0) + 1;
}
- res.json({ aesthetics, vendors, total: list.length });
+ // total: the fully-filtered set (same predicates as /api/products)
+ const total = PRODUCTS_NICHE.filter(p => byQ(p) && byAesthetic(p)).length;
+
+ res.json({ aesthetics, vendors, total });
});
app.get('/api/health', (req, res) => res.json({ status: 'ok', count: PRODUCTS_NICHE.length, dropped: DROPPED }));
← f62cb8c remove public/index.html.bak-20260526152130 (was served by e
·
back to Raffiawallcoverings
·
noopener,noreferrer on constellation window.open popup (FB t dcda1f6 →