Welcome to the Jose Madrid Salsa developer docs — explore features, APIs, and deployment guides.
Jose Madrid SalsaJMS Docs

Product Catalog

Product catalog with categories, heat levels, filtering, search, comparison, and quick view for the Jose Madrid Salsa storefront

Product Catalog

The product catalog powers the public storefront and admin product management. Products support categories, heat levels, nutritional info, compare-at pricing, and tag-based organization.

Architecture

page.tsx
products-client.tsx
product-card.tsx
product-grid.tsx
product-comparison.tsx
product-recommendations.tsx
quick-view-modal.tsx
heat-gauge.tsx

Data Model

Each product record includes:

FieldTypeDescription
namestringDisplay name
slugstringURL-safe identifier
skustringStock keeping unit
priceDecimalCurrent price
compareAtPriceDecimal?Original price for sale badges
heatLevelenumMILD, MEDIUM, HOT, EXTRA_HOT
inventoryintUnits in stock
featuredImagestring?Primary image URL
ingredientsstring[]Ingredient list
nutritionalInfoJSON?Calories, sodium, fat, carbs, etc.
isFeaturedbooleanShow on homepage
isActivebooleanVisible in storefront
categoryIdstring?FK to Category

Product Card

The ProductCard component (components/store/product-card.tsx) renders each product with:

  • Featured and % OFF badges computed from compareAtPrice
  • Heat level badge with color coding via getHeatLevelColor()
  • Out of stock indicator when inventory <= 0
  • Low stock warning when inventory <= 5
  • Quick view modal (eye icon on hover) with full details and add-to-cart
  • Wishlist toggle (heart icon, requires authentication)
  • Comparison toggle (scale icon, max 4 products)
// Product interface used by ProductCard
interface Product {
  id: string
  name: string
  slug: string
  description: string | null
  price: number
  compareAtPrice?: number | null
  featuredImage: string | null
  heatLevel: string
  sku: string
  inventory: number
  isFeatured: boolean
  ingredients: string[] | null
  nutritionalInfo?: { calories: number; sodiumMg: number; ... } | null
}

The products page (app/(public)/products/page.tsx) is a server component that accepts URL search params:

ParamDescription
categoryFilter by category slug
heatLevelFilter by heat level
searchFull-text search
viewgrid or list layout

The server component calls getProducts() from lib/db/products.ts with these filters, then passes results to the ProductsClient component for interactive filtering and view toggling.

Search params are async in Next.js 15+. The page awaits searchParams before passing them to the query layer.

Product Comparison

Users can compare up to 4 products side-by-side using the comparison store (lib/store/comparison.ts). The comparison panel shows:

  • Price differences
  • Heat level comparison
  • Ingredient lists
  • Nutritional info (when available)
  • Weight and dimensions

Admin Product Management

Admin users manage products at /admin/products with CRUD operations, image upload via UploadThing, category assignment, and inventory adjustments.

Collections

Collections (/admin/collections, under Products) are curated, marketing-facing groups of products — "Gift Sets", "New Arrivals", "Staff Picks" — distinct from Categories (the taxonomy each product belongs to exactly one of). A product can appear in many collections and a collection holds many products, so the relation is many-to-many through the CollectionProduct join, which also carries each product's display order within the collection.

  • Model: Collection (name, slug, description, image, SEO fields, isActive, sortOrder) mirrors Category; membership and per-collection order live on CollectionProduct (@@unique([collectionId, productId])).
  • Admin CRUD: app/admin/collections (list + create/edit dialogs) and app/api/admin/collections/** (products:read/products:write, audit-logged — the nav item, the API, and the Products parent menu all share the same permission so visibility and access line up). The form includes a searchable product multi-select; products are stored in the order selected. Deleting a collection detaches its products (the join rows cascade) — the products themselves are never deleted.
  • Storefront: /collections/[slug] renders the collection's active products, in order, through the same ProductCard grid as the catalog. Query: getCollectionBySlug in lib/db/products.ts.
  • Slug + rows logic is in lib/collections.ts (pure, tested). Migration 20260815130000_add_collections.

How is this guide?

Edit on GitHub

Last updated on

On this page