- Refactored CheckoutForm.vue to utilize an extended user type, incorporating additional address fields for improved user data handling. - Updated OrderSummary.vue to conditionally display salutation based on user input. - Standardized error alert styling across multiple pages, changing variant from 'destructive' to 'error' for consistency. - Adjusted button styles in various components to align with the new 'experimenta' variant. These changes aim to improve user experience by ensuring accurate data representation and consistent UI elements across the checkout and order processes.
239 lines
7.4 KiB
Vue
239 lines
7.4 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Order Success Page (/order/success/[orderId])
|
|
*
|
|
* Features:
|
|
* - Requires authentication (middleware: auth)
|
|
* - Fetches order details from /api/orders/[orderId]
|
|
* - Validates order belongs to user (server-side)
|
|
* - Validates order status is 'completed'
|
|
* - Shows success message and animation
|
|
* - Shows order number
|
|
* - Shows OrderSummary component (read-only)
|
|
* - Links to homepage and product pages
|
|
*/
|
|
|
|
definePageMeta({
|
|
middleware: 'auth',
|
|
layout: 'default',
|
|
})
|
|
|
|
const route = useRoute()
|
|
const orderId = computed(() => route.params.orderId as string)
|
|
|
|
// Get cart composable to refresh cart state
|
|
const { fetchCart } = useCart()
|
|
|
|
// Order data
|
|
const order = ref<any>(null)
|
|
const isLoading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
// Fetch order details
|
|
async function fetchOrder() {
|
|
if (!orderId.value) return
|
|
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
order.value = await $fetch(`/api/orders/${orderId.value}`)
|
|
|
|
// Check order status
|
|
if (order.value.status !== 'completed') {
|
|
error.value = `Diese Bestellung wurde noch nicht abgeschlossen. Status: ${order.value.status}`
|
|
|
|
// Redirect to confirmation page if still pending
|
|
if (order.value.status === 'pending') {
|
|
setTimeout(() => {
|
|
navigateTo(`/order/confirm/${orderId.value}`)
|
|
}, 2000)
|
|
}
|
|
} else {
|
|
// Order completed successfully - refresh cart to show it's empty
|
|
await fetchCart()
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to fetch order:', err)
|
|
|
|
if (err.statusCode === 404) {
|
|
error.value = 'Bestellung nicht gefunden'
|
|
} else if (err.statusCode === 403) {
|
|
error.value = 'Du hast keine Berechtigung, diese Bestellung zu sehen'
|
|
} else {
|
|
error.value = 'Fehler beim Laden der Bestellung'
|
|
}
|
|
|
|
// Redirect to homepage after 3 seconds
|
|
setTimeout(() => {
|
|
navigateTo('/')
|
|
}, 3000)
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Fetch order on mount
|
|
onMounted(() => {
|
|
fetchOrder()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<CommonHeader />
|
|
|
|
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
|
<!-- Error Alert -->
|
|
<Alert v-if="error" variant="error" class="mb-6">
|
|
<AlertTitle>Fehler</AlertTitle>
|
|
<AlertDescription>{{ error }}</AlertDescription>
|
|
</Alert>
|
|
|
|
<!-- Loading State -->
|
|
<div v-if="isLoading" class="text-center py-12">
|
|
<div
|
|
class="animate-spin rounded-full h-12 w-12 border-4 border-white/20 border-t-white mx-auto mb-4"
|
|
/>
|
|
<p class="text-white/60">Lade Bestellung...</p>
|
|
</div>
|
|
|
|
<!-- Success Content -->
|
|
<div v-else-if="order && order.status === 'completed'" class="space-y-8">
|
|
<!-- Success Header with Animation -->
|
|
<div class="text-center space-y-4 py-8">
|
|
<!-- Success Icon (animated checkmark) -->
|
|
<div class="flex justify-center mb-6">
|
|
<div
|
|
class="rounded-full bg-green-500/20 p-6 border-4 border-green-500/50 animate-pulse"
|
|
>
|
|
<svg
|
|
class="w-16 h-16 text-green-500"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 13l4 4L19 7"
|
|
></path>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Success Message -->
|
|
<h1 class="text-4xl font-bold text-white mb-2">
|
|
Vielen Dank für deine Bestellung!
|
|
</h1>
|
|
<p class="text-xl text-white/70">Deine Bestellung wurde erfolgreich abgeschlossen.</p>
|
|
|
|
<!-- Order Number -->
|
|
<div class="inline-block mt-4 px-6 py-3 bg-white/5 rounded-lg border border-white/10">
|
|
<p class="text-sm text-white/60">Bestellnummer</p>
|
|
<p class="text-2xl font-mono font-bold text-experimenta-accent">
|
|
{{ order.orderNumber }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Next Steps Info -->
|
|
<Alert class="border-blue-500/50 bg-blue-500/10">
|
|
<div class="flex items-start gap-3">
|
|
<svg
|
|
class="w-5 h-5 text-blue-400 mt-0.5 flex-shrink-0"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
></path>
|
|
</svg>
|
|
<div>
|
|
<AlertTitle class="text-blue-400">Wie geht es weiter?</AlertTitle>
|
|
<AlertDescription class="text-blue-100/90 space-y-2">
|
|
<p>
|
|
Du erhältst in Kürze eine Bestätigungs-E-Mail mit allen Details zu deiner
|
|
Bestellung.
|
|
</p>
|
|
<p>
|
|
Deine Makerspace-Jahreskarte wird bearbeitet und steht dir bald zur
|
|
Verfügung.
|
|
</p>
|
|
</AlertDescription>
|
|
</div>
|
|
</div>
|
|
</Alert>
|
|
|
|
<!-- Order Summary Card -->
|
|
<Card class="p-6">
|
|
<OrderSummary :order="order" :show-address="true" />
|
|
</Card>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<NuxtLink to="/" class="w-full">
|
|
<Button variant="secondary" size="experimenta" class="w-full">
|
|
<svg
|
|
class="w-5 h-5 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
|
></path>
|
|
</svg>
|
|
Zurück zur Startseite
|
|
</Button>
|
|
</NuxtLink>
|
|
|
|
<NuxtLink to="/experimenta" class="w-full">
|
|
<Button variant="experimenta" size="experimenta" class="w-full">
|
|
<svg
|
|
class="w-5 h-5 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"
|
|
></path>
|
|
</svg>
|
|
Weitere Produkte kaufen
|
|
</Button>
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<!-- Support Info -->
|
|
<div class="text-center pt-4 space-y-2">
|
|
<p class="text-sm text-white/60">
|
|
Fragen zu deiner Bestellung? Kontaktiere uns gerne:
|
|
</p>
|
|
<a
|
|
href="mailto:info@experimenta.science"
|
|
class="text-sm text-experimenta-accent hover:underline"
|
|
>
|
|
info@experimenta.science
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|