diff --git a/src/modules/restaurant/components/CartLineItem.vue b/src/modules/restaurant/components/CartLineItem.vue index 0149867..cd27b60 100644 --- a/src/modules/restaurant/components/CartLineItem.vue +++ b/src/modules/restaurant/components/CartLineItem.vue @@ -10,7 +10,6 @@ import type { CartLine } from '../stores/cart' const props = defineProps<{ line: CartLine - currencyHint?: string }>() const emit = defineEmits<{ @@ -24,14 +23,12 @@ const modifierSummary = computed(() => ) const lineTotal = computed(() => { - // unit_msat is base + modifier delta snapshot; the cart store - // currently stores it as a sat-major number (price * 1000-less - // because the extension's `price` is already in the declared - // currency, not msat — see useCheckout's buildCreateOrder for the - // canonical conversion at order-place time). + // Displayed in the menu item's currency (e.g. GTQ). Authoritative + // sat conversion happens server-side at /orders/quote — the + // checkout page surfaces that as a "≈ X sat" badge. const fmt = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }) - const total = props.line.unit_msat * props.line.quantity - return `${fmt.format(total)} ${props.currencyHint || 'sat'}` + const total = props.line.unit_price * props.line.quantity + return `${fmt.format(total)} ${props.line.currency}` }) diff --git a/src/modules/restaurant/stores/cart.ts b/src/modules/restaurant/stores/cart.ts index 7beb1e4..dfc6839 100644 --- a/src/modules/restaurant/stores/cart.ts +++ b/src/modules/restaurant/stores/cart.ts @@ -30,8 +30,16 @@ export interface CartLine { /** Snapshot at add-time so the cart still renders if the menu * item is later edited / deleted. */ name: string - /** Base price + selected modifier deltas, in msat. */ - unit_msat: number + /** + * Base price + selected modifier deltas, **in the menu item's + * declared currency** (e.g. 25 for "25 GTQ" or 100 for "100 sat"). + * Authoritative sat conversion happens server-side via + * `POST /orders/quote`; the cart's value is for display only. + */ + unit_price: number + /** ISO-ish currency code from the menu item, e.g. "GTQ", "USD", + * "sat". Used for cart-side display labels. */ + currency: string quantity: number selected_modifiers: SelectedModifier[] note?: string | null @@ -91,19 +99,42 @@ export const useCartStore = defineStore('restaurant-cart', () => { return n }) - const restaurantTotalsMsat = computed>(() => { - const out: Record = {} + /** Per-restaurant subtotal in that restaurant's declared currency. */ + const restaurantTotals = computed< + Record + >(() => { + const out: Record = {} for (const rid of Object.keys(lines.value)) { - out[rid] = lines.value[rid].reduce( - (s, l) => s + l.unit_msat * l.quantity, - 0 - ) + const bucket = lines.value[rid] + if (!bucket.length) continue + out[rid] = { + amount: bucket.reduce( + (s, l) => s + l.unit_price * l.quantity, + 0 + ), + currency: bucket[0].currency, + } } return out }) - const grandTotalMsat = computed(() => - Object.values(restaurantTotalsMsat.value).reduce((s, v) => s + v, 0) + /** + * Single-currency cart subtotal. Returns null if the cart spans + * multiple currencies (e.g. one restaurant priced in GTQ + another + * in USD via the future festival aggregator) — the UI then falls + * back to per-restaurant subtotals only. + */ + const grandTotal = computed<{ amount: number; currency: string } | null>( + () => { + const totals = Object.values(restaurantTotals.value) + if (!totals.length) return null + const currencies = new Set(totals.map((t) => t.currency)) + if (currencies.size !== 1) return null + return { + amount: totals.reduce((s, t) => s + t.amount, 0), + currency: totals[0].currency, + } + } ) function linesFor(restaurantId: string): CartLine[] { @@ -214,8 +245,8 @@ export const useCartStore = defineStore('restaurant-cart', () => { // getters restaurantsInCart, itemCount, - restaurantTotalsMsat, - grandTotalMsat, + restaurantTotals, + grandTotal, linesFor, // actions setActiveRestaurant, diff --git a/src/modules/restaurant/views/CartPage.vue b/src/modules/restaurant/views/CartPage.vue index 7771c85..a485420 100644 --- a/src/modules/restaurant/views/CartPage.vue +++ b/src/modules/restaurant/views/CartPage.vue @@ -25,12 +25,12 @@ const buckets = computed(() => // a separate fetch for the cart page header. restaurantSlug: cart.linesFor(rid)[0]?.restaurant_slug ?? '', lines: cart.linesFor(rid), - totalMsat: cart.restaurantTotalsMsat[rid] ?? 0, + total: cart.restaurantTotals[rid] ?? null, })) ) -function fmtSat(value: number): string { - return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(value)} sat` +function fmt(value: number, currency: string): string { + return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(value)} ${currency}` } @@ -70,7 +70,7 @@ function fmtSat(value: number): string { - {{ fmtSat(b.totalMsat) }} + {{ b.total ? fmt(b.total.amount, b.total.currency) : '—' }} @@ -91,12 +91,16 @@ function fmtSat(value: number): string {
-
+
Subtotal - {{ fmtSat(cart.grandTotalMsat) }} + {{ fmt(cart.grandTotal.amount, cart.grandTotal.currency) }}
+

+ Cart spans multiple currencies — per-restaurant subtotals above. + Lightning sat amount shown at checkout. +

+
+ + ≈ {{ new Intl.NumberFormat().format(previewSatPerRestaurant[b.restaurantId]!) }} sat +
@@ -141,10 +194,22 @@ async function placeOrder() {
-
+
Total - {{ fmtSat(cart.grandTotalMsat) }} + {{ fmt(cart.grandTotal.amount, cart.grandTotal.currency) }} + +
+
+ + + Pay in sats + + + ≈ {{ new Intl.NumberFormat().format(previewSatTotal) }} sat
diff --git a/src/modules/restaurant/views/ItemPage.vue b/src/modules/restaurant/views/ItemPage.vue index 945a4ca..2b971f7 100644 --- a/src/modules/restaurant/views/ItemPage.vue +++ b/src/modules/restaurant/views/ItemPage.vue @@ -87,7 +87,8 @@ function addToCart() { restaurant_slug: restaurant.value.slug, menu_item_id: item.value.id, name: item.value.name, - unit_msat: unitPrice.value, + unit_price: unitPrice.value, + currency: item.value.currency || restaurant.value.currency, quantity: quantity.value, selected_modifiers: selectedModifiers.value, note: note.value || null, diff --git a/src/modules/restaurant/views/RestaurantPage.vue b/src/modules/restaurant/views/RestaurantPage.vue index 3d97877..d9f06f0 100644 --- a/src/modules/restaurant/views/RestaurantPage.vue +++ b/src/modules/restaurant/views/RestaurantPage.vue @@ -84,7 +84,8 @@ function quickAdd(itemId: string) { restaurant_slug: restaurant.value.slug, menu_item_id: it.id, name: it.name, - unit_msat: it.price, + unit_price: it.price, + currency: it.currency || restaurant.value.currency, quantity: 1, selected_modifiers: [], note: null,