You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

187 lines
5.1 KiB

<script setup lang="ts">
/**
* OrderSummary Component
*
* Displays order details including:
* - Product list with quantities and prices
* - Subtotal, VAT, and total
* - Billing address
*
* Used on:
* - Order confirmation page (/bestellung/bestaetigen/[orderId])
* - Order success page (/bestellung/erfolg/[orderId])
*/
interface OrderItem {
id: string
productId: string
quantity: number
priceSnapshot: string
productSnapshot: {
name: string
description?: string
}
product?: {
name: string
imageUrl?: string | null
}
subtotal: number
}
interface BillingAddress {
salutation: 'male' | 'female' | 'other'
firstName: string
lastName: string
dateOfBirth: string
street: string
postCode: string
city: string
countryCode: string
}
interface Order {
id: string
orderNumber: string
totalAmount: string | number
status: string
billingAddress: BillingAddress
items: OrderItem[]
createdAt: Date | string
}
interface Props {
order: Order
/**
* Show billing address section
*/
showAddress?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showAddress: true,
})
// Format currency in EUR
const formatCurrency = (amount: number | string) => {
const value = typeof amount === 'string' ? Number.parseFloat(amount) : amount
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
}).format(value)
}
// Calculate total from items (for client-side display)
const total = computed(() => {
return typeof props.order.totalAmount === 'string'
? Number.parseFloat(props.order.totalAmount)
: props.order.totalAmount
})
// Calculate VAT (7% already included in total)
const vatAmount = computed(() => {
return total.value * (0.07 / 1.07)
})
// Format salutation
const formatSalutation = (salutation: string) => {
const map: Record<string, string> = {
male: 'Herr',
female: 'Frau',
other: 'Keine Angabe',
}
return map[salutation] || salutation
}
// Format date
const formatDate = (date: Date | string) => {
const d = typeof date === 'string' ? new Date(date) : date
return new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
}).format(d)
}
</script>
<template>
<div class="space-y-6">
<!-- Order Header -->
<div class="pb-4 border-b border-white/20">
<h2 class="text-2xl font-bold text-white">Bestellübersicht</h2>
<p class="text-sm text-white/60 mt-1">
Bestellnummer: <span class="font-mono text-white/80">{{ order.orderNumber }}</span>
</p>
<p class="text-sm text-white/60">
Erstellt am: {{ formatDate(order.createdAt) }}
</p>
</div>
<!-- Order Items -->
<div class="space-y-4">
<h3 class="text-lg font-semibold text-white">Artikel</h3>
<div class="space-y-3">
<div
v-for="item in order.items"
:key="item.id"
class="flex items-start justify-between gap-4 p-4 rounded-lg bg-white/5 border border-white/10"
>
<div class="flex-1">
<h4 class="font-medium text-white">
{{ item.productSnapshot?.name || item.product?.name }}
</h4>
<p class="text-sm text-white/60 mt-1">
{{ item.quantity }}x {{ formatCurrency(item.priceSnapshot) }}
</p>
</div>
<div class="text-right">
<p class="font-semibold text-white">
{{ formatCurrency(item.subtotal) }}
</p>
</div>
</div>
</div>
</div>
<!-- Price Breakdown -->
<div class="space-y-3 pt-4 border-t border-white/20">
<!-- Subtotal -->
<div class="flex items-center justify-between text-white/80">
<span class="text-sm">Zwischensumme</span>
<span class="font-medium">{{ formatCurrency(total) }}</span>
</div>
<!-- VAT (included) -->
<div class="flex items-center justify-between text-white/60 text-sm">
<span>inkl. MwSt. (7%)</span>
<span>{{ formatCurrency(vatAmount) }}</span>
</div>
<!-- Total -->
<div class="flex items-center justify-between pt-3 border-t border-white/20">
<span class="text-lg font-bold text-white">Gesamt</span>
<span class="text-2xl font-bold text-experimenta-accent">
{{ formatCurrency(total) }}
</span>
</div>
</div>
<!-- Billing Address -->
<div v-if="showAddress" class="space-y-3 pt-6 border-t border-white/20">
<h3 class="text-lg font-semibold text-white">Rechnungsadresse</h3>
<div class="p-4 rounded-lg bg-white/5 border border-white/10 space-y-1 text-sm">
<p class="text-white">
{{ formatSalutation(order.billingAddress.salutation) }}
{{ order.billingAddress.firstName }}
{{ order.billingAddress.lastName }}
</p>
<p class="text-white/80">{{ order.billingAddress.street }}</p>
<p class="text-white/80">
{{ order.billingAddress.postCode }} {{ order.billingAddress.city }}
</p>
<p class="text-white/80">{{ order.billingAddress.countryCode }}</p>
</div>
</div>
</div>
</template>