diff --git a/app/konto/components/LogoutButton.tsx b/app/konto/components/LogoutButton.tsx index ad5f88d..28985f5 100644 --- a/app/konto/components/LogoutButton.tsx +++ b/app/konto/components/LogoutButton.tsx @@ -2,12 +2,20 @@ import { useRouter } from "next/navigation"; import { dispatchAuthChanged } from "../../lib/auth"; +import { clearCart } from "../../lib/cart"; export function LogoutButton() { const router = useRouter(); async function handleLogout() { await fetch("/api/account/logout", { method: "POST" }); + // The local cart is already mirrored server-side by CartSync, so it's + // safe to clear it here — the next login's mergeServerCartIntoLocal() + // restores it from the server. Without this, the local cart survived + // logout untouched, and mergeServerCartIntoLocal()'s additive merge + // (existing.qty += qty) would add the already-synced server quantities + // on top of it on every login, doubling every logout/login cycle. + clearCart(); dispatchAuthChanged(); router.push("/"); router.refresh(); diff --git a/app/lib/cart.ts b/app/lib/cart.ts index 519189b..18ffcce 100644 --- a/app/lib/cart.ts +++ b/app/lib/cart.ts @@ -18,7 +18,8 @@ function readCart(): CartItem[] { if (typeof window === "undefined") return []; try { const raw = window.localStorage.getItem(CART_KEY); - return raw ? JSON.parse(raw) : []; + const parsed = raw ? JSON.parse(raw) : []; + return Array.isArray(parsed) ? parsed : []; } catch { return []; } @@ -90,7 +91,8 @@ export function getCart(): CartItem[] { if (raw === cachedRaw) return cachedItems; cachedRaw = raw; try { - cachedItems = raw ? JSON.parse(raw) : EMPTY_CART; + const parsed = raw ? JSON.parse(raw) : EMPTY_CART; + cachedItems = Array.isArray(parsed) ? parsed : EMPTY_CART; } catch { cachedItems = EMPTY_CART; } @@ -125,9 +127,14 @@ export function useCart(): CartItem[] { // Called right after a successful login (LoginForm.tsx, CheckoutContent.tsx's // inline login toggle) — folds whatever was saved server-side into the // local cart by quantity (addToCart adds to an existing line rather than -// overwriting it), so items added before logging in aren't lost. CartSync -// then picks up the resulting change and pushes the merged cart back to -// the server on its own, closing the loop without a separate save call here. +// overwriting it), so items added as a guest before logging in aren't lost. +// This only stays correct because LogoutButton.tsx clears the local cart on +// logout — the local cart is always either empty (no guest additions since +// the last logout) or holds only genuinely new guest-session items, never a +// stale copy of what's already in the server cart, so this add never +// double-counts. CartSync then picks up the resulting change and pushes the +// merged cart back to the server on its own, closing the loop without a +// separate save call here. export async function mergeServerCartIntoLocal(): Promise { try { const res = await fetch("/api/account/cart");