feat: Kaufjourney-Optimierungen fuer Kund:innen (Shop, Produktseite, Warenkorb, Checkout)
Konkrete Reibungspunkte entlang der Kauf-Journey behoben: - Shop-Suche als sichtbares, editierbares Feld im Filter-Formular statt nur in der kleinen Kopfzeilen-Suche — Suche/Kategorie/Sortierung/Verfuegbarkeit lassen sich jetzt in beide Richtungen kombinieren, ohne sich gegenseitig zurueckzusetzen; plus "Suche zuruecksetzen"-Link. - Versandkosten- und Lieferzeit-Hinweis direkt auf der Produktdetailseite (bisher erst im Warenkorb/Checkout sichtbar). - Produktgalerie per Touch-Wisch bedienbar, nicht nur ueber die Vorschau-Kacheln (mobile Kaufentscheidung). - Immer sichtbare mobile Kaufleiste auf der Produktseite, damit der Warenkorb-Button auf langen Seiten (Bewertungen, verwandte Produkte) nicht wieder erst erscrollt werden muss. - Live-Lagerbestand (Ausverkauft/Nur noch N) jetzt auch im Cart-Drawer und auf der Warenkorb-Seite sichtbar, nicht nur auf der Produktkarte — neuer Endpunkt /api/shop/cart-stock + useCartStock-Hook. - Checkout- und Adressformulare: autoComplete/inputMode/type auf allen Feldern (given-name, tel, postal-code, ...) fuer Mobil-Autofill und passende Bildschirmtastaturen. - Bestellpositionen in "Meine Bestellungen" und der Bestellverfolgung verlinken jetzt zur Produktseite (samt Bewertungs-Anker) — bisher war die Bewertungsfunktion nach dem Kauf nur durch manuelles Zurueeknavigieren auffindbar. Neue Tests fuer die Lagerbestand-Hinweise im Warenkorb; tsc/vitest/build bleiben gruen (142 Tests).
This commit is contained in:
1 parent
54919d82ed
commit
0b2196460a
16 files changed
+445
-84
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getProductsByIds } from "@/lib/data";
|
||||
import { rateLimit, getClientIp } from "@/lib/rate-limit";
|
||||
|
||||
const MAX_IDS_PER_REQUEST = 50;
|
||||
const STOCK_ATTEMPTS_PER_WINDOW = 60;
|
||||
const STOCK_WINDOW_MS = 60 * 1000;
|
||||
|
||||
/**
|
||||
* Aktueller Lagerbestand fuer eine Liste von Produkt-IDs — der Warenkorb
|
||||
* (Zustand/localStorage) kennt nur den Stand beim letzten "Hinzufuegen",
|
||||
* nicht den aktuellen. Cart-Drawer und Warenkorb-Seite fragen das hierueber
|
||||
* live ab, damit "Ausverkauft"/"Nur noch N"-Hinweise auch dort erscheinen,
|
||||
* nicht nur auf der Produktkarte/-detailseite (siehe product-card.tsx).
|
||||
* Rein oeffentliche, bereits im Shop sichtbare Bestandsdaten — keine
|
||||
* Authentifizierung noetig, aber wie jeder andere oeffentliche Endpunkt
|
||||
* rate-limitiert.
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
const ip = await getClientIp();
|
||||
if (!rateLimit(`cart-stock:${ip}`, STOCK_ATTEMPTS_PER_WINDOW, STOCK_WINDOW_MS)) {
|
||||
return NextResponse.json({ stock: {} }, { status: 429 });
|
||||
}
|
||||
|
||||
const idsParam = new URL(request.url).searchParams.get("ids") ?? "";
|
||||
const ids = idsParam
|
||||
.split(",")
|
||||
.map((id) => id.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, MAX_IDS_PER_REQUEST);
|
||||
|
||||
if (ids.length === 0) {
|
||||
return NextResponse.json({ stock: {} });
|
||||
}
|
||||
|
||||
const products = await getProductsByIds(ids);
|
||||
const stock: Record<string, number> = {};
|
||||
for (const product of products) {
|
||||
stock[product.id] = product.isActive ? (product.inventory?.quantity ?? 0) : 0;
|
||||
}
|
||||
|
||||
return NextResponse.json({ stock });
|
||||
}
|
||||
Reference in new issue
Block a user