chris 623b237826 feat: multi-category items and fix new items not appearing
Items can now belong to multiple Square categories and appear in all
matching tabs (e.g. a Mother's Day balloon also shows under Easter).

Also fixes new items not appearing when the Square account has no
"online" category — previously this caused zero items to load; now
it falls back to showing all items.

Changes:
- CatalogItem gains categories[] + categoryLabels[] (multi-category)
- square.ts collects all non-skip categories per item; "online" filter
  is now optional (show all if category doesn't exist in Square)
- catalog/route.ts propagates categoryOverride into categories[0]
- FeaturedProducts: tabs and filter use the full categories array
- Admin CategoryDisplayEditor sees all categories from multi-cat items

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:39:31 -04:00

61 lines
2.6 KiB
TypeScript

import { NextResponse } from 'next/server'
import { getCatalog } from '@/lib/catalog-cache'
import { readOverrides } from '@/lib/overrides'
import type { CatalogItem } from '@/data/mock-catalog'
function applyOverrides(items: CatalogItem[]): CatalogItem[] {
const overrides = readOverrides()
return items
.map((item) => {
const ov = overrides[item.id]
if (!ov) return item
return {
...item,
featured: ov.featured ?? item.featured,
category: ov.categoryOverride ?? item.category,
categoryLabel: ov.categoryLabelOverride ?? item.categoryLabel,
categories: ov.categoryOverride
? [ov.categoryOverride, ...(item.categories ?? [item.category]).slice(1)]
: (item.categories ?? [item.category]),
categoryLabels: ov.categoryLabelOverride
? [ov.categoryLabelOverride, ...(item.categoryLabels ?? [item.categoryLabel]).slice(1)]
: (item.categoryLabels ?? [item.categoryLabel]),
showColors: ov.showColors != null ? ov.showColors : item.showColors,
colorMin: ov.colorMin ?? item.colorMin,
colorMax: ov.colorMax !== undefined ? ov.colorMax : item.colorMax,
chromeSurchargePerColor: ov.chromeSurchargePerColor ?? item.chromeSurchargePerColor,
disabledColors: ov.disabledColors?.length ? ov.disabledColors : item.disabledColors,
quantityUnit: ov.quantityUnit ?? item.quantityUnit,
description: ov.descriptionOverride ?? item.description,
variations: item.variations
.filter((v) => !(ov.hiddenVariationIds ?? []).includes(v.id)),
modifiers: item.modifiers
.filter((m) => !(ov.hiddenModifierIds ?? []).includes(m.id))
.map((m) => {
const minOverride = ov.modifierMinSelected?.[m.id]
return minOverride !== undefined ? { ...m, minSelected: minOverride } : m
}),
}
})
.filter((item) => !(overrides[item.id]?.hidden))
.sort((a, b) => {
const featDiff = (b.featured ? 1 : 0) - (a.featured ? 1 : 0)
if (featDiff !== 0) return featDiff
const aOrder = overrides[a.id]?.sortOrder ?? 0
const bOrder = overrides[b.id]?.sortOrder ?? 0
return aOrder - bOrder
})
}
export async function GET() {
try {
const { items: rawItems } = await getCatalog()
const items = applyOverrides(rawItems)
return NextResponse.json({ items, source: 'square' })
} catch (err) {
console.error('[catalog] error:', err)
return NextResponse.json({ items: [], source: 'error' }, { status: 500 })
}
}