Files
my2/app/pages/products/[id].vue
Bastian Masanek 81495d5e17 Enhance navigation and UI components for improved user experience
- Added new AppHeader and BottomNav components for better navigation across the application.
- Introduced AreaTabs for product area navigation and integrated RoleSwitcher for user role management.
- Created CartButton component to display cart status and item count.
- Implemented UserMenu with login/logout functionality and user greeting.
- Added Badge component for notifications and status indicators.
- Updated layout to accommodate new navigation components and ensure mobile responsiveness.
- Created product detail demo page to showcase design patterns and features.
- Enhanced existing components with improved styling and functionality.
2025-11-01 19:51:02 +01:00

194 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* Product Detail Page
*
* Displays full details for a single product with large image and description.
* Includes placeholder "Add to Cart" functionality for future implementation.
*/
import { ArrowLeft, CheckCircle } from 'lucide-vue-next'
// Page metadata
definePageMeta({
layout: 'default',
})
// Get product ID from route
const route = useRoute()
const productId = route.params.id as string
// Type definition for product
interface Product {
id: string
navProductId: string
name: string
description: string
price: string
stockQuantity: number
category: string
active: boolean
createdAt: Date
updatedAt: Date
}
// Fetch product from API
const { data: product, error, pending } = await useFetch<Product>(`/api/products/${productId}`)
// Format price in EUR
const formattedPrice = computed(() => {
if (!product.value) return ''
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
}).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.')
}
</script>
<template>
<NuxtLayout name="default">
<div class="min-h-screen bg-gradient-primary px-4 py-12 md:px-6 lg:px-8">
<!-- Back Button -->
<div class="mx-auto mb-8 max-w-container-narrow">
<NuxtLink
to="/products"
class="inline-flex items-center gap-2 text-white/80 transition-colors hover:text-white"
>
<ArrowLeft :size="20" />
<span>Zurück zur Übersicht</span>
</NuxtLink>
</div>
<!-- Loading State -->
<div v-if="pending" class="mx-auto max-w-container-narrow text-center">
<div class="card-glass inline-block">
<p class="text-white/80">Produkt wird geladen...</p>
</div>
</div>
<!-- Error State (404 or other errors) -->
<div v-else-if="error" class="mx-auto max-w-container-narrow">
<div class="card-glass border-red/50 bg-red/10">
<h2 class="mb-2 text-2xl font-semibold text-red">
{{ error.statusCode === 404 ? 'Produkt nicht gefunden' : 'Fehler beim Laden' }}
</h2>
<p class="mb-6 text-white/80">
{{
error.statusCode === 404
? 'Das angeforderte Produkt existiert nicht oder ist nicht verfügbar.'
: error.message || 'Ein unbekannter Fehler ist aufgetreten.'
}}
</p>
<NuxtLink to="/products">
<Button variant="experimenta" size="experimenta">
Zur Produktübersicht
</Button>
</NuxtLink>
</div>
</div>
<!-- Product Detail -->
<div v-else-if="product" class="mx-auto max-w-container-narrow">
<div class="overflow-hidden rounded-2xl border border-white/20 bg-white/10 shadow-glass backdrop-blur-lg">
<!-- Product Image (no padding, flush with top) -->
<div class="relative aspect-[16/9] w-full overflow-hidden bg-purple-dark">
<img
src="/img/makerspace-jk-2025.jpg"
:alt="product.name"
class="h-full w-full object-cover"
/>
<!-- Gradient overlay -->
<div
class="absolute inset-0 bg-gradient-to-t from-purple-darkest/80 via-transparent to-transparent"
/>
</div>
<!-- Product Content -->
<div class="space-y-6 p-8">
<!-- Title -->
<h1 class="text-3xl font-bold text-white md:text-4xl">
{{ product.name }}
</h1>
<!-- Description -->
<p class="text-lg leading-relaxed text-white/90">
{{ product.description }}
</p>
<!-- Product Details -->
<div class="grid gap-4 sm:grid-cols-2">
<!-- Price Card -->
<div class="rounded-xl bg-white/5 p-4 backdrop-blur-sm">
<span class="mb-1 block text-xs uppercase tracking-wide text-white/60">Preis</span>
<span class="text-3xl font-bold text-experimenta-accent">
{{ formattedPrice }}
</span>
</div>
<!-- Availability Card -->
<div class="rounded-xl bg-white/5 p-4 backdrop-blur-sm">
<span class="mb-1 block text-xs uppercase tracking-wide text-white/60">Verfügbarkeit</span>
<div
:class="[
'flex items-center gap-2 text-xl font-semibold',
product.stockQuantity > 0 ? 'text-green' : 'text-red',
]"
>
<CheckCircle v-if="product.stockQuantity > 0" :size="24" />
<span>{{ product.stockQuantity > 0 ? 'Sofort' : 'Nicht verfügbar' }}</span>
</div>
</div>
</div>
<!-- Features / Benefits -->
<div class="rounded-xl border border-white/20 bg-white/5 p-6 backdrop-blur-sm">
<h2 class="mb-4 text-xl font-semibold text-white">
Was du mit dieser Karte bekommst:
</h2>
<ul class="space-y-3 text-white/90">
<li class="flex items-start gap-2">
<span class="mt-1 text-experimenta-accent"></span>
<span>365 Tage unbegrenzter Zugang</span>
</li>
<li class="flex items-start gap-2">
<span class="mt-1 text-experimenta-accent"></span>
<span>Keine versteckten Kosten</span>
</li>
<li class="flex items-start gap-2">
<span class="mt-1 text-experimenta-accent"></span>
<span>Sofort einsatzbereit nach Kauf</span>
</li>
<li class="flex items-start gap-2">
<span class="mt-1 text-experimenta-accent"></span>
<span>Flexible Nutzung komme so oft du möchtest</span>
</li>
</ul>
</div>
<!-- Action Buttons -->
<div class="flex flex-col gap-4 sm:flex-row">
<Button
variant="experimenta"
size="experimenta"
class="flex-1"
:disabled="product.stockQuantity === 0"
@click="handleAddToCart"
>
{{ product.stockQuantity > 0 ? 'In den Warenkorb' : 'Nicht verfügbar' }}
</Button>
<NuxtLink to="/products" class="btn-secondary flex-1 text-center">
Weitere Produkte ansehen
</NuxtLink>
</div>
</div>
</div>
</div>
</div>
</NuxtLayout>
</template>