From b372e2cf7860568557e155c59475e64a7c23af67 Mon Sep 17 00:00:00 2001 From: Bastian Masanek Date: Mon, 3 Nov 2025 12:43:13 +0100 Subject: [PATCH] Implement shopping cart functionality with UI components and API integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added CartItem, CartSummary, CartEmpty, CartSidebar, and CartSheet components for managing cart display and interactions. - Integrated useCart and useCartUI composables for cart state management and UI control. - Implemented API endpoints for cart operations, including fetching, adding, updating, and removing items. - Enhanced user experience with loading states and notifications using vue-sonner for cart actions. - Configured session management for guest and authenticated users, ensuring cart persistence across sessions. This commit completes the shopping cart feature, enabling users to add items, view their cart, and proceed to checkout. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/settings.local.json | 6 +- .env.example | 9 + app/components/Cart/CartEmpty.vue | 45 +++++ app/components/Cart/CartItem.vue | 180 +++++++++++++++++ app/components/Cart/CartSheet.vue | 77 +++++++ app/components/Cart/CartSidebar.vue | 77 +++++++ app/components/Cart/CartSummary.vue | 116 +++++++++++ app/components/Cart/index.ts | 6 + app/components/navigation/AreaTabs.vue | 25 ++- app/components/navigation/CartButton.vue | 15 +- app/components/ui/scroll-area/ScrollArea.vue | 27 +++ app/components/ui/scroll-area/ScrollBar.vue | 28 +++ app/components/ui/scroll-area/index.ts | 2 + app/components/ui/separator/Separator.vue | 12 +- app/components/ui/sheet/Sheet.vue | 15 ++ app/components/ui/sheet/SheetClose.vue | 12 ++ app/components/ui/sheet/SheetContent.vue | 54 +++++ app/components/ui/sheet/SheetDescription.vue | 20 ++ app/components/ui/sheet/SheetFooter.vue | 19 ++ app/components/ui/sheet/SheetHeader.vue | 16 ++ app/components/ui/sheet/SheetTitle.vue | 20 ++ app/components/ui/sheet/SheetTrigger.vue | 12 ++ app/components/ui/sheet/index.ts | 32 +++ app/components/ui/sonner/Sonner.vue | 23 +++ app/components/ui/sonner/index.ts | 1 + app/composables/useCart.ts | 178 ++++++++++++++++ app/composables/useCartUI.ts | 104 ++++++++++ app/layouts/default.vue | 19 ++ app/pages/products/[id].vue | 82 +++++++- app/types/cart.ts | 43 ++++ docs/PRD.md | 31 +++ nuxt.config.ts | 6 + package.json | 4 +- pnpm-lock.yaml | 32 +++ server/api/cart/index.get.ts | 44 ++++ server/api/cart/items.post.ts | 91 +++++++++ server/api/cart/items/[id].delete.ts | 65 ++++++ server/api/cart/items/[id].patch.ts | 96 +++++++++ server/utils/cart-cleanup.ts | 116 +++++++++++ server/utils/cart-helpers.ts | 202 +++++++++++++++++++ server/utils/cart-session.ts | 65 ++++++ server/utils/cart-validation.ts | 100 +++++++++ tasks/00-PROGRESS.md | 181 ++++++++++------- tasks/04-cart.md | 34 ++-- 44 files changed, 2214 insertions(+), 128 deletions(-) create mode 100644 app/components/Cart/CartEmpty.vue create mode 100644 app/components/Cart/CartItem.vue create mode 100644 app/components/Cart/CartSheet.vue create mode 100644 app/components/Cart/CartSidebar.vue create mode 100644 app/components/Cart/CartSummary.vue create mode 100644 app/components/Cart/index.ts create mode 100644 app/components/ui/scroll-area/ScrollArea.vue create mode 100644 app/components/ui/scroll-area/ScrollBar.vue create mode 100644 app/components/ui/scroll-area/index.ts create mode 100644 app/components/ui/sheet/Sheet.vue create mode 100644 app/components/ui/sheet/SheetClose.vue create mode 100644 app/components/ui/sheet/SheetContent.vue create mode 100644 app/components/ui/sheet/SheetDescription.vue create mode 100644 app/components/ui/sheet/SheetFooter.vue create mode 100644 app/components/ui/sheet/SheetHeader.vue create mode 100644 app/components/ui/sheet/SheetTitle.vue create mode 100644 app/components/ui/sheet/SheetTrigger.vue create mode 100644 app/components/ui/sheet/index.ts create mode 100644 app/components/ui/sonner/Sonner.vue create mode 100644 app/components/ui/sonner/index.ts create mode 100644 app/composables/useCart.ts create mode 100644 app/composables/useCartUI.ts create mode 100644 app/types/cart.ts create mode 100644 server/api/cart/index.get.ts create mode 100644 server/api/cart/items.post.ts create mode 100644 server/api/cart/items/[id].delete.ts create mode 100644 server/api/cart/items/[id].patch.ts create mode 100644 server/utils/cart-cleanup.ts create mode 100644 server/utils/cart-helpers.ts create mode 100644 server/utils/cart-session.ts create mode 100644 server/utils/cart-validation.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 511eeaf..d9535b9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -67,7 +67,11 @@ "Bash(pnpm exec shadcn-nuxt@latest add:*)", "Bash(pnpm exec shadcn-nuxt:*)", "mcp__playwright__browser_press_key", - "Bash(pnpm db:migrate:*)" + "Bash(pnpm db:migrate:*)", + "Bash(pnpm shadcn-nuxt add:*)", + "Bash(npm run:*)", + "Bash(pnpm exec eslint:*)", + "Bash(npx -y vue-tsc:*)" ], "deny": [], "ask": [] diff --git a/.env.example b/.env.example index 8d201c3..e2ac3cc 100644 --- a/.env.example +++ b/.env.example @@ -117,6 +117,15 @@ INTERNAL_AUTH_ENABLED=true INTERNAL_AUTH_USERNAME=experimenta INTERNAL_AUTH_PASSWORD=change-me-to-secure-password +# ============================================== +# SHOPPING CART +# ============================================== +# Cart session cookie name +CART_SESSION_COOKIE_NAME=cart-session + +# Cart expiry in days (for both user and guest carts) +CART_EXPIRY_DAYS=30 + # ============================================== # DEVELOPMENT TOOLS # ============================================== diff --git a/app/components/Cart/CartEmpty.vue b/app/components/Cart/CartEmpty.vue new file mode 100644 index 0000000..f7ab7ee --- /dev/null +++ b/app/components/Cart/CartEmpty.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/components/Cart/CartItem.vue b/app/components/Cart/CartItem.vue new file mode 100644 index 0000000..36313e6 --- /dev/null +++ b/app/components/Cart/CartItem.vue @@ -0,0 +1,180 @@ + + + diff --git a/app/components/Cart/CartSheet.vue b/app/components/Cart/CartSheet.vue new file mode 100644 index 0000000..fdac21f --- /dev/null +++ b/app/components/Cart/CartSheet.vue @@ -0,0 +1,77 @@ + + + diff --git a/app/components/Cart/CartSidebar.vue b/app/components/Cart/CartSidebar.vue new file mode 100644 index 0000000..7a354bc --- /dev/null +++ b/app/components/Cart/CartSidebar.vue @@ -0,0 +1,77 @@ + + + diff --git a/app/components/Cart/CartSummary.vue b/app/components/Cart/CartSummary.vue new file mode 100644 index 0000000..af1e787 --- /dev/null +++ b/app/components/Cart/CartSummary.vue @@ -0,0 +1,116 @@ + + + diff --git a/app/components/Cart/index.ts b/app/components/Cart/index.ts new file mode 100644 index 0000000..f5bb864 --- /dev/null +++ b/app/components/Cart/index.ts @@ -0,0 +1,6 @@ +export { default as CartItem } from './CartItem.vue' +export { default as CartSummary } from './CartSummary.vue' +export { default as CartEmpty } from './CartEmpty.vue' +export { default as CartFAB } from './CartFAB.vue' +export { default as CartSidebar } from './CartSidebar.vue' +export { default as CartSheet } from './CartSheet.vue' diff --git a/app/components/navigation/AreaTabs.vue b/app/components/navigation/AreaTabs.vue index f2d07b3..fce06a8 100644 --- a/app/components/navigation/AreaTabs.vue +++ b/app/components/navigation/AreaTabs.vue @@ -66,11 +66,11 @@ function navigateToArea(area: ProductArea) {
- +
-
+
diff --git a/app/components/navigation/CartButton.vue b/app/components/navigation/CartButton.vue index 7ba3622..e92908b 100644 --- a/app/components/navigation/CartButton.vue +++ b/app/components/navigation/CartButton.vue @@ -26,12 +26,12 @@ function handleClick(e: Event) { diff --git a/app/components/ui/scroll-area/ScrollArea.vue b/app/components/ui/scroll-area/ScrollArea.vue new file mode 100644 index 0000000..7b8eb39 --- /dev/null +++ b/app/components/ui/scroll-area/ScrollArea.vue @@ -0,0 +1,27 @@ + + + diff --git a/app/components/ui/scroll-area/ScrollBar.vue b/app/components/ui/scroll-area/ScrollBar.vue new file mode 100644 index 0000000..04e33ee --- /dev/null +++ b/app/components/ui/scroll-area/ScrollBar.vue @@ -0,0 +1,28 @@ + + + diff --git a/app/components/ui/scroll-area/index.ts b/app/components/ui/scroll-area/index.ts new file mode 100644 index 0000000..c416759 --- /dev/null +++ b/app/components/ui/scroll-area/index.ts @@ -0,0 +1,2 @@ +export { default as ScrollArea } from "./ScrollArea.vue" +export { default as ScrollBar } from "./ScrollBar.vue" diff --git a/app/components/ui/separator/Separator.vue b/app/components/ui/separator/Separator.vue index 773edac..28f1a31 100644 --- a/app/components/ui/separator/Separator.vue +++ b/app/components/ui/separator/Separator.vue @@ -1,10 +1,16 @@