- 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)
66 lines
2.1 KiB
Vue
66 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
import { ShoppingCart } from 'lucide-vue-next'
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
const { itemCount, total } = useCart()
|
|
const { open } = useCartUI()
|
|
|
|
const hasItems = computed(() => itemCount.value > 0)
|
|
|
|
// Format total price in German locale (EUR)
|
|
const formattedTotal = computed(() => {
|
|
return new Intl.NumberFormat('de-DE', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
}).format(total.value)
|
|
})
|
|
|
|
// Handle button click to open cart sidebar
|
|
function handleClick(e: Event) {
|
|
e.preventDefault()
|
|
open()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Desktop cart button (visible only on lg and up) -->
|
|
<button
|
|
@click="handleClick"
|
|
class="relative hidden lg:flex items-center gap-5 rounded-[25px] px-[30px] py-[10px] transition-all hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-experimenta-accent focus:ring-offset-2 focus:ring-offset-transparent"
|
|
aria-label="Warenkorb öffnen"
|
|
>
|
|
<!-- Cart icon with item count badge -->
|
|
<div class="relative inline-flex items-center justify-center">
|
|
<ShoppingCart class="h-6 w-6 text-white" strokeWidth="2" />
|
|
|
|
<!-- Item count badge -->
|
|
<Transition
|
|
enter-active-class="transition-all duration-200 ease-out"
|
|
enter-from-class="scale-0 opacity-0"
|
|
enter-to-class="scale-100 opacity-100"
|
|
leave-active-class="transition-all duration-150 ease-in"
|
|
leave-from-class="scale-100 opacity-100"
|
|
leave-to-class="scale-0 opacity-0"
|
|
>
|
|
<Badge
|
|
v-if="hasItems"
|
|
class="absolute -top-2.5 -right-3.5 h-5.5 min-w-[22px] px-1.5 flex items-center justify-center bg-experimenta-accent text-white text-xs font-bold border-2 border-purple-darkest shadow-lg"
|
|
>
|
|
{{ itemCount > 99 ? '99+' : itemCount }}
|
|
</Badge>
|
|
</Transition>
|
|
</div>
|
|
|
|
<!-- Total price (desktop only) -->
|
|
<span class="text-base font-bold text-white tabular-nums">
|
|
{{ formattedTotal }}
|
|
</span>
|
|
|
|
<!-- Static background -->
|
|
<span
|
|
class="absolute inset-0 rounded-[25px] bg-white/10"
|
|
aria-hidden="true"
|
|
/>
|
|
</button>
|
|
</template>
|