diff --git a/src/routes/items/$menuItemId.tsx b/src/routes/items/$menuItemId.tsx index 00a5425..ee84c4b 100644 --- a/src/routes/items/$menuItemId.tsx +++ b/src/routes/items/$menuItemId.tsx @@ -1,4 +1,4 @@ -import { Link, createFileRoute } from '@tanstack/react-router' +import { Link, createFileRoute, notFound } from '@tanstack/react-router' import { MenuItemDetail } from '#/components/ordering/MenuItemDetail.tsx' import { useBasket } from '#/lib/basket.tsx' @@ -48,7 +48,7 @@ export const Route = createFileRoute('/items/$menuItemId')({ const brand = await getBrand() const menu = await getMenu({ data: { brandId: brand.id } }) const item = menu.find((menuItem) => menuItem.id === Number(params.menuItemId)) - if (!item) throw new Error('Menu item not found') + if (!item) throw notFound() return { item } }, component: MenuItemRoute, diff --git a/src/routes/orders/$orderId.tsx b/src/routes/orders/$orderId.tsx index 7f83a98..a22ab4a 100644 --- a/src/routes/orders/$orderId.tsx +++ b/src/routes/orders/$orderId.tsx @@ -1,4 +1,4 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, notFound } from '@tanstack/react-router' import { z } from 'zod' import { OrderStatusView } from '#/components/orders/OrderStatusView.tsx' @@ -6,6 +6,16 @@ import { getOrderStatus } from '#/server/orders.ts' const searchSchema = z.object({ token: z.string().optional() }) +// Errors cross the server-function boundary as a plain `Error` carrying only +// `.message` (TanStack Start's ShallowErrorPlugin drops the subclass), so an +// unknown order and a wrong/missing guest token are told apart by message +// rather than `instanceof`. Both collapse to a 404 rather than a real error +// page, so a guessed order id can't be used to probe which ones exist. +const notFoundMessages = new Set([ + 'Order not found', + 'You do not have access to this Order', +]) + const OrderStatusRoute = () => { const status = Route.useLoaderData() const { token } = Route.useSearch() @@ -17,12 +27,20 @@ export const Route = createFileRoute('/orders/$orderId')({ validateSearch: (search: Record) => searchSchema.parse(search), loaderDeps: ({ search }) => ({ token: search.token }), - loader: ({ params, deps }) => - getOrderStatus({ - data: { - orderId: Number(params.orderId), - guestAccessToken: deps.token, - }, - }), + loader: async ({ params, deps }) => { + try { + return await getOrderStatus({ + data: { + orderId: Number(params.orderId), + guestAccessToken: deps.token, + }, + }) + } catch (error) { + if (error instanceof Error && notFoundMessages.has(error.message)) { + throw notFound() + } + throw error + } + }, component: OrderStatusRoute, })