Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dlampatricio/florale/llms.txt

Use this file to discover all available pages before exploring further.

Floralé’s shared TypeScript types live in types/index.ts and form the contract between the Supabase database, the data-fetching layer, and the UI components. Understanding these shapes — especially the categoryIdcategory_id mapping and the two variants of CartItem — will save you time when adding new features or debugging type errors.

Product

Represents a single item available for purchase in the shop. This interface is returned by getProducts, getProductById, and getProductsByCategory.
export interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  image: string;
  categoryId: string;
}

Fields

id
string
required
UUID that uniquely identifies the product. Sourced directly from the id column in the products table.
name
string
required
The human-readable product name shown in listings and on the product detail page (e.g. "Dried Pampas Bouquet").
description
string
required
A long-form description of the product. The mapProduct helper coerces database null to an empty string, so this field is always a string in TypeScript even if the DB row has no description.
price
number
required
The product price as stored in the database. Treat this as your store’s base currency unit (e.g. dollars). Format it for display using toLocaleString or a currency formatter — Floralé does not store a currency symbol.
image
string
required
The URL or path of the product’s primary image. Coerced from null to an empty string by mapProduct, so check for emptiness before rendering an <img> tag (e.g. image || '/placeholder.png').
categoryId
string
required
The ID of the category this product belongs to. This field is mapped from the category_id column in the database — see the snake_case mapping note below.

Usage in practice

import type { Product } from '@/types';

interface ProductCardProps {
  product: Product;
}

export function ProductCard({ product }: ProductCardProps) {
  return (
    <article>
      <img
        src={product.image || '/placeholder.png'}
        alt={product.name}
      />
      <h2>{product.name}</h2>
      <p>{product.description}</p>
      <strong>
        {product.price.toLocaleString('en-US', {
          style: 'currency',
          currency: 'USD',
        })}
      </strong>
    </article>
  );
}

Category

Represents a top-level organisational grouping for products (e.g. “Dried Florals”, “Candles”, “Gift Sets”). Returned by getCategories.
export interface Category {
  id: string;
  name: string;
  description: string;
}

Fields

id
string
required
Unique identifier for the category. Used as the category_id foreign key in the products table and as the value of Product.categoryId.
name
string
required
The human-readable category label shown in navigation menus and headings (e.g. "Dried Florals").
description
string
required
A short description of what kinds of products belong in the category. Useful for category landing-page hero text.

Usage in practice

import type { Category } from '@/types';
import Link from 'next/link';

interface CategoryNavProps {
  categories: Category[];
}

export function CategoryNav({ categories }: CategoryNavProps) {
  return (
    <nav aria-label="Product categories">
      {categories.map((cat) => (
        <Link key={cat.id} href={`/categories/${cat.id}`}>
          {cat.name}
        </Link>
      ))}
    </nav>
  );
}

CartItem

A minimal representation of one line in the shopping cart as defined in types/index.ts.
export interface CartItem {
  productId: string;
  quantity: number;
}

Fields

productId
string
required
The UUID of the product this line item refers to. Use this to look up the full Product object when you need to display the name, image, or price.
quantity
number
required
How many units of the product the customer wants. Always a positive integer in a valid cart state.

The note field difference

The CartItem exported from types/index.ts has no note field. The internal CartItem interface inside lib/cart-store.ts extends this shape with an additional note: string for personalisation messages. The store exports its own version — import from @/lib/cart-store when you need access to note, and from @/types when you only need productId and quantity.
A comparison of the two interfaces:
Fieldtypes/index.tslib/cart-store.ts
productIdstringstring
quantitynumbernumber
note❌ not presentstring (defaults to "")

Usage in practice

import type { CartItem } from '@/types';

interface OrderPayload {
  items: CartItem[];
  customerEmail: string;
}

async function submitOrder(payload: OrderPayload) {
  await fetch('/api/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
}

snake_case ↔ camelCase mapping

The Supabase products table uses PostgreSQL naming conventions (snake_case). The TypeScript layer converts column names to camelCase via the internal mapProduct function in lib/products.ts. Only one field is affected:
Database column (snake_case)TypeScript field (camelCase)
category_idcategoryId
All other columns (id, name, description, price, image) share the same name in both the database and TypeScript. This means when you write PostgREST filter queries manually you must use category_id, but once the data has been fetched and mapped, your TypeScript code always refers to categoryId.
The Category interface is not transformed by mapProduct — its fields (id, name, description) happen to match the database column names exactly, so no renaming is needed. Only Product requires the category_idcategoryId remap.

Build docs developers (and LLMs) love