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.
This commit is contained in:
@@ -64,7 +64,7 @@ interface Props {
|
|||||||
loading?: boolean
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
defineProps<Props>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
submit: [data: z.infer<typeof checkoutSchema>]
|
submit: [data: z.infer<typeof checkoutSchema>]
|
||||||
@@ -72,20 +72,36 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
|
|
||||||
|
// Extended user type with address fields
|
||||||
|
type ExtendedUser = typeof user.value & {
|
||||||
|
id?: string
|
||||||
|
firstName?: string
|
||||||
|
lastName?: string
|
||||||
|
email?: string
|
||||||
|
salutation?: 'male' | 'female' | 'other' | null
|
||||||
|
dateOfBirth?: Date | string | null
|
||||||
|
street?: string | null
|
||||||
|
postCode?: string | null
|
||||||
|
city?: string | null
|
||||||
|
countryCode?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const extendedUser = user.value as ExtendedUser
|
||||||
|
|
||||||
// Form state
|
// Form state
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
salutation: (user.value?.salutation as 'male' | 'female' | 'other') || 'other',
|
salutation: (extendedUser?.salutation as 'male' | 'female' | 'other') || 'other',
|
||||||
firstName: user.value?.firstName || '',
|
firstName: extendedUser?.firstName || '',
|
||||||
lastName: user.value?.lastName || '',
|
lastName: extendedUser?.lastName || '',
|
||||||
dateOfBirth: user.value?.dateOfBirth
|
dateOfBirth: extendedUser?.dateOfBirth
|
||||||
? new Date(user.value.dateOfBirth).toISOString().split('T')[0]
|
? new Date(extendedUser.dateOfBirth).toISOString().split('T')[0]
|
||||||
: '',
|
: '',
|
||||||
street: user.value?.street || '',
|
street: extendedUser?.street || '',
|
||||||
postCode: user.value?.postCode || '',
|
postCode: extendedUser?.postCode || '',
|
||||||
city: user.value?.city || '',
|
city: extendedUser?.city || '',
|
||||||
countryCode: user.value?.countryCode || 'DE',
|
countryCode: extendedUser?.countryCode || 'DE',
|
||||||
// Pre-checked if user doesn't have address yet
|
// Pre-checked if user doesn't have address yet
|
||||||
saveAddress: !user.value?.street,
|
saveAddress: !extendedUser?.street,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Validation errors
|
// Validation errors
|
||||||
@@ -131,7 +147,7 @@ function getError(field: string): string {
|
|||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label class="text-sm font-medium text-white">Anrede *</label>
|
<label class="text-sm font-medium text-white">Anrede *</label>
|
||||||
<Select v-model="form.salutation" :disabled="loading">
|
<Select v-model="form.salutation" :disabled="loading">
|
||||||
<SelectTrigger :class="{ 'border-red-500': hasError('salutation') }">
|
<SelectTrigger :class="{ 'border-warning/50': hasError('salutation') }">
|
||||||
<SelectValue placeholder="Bitte wählen" />
|
<SelectValue placeholder="Bitte wählen" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -140,7 +156,8 @@ function getError(field: string): string {
|
|||||||
<SelectItem value="other">Keine Angabe</SelectItem>
|
<SelectItem value="other">Keine Angabe</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<p v-if="hasError('salutation')" class="text-sm text-red-400">
|
<p v-if="hasError('salutation')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('salutation') }}
|
{{ getError('salutation') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,9 +171,10 @@ function getError(field: string): string {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Max"
|
placeholder="Max"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('firstName') }"
|
:class="{ 'border-warning/50': hasError('firstName') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('firstName')" class="text-sm text-red-400">
|
<p v-if="hasError('firstName')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('firstName') }}
|
{{ getError('firstName') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,9 +188,10 @@ function getError(field: string): string {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Mustermann"
|
placeholder="Mustermann"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('lastName') }"
|
:class="{ 'border-warning/50': hasError('lastName') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('lastName')" class="text-sm text-red-400">
|
<p v-if="hasError('lastName')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('lastName') }}
|
{{ getError('lastName') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,9 +206,10 @@ function getError(field: string): string {
|
|||||||
v-model="form.dateOfBirth"
|
v-model="form.dateOfBirth"
|
||||||
type="date"
|
type="date"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('dateOfBirth') }"
|
:class="{ 'border-warning/50': hasError('dateOfBirth') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('dateOfBirth')" class="text-sm text-red-400">
|
<p v-if="hasError('dateOfBirth')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('dateOfBirth') }}
|
{{ getError('dateOfBirth') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,9 +225,10 @@ function getError(field: string): string {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Musterstraße 123"
|
placeholder="Musterstraße 123"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('street') }"
|
:class="{ 'border-warning/50': hasError('street') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('street')" class="text-sm text-red-400">
|
<p v-if="hasError('street')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('street') }}
|
{{ getError('street') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,9 +243,10 @@ function getError(field: string): string {
|
|||||||
placeholder="74072"
|
placeholder="74072"
|
||||||
maxlength="5"
|
maxlength="5"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('postCode') }"
|
:class="{ 'border-warning/50': hasError('postCode') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('postCode')" class="text-sm text-red-400">
|
<p v-if="hasError('postCode')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('postCode') }}
|
{{ getError('postCode') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,9 +260,10 @@ function getError(field: string): string {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Heilbronn"
|
placeholder="Heilbronn"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:class="{ 'border-red-500': hasError('city') }"
|
:class="{ 'border-warning/50': hasError('city') }"
|
||||||
/>
|
/>
|
||||||
<p v-if="hasError('city')" class="text-sm text-red-400">
|
<p v-if="hasError('city')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('city') }}
|
{{ getError('city') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,7 +272,7 @@ function getError(field: string): string {
|
|||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="countryCode" class="text-sm font-medium text-white">Land *</label>
|
<label for="countryCode" class="text-sm font-medium text-white">Land *</label>
|
||||||
<Select v-model="form.countryCode" :disabled="loading">
|
<Select v-model="form.countryCode" :disabled="loading">
|
||||||
<SelectTrigger :class="{ 'border-red-500': hasError('countryCode') }">
|
<SelectTrigger :class="{ 'border-warning/50': hasError('countryCode') }">
|
||||||
<SelectValue placeholder="Bitte wählen" />
|
<SelectValue placeholder="Bitte wählen" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -258,7 +281,8 @@ function getError(field: string): string {
|
|||||||
<SelectItem value="CH">Schweiz</SelectItem>
|
<SelectItem value="CH">Schweiz</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<p v-if="hasError('countryCode')" class="text-sm text-red-400">
|
<p v-if="hasError('countryCode')" class="form-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning flex-shrink-0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
{{ getError('countryCode') }}
|
{{ getError('countryCode') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -281,8 +305,9 @@ function getError(field: string): string {
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
class="w-full bg-gradient-button bg-size-300 bg-left hover:bg-right transition-all duration-300 font-bold text-white shadow-lg hover:shadow-2xl"
|
variant="experimenta"
|
||||||
size="lg"
|
size="experimenta"
|
||||||
|
class="w-full"
|
||||||
>
|
>
|
||||||
<span v-if="!loading">Weiter zur Zahlung</span>
|
<span v-if="!loading">Weiter zur Zahlung</span>
|
||||||
<span v-else class="flex items-center gap-2">
|
<span v-else class="flex items-center gap-2">
|
||||||
|
|||||||
@@ -172,7 +172,9 @@ const formatDate = (date: Date | string) => {
|
|||||||
<h3 class="text-lg font-semibold text-white">Rechnungsadresse</h3>
|
<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">
|
<div class="p-4 rounded-lg bg-white/5 border border-white/10 space-y-1 text-sm">
|
||||||
<p class="text-white">
|
<p class="text-white">
|
||||||
|
<template v-if="order.billingAddress.salutation !== 'other'">
|
||||||
{{ formatSalutation(order.billingAddress.salutation) }}
|
{{ formatSalutation(order.billingAddress.salutation) }}
|
||||||
|
</template>
|
||||||
{{ order.billingAddress.firstName }}
|
{{ order.billingAddress.firstName }}
|
||||||
{{ order.billingAddress.lastName }}
|
{{ order.billingAddress.lastName }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ async function handleCheckout(checkoutData: CheckoutData) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Error Alert -->
|
<!-- Error Alert -->
|
||||||
<Alert v-if="error" variant="destructive" class="mb-6">
|
<Alert v-if="error" variant="error" class="mb-6">
|
||||||
<AlertTitle>Fehler</AlertTitle>
|
<AlertTitle>Fehler</AlertTitle>
|
||||||
<AlertDescription>{{ error }}</AlertDescription>
|
<AlertDescription>{{ error }}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Error Alert -->
|
<!-- Error Alert -->
|
||||||
<Alert v-if="error" variant="destructive" class="mb-6">
|
<Alert v-if="error" variant="error" class="mb-6">
|
||||||
<AlertTitle>Fehler</AlertTitle>
|
<AlertTitle>Fehler</AlertTitle>
|
||||||
<AlertDescription>{{ error }}</AlertDescription>
|
<AlertDescription>{{ error }}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
@@ -165,8 +165,9 @@ onMounted(() => {
|
|||||||
<Button
|
<Button
|
||||||
@click="confirmOrder"
|
@click="confirmOrder"
|
||||||
:disabled="isConfirming"
|
:disabled="isConfirming"
|
||||||
class="w-full bg-gradient-button bg-size-300 bg-left hover:bg-right transition-all duration-300 font-bold text-white shadow-lg hover:shadow-2xl"
|
variant="experimenta"
|
||||||
size="lg"
|
size="experimenta"
|
||||||
|
class="w-full"
|
||||||
>
|
>
|
||||||
<span v-if="!isConfirming" class="flex items-center justify-center gap-2">
|
<span v-if="!isConfirming" class="flex items-center justify-center gap-2">
|
||||||
<svg
|
<svg
|
||||||
@@ -209,8 +210,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- Back Link -->
|
<!-- Back Link -->
|
||||||
<div class="text-center pt-4">
|
<div class="text-center pt-4">
|
||||||
<NuxtLink to="/warenkorb" class="text-sm text-experimenta-accent hover:underline">
|
<NuxtLink to="/checkout" class="text-sm text-experimenta-accent hover:underline">
|
||||||
← Zurück zum Warenkorb
|
← Zurück zur Kasse
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
||||||
<!-- Error Alert -->
|
<!-- Error Alert -->
|
||||||
<Alert v-if="error" variant="destructive" class="mb-6">
|
<Alert v-if="error" variant="error" class="mb-6">
|
||||||
<AlertTitle>Fehler</AlertTitle>
|
<AlertTitle>Fehler</AlertTitle>
|
||||||
<AlertDescription>{{ error }}</AlertDescription>
|
<AlertDescription>{{ error }}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
@@ -179,12 +179,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<NuxtLink to="/">
|
<NuxtLink to="/" class="w-full">
|
||||||
<Button
|
<Button variant="secondary" size="experimenta" class="w-full">
|
||||||
variant="outline"
|
|
||||||
class="w-full border-white/20 hover:bg-white/10 text-white"
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5 mr-2"
|
class="w-5 h-5 mr-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -203,11 +199,8 @@ onMounted(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink to="/experimenta">
|
<NuxtLink to="/experimenta" class="w-full">
|
||||||
<Button
|
<Button variant="experimenta" size="experimenta" class="w-full">
|
||||||
class="w-full bg-gradient-button bg-size-300 bg-left hover:bg-right transition-all duration-300 font-bold text-white shadow-lg hover:shadow-2xl"
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5 mr-2"
|
class="w-5 h-5 mr-2"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function handlePaymentSuccess() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Error Alert -->
|
<!-- Error Alert -->
|
||||||
<Alert v-if="error" variant="destructive" class="mb-6">
|
<Alert v-if="error" variant="error" class="mb-6">
|
||||||
<AlertTitle>Fehler</AlertTitle>
|
<AlertTitle>Fehler</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{{ error }}. Du wirst zum Warenkorb weitergeleitet...
|
{{ error }}. Du wirst zum Warenkorb weitergeleitet...
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
|
salutation: user.salutation,
|
||||||
|
dateOfBirth: user.dateOfBirth,
|
||||||
|
street: user.street,
|
||||||
|
postCode: user.postCode,
|
||||||
|
city: user.city,
|
||||||
|
countryCode: user.countryCode,
|
||||||
},
|
},
|
||||||
accessToken: tokens.access_token, // Store for logout
|
accessToken: tokens.access_token, // Store for logout
|
||||||
loggedInAt: new Date().toISOString(),
|
loggedInAt: new Date().toISOString(),
|
||||||
|
|||||||
29
types/auth.d.ts
vendored
Normal file
29
types/auth.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Type augmentation for nuxt-auth-utils UserSession
|
||||||
|
*
|
||||||
|
* Extends the User type with additional properties from our database schema
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '#auth-utils' {
|
||||||
|
interface User {
|
||||||
|
id: string
|
||||||
|
experimentaId: string | null
|
||||||
|
email: string
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
salutation: 'male' | 'female' | 'other' | null
|
||||||
|
dateOfBirth: Date | string | null
|
||||||
|
street: string | null
|
||||||
|
postCode: string | null
|
||||||
|
city: string | null
|
||||||
|
countryCode: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSession {
|
||||||
|
user: User
|
||||||
|
accessToken?: string
|
||||||
|
loggedInAt?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {}
|
||||||
Reference in New Issue
Block a user