import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types"; export const OFFER_FILTERS = [ "all", "free", "paid", "bundles", "limited", ] as const; export type OfferFilter = (typeof OFFER_FILTERS)[number]; type DiscoverableOffer = Pick< CatalogItemData, | "id" | "catalogName" | "baseItemName" | "baseName" | "itemIds" | "costCredits" | "costPoints" | "limitedStack" >; function matchesFilter(item: DiscoverableOffer, filter: OfferFilter) { switch (filter) { case "all": return true; case "free": return item.costCredits === 0 && item.costPoints === 0; case "paid": return item.costCredits > 0 || item.costPoints > 0; case "bundles": return ( item.itemIds.split(";").filter((part) => part.trim().length > 0) .length > 1 ); case "limited": return item.limitedStack > 0; } } /** Facet counts apply to the text search so changing a facet never hides its available matches. */ export function discoverOffers( items: readonly T[], query: string, filter: OfferFilter, ) { const normalized = query.trim().toLowerCase(); const exactId = /^#\d+$/.test(normalized) ? normalized.slice(1) : null; const matches = exactId !== null ? items.filter((item) => String(item.id) === exactId) : normalized ? items.filter((item) => [ item.catalogName, item.baseItemName, item.baseName, item.itemIds, String(item.id), ].some((value) => value.toLowerCase().includes(normalized)), ) : [...items]; const counts = { all: matches.length, free: 0, paid: 0, bundles: 0, limited: 0, }; for (const item of matches) for (const facet of OFFER_FILTERS) { if (facet !== "all" && matchesFilter(item, facet)) counts[facet] += 1; } return { items: matches.filter((item) => matchesFilter(item, filter)), counts, }; }