Implement shopping cart functionality with UI components and API integration

- 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)
This commit is contained in:
Bastian Masanek
2025-11-03 12:43:13 +01:00
parent 9d0e77fc98
commit b372e2cf78
44 changed files with 2209 additions and 123 deletions

View File

@@ -3,10 +3,11 @@
* Product Detail Page
*
* Displays full details for a single product with large image and description.
* Includes placeholder "Add to Cart" functionality for future implementation.
* Includes functional "Add to Cart" functionality with notifications and cart UI integration.
*/
import { ArrowLeft, CheckCircle } from 'lucide-vue-next'
import { ArrowLeft, CheckCircle, Loader2 } from 'lucide-vue-next'
import { toast } from 'vue-sonner'
// Page metadata
definePageMeta({
@@ -43,10 +44,54 @@ const formattedPrice = computed(() => {
}).format(Number(product.value.price))
})
// Handle "Add to Cart" action (placeholder for future implementation)
const handleAddToCart = () => {
// TODO: Implement cart functionality in future phase
alert('Add to Cart funktioniert noch nicht. Diese Funktion wird in einer späteren Phase implementiert.')
// Cart composables
const { addItem, loading: cartLoading, items } = useCart()
const { open: openCart } = useCartUI()
// Local loading state for this specific button
const isAddingToCart = ref(false)
// Check if product is already in cart
const isInCart = computed(() => {
if (!product.value) return false
return items.value.some((item) => item.productId === product.value.id)
})
// Handle "Add to Cart" action
const handleAddToCart = async () => {
if (!product.value) return
// Prevent adding if out of stock
if (product.value.stockQuantity === 0) {
toast.error('Nicht verfügbar', {
description: 'Dieses Produkt ist derzeit nicht auf Lager.',
})
return
}
isAddingToCart.value = true
try {
// Add item to cart (quantity: 1)
await addItem(product.value.id, 1)
// Show success notification
toast.success('In den Warenkorb gelegt', {
description: `${product.value.name} wurde zu deinem Warenkorb hinzugefügt.`,
})
// Open cart sidebar/sheet to show added item
openCart()
} catch (error) {
console.error('Failed to add item to cart:', error)
// Show error notification
toast.error('Fehler beim Hinzufügen', {
description: 'Das Produkt konnte nicht in den Warenkorb gelegt werden. Bitte versuche es erneut.',
})
} finally {
isAddingToCart.value = false
}
}
</script>
@@ -175,16 +220,35 @@ const handleAddToCart = () => {
<Button
variant="experimenta"
size="experimenta"
class="flex-1"
:disabled="product.stockQuantity === 0"
class="flex-1 relative"
:disabled="product.stockQuantity === 0 || isAddingToCart"
@click="handleAddToCart"
>
{{ product.stockQuantity > 0 ? 'In den Warenkorb' : 'Nicht verfügbar' }}
<!-- Loading spinner -->
<Loader2
v-if="isAddingToCart"
:size="20"
class="mr-2 animate-spin"
/>
<!-- Button text -->
<span v-if="isAddingToCart">Wird hinzugefügt...</span>
<span v-else-if="product.stockQuantity === 0">Nicht verfügbar</span>
<span v-else>In den Warenkorb</span>
</Button>
<NuxtLink to="/products" class="btn-secondary flex-1 text-center">
Weitere Produkte ansehen
</NuxtLink>
</div>
<!-- Already in cart hint -->
<div
v-if="isInCart && product.stockQuantity > 0"
class="mt-2 text-center text-sm text-white/70"
>
Dieses Produkt befindet sich bereits in deinem Warenkorb.
</div>
</div>
</div>
</div>