Cap add-to-cart quantity at actual remaining stock

Stock was only checked at checkout; a shopper could add more of a
product to the cart than was actually in stock and only find out at
the last step. Product/variant now carry a real maxQty, and
AddToCartButton/AddToCartInlineButton/the cart's quantity stepper all
disable or cap once the cart already holds that many.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-23 17:53:14 +00:00
parent 1212b9d115
commit 2d88fb86a1
10 changed files with 80 additions and 21 deletions
+14 -1
View File
@@ -191,6 +191,19 @@ export function CartContent({
const lowStock = entry.variant
? (product.variants.find((v) => v.name === entry.variant)?.lowStock ?? false)
: product.lowStock;
// Same per-line resolution as lowStock above — caps how high
// the quantity stepper below can go, instead of only finding
// out at checkout that this many aren't actually available
// (api/checkout/route.ts's own stock check stays as the
// authoritative server-side guard). null (no cap) falls back
// to the stepper's original fixed 1-9 range; at least 1 is
// always offered even if maxQty is somehow lower than the
// qty already in this line, so the remove (×) button stays
// the only way down, never an empty <select>.
const maxQty = entry.variant
? (product.variants.find((v) => v.name === entry.variant)?.maxQty ?? null)
: product.maxQty;
const qtyOptions = Array.from({ length: Math.max(1, Math.min(9, maxQty ?? 9)) }, (_, n) => n + 1);
return (
<div key={lineKey} className="w-full">
{i > 0 && <div className="h-px bg-border w-full mb-6" />}
@@ -238,7 +251,7 @@ export function CartContent({
onChange={(e) => setQuantity(product.id, Number(e.target.value), entry.variant)}
className="border border-border rounded-sm px-3.5 py-2 text-body-sm text-text-primary outline-none focus:border-brand transition-colors"
>
{Array.from({ length: 9 }, (_, n) => n + 1).map((n) => (
{qtyOptions.map((n) => (
<option key={n} value={n}>{n}</option>
))}
</select>
+1 -1
View File
@@ -198,7 +198,7 @@ export function RelatedProducts({ defaultTaxRate }: { defaultTaxRate: number })
<p className="min-h-[1.05rem] text-label font-bold text-warning">
{anyLowStock ? "Nur noch wenige verfügbar" : null}
</p>
<AddToCartInlineButton id={product.id} outOfStock={product.outOfStock} variants={product.variants} />
<AddToCartInlineButton id={product.id} outOfStock={product.outOfStock} maxQty={product.maxQty} variants={product.variants} />
</div>
</div>
);