Implement experimenta product listing page and enhance navigation components
- Added a new page for displaying experimenta annual passes, integrating ProductCard and ProductGrid components for product presentation. - Updated API to filter products by category, allowing for specific product queries. - Enhanced navigation components, including AreaTabs and RoleSwitcher, to improve user experience and accessibility. - Refactored existing components for better styling consistency and responsiveness. - Improved dropdown menu components with updated styles and hover effects for better usability.
This commit is contained in:
142
app/pages/experimenta/index.vue
Normal file
142
app/pages/experimenta/index.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* experimenta Products Listing Page
|
||||
*
|
||||
* Displays experimenta annual passes using ProductCard and ProductGrid components.
|
||||
* Fetches only experimenta category products from the database via API.
|
||||
*/
|
||||
|
||||
// Page metadata
|
||||
definePageMeta({
|
||||
layout: 'default',
|
||||
})
|
||||
|
||||
// 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 products from API - only experimenta passes
|
||||
const { data: products, error, pending } = await useFetch<Product[]>('/api/products', {
|
||||
query: {
|
||||
category: 'annual-pass',
|
||||
},
|
||||
})
|
||||
|
||||
// Map product categories to badges
|
||||
const getBadge = (category: string): string | undefined => {
|
||||
const badges: Record<string, string> = {
|
||||
'annual-pass': 'Beliebt',
|
||||
}
|
||||
return badges[category]
|
||||
}
|
||||
|
||||
// Map product categories to discount percentages (for demo)
|
||||
const getDiscount = (category: string): number | undefined => {
|
||||
const discounts: Record<string, number> = {
|
||||
// No discounts for now
|
||||
}
|
||||
return discounts[category]
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLayout name="default">
|
||||
<div class="min-h-screen bg-gradient-primary px-4 py-12 md:px-6 lg:px-8">
|
||||
<!-- Page Header -->
|
||||
<div class="mx-auto mb-12 max-w-container-wide text-center">
|
||||
<h1 class="mb-4 text-4xl font-bold text-white md:text-5xl">
|
||||
experimenta Jahreskarten
|
||||
</h1>
|
||||
<p class="mx-auto max-w-2xl text-lg text-white/80">
|
||||
Erlebe die Ausstellungswelt der experimenta ein ganzes Jahr lang.
|
||||
Mit freiem Eintritt zu allen Ausstellungen, Science Dome Shows und Sonderausstellungen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="pending" class="mx-auto max-w-container-wide text-center">
|
||||
<div class="card-glass inline-block">
|
||||
<p class="text-white/80">Produkte werden geladen...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-else-if="error" class="mx-auto max-w-container-wide">
|
||||
<div class="card-glass border-red/50 bg-red/10">
|
||||
<h2 class="mb-2 text-xl font-semibold text-red">
|
||||
Fehler beim Laden der Produkte
|
||||
</h2>
|
||||
<p class="text-white/80">
|
||||
{{ error.message || 'Ein unbekannter Fehler ist aufgetreten.' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Products Grid -->
|
||||
<div v-else-if="products && products.length > 0" class="mx-auto max-w-container-wide">
|
||||
<ProductGrid :columns="3">
|
||||
<ProductCard
|
||||
v-for="product in products"
|
||||
:key="product.id"
|
||||
image="/img/makerspace-jk-2025.jpg"
|
||||
:title="product.name"
|
||||
:description="product.description"
|
||||
:price="Number(product.price)"
|
||||
:badge="getBadge(product.category)"
|
||||
:discount-percentage="getDiscount(product.category)"
|
||||
:product-id="product.id"
|
||||
/>
|
||||
</ProductGrid>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div v-else class="mx-auto max-w-container-wide text-center">
|
||||
<div class="card-glass">
|
||||
<h2 class="mb-2 text-xl font-semibold text-white">
|
||||
Keine Produkte verfügbar
|
||||
</h2>
|
||||
<p class="text-white/80">
|
||||
Aktuell sind keine Produkte verfügbar. Bitte schaue später noch einmal vorbei.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Card -->
|
||||
<div class="mx-auto mt-12 max-w-container-wide">
|
||||
<div class="card-info card-accent-border">
|
||||
<h2 class="mb-3 text-xl font-semibold text-experimenta-accent">
|
||||
Warum eine experimenta Jahreskarte?
|
||||
</h2>
|
||||
<ul class="space-y-2 text-white/90">
|
||||
<li class="flex items-start gap-2">
|
||||
<span class="mt-1 text-experimenta-accent">✓</span>
|
||||
<span>365 Tage unbegrenzter Zugang zur Ausstellungswelt</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<span class="mt-1 text-experimenta-accent">✓</span>
|
||||
<span>Freier Eintritt zu allen Science Dome Shows</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<span class="mt-1 text-experimenta-accent">✓</span>
|
||||
<span>Zugang zu wechselnden Sonderausstellungen</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>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
Reference in New Issue
Block a user