feat: Wabenhain Honig-Shop Anwendung
Vollstaendige Next.js/Prisma-E-Commerce-Anwendung fuer eine Imkerei: Shop, Warenkorb, Stripe-Checkout, Kundenkonto mit 2FA, Admin-Bereich (Produkte, Ernten, Chargen, Bestellungen, Gutscheine, admin-editierbare Website-Inhalte), i18n (DE/EN), Rate-Limiting/CSP/Audit-Log-Haertung, PostgreSQL- und MySQL-Unterstuetzung ueber austauschbare Prisma- Treiber-Adapter, Unit-/Komponenten-/E2E-Tests.
This commit is contained in:
1 parent
5c76fcc266
commit
c7927f2883
240 files changed
+26096
-137
No files matched your search
@@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
export interface CartItem {
|
||||
productId: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
weightGrams: number;
|
||||
priceCents: number;
|
||||
image: string | null;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface CartState {
|
||||
items: CartItem[];
|
||||
isOpen: boolean;
|
||||
openCart: () => void;
|
||||
closeCart: () => void;
|
||||
addItem: (item: Omit<CartItem, "quantity">, quantity?: number) => void;
|
||||
removeItem: (productId: string) => void;
|
||||
setQuantity: (productId: string, quantity: number) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
export const useCartStore = create<CartState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
items: [],
|
||||
isOpen: false,
|
||||
openCart: () => set({ isOpen: true }),
|
||||
closeCart: () => set({ isOpen: false }),
|
||||
addItem: (item, quantity = 1) =>
|
||||
set((state) => {
|
||||
const existing = state.items.find((i) => i.productId === item.productId);
|
||||
if (existing) {
|
||||
return {
|
||||
items: state.items.map((i) =>
|
||||
i.productId === item.productId
|
||||
? { ...i, quantity: i.quantity + quantity }
|
||||
: i
|
||||
),
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
return { items: [...state.items, { ...item, quantity }], isOpen: true };
|
||||
}),
|
||||
removeItem: (productId) =>
|
||||
set((state) => ({
|
||||
items: state.items.filter((i) => i.productId !== productId),
|
||||
})),
|
||||
setQuantity: (productId, quantity) =>
|
||||
set((state) => ({
|
||||
items: state.items
|
||||
.map((i) => (i.productId === productId ? { ...i, quantity } : i))
|
||||
.filter((i) => i.quantity > 0),
|
||||
})),
|
||||
clear: () => set({ items: [] }),
|
||||
}),
|
||||
{
|
||||
name: "wabenhain-cart",
|
||||
// Nur die Artikel persistieren — "isOpen" ist ein rein transienter
|
||||
// UI-Zustand und darf nach einem Seiten-Reload nicht wiederhergestellt
|
||||
// werden (sonst poppt das Cart-Drawer nach jedem Reload erneut auf).
|
||||
partialize: (state) => ({ items: state.items }),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export function cartTotalCents(items: CartItem[]): number {
|
||||
return items.reduce((sum, item) => sum + item.priceCents * item.quantity, 0);
|
||||
}
|
||||
|
||||
export function cartItemCount(items: CartItem[]): number {
|
||||
return items.reduce((sum, item) => sum + item.quantity, 0);
|
||||
}
|
||||
Reference in new issue
Block a user