From ac98cf0f5bcfe1699506c92905731292853bc8fc Mon Sep 17 00:00:00 2001 From: Giovanni Lupo Date: Wed, 1 Jul 2026 18:23:19 +0000 Subject: [PATCH 1/2] STS-3.0.1: collapse variant groups to a single item in listings and counts Adds lib/variants.ts with dedupeVariantGroups() and countDistinctProducts() helpers. Variant sets (e.g. sizes/colors) now render as one product card and count as one item on the homepage, collections overview, collection detail, and shop-all pages, instead of once per size/color folder. --- VERSIONING.md | 1 + app/collections/CollectionCard.tsx | 5 +++-- app/collections/[collectionId]/page.tsx | 8 +++----- app/page.tsx | 7 ++++--- app/shop-all/page.tsx | 3 ++- lib/variants.ts | 25 +++++++++++++++++++++++++ lib/version.ts | 2 +- package.json | 2 +- 8 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 lib/variants.ts diff --git a/VERSIONING.md b/VERSIONING.md index 6f536e1..006023a 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -214,3 +214,4 @@ Potential future additions to version system: ## Questions? For questions about the versioning system, see the About page or contact via the FAQ section. +| STS-3.0.1 | 2026-07-01 | Variant groups count & list as one item across all pages (shared lib/variants.ts helper) | diff --git a/app/collections/CollectionCard.tsx b/app/collections/CollectionCard.tsx index cecf381..777b8ef 100644 --- a/app/collections/CollectionCard.tsx +++ b/app/collections/CollectionCard.tsx @@ -3,6 +3,7 @@ import Link from 'next/link'; import { useEffect, useState, useMemo } from 'react'; import FadeImage from '@/components/FadeImage'; +import { countDistinctProducts } from '@/lib/variants'; interface CollectionCardProps { collection: { @@ -92,8 +93,8 @@ export default function CollectionCard({ collection, design, tick }: CollectionC fontFamily: design.fonts.bodyFont, }} > - {collection.products.length}{' '} - {collection.products.length === 1 ? 'product' : 'products'} + {countDistinctProducts(collection.products)}{' '} + {countDistinctProducts(collection.products) === 1 ? 'product' : 'products'}

{ - if (!product.variants || product.variants.length === 0) return true; - return product.variants[0].id === product.id; - }); + // One card per variant group (shared helper — see lib/variants.ts) + const displayProducts = dedupeVariantGroups(sortedProducts); return (
diff --git a/app/page.tsx b/app/page.tsx index 97de2d8..84c296a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,6 +3,7 @@ import Link from 'next/link'; import { useEffect, useState } from 'react'; import FadeImage from '@/components/FadeImage'; +import { countDistinctProducts, dedupeVariantGroups } from '@/lib/variants'; export default function Home() { const [design, setDesign] = useState(null); @@ -33,7 +34,7 @@ export default function Home() { products.push(product); }); }); - setAllProducts(products); + setAllProducts(dedupeVariantGroups(products)); try { const inventoryResponse = await fetch('/api/inventory'); @@ -299,8 +300,8 @@ export default function Home() { fontFamily: design.fonts.bodyFont, }} > - {collection.products.length}{' '} - {collection.products.length === 1 ? 'product' : 'products'} + {countDistinctProducts(collection.products)}{' '} + {countDistinctProducts(collection.products) === 1 ? 'product' : 'products'}

(products: T[]): T[] { + return products.filter( + (p) => !p.variants || p.variants.length === 0 || p.variants[0].id === p.id + ); +} + +// Count of distinct products, treating each variant group as a single item. +export function countDistinctProducts(products: VariantLike[]): number { + return dedupeVariantGroups(products).length; +} diff --git a/lib/version.ts b/lib/version.ts index 5e70c77..3cfb870 100644 --- a/lib/version.ts +++ b/lib/version.ts @@ -36,7 +36,7 @@ * 4. Commit with version number in commit message */ -export const VERSION = 'STS-2.6.3'; +export const VERSION = 'STS-3.0.1'; export const VERSION_INFO = { name: 'Shop Template System', diff --git a/package.json b/package.json index c370421..e160e27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shop-template-system", - "version": "3.0.0", + "version": "3.0.1", "private": true, "scripts": { "dev": "next dev", From d2640003a246d2de119f63fda0277e21911d17c7 Mon Sep 17 00:00:00 2001 From: Giovanni Lupo Date: Wed, 1 Jul 2026 18:33:20 +0000 Subject: [PATCH 2/2] STS-3.0.1: variant cards show base name + value chips on listings Listing cards (shop-all, homepage single-collection) now display the variant group base name (e.g. "Branded Sneakers") instead of a single variant folder name ("Branded Sneakers (10)"), and render the available values as chips below via a shared components/VariantChips.tsx, matching the collection detail page. Adds variantDisplayName() to lib/variants.ts. --- app/page.tsx | 8 +++-- app/shop-all/page.tsx | 8 +++-- components/VariantChips.tsx | 59 +++++++++++++++++++++++++++++++++++++ lib/variants.ts | 6 ++++ 4 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 components/VariantChips.tsx diff --git a/app/page.tsx b/app/page.tsx index 84c296a..71226ca 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,8 @@ import Link from 'next/link'; import { useEffect, useState } from 'react'; import FadeImage from '@/components/FadeImage'; -import { countDistinctProducts, dedupeVariantGroups } from '@/lib/variants'; +import VariantChips from '@/components/VariantChips'; +import { countDistinctProducts, dedupeVariantGroups, variantDisplayName } from '@/lib/variants'; export default function Home() { const [design, setDesign] = useState(null); @@ -216,7 +217,7 @@ export default function Home() {
@@ -232,8 +233,9 @@ export default function Home() { className="font-bold text-lg mb-1" style={{ color: design.colors.primary, fontFamily: design.fonts.titleFont }} > - {product.name} + {variantDisplayName(product)} +

SKU: {product.sku}

diff --git a/app/shop-all/page.tsx b/app/shop-all/page.tsx index 2bf332f..93634c5 100644 --- a/app/shop-all/page.tsx +++ b/app/shop-all/page.tsx @@ -3,7 +3,8 @@ import Link from 'next/link'; import { useEffect, useState } from 'react'; import FadeImage from '@/components/FadeImage'; -import { dedupeVariantGroups } from '@/lib/variants'; +import VariantChips from '@/components/VariantChips'; +import { dedupeVariantGroups, variantDisplayName } from '@/lib/variants'; type SortOption = 'name-asc' | 'name-desc' | 'price-asc' | 'price-desc' | 'units-asc' | 'units-desc' | 'total-asc' | 'total-desc'; @@ -152,7 +153,7 @@ export default function ShopAllPage() {
@@ -182,8 +183,9 @@ export default function ShopAllPage() { fontFamily: design.fonts.titleFont, }} > - {product.name} + {variantDisplayName(product)} +

; +}): ReactElement | null { + if (!product.variants || product.variants.length === 0) return null; + const seen = new Set(); + const uniqueValues = product.variants + .map((v) => v.values[0]) + .filter((val) => val && !seen.has(val) && !!seen.add(val)); + + return ( +

+

+ {product.variantDimensions?.[0] ?? 'Options'}: +

+
+ {uniqueValues.map((value) => { + const withValue = product.variants!.filter((v) => v.values[0] === value); + const allOOS = withValue.length > 0 && withValue.every( + (v) => stockMap[v.id] !== undefined && stockMap[v.id] <= 0 + ); + const isActive = product.variantValues?.[0] === value; + return ( + + {value} + + ); + })} +
+
+ ); +} diff --git a/lib/variants.ts b/lib/variants.ts index a2b2a3e..28cd5d3 100644 --- a/lib/variants.ts +++ b/lib/variants.ts @@ -23,3 +23,9 @@ export function dedupeVariantGroups(products: T[]): T[] { export function countDistinctProducts(products: VariantLike[]): number { return dedupeVariantGroups(products).length; } + +// Display name for a card: the variant group base name (e.g. "Branded Sneakers") +// rather than the folder name of one variant ("Branded Sneakers (10)"). +export function variantDisplayName(product: { name: string; variantGroup?: string }): string { + return product.variantGroup || product.name; +}