init
This commit is contained in:
commit
c19b910c36
80 changed files with 16134 additions and 0 deletions
41
app/lib/pricing.ts
Normal file
41
app/lib/pricing.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import type { ItemId, StoreItem } from "@/app/types";
|
||||
import { CATALOG } from "./catalog";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PriceProvider: the single seam between "where prices come from" and the
|
||||
// rest of the app. The cost math only ever calls `getItem`.
|
||||
//
|
||||
// To use live retailer prices later:
|
||||
// 1. Fetch prices on the SERVER (a Server Action or route handler) so the
|
||||
// API key never reaches the browser.
|
||||
// 2. Build a snapshot `StoreItem[]` from the response.
|
||||
// 3. Pass `new SnapshotPriceProvider("Live grocery prices", snapshot)` into
|
||||
// the grocery/planner functions instead of `averageGroceryProvider`.
|
||||
// The math stays synchronous and testable because it works on a snapshot.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface PriceProvider {
|
||||
/** Shown in the UI so users know where prices came from. */
|
||||
readonly label: string;
|
||||
getItem(id: ItemId): StoreItem | undefined;
|
||||
}
|
||||
|
||||
export class SnapshotPriceProvider implements PriceProvider {
|
||||
private readonly byId: Map<ItemId, StoreItem>;
|
||||
|
||||
constructor(
|
||||
readonly label: string,
|
||||
items: StoreItem[],
|
||||
) {
|
||||
this.byId = new Map(items.map((item) => [item.id, item]));
|
||||
}
|
||||
|
||||
getItem(id: ItemId): StoreItem | undefined {
|
||||
return this.byId.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
export const averageGroceryProvider: PriceProvider = new SnapshotPriceProvider(
|
||||
"Based on average grocery prices as of October 2026",
|
||||
CATALOG,
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue