Files
my2/app/pages/order/confirm/[orderId].vue
Bastian Masanek 782bd6cdd7 Enhance CheckoutForm and Order components with user data integration
- 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.
2025-11-03 17:00:22 +01:00

221 lines
6.6 KiB
Vue

<script setup lang="ts">
/**
* Order Confirmation Page (/order/confirm/[orderId])
*
* Features:
* - Requires authentication (middleware: auth)
* - Fetches order details from /api/orders/[orderId]
* - Validates order belongs to user (server-side)
* - Validates order status is 'pending'
* - Shows OrderSummary component
* - Shows billing address
* - Warning text before final confirmation
* - "Jetzt verbindlich bestellen" button
* - Redirects to success page after confirmation
*/
definePageMeta({
middleware: 'auth',
layout: 'default',
})
const route = useRoute()
const orderId = computed(() => route.params.orderId as string)
// Order data
const order = ref<any>(null)
const isLoading = ref(false)
const isConfirming = 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') {
// Order already completed - redirect to success page
navigateTo(`/order/success/${orderId.value}`)
return
}
if (order.value.status !== 'pending') {
error.value = `Bestellung kann nicht bestätigt werden. Status: ${order.value.status}`
}
} 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 cart after 3 seconds
setTimeout(() => {
navigateTo('/cart')
}, 3000)
} finally {
isLoading.value = false
}
}
// Confirm order
async function confirmOrder() {
if (!orderId.value) return
isConfirming.value = true
error.value = null
try {
const response = await $fetch(`/api/orders/confirm/${orderId.value}`, {
method: 'POST',
})
if (response.success) {
// Redirect to success page
navigateTo(`/order/success/${orderId.value}`)
}
} catch (err: any) {
console.error('Failed to confirm order:', err)
error.value =
err.data?.message ||
'Fehler beim Bestätigen der Bestellung. Bitte versuche es erneut.'
} finally {
isConfirming.value = false
}
}
// Fetch order on mount
onMounted(() => {
fetchOrder()
})
</script>
<template>
<div>
<CommonHeader />
<div class="container mx-auto px-4 py-8 max-w-4xl">
<!-- Page Header -->
<div class="mb-8 text-center">
<h1 class="text-4xl font-bold text-white mb-2">Bestellung bestätigen</h1>
<p class="text-white/70">Bitte überprüfe deine Bestellung vor der finalen Bestätigung</p>
</div>
<!-- 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>
<!-- Order Content -->
<div v-else-if="order" class="space-y-6">
<!-- Order Summary Card -->
<Card class="p-6">
<OrderSummary :order="order" :show-address="true" />
</Card>
<!-- Warning Notice -->
<Alert class="border-yellow-500/50 bg-yellow-500/10">
<div class="flex items-start gap-3">
<svg
class="w-5 h-5 text-yellow-500 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="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
></path>
</svg>
<div>
<AlertTitle class="text-yellow-500">Wichtiger Hinweis</AlertTitle>
<AlertDescription class="text-yellow-100/90">
Bitte überprüfe alle Angaben sorgfältig. Nach der Bestätigung ist die
Bestellung verbindlich und kann nicht mehr geändert werden.
</AlertDescription>
</div>
</div>
</Alert>
<!-- Confirmation Button -->
<Card class="p-6">
<div class="space-y-4">
<Button
@click="confirmOrder"
:disabled="isConfirming"
variant="experimenta"
size="experimenta"
class="w-full"
>
<span v-if="!isConfirming" class="flex items-center justify-center gap-2">
<svg
class="w-5 h-5"
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="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>Jetzt verbindlich bestellen</span>
</span>
<span v-else class="flex items-center gap-2">
<div
class="animate-spin rounded-full h-4 w-4 border-2 border-white/20 border-t-white"
/>
Bestätigung läuft...
</span>
</Button>
<p class="text-xs text-white/60 text-center">
Mit dem Klick auf "Jetzt verbindlich bestellen" akzeptierst du unsere
<NuxtLink to="/agb" class="text-experimenta-accent hover:underline">
AGB
</NuxtLink>
und
<NuxtLink to="/datenschutz" class="text-experimenta-accent hover:underline">
Datenschutzerklärung
</NuxtLink>
.
</p>
</div>
</Card>
<!-- Back Link -->
<div class="text-center pt-4">
<NuxtLink to="/checkout" class="text-sm text-experimenta-accent hover:underline">
Zurück zur Kasse
</NuxtLink>
</div>
</div>
</div>
</div>
</template>