From ee1590fe627d6f78024c81feb8d2a35fcb8f4034 Mon Sep 17 00:00:00 2001 From: "Prit V. Vasani" Date: Sat, 10 Oct 2026 07:20:10 +0000 Subject: [PATCH] fix(cart): drop stale menu items on checkout 400 When POST /orders returns 'Menu item not found: ' (the browser cart still holds an item deleted from the current DB), CartContext now removes just that line and surfaces a clear message instead of a hard checkout failure. Pure staleItemId() helper + test (101 total). Lint 0, 100 tests, build OK. --- .../src/components/CartContext.jsx | 15 ++++++++++++++- cafe-management-sys/src/utils/cartPricing.js | 6 ++++++ cafe-management-sys/src/utils/cartPricing.test.js | 8 ++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/cafe-management-sys/src/components/CartContext.jsx b/cafe-management-sys/src/components/CartContext.jsx index 8f1142b..462b1c1 100644 --- a/cafe-management-sys/src/components/CartContext.jsx +++ b/cafe-management-sys/src/components/CartContext.jsx @@ -8,6 +8,7 @@ import { generateCartItemId, cartTotals, buildOrderItems, + staleItemId, } from '../utils/cartPricing'; const CartContext = createContext(); @@ -181,7 +182,19 @@ export const CartProvider = ({ children }) => { setOrderError(errorMessage); setOrderConfirmation(null); - throw new Error(errorMessage); + // A stored cart line may reference a menu item that no longer exists + // (e.g. deleted after a menu/DB change). Drop just that line so a + // retry succeeds and the customer can finish checkout. + const staleId = staleItemId(errorMessage); + if (staleId) { + setCartItems(prev => prev.filter((it) => it._id !== staleId)); + setOrderError('One item in your cart is no longer available and was removed.'); + } + throw new Error( + staleId + ? 'An item in your cart is no longer available and was removed. Please review and try again.' + : errorMessage + ); } finally { setIsLoading(false); } diff --git a/cafe-management-sys/src/utils/cartPricing.js b/cafe-management-sys/src/utils/cartPricing.js index 0b54552..0442f35 100644 --- a/cafe-management-sys/src/utils/cartPricing.js +++ b/cafe-management-sys/src/utils/cartPricing.js @@ -64,6 +64,12 @@ export const generateCartItemId = (id, size, options) => .map(([key, value]) => `${key}:${value}`) .join('|')}`; +/** Extracts the menu id from a server 'Menu item not found: ' error. */ +export const staleItemId = (message = '') => { + const m = String(message).match(/Menu item not found: ([0-9a-f-]{8,})/i); + return m ? m[1] : null; +}; + /** Shape cart lines into the server's `POST /orders` item contract. */ export const buildOrderItems = (items = []) => items.map((item) => ({ diff --git a/cafe-management-sys/src/utils/cartPricing.test.js b/cafe-management-sys/src/utils/cartPricing.test.js index 6d3a021..e191b69 100644 --- a/cafe-management-sys/src/utils/cartPricing.test.js +++ b/cafe-management-sys/src/utils/cartPricing.test.js @@ -9,6 +9,7 @@ import { estimateTotals, generateCartItemId, buildOrderItems, + staleItemId, } from './cartPricing'; const legacy = { _id: 'm1', price: { medium: 100, large: 150 }, quantity: 2, selectedSize: 'large' }; @@ -96,4 +97,11 @@ describe('cartPricing', () => { specialInstructions: 'no ice', }); }); + + it('staleItemId extracts a stale menu id from the server error', () => { + expect(staleItemId('Menu item not found: 55ffe79f-c6de-4a58-8bf3-7db2992c8fdf')) + .toBe('55ffe79f-c6de-4a58-8bf3-7db2992c8fdf'); + expect(staleItemId('Something else')).toBeNull(); + expect(staleItemId('')).toBeNull(); + }); });