Browse Source
- 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.main
9 changed files with 207 additions and 64 deletions
@ -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> |
||||
Loading…
Reference in new issue