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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user