Fix bouquet builder: bypass Online filter via dedicated API endpoint
Items in the Square "Build" category don't need the "Online" category
to appear in the bouquet builder. A new /api/bouquet-items endpoint
calls getSquareCatalog({ filterCategory: 'build' }) which skips the
Online filter and returns only Build-category items directly from Square.
BouquetPicker now fetches from /api/bouquet-items instead of /api/catalog.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0e1d8e5af4
commit
5e1db823cb
23
estore/src/app/api/bouquet-items/route.ts
Normal file
23
estore/src/app/api/bouquet-items/route.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getSquareCatalog } from '@/lib/square'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* GET /api/bouquet-items
|
||||
* Returns catalog items in the Square "Build" category, bypassing the
|
||||
* Online-category filter so bouquet components don't need to be listed
|
||||
* items in the main storefront.
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const items = await getSquareCatalog({ filterCategory: 'build' })
|
||||
return NextResponse.json({ items }, {
|
||||
headers: { 'Cache-Control': 'public, max-age=30, stale-while-revalidate=60' },
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : String(err)
|
||||
console.error('[bouquet-items] error:', msg)
|
||||
return NextResponse.json({ items: [] }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@ -63,22 +63,13 @@ export default function BouquetPicker({ product, onClose }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetch(BASE + '/api/catalog').then((r) => r.ok ? r.json() : { items: [] }),
|
||||
fetch(BASE + '/api/bouquet-items').then((r) => r.ok ? r.json() : { items: [] }),
|
||||
fetch(BASE + '/colors.json').then((r) => r.ok ? r.json() : []),
|
||||
]).then(([{ items }, families]: [{ items: CatalogItem[] }, ColorFamily[]]) => {
|
||||
// Log all unique category slugs so we can verify "build" is present
|
||||
const allSlugs = Array.from(new Set((items as CatalogItem[]).flatMap((i) => i.categories ?? [i.category]))).sort()
|
||||
console.log('[BouquetPicker] category slugs in catalog:', allSlugs)
|
||||
setDebugSlugs(allSlugs)
|
||||
|
||||
const inBuild = (item: CatalogItem) =>
|
||||
(item.categories ?? []).includes('build') || item.category === 'build'
|
||||
|
||||
// Everything in the Build category — non-showColors items get quantity pickers,
|
||||
// showColors items (e.g. the 11" latex) get the color picker treatment
|
||||
const mylars = items.filter((i) => inBuild(i) && !i.showColors)
|
||||
const latex = items.filter((i) => inBuild(i) && i.showColors)
|
||||
console.log(`[BouquetPicker] found ${mylars.length} mylar items, ${latex.length} latex items in "build" category`)
|
||||
// /api/bouquet-items already filters by the Build category server-side
|
||||
const mylars = (items as CatalogItem[]).filter((i) => !i.showColors)
|
||||
const latex = (items as CatalogItem[]).filter((i) => i.showColors)
|
||||
setDebugSlugs([`${(items as CatalogItem[]).length} items fetched (${mylars.length} mylar, ${latex.length} latex)`])
|
||||
|
||||
setBuildItems([...mylars, ...latex])
|
||||
setColorFamilies(families)
|
||||
|
||||
@ -26,7 +26,12 @@ function getCatalogClient() {
|
||||
return new Client({ accessToken: token, environment: env })
|
||||
}
|
||||
|
||||
export async function getSquareCatalog(): Promise<CatalogItem[]> {
|
||||
/**
|
||||
* When filterCategory is supplied, the Online-category filter is skipped and only
|
||||
* items belonging to the named category (case-insensitive) are returned.
|
||||
*/
|
||||
export async function getSquareCatalog(options: { filterCategory?: string } = {}): Promise<CatalogItem[]> {
|
||||
const { filterCategory } = options
|
||||
const client = getCatalogClient()
|
||||
|
||||
// Fetch all pages (Square paginates listCatalog)
|
||||
@ -88,14 +93,25 @@ export async function getSquareCatalog(): Promise<CatalogItem[]> {
|
||||
if (c.id && c.categoryData?.name) categoryNameMap.set(c.id, c.categoryData.name)
|
||||
})
|
||||
|
||||
// Resolve the target category ID when filtering by a specific category name
|
||||
const filterCategoryId = filterCategory
|
||||
? objects.find(
|
||||
(o) => o.type === 'CATEGORY' && o.categoryData?.name?.toLowerCase() === filterCategory.toLowerCase()
|
||||
)?.id
|
||||
: undefined
|
||||
|
||||
const items = objects
|
||||
.filter((o) => o.type === 'ITEM')
|
||||
.filter((o) =>
|
||||
// If an "online" category exists in Square, only show items tagged with it.
|
||||
// If the category doesn't exist in this account, show all items.
|
||||
!onlineCategoryId ||
|
||||
(o.itemData?.categories ?? []).some((c: { id?: string }) => c.id === onlineCategoryId)
|
||||
)
|
||||
.filter((o) => {
|
||||
const cats: { id?: string }[] = o.itemData?.categories ?? []
|
||||
// When a specific category is requested, skip the Online filter entirely
|
||||
// and instead only keep items that belong to that category.
|
||||
if (filterCategoryId) {
|
||||
return cats.some((c) => c.id === filterCategoryId)
|
||||
}
|
||||
// Normal storefront path: apply the Online filter.
|
||||
return !onlineCategoryId || cats.some((c) => c.id === onlineCategoryId)
|
||||
})
|
||||
.map((item) => {
|
||||
const data = item.itemData!
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user