/**
* The shopper's cart, read one way for every band that sells — a cart page,
* a drawer, a checkout, the count on the store's header — so the lines, the
* quantities and the totals agree wherever they are shown.
*
* The store keeps no carts, so the sample shopper's is a rule over the shelf:
* the shop's three bestsellers under `CART_CEILING_CENTS`, each from a
* different department, one of each. A small first order that sits under the
* free-delivery line, so a band can show the line being crossed. Replace
* `shopperCart` with your cart store and every band follows.
*
* Server-only, because it reads the shelf: a section hands the lines to its
* island as props, and the island totals them with `checkout.ts`.
*/
import type { YearMonth } from "@/lib/dashboards/ecommerce/checkout"
import { storeShelf, type StockState } from "@/lib/dashboards/ecommerce/storefront"
import { REFERENCE_DATE, db } from "@/lib/sample-data"
/** The most a product in the sample cart costs, in cents. */
export const CART_CEILING_CENTS = 3_000
/** A line of the cart: the product as the cart shows it, its price, how many, and the most it can hold. */
export type CartLine = {
productId: string
name: string
category: string
href: string
/** What one costs, in cents: the catalogue's list price. */
unitCents: number
qty: number
/** Units on hand: a line never holds more. */
stock: number
stockState: StockState
}
/**
* The sample shopper's cart: the bestselling products in stock at or under
* `CART_CEILING_CENTS`, one per department until there are three, one of each,
* in the shelf's order.
*/
export function shopperCart(): CartLine[] {
const departments = new Set<string>()
const lines: CartLine[] = []
for (const product of storeShelf()) {
if (lines.length === 3) break
if (product.priceCents > CART_CEILING_CENTS || product.stock === 0 || departments.has(product.category)) continue
departments.add(product.category)
lines.push({
productId: product.id,
name: product.name,
category: product.category,
href: product.href,
unitCents: product.priceCents,
qty: 1,
stock: product.stock,
stockState: product.stockState,
})
}
return lines
}
/** Every country the shop has a customer in, A to Z: the places it delivers to. */
export function shipsTo(): string[] {
return [...new Set(db.customers.all().map((customer) => customer.country))].sort((a, b) => a.localeCompare(b))
}
/**
* "Now", as a checkout needs it: the instant an order placed now would be
* placed at — `REFERENCE_DATE`, as an ISO string an island can take — and the
* month a card must not have run out before.
*/
export function checkoutNow(): { placedAt: string; month: YearMonth } {
return {
placedAt: REFERENCE_DATE.toISOString(),
month: { year: REFERENCE_DATE.getUTCFullYear(), month: REFERENCE_DATE.getUTCMonth() + 1 },
}
}
/** The number the next order would be given: one past the book's last, in its own series. */
export function nextOrderNumber(): string {
return `ORD-${100_000 + db.orders.all().length + 1}`
}