@shopbite-de/storefront 1.18.1 → 1.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app.vue +3 -9
- package/app/components/Cart/Item.vue +5 -5
- package/app/components/Category/Listing.vue +141 -92
- package/app/components/Checkout/DeliveryTimeSelect.vue +8 -6
- package/app/components/Checkout/PaymentAndDelivery.vue +31 -33
- package/app/components/Checkout/PaymentMethod.vue +1 -1
- package/app/components/Checkout/ShippingMethod.vue +1 -1
- package/app/components/Checkout/Summary.vue +37 -48
- package/app/components/Header/Right.vue +3 -3
- package/app/components/Header/Title.vue +7 -14
- package/app/components/Header.vue +2 -2
- package/app/components/ImageGallery.vue +26 -13
- package/app/components/Product/Card.vue +13 -11
- package/app/components/Product/CardSkeleton.vue +26 -0
- package/app/components/Product/{Detail2.vue → Detail.vue} +1 -1
- package/app/components/User/Detail.vue +2 -2
- package/app/composables/useShopBiteConfig.ts +5 -20
- package/app/pages/bestellung/bestaetigen.vue +14 -0
- package/app/pages/bestellung/warenkorb.vue +49 -0
- package/app/pages/bestellung/zahlung-versand.vue +23 -0
- package/app/pages/bestellung.vue +36 -84
- package/app/pages/index.vue +1 -1
- package/app/pages/kontakt.vue +1 -1
- package/app/pages/speisekarte/[...all].vue +1 -0
- package/app/stores/checkout.ts +22 -0
- package/nuxt.config.ts +5 -0
- package/package.json +3 -1
- package/test/e2e/simple-checkout-as-recurring-customer.test.ts +8 -8
- package/test/nuxt/useShopBiteConfig.test.ts +16 -20
- package/test/nuxt/HeaderTitle.test.ts +0 -42
- /package/app/components/Product/{Configurator2.vue → Configurator.vue} +0 -0
package/app/app.vue
CHANGED
|
@@ -7,11 +7,11 @@ const TOAST_CONFIG = {
|
|
|
7
7
|
title: "Wir haben geöffnet!",
|
|
8
8
|
color: "primary" as const,
|
|
9
9
|
progress: false,
|
|
10
|
-
duration:
|
|
10
|
+
duration: 5000,
|
|
11
11
|
icon: "i-lucide-party-popper",
|
|
12
12
|
actions: [
|
|
13
13
|
{
|
|
14
|
-
icon: "i-lucide-
|
|
14
|
+
icon: "i-lucide-utensils-crossed",
|
|
15
15
|
label: "Zur Speisekarte",
|
|
16
16
|
color: "neutral" as const,
|
|
17
17
|
variant: "outline" as const,
|
|
@@ -89,14 +89,8 @@ if (import.meta.client) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
const { refresh: refreshToppings } = useShopBiteConfig();
|
|
93
|
-
|
|
94
92
|
onMounted(async () => {
|
|
95
|
-
await Promise.all([
|
|
96
|
-
refreshHolidays(),
|
|
97
|
-
refreshBusinessHours(),
|
|
98
|
-
refreshToppings(),
|
|
99
|
-
]);
|
|
93
|
+
await Promise.all([refreshHolidays(), refreshBusinessHours()]);
|
|
100
94
|
refreshCart();
|
|
101
95
|
displayStoreStatus();
|
|
102
96
|
});
|
|
@@ -64,7 +64,7 @@ const handleRemoveItem = () => {
|
|
|
64
64
|
? cartItem?.children?.[0]?.payload?.options
|
|
65
65
|
: cartItem?.payload?.options"
|
|
66
66
|
:key="option.group + option.option"
|
|
67
|
-
class="text-sm text-pretty text-
|
|
67
|
+
class="text-sm text-pretty text-muted mt-1"
|
|
68
68
|
>
|
|
69
69
|
{{ option.group }}: {{ option.option }}
|
|
70
70
|
</p>
|
|
@@ -84,14 +84,14 @@ const handleRemoveItem = () => {
|
|
|
84
84
|
:min="1"
|
|
85
85
|
:max="100"
|
|
86
86
|
class="max-w-46"
|
|
87
|
-
aria-label="
|
|
87
|
+
aria-label="Menge"
|
|
88
88
|
/>
|
|
89
89
|
<UButton
|
|
90
90
|
v-if="withDeleteButton"
|
|
91
91
|
icon="i-lucide-trash"
|
|
92
92
|
variant="soft"
|
|
93
93
|
color="neutral"
|
|
94
|
-
aria-label="
|
|
94
|
+
aria-label="Artikel entfernen"
|
|
95
95
|
@click="handleRemoveItem"
|
|
96
96
|
/>
|
|
97
97
|
</div>
|
|
@@ -101,14 +101,14 @@ const handleRemoveItem = () => {
|
|
|
101
101
|
icon="i-lucide-trash"
|
|
102
102
|
variant="outline"
|
|
103
103
|
color="error"
|
|
104
|
-
aria-label="
|
|
104
|
+
aria-label="Artikel entfernen"
|
|
105
105
|
@click="handleRemoveItem"
|
|
106
106
|
/>
|
|
107
107
|
</div>
|
|
108
108
|
|
|
109
109
|
<!-- Empty state -->
|
|
110
110
|
<div v-else class="text-center py-4">
|
|
111
|
-
<p class="text-
|
|
111
|
+
<p class="text-muted">Warenkorb ist leer...</p>
|
|
112
112
|
</div>
|
|
113
113
|
</div>
|
|
114
114
|
</template>
|
|
@@ -46,12 +46,14 @@ const {
|
|
|
46
46
|
loading,
|
|
47
47
|
search,
|
|
48
48
|
getElements,
|
|
49
|
+
getCurrentListing,
|
|
49
50
|
getCurrentSortingOrder,
|
|
50
51
|
getSortingOrders,
|
|
51
52
|
changeCurrentSortingOrder,
|
|
52
53
|
getAvailableFilters,
|
|
53
54
|
getCurrentFilters,
|
|
54
55
|
setCurrentFilters,
|
|
56
|
+
setInitialListing,
|
|
55
57
|
} = useListing({
|
|
56
58
|
listingType: "categoryListing",
|
|
57
59
|
categoryId: props.id,
|
|
@@ -85,9 +87,27 @@ const selectedListingFilters = computed<ShortcutFilterParam[]>(() => {
|
|
|
85
87
|
];
|
|
86
88
|
});
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
90
|
+
const nuxtApp = useNuxtApp();
|
|
91
|
+
const { data: listingPayload, pending } = await useAsyncData(
|
|
92
|
+
`listing${categoryId.value}`,
|
|
93
|
+
async () => {
|
|
94
|
+
await search(searchCriteria);
|
|
95
|
+
// Return the result so it gets serialized into the SSR payload.
|
|
96
|
+
// On the client, useAsyncData will restore this without re-running search().
|
|
97
|
+
return getCurrentListing.value;
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Populate useListing state from the SSR payload on the client.
|
|
102
|
+
// useListing uses plain refs (not useState), so its state is not automatically
|
|
103
|
+
// hydrated — we restore it via setInitialListing.
|
|
104
|
+
if (listingPayload.value) {
|
|
105
|
+
await setInitialListing(listingPayload.value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// During SSR hydration, pending may briefly be true before the payload cache is applied.
|
|
109
|
+
// Suppress the skeleton in that window to prevent a hydration mismatch.
|
|
110
|
+
const showSkeleton = computed(() => pending.value && !nuxtApp.isHydrating);
|
|
91
111
|
|
|
92
112
|
watch(selectedListingFilters, (newFilters, oldFilters) => {
|
|
93
113
|
if (newFilters[0]?.value === oldFilters?.[0]?.value) {
|
|
@@ -139,66 +159,77 @@ const moreThanOneFilterAndOption = computed<boolean>(
|
|
|
139
159
|
:items="getSortingOrders"
|
|
140
160
|
placeholder="Sortierung"
|
|
141
161
|
/>
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
icon="i-lucide-sliders-horizontal"
|
|
151
|
-
color="neutral"
|
|
152
|
-
variant="subtle"
|
|
153
|
-
/>
|
|
162
|
+
<ClientOnly v-if="moreThanOneFilterAndOption">
|
|
163
|
+
<UDrawer class="lg:hidden" title="Filter" direction="right">
|
|
164
|
+
<UButton
|
|
165
|
+
label="Filter"
|
|
166
|
+
icon="i-lucide-sliders-horizontal"
|
|
167
|
+
color="neutral"
|
|
168
|
+
variant="subtle"
|
|
169
|
+
/>
|
|
154
170
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
>
|
|
162
|
-
<UCollapsible
|
|
163
|
-
class="flex flex-col gap-2 w-48"
|
|
164
|
-
:default-open="true"
|
|
171
|
+
<template #body>
|
|
172
|
+
<div class="flex flex-col gap-4">
|
|
173
|
+
<div
|
|
174
|
+
v-for="filter in propertyFilters"
|
|
175
|
+
:key="filter.id"
|
|
176
|
+
class="flex flex-col gap-4"
|
|
165
177
|
>
|
|
166
|
-
<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
v-model="selectedPropertyFilters"
|
|
181
|
-
:items="filter.options"
|
|
182
|
-
value-key="id"
|
|
183
|
-
label-key="translated.name"
|
|
178
|
+
<UCollapsible
|
|
179
|
+
class="flex flex-col gap-2 w-48"
|
|
180
|
+
:default-open="true"
|
|
181
|
+
>
|
|
182
|
+
<UButton
|
|
183
|
+
:label="filter.translated.name"
|
|
184
|
+
color="neutral"
|
|
185
|
+
variant="subtle"
|
|
186
|
+
trailing-icon="i-lucide-chevron-down"
|
|
187
|
+
block
|
|
188
|
+
:ui="{
|
|
189
|
+
trailingIcon:
|
|
190
|
+
'group-data-[state=open]:rotate-180 transition-transform duration-200',
|
|
191
|
+
}"
|
|
184
192
|
/>
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
|
|
194
|
+
<template #content>
|
|
195
|
+
<UCheckboxGroup
|
|
196
|
+
v-model="selectedPropertyFilters"
|
|
197
|
+
:items="filter.options"
|
|
198
|
+
value-key="id"
|
|
199
|
+
label-key="translated.name"
|
|
200
|
+
/>
|
|
201
|
+
</template>
|
|
202
|
+
</UCollapsible>
|
|
203
|
+
</div>
|
|
204
|
+
<UButton
|
|
205
|
+
label="Zurücksetzen"
|
|
206
|
+
variant="outline"
|
|
207
|
+
block
|
|
208
|
+
@click="handleFilterRest"
|
|
209
|
+
/>
|
|
187
210
|
</div>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@click="handleFilterRest"
|
|
193
|
-
/>
|
|
194
|
-
</div>
|
|
211
|
+
</template>
|
|
212
|
+
</UDrawer>
|
|
213
|
+
<template #fallback>
|
|
214
|
+
<USkeleton class="h-8 w-20 lg:hidden" />
|
|
195
215
|
</template>
|
|
196
|
-
</
|
|
216
|
+
</ClientOnly>
|
|
197
217
|
</div>
|
|
198
218
|
|
|
199
|
-
<
|
|
219
|
+
<div
|
|
220
|
+
v-if="showSkeleton"
|
|
221
|
+
class="flex flex-col gap-4"
|
|
222
|
+
aria-busy="true"
|
|
223
|
+
aria-label="Produkte werden geladen"
|
|
224
|
+
>
|
|
225
|
+
<LazyProductCardSkeleton v-for="i in 6" :key="i" />
|
|
226
|
+
</div>
|
|
200
227
|
|
|
201
|
-
<div
|
|
228
|
+
<div
|
|
229
|
+
v-else
|
|
230
|
+
class="flex flex-col gap-4 transition-opacity duration-200"
|
|
231
|
+
:class="{ 'opacity-40 pointer-events-none': loading }"
|
|
232
|
+
>
|
|
202
233
|
<ProductCard
|
|
203
234
|
v-for="product in getElements"
|
|
204
235
|
:key="product.id"
|
|
@@ -212,46 +243,64 @@ const moreThanOneFilterAndOption = computed<boolean>(
|
|
|
212
243
|
|
|
213
244
|
<template #right>
|
|
214
245
|
<UPageAside>
|
|
215
|
-
<
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
<UCollapsible
|
|
223
|
-
class="flex flex-col gap-2 w-48"
|
|
224
|
-
:default-open="true"
|
|
246
|
+
<ClientOnly v-if="moreThanOneFilterAndOption">
|
|
247
|
+
<div class="flex flex-col gap-4">
|
|
248
|
+
<h2 class="text-3xl md:text-4xl mb-3 pb-2">Filter</h2>
|
|
249
|
+
<div
|
|
250
|
+
v-for="filter in propertyFilters"
|
|
251
|
+
:key="filter.id"
|
|
252
|
+
class="flex flex-col gap-4"
|
|
225
253
|
>
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
v-model="selectedPropertyFilters"
|
|
241
|
-
:items="filter.options"
|
|
242
|
-
value-key="id"
|
|
243
|
-
label-key="translated.name"
|
|
254
|
+
<UCollapsible
|
|
255
|
+
class="flex flex-col gap-2 w-48"
|
|
256
|
+
:default-open="true"
|
|
257
|
+
>
|
|
258
|
+
<UButton
|
|
259
|
+
:label="filter.translated.name"
|
|
260
|
+
color="neutral"
|
|
261
|
+
variant="subtle"
|
|
262
|
+
trailing-icon="i-lucide-chevron-down"
|
|
263
|
+
block
|
|
264
|
+
:ui="{
|
|
265
|
+
trailingIcon:
|
|
266
|
+
'group-data-[state=open]:rotate-180 transition-transform duration-200',
|
|
267
|
+
}"
|
|
244
268
|
/>
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
|
|
270
|
+
<template #content>
|
|
271
|
+
<UCheckboxGroup
|
|
272
|
+
v-model="selectedPropertyFilters"
|
|
273
|
+
:items="filter.options"
|
|
274
|
+
value-key="id"
|
|
275
|
+
label-key="translated.name"
|
|
276
|
+
/>
|
|
277
|
+
</template>
|
|
278
|
+
</UCollapsible>
|
|
279
|
+
</div>
|
|
280
|
+
<UButton
|
|
281
|
+
label="Zurücksetzen"
|
|
282
|
+
variant="outline"
|
|
283
|
+
block
|
|
284
|
+
@click="handleFilterRest"
|
|
285
|
+
/>
|
|
247
286
|
</div>
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
287
|
+
<template #fallback>
|
|
288
|
+
<div class="flex flex-col gap-4">
|
|
289
|
+
<USkeleton class="h-9 w-20" />
|
|
290
|
+
<div class="flex flex-col gap-2">
|
|
291
|
+
<USkeleton class="h-8 w-48" />
|
|
292
|
+
<USkeleton class="h-4 w-36" />
|
|
293
|
+
<USkeleton class="h-4 w-32" />
|
|
294
|
+
<USkeleton class="h-4 w-40" />
|
|
295
|
+
</div>
|
|
296
|
+
<div class="flex flex-col gap-2">
|
|
297
|
+
<USkeleton class="h-8 w-48" />
|
|
298
|
+
<USkeleton class="h-4 w-36" />
|
|
299
|
+
<USkeleton class="h-4 w-32" />
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</template>
|
|
303
|
+
</ClientOnly>
|
|
255
304
|
</UPageAside>
|
|
256
305
|
</template>
|
|
257
306
|
</UPage>
|
|
@@ -136,24 +136,26 @@ function handleTimeInput(event: Event): void {
|
|
|
136
136
|
<template>
|
|
137
137
|
<div v-if="isClosedHoliday(now) === false" class="flex flex-col gap-2 mt-4">
|
|
138
138
|
<div class="flex flex-row items-center justify-between gap-4">
|
|
139
|
-
<
|
|
139
|
+
<label for="delivery-time" class="flex-1">
|
|
140
|
+
Wunschlieferung- oder Abholzeit ab:
|
|
141
|
+
</label>
|
|
140
142
|
<client-only>
|
|
141
|
-
<
|
|
143
|
+
<UInput
|
|
144
|
+
id="delivery-time"
|
|
142
145
|
type="time"
|
|
143
146
|
:min="minTime ?? undefined"
|
|
144
147
|
:max="maxTime ?? undefined"
|
|
145
148
|
:disabled="isClosedToday || isClosedHoliday(now) === true"
|
|
146
|
-
:value="selected"
|
|
149
|
+
:model-value="selected"
|
|
147
150
|
step="300"
|
|
148
|
-
class="border rounded px-2 py-1"
|
|
149
151
|
@input="handleTimeInput"
|
|
150
152
|
/>
|
|
151
153
|
</client-only>
|
|
152
154
|
</div>
|
|
153
|
-
<p v-if="validationError" class="text-sm text-
|
|
155
|
+
<p v-if="validationError" class="text-sm text-error">
|
|
154
156
|
{{ validationError }}
|
|
155
157
|
</p>
|
|
156
|
-
<p v-else class="text-sm text-
|
|
158
|
+
<p v-else class="text-sm text-muted">{{ helperText }}</p>
|
|
157
159
|
<UBadge
|
|
158
160
|
:label="deliveryTimeInfo"
|
|
159
161
|
icon="i-lucide-clock"
|
|
@@ -82,42 +82,40 @@ watch(
|
|
|
82
82
|
</script>
|
|
83
83
|
|
|
84
84
|
<template>
|
|
85
|
-
<
|
|
86
|
-
<div class="flex flex-col
|
|
87
|
-
<div class="
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
icon="i-lucide-circle-question-mark"
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
<URadioGroup
|
|
99
|
-
v-model="selectedPaymentMethodId"
|
|
100
|
-
:items="selectablePaymentMethods"
|
|
101
|
-
variant="card"
|
|
85
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
86
|
+
<div class="flex flex-col gap-4">
|
|
87
|
+
<div class="flex items-center gap-2">
|
|
88
|
+
<UIcon name="i-lucide-badge-euro" class="size-5 text-muted" />
|
|
89
|
+
<h2 class="text-lg font-semibold">Zahlungsarten</h2>
|
|
90
|
+
<UButton
|
|
91
|
+
to="/zahlung-und-versand"
|
|
92
|
+
size="sm"
|
|
93
|
+
variant="ghost"
|
|
94
|
+
icon="i-lucide-circle-help"
|
|
102
95
|
/>
|
|
103
96
|
</div>
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
97
|
+
<URadioGroup
|
|
98
|
+
v-model="selectedPaymentMethodId"
|
|
99
|
+
:items="selectablePaymentMethods"
|
|
100
|
+
variant="card"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="flex flex-col gap-4">
|
|
104
|
+
<div class="flex items-center gap-2">
|
|
105
|
+
<UIcon name="i-lucide-car" class="size-5 text-muted" />
|
|
106
|
+
<h2 class="text-lg font-semibold">Versandarten</h2>
|
|
107
|
+
<UButton
|
|
108
|
+
to="/zahlung-und-versand"
|
|
109
|
+
size="sm"
|
|
110
|
+
variant="ghost"
|
|
111
|
+
icon="i-lucide-circle-help"
|
|
119
112
|
/>
|
|
120
113
|
</div>
|
|
114
|
+
<URadioGroup
|
|
115
|
+
v-model="selectedShippingMethodId"
|
|
116
|
+
:items="selectableShippingMethods"
|
|
117
|
+
variant="card"
|
|
118
|
+
/>
|
|
121
119
|
</div>
|
|
122
|
-
</
|
|
120
|
+
</div>
|
|
123
121
|
</template>
|
|
@@ -22,7 +22,7 @@ const { paymentMethod } = toRefs(props);
|
|
|
22
22
|
<h3 class="text-base text-pretty font-semibold text-highlighted">
|
|
23
23
|
Zahlart
|
|
24
24
|
</h3>
|
|
25
|
-
<p class="text-[15px] text-pretty text-
|
|
25
|
+
<p class="text-[15px] text-pretty text-muted mt-1">
|
|
26
26
|
{{ paymentMethod.distinguishableName }}
|
|
27
27
|
</p>
|
|
28
28
|
</div>
|
|
@@ -22,7 +22,7 @@ const { shippingMethod } = toRefs(props);
|
|
|
22
22
|
<h3 class="text-base text-pretty font-semibold text-highlighted">
|
|
23
23
|
Versandart
|
|
24
24
|
</h3>
|
|
25
|
-
<p class="text-[15px] text-pretty text-
|
|
25
|
+
<p class="text-[15px] text-pretty text-muted mt-1">
|
|
26
26
|
{{ shippingMethod.name }}
|
|
27
27
|
</p>
|
|
28
28
|
</div>
|
|
@@ -23,8 +23,6 @@ onMounted(() => {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
watch(isCheckoutEnabled, () => console.log(isCheckoutEnabled.value));
|
|
27
|
-
|
|
28
26
|
useIntervalFn(refresh, 10000);
|
|
29
27
|
|
|
30
28
|
async function handleCreateOrder() {
|
|
@@ -63,10 +61,6 @@ const isValidToProceed = computed(
|
|
|
63
61
|
const selectedDeliveryTime = ref("");
|
|
64
62
|
const isValidTime = ref(true);
|
|
65
63
|
|
|
66
|
-
watch(selectedDeliveryTime, (newValue) => {
|
|
67
|
-
console.log(newValue);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
64
|
const checkoutButtonLabel = computed<string>(() => {
|
|
71
65
|
if (!customerDataAvailable.value) {
|
|
72
66
|
return "Bitte einloggen oder Kundendaten erfassen";
|
|
@@ -85,52 +79,47 @@ const checkoutButtonLabel = computed<string>(() => {
|
|
|
85
79
|
</script>
|
|
86
80
|
|
|
87
81
|
<template>
|
|
88
|
-
<
|
|
89
|
-
<div class="
|
|
82
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 py-8">
|
|
83
|
+
<div class="flex flex-col gap-6">
|
|
90
84
|
<div class="flex flex-col gap-4">
|
|
91
|
-
<h3 class="text-
|
|
85
|
+
<h3 class="text-lg font-semibold">Kundendaten</h3>
|
|
92
86
|
<UserDetail v-if="customerDataAvailable" />
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<CheckoutPaymentMethod :payment-method="selectedPaymentMethod" />
|
|
97
|
-
<CheckoutShippingMethod :shipping-method="selectedShippingMethod" />
|
|
98
|
-
<CheckoutDeliveryTimeSelect
|
|
99
|
-
v-model:valid="isValidTime"
|
|
100
|
-
v-model="selectedDeliveryTime"
|
|
101
|
-
/>
|
|
102
|
-
</div>
|
|
87
|
+
<p v-else class="text-muted">
|
|
88
|
+
Bitte vorher einloggen oder Kundendaten erfassen
|
|
89
|
+
</p>
|
|
103
90
|
</div>
|
|
104
91
|
<div class="flex flex-col gap-4">
|
|
105
|
-
<h3 class="text-
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<CheckoutVoucherInput />
|
|
113
|
-
<UButton
|
|
114
|
-
:icon="
|
|
115
|
-
isValidToProceed ? 'i-lucide-shopping-cart' : 'i-lucide-lock'
|
|
116
|
-
"
|
|
117
|
-
:disabled="!isValidToProceed"
|
|
118
|
-
:label="checkoutButtonLabel"
|
|
119
|
-
size="xl"
|
|
120
|
-
block
|
|
121
|
-
@click="handleCreateOrder"
|
|
122
|
-
/>
|
|
123
|
-
<p class="font-light text-muted">
|
|
124
|
-
Mit Klick auf den Button "Jetzt bestellen!" erklärst du dich mit
|
|
125
|
-
unseren
|
|
126
|
-
<ULink to="/agb" class="text-primary font-medium">AGB</ULink> und
|
|
127
|
-
<ULink to="/datenschutz" class="text-primary font-medium"
|
|
128
|
-
>Datenschutzbestimmungen</ULink
|
|
129
|
-
>
|
|
130
|
-
einverstanden.
|
|
131
|
-
</p>
|
|
132
|
-
</div>
|
|
92
|
+
<h3 class="text-lg font-semibold">Versand & Zahlung</h3>
|
|
93
|
+
<CheckoutPaymentMethod :payment-method="selectedPaymentMethod" />
|
|
94
|
+
<CheckoutShippingMethod :shipping-method="selectedShippingMethod" />
|
|
95
|
+
<CheckoutDeliveryTimeSelect
|
|
96
|
+
v-model:valid="isValidTime"
|
|
97
|
+
v-model="selectedDeliveryTime"
|
|
98
|
+
/>
|
|
133
99
|
</div>
|
|
134
100
|
</div>
|
|
135
|
-
|
|
101
|
+
<div class="flex flex-col gap-4">
|
|
102
|
+
<h3 class="text-lg font-semibold">Warenkorb</h3>
|
|
103
|
+
<UCard>
|
|
104
|
+
<QuickView :with-quantity-input="false" :with-delete-button="false" />
|
|
105
|
+
</UCard>
|
|
106
|
+
<CheckoutVoucherInput />
|
|
107
|
+
<UButton
|
|
108
|
+
:icon="isValidToProceed ? 'i-lucide-shopping-cart' : 'i-lucide-lock'"
|
|
109
|
+
:disabled="!isValidToProceed"
|
|
110
|
+
:label="checkoutButtonLabel"
|
|
111
|
+
size="xl"
|
|
112
|
+
block
|
|
113
|
+
@click="handleCreateOrder"
|
|
114
|
+
/>
|
|
115
|
+
<p class="text-sm text-muted">
|
|
116
|
+
Mit Klick auf "Jetzt bestellen!" erklärst du dich mit unseren
|
|
117
|
+
<ULink to="/agb" class="text-primary font-medium">AGB</ULink> und
|
|
118
|
+
<ULink to="/datenschutz" class="text-primary font-medium"
|
|
119
|
+
>Datenschutzbestimmungen</ULink
|
|
120
|
+
>
|
|
121
|
+
einverstanden.
|
|
122
|
+
</p>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
136
125
|
</template>
|
|
@@ -110,7 +110,7 @@ const dropDownMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
110
110
|
/>
|
|
111
111
|
</UChip>
|
|
112
112
|
</div>
|
|
113
|
-
<
|
|
113
|
+
<LazyUDrawer
|
|
114
114
|
v-if="isCheckoutEnabled"
|
|
115
115
|
v-model:open="cartQuickViewOpen"
|
|
116
116
|
title="Warenkorb"
|
|
@@ -134,11 +134,11 @@ const dropDownMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
134
134
|
</div>
|
|
135
135
|
</template>
|
|
136
136
|
<template #body>
|
|
137
|
-
<
|
|
137
|
+
<LazyCartQuickView
|
|
138
138
|
:with-to-cart-button="true"
|
|
139
139
|
class="md:min-w-90"
|
|
140
140
|
@go-to-cart="cartQuickViewOpen = false"
|
|
141
141
|
/>
|
|
142
142
|
</template>
|
|
143
|
-
</
|
|
143
|
+
</LazyUDrawer>
|
|
144
144
|
</template>
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const config = useRuntimeConfig();
|
|
3
|
-
const siteName = computed(() => config.public.site.name);
|
|
4
|
-
</script>
|
|
5
|
-
|
|
1
|
+
<script setup lang="ts"></script>
|
|
6
2
|
<template>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class="h-12 w-auto"
|
|
14
|
-
/>
|
|
15
|
-
</NuxtLink>
|
|
3
|
+
<UColorModeImage
|
|
4
|
+
alt="Logo"
|
|
5
|
+
light="/light/Logo.png"
|
|
6
|
+
dark="/dark/Logo.png"
|
|
7
|
+
width="150"
|
|
8
|
+
/>
|
|
16
9
|
</template>
|
|
@@ -17,7 +17,7 @@ const loginSlide = ref(false);
|
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<template #body>
|
|
20
|
-
<
|
|
20
|
+
<LazyHeaderBody />
|
|
21
21
|
</template>
|
|
22
22
|
</UHeader>
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ const loginSlide = ref(false);
|
|
|
28
28
|
>
|
|
29
29
|
<template #body>
|
|
30
30
|
<div class="h-full m-4">
|
|
31
|
-
<
|
|
31
|
+
<LazyUserLoginForm />
|
|
32
32
|
</div>
|
|
33
33
|
</template>
|
|
34
34
|
</USlideover>
|