Make "Buy again" add the item to the basket
The order status view now carries each item's menu item id and the ids of its current Options matching the ordered ones by name, since editing a Menu Item recreates its Options under new ids. Options no longer on the menu are left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { Link, useRouter } from '@tanstack/react-router'
|
||||
import { Link, useNavigate, useRouter } from '@tanstack/react-router'
|
||||
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion'
|
||||
import { useState } from 'react'
|
||||
|
||||
import type { OrderStatus } from '#/db/schema.ts'
|
||||
import type { OrderItemStatusView } from '#/domain/ordering.ts'
|
||||
import { useBasket } from '#/lib/basket.tsx'
|
||||
import {
|
||||
advanceSimulatedKitchen,
|
||||
cancelOrderItem,
|
||||
@@ -146,6 +147,8 @@ export const OrderStatusView = ({
|
||||
backTo = '/orders',
|
||||
}: OrderStatusViewProps) => {
|
||||
const router = useRouter()
|
||||
const navigate = useNavigate()
|
||||
const basket = useBasket()
|
||||
const [pendingItemId, setPendingItemId] = useState<number | null>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [simulating, setSimulating] = useState(false)
|
||||
@@ -162,6 +165,11 @@ export const OrderStatusView = ({
|
||||
? { duration: 0 }
|
||||
: { duration: 0.4, ease: 'easeInOut' as const }
|
||||
|
||||
const buyAgain = (item: OrderItemStatusView) => {
|
||||
basket.add(item.menuItemId, item.buyAgainOptionIds)
|
||||
void navigate({ to: '/basket' })
|
||||
}
|
||||
|
||||
const toggleSteps = (orderItemId: number) => {
|
||||
setExpandedItemIds((current) => {
|
||||
const next = new Set(current)
|
||||
@@ -344,9 +352,7 @@ export const OrderStatusView = ({
|
||||
<button
|
||||
type="button"
|
||||
disabled={pendingItemId === item.id}
|
||||
onClick={() => {
|
||||
//
|
||||
}}
|
||||
onClick={() => buyAgain(item)}
|
||||
>
|
||||
<ActionIcon name="buyAgain" />
|
||||
Buy again
|
||||
|
||||
+25
-3
@@ -799,6 +799,11 @@ export type OrderItemStatusView = {
|
||||
queuePosition: number | null
|
||||
}>
|
||||
options: Array<{ name: string; priceDeltaCents: number }>
|
||||
menuItemId: number
|
||||
// The Menu Item's current Options matching this item's by name — editing a
|
||||
// Menu Item recreates its Options under new ids, so the ids at order time
|
||||
// can't be reused. Options since removed from the menu are left out.
|
||||
buyAgainOptionIds: Array<number>
|
||||
etaSeconds: number | null
|
||||
}
|
||||
|
||||
@@ -838,6 +843,11 @@ export const getOrderStatus = async (
|
||||
.from(schema.orderItemOptions)
|
||||
.where(inArray(schema.orderItemOptions.orderItemId, orderItemIds))
|
||||
|
||||
const currentOptionRows = await db
|
||||
.select()
|
||||
.from(schema.menuItemOptions)
|
||||
.where(inArray(schema.menuItemOptions.menuItemId, menuItemIds))
|
||||
|
||||
const recipeRows =
|
||||
orderItemIds.length === 0
|
||||
? []
|
||||
@@ -1008,6 +1018,9 @@ export const getOrderStatus = async (
|
||||
}
|
||||
|
||||
const currentInstance = instances.find((row) => row.status !== 'done')
|
||||
const options = optionRows.filter(
|
||||
(option) => option.orderItemId === item.id,
|
||||
)
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
@@ -1020,9 +1033,18 @@ export const getOrderStatus = async (
|
||||
item.status !== 'picked_up' &&
|
||||
currentInstance?.status === 'queued',
|
||||
steps,
|
||||
options: optionRows
|
||||
.filter((option) => option.orderItemId === item.id)
|
||||
.map(({ name, priceDeltaCents }) => ({ name, priceDeltaCents })),
|
||||
options: options.map(({ name, priceDeltaCents }) => ({
|
||||
name,
|
||||
priceDeltaCents,
|
||||
})),
|
||||
menuItemId: item.menuItemId,
|
||||
buyAgainOptionIds: currentOptionRows
|
||||
.filter(
|
||||
(current) =>
|
||||
current.menuItemId === item.menuItemId &&
|
||||
options.some((option) => option.name === current.name),
|
||||
)
|
||||
.map((current) => current.id),
|
||||
etaSeconds,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -698,6 +698,64 @@ describe('kitchen simulation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('buying an item again', () => {
|
||||
const pizzaOption = (name: string) =>
|
||||
seed.menuItemOptions.pizza.find((option) => option.name === name)!
|
||||
|
||||
it("offers the Menu Item's current Options matching the ones ordered", async () => {
|
||||
const order = await placeGuestOrder([
|
||||
{
|
||||
menuItemId: seed.menuItems.pizza.id,
|
||||
selectedOptionIds: [
|
||||
pizzaOption('Extra cheese').id,
|
||||
pizzaOption('No onions').id,
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [item] = (await getStatus(order)).items
|
||||
expect(item.menuItemId).toBe(seed.menuItems.pizza.id)
|
||||
expect(item.buyAgainOptionIds).toEqual(
|
||||
expect.arrayContaining([
|
||||
pizzaOption('Extra cheese').id,
|
||||
pizzaOption('No onions').id,
|
||||
]),
|
||||
)
|
||||
expect(item.buyAgainOptionIds).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('leaves out an Option that is no longer on the menu', async () => {
|
||||
const order = await placeGuestOrder([
|
||||
{
|
||||
menuItemId: seed.menuItems.pizza.id,
|
||||
selectedOptionIds: [
|
||||
pizzaOption('Extra cheese').id,
|
||||
pizzaOption('No onions').id,
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const noOnionsEdit = eq(
|
||||
schema.menuItemOptions.id,
|
||||
pizzaOption('No onions').id,
|
||||
)
|
||||
await testDb.db
|
||||
.update(schema.menuItemOptions)
|
||||
.set({ name: 'Extra onions' })
|
||||
.where(noOnionsEdit)
|
||||
|
||||
try {
|
||||
const [item] = (await getStatus(order)).items
|
||||
expect(item.buyAgainOptionIds).toEqual([pizzaOption('Extra cheese').id])
|
||||
} finally {
|
||||
await testDb.db
|
||||
.update(schema.menuItemOptions)
|
||||
.set({ name: 'No onions' })
|
||||
.where(noOnionsEdit)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('recipe snapshot', () => {
|
||||
it("keeps an Order Item on the Recipe as it existed at order time, even if the Menu Item's live Recipe is edited afterwards", async () => {
|
||||
const order = await placeGuestOrder([
|
||||
|
||||
Reference in New Issue
Block a user