@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
|
@@ -26,19 +26,32 @@ defineProps<{
|
|
|
26
26
|
:links="links"
|
|
27
27
|
>
|
|
28
28
|
<template #body>
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
<ClientOnly>
|
|
30
|
+
<UCarousel
|
|
31
|
+
v-slot="{ item, index }"
|
|
32
|
+
arrows
|
|
33
|
+
:items="images"
|
|
34
|
+
class="w-full max-w-2xl mx-auto"
|
|
35
|
+
>
|
|
36
|
+
<img
|
|
37
|
+
:src="item.image"
|
|
38
|
+
:alt="item.alt"
|
|
39
|
+
:fetchpriority="index === 0 ? 'high' : 'auto'"
|
|
40
|
+
class="rounded-lg"
|
|
41
|
+
/>
|
|
42
|
+
</UCarousel>
|
|
43
|
+
<template #fallback>
|
|
44
|
+
<div class="w-full max-w-2xl mx-auto">
|
|
45
|
+
<img
|
|
46
|
+
v-if="images?.[0]"
|
|
47
|
+
:src="images[0].image"
|
|
48
|
+
:alt="images[0].alt"
|
|
49
|
+
fetchpriority="high"
|
|
50
|
+
class="rounded-lg"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
</ClientOnly>
|
|
42
55
|
</template>
|
|
43
56
|
</UPageSection>
|
|
44
57
|
</template>
|
|
@@ -81,7 +81,7 @@ function onVariantSelected(variant: Schemas["Product"]) {
|
|
|
81
81
|
:ui="{ footer: 'w-full', root: 'shadow-lg' }"
|
|
82
82
|
>
|
|
83
83
|
<template #header>
|
|
84
|
-
<
|
|
84
|
+
<LazyUBadge
|
|
85
85
|
v-if="isVegi"
|
|
86
86
|
icon="i-lucide-leaf"
|
|
87
87
|
color="success"
|
|
@@ -92,7 +92,7 @@ function onVariantSelected(variant: Schemas["Product"]) {
|
|
|
92
92
|
</template>
|
|
93
93
|
|
|
94
94
|
<div v-if="product.cover?.media?.url">
|
|
95
|
-
<
|
|
95
|
+
<LazyNuxtImg
|
|
96
96
|
:src="product.cover.media.url"
|
|
97
97
|
class="rounded-md h-auto max-w-full object-contain ransition-opacity duration-700"
|
|
98
98
|
sizes="(min-width: 1024px) 50vw, 100vw"
|
|
@@ -138,15 +138,17 @@ function onVariantSelected(variant: Schemas["Product"]) {
|
|
|
138
138
|
/>
|
|
139
139
|
</div>
|
|
140
140
|
</div>
|
|
141
|
-
<
|
|
142
|
-
<
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
<ClientOnly>
|
|
142
|
+
<UCollapsible v-model:open="openDetails" class="flex flex-col gap-2">
|
|
143
|
+
<template #content>
|
|
144
|
+
<LazyProductDetail
|
|
145
|
+
:product-id="product.id"
|
|
146
|
+
@product-added="toggleDetails"
|
|
147
|
+
@variant-selected="onVariantSelected"
|
|
148
|
+
/>
|
|
149
|
+
</template>
|
|
150
|
+
</UCollapsible>
|
|
151
|
+
</ClientOnly>
|
|
150
152
|
</template>
|
|
151
153
|
</UPageCard>
|
|
152
154
|
</AnimatedSection>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="ring-1 ring-accented rounded-xl shadow-lg p-4 flex flex-row gap-4"
|
|
4
|
+
>
|
|
5
|
+
<div class="flex flex-col gap-3 flex-1 min-w-0">
|
|
6
|
+
<USkeleton class="h-5 w-20 rounded-full" />
|
|
7
|
+
<div class="flex items-baseline gap-2">
|
|
8
|
+
<USkeleton class="h-3.5 w-7" />
|
|
9
|
+
<USkeleton class="h-3.5 w-36" />
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex flex-col gap-2">
|
|
12
|
+
<USkeleton class="h-3 w-full" />
|
|
13
|
+
<USkeleton class="h-3 w-5/6" />
|
|
14
|
+
<USkeleton class="h-3 w-1/2" />
|
|
15
|
+
</div>
|
|
16
|
+
<div class="flex justify-between items-center mt-auto pt-2">
|
|
17
|
+
<USkeleton class="h-4 w-14" />
|
|
18
|
+
<div class="flex gap-2">
|
|
19
|
+
<USkeleton class="h-8 w-8 rounded-md" />
|
|
20
|
+
<USkeleton class="h-8 w-8 rounded-md" />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<USkeleton class="size-28 rounded-md shrink-0" />
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
@@ -142,7 +142,7 @@ watch(productDetails, () => {
|
|
|
142
142
|
<template>
|
|
143
143
|
<div v-if="!pending">
|
|
144
144
|
<div v-if="productDetails?.configurator">
|
|
145
|
-
<
|
|
145
|
+
<ProductConfigurator
|
|
146
146
|
v-if="productDetails?.configurator"
|
|
147
147
|
:p="productDetails.product"
|
|
148
148
|
:c="productDetails.configurator"
|
|
@@ -47,7 +47,7 @@ onMounted(() => {
|
|
|
47
47
|
</script>
|
|
48
48
|
|
|
49
49
|
<template>
|
|
50
|
-
<
|
|
50
|
+
<UCard class="mb-4">
|
|
51
51
|
<div>{{ fullName }}</div>
|
|
52
52
|
<div>{{ user?.email }}</div>
|
|
53
53
|
<USeparator
|
|
@@ -76,5 +76,5 @@ onMounted(() => {
|
|
|
76
76
|
:with-edit-button="withEditButton"
|
|
77
77
|
@update:address="handleAddressUpdate"
|
|
78
78
|
/>
|
|
79
|
-
</
|
|
79
|
+
</UCard>
|
|
80
80
|
</template>
|
|
@@ -1,31 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Schemas } from "#shopware";
|
|
3
|
-
|
|
4
|
-
type useShopBiteConfigReturn = {
|
|
5
|
-
deliveryTime: ComputedRef<number>;
|
|
6
|
-
isCheckoutEnabled: ComputedRef<boolean>;
|
|
7
|
-
refresh(): Promise<Schemas["ShopBiteConfig"]>;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export function useShopBiteConfig(): useShopBiteConfigReturn {
|
|
1
|
+
export function useShopBiteConfig() {
|
|
11
2
|
const { apiClient } = useShopwareContext();
|
|
12
3
|
|
|
13
|
-
const
|
|
14
|
-
const _isCheckoutEnabled = useContext<boolean>("isCheckoutActive");
|
|
15
|
-
|
|
16
|
-
async function refresh(): Promise<Schemas["ShopBiteConfig"]> {
|
|
4
|
+
const { data, refresh } = useAsyncData("shopbite-config", async () => {
|
|
17
5
|
const { data } = await apiClient.invoke(
|
|
18
6
|
"shopbite.config.get get /shopbite/config",
|
|
19
7
|
);
|
|
20
|
-
_deliveryTime.value = data.deliveryTime;
|
|
21
|
-
_isCheckoutEnabled.value = data.isCheckoutEnabled;
|
|
22
|
-
|
|
23
8
|
return data;
|
|
24
|
-
}
|
|
9
|
+
});
|
|
25
10
|
|
|
26
11
|
return {
|
|
27
|
-
deliveryTime: computed(() =>
|
|
28
|
-
isCheckoutEnabled: computed(() =>
|
|
12
|
+
deliveryTime: computed(() => data.value?.deliveryTime ?? 30),
|
|
13
|
+
isCheckoutEnabled: computed(() => data.value?.isCheckoutEnabled ?? false),
|
|
29
14
|
refresh,
|
|
30
15
|
};
|
|
31
16
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { isLoggedIn, isGuestSession } = useUser();
|
|
3
|
+
const { setStep } = useCheckoutStore();
|
|
4
|
+
|
|
5
|
+
if (!isLoggedIn.value && !isGuestSession.value) {
|
|
6
|
+
await navigateTo("/bestellung/warenkorb");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
setStep(2);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<CheckoutSummary />
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import LoginOrRegister from "~/components/Checkout/LoginOrRegister.vue";
|
|
3
|
+
|
|
4
|
+
const { isLoggedIn, isGuestSession } = useUser();
|
|
5
|
+
const { isEmpty } = useCart();
|
|
6
|
+
const { setStep } = useCheckoutStore();
|
|
7
|
+
|
|
8
|
+
setStep(0);
|
|
9
|
+
|
|
10
|
+
const isCustomerAvailable = computed<boolean>(() => {
|
|
11
|
+
return isLoggedIn.value || isGuestSession.value;
|
|
12
|
+
});
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 py-8">
|
|
17
|
+
<div class="flex flex-col gap-4">
|
|
18
|
+
<h2 class="text-lg font-semibold">
|
|
19
|
+
{{ isCustomerAvailable ? "Deine Daten" : "Anmelden oder registrieren" }}
|
|
20
|
+
</h2>
|
|
21
|
+
<LoginOrRegister v-if="!isLoggedIn && !isGuestSession" />
|
|
22
|
+
<UserDetail v-else :with-edit-button="true" />
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="flex flex-col gap-4">
|
|
26
|
+
<h2 class="text-lg font-semibold">Warenkorb</h2>
|
|
27
|
+
<UCard>
|
|
28
|
+
<CartQuickView />
|
|
29
|
+
</UCard>
|
|
30
|
+
<UButton
|
|
31
|
+
v-if="!isEmpty && isCustomerAvailable"
|
|
32
|
+
label="Zahlungs- und Versandart auswählen"
|
|
33
|
+
size="xl"
|
|
34
|
+
block
|
|
35
|
+
trailing-icon="i-lucide-arrow-right"
|
|
36
|
+
to="/bestellung/zahlung-versand"
|
|
37
|
+
/>
|
|
38
|
+
<UButton
|
|
39
|
+
v-if="isEmpty"
|
|
40
|
+
label="Zur Speisekarte"
|
|
41
|
+
size="xl"
|
|
42
|
+
block
|
|
43
|
+
variant="outline"
|
|
44
|
+
icon="i-lucide-arrow-left"
|
|
45
|
+
to="/speisekarte"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { isLoggedIn, isGuestSession } = useUser();
|
|
3
|
+
const { setStep } = useCheckoutStore();
|
|
4
|
+
|
|
5
|
+
if (!isLoggedIn.value && !isGuestSession.value) {
|
|
6
|
+
await navigateTo("/bestellung/warenkorb");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
setStep(1);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div class="flex flex-col gap-8 py-8">
|
|
14
|
+
<CheckoutPaymentAndDelivery />
|
|
15
|
+
<UButton
|
|
16
|
+
label="Weiter zu Prüfen & Bestellen"
|
|
17
|
+
trailing-icon="i-lucide-arrow-right"
|
|
18
|
+
size="xl"
|
|
19
|
+
block
|
|
20
|
+
to="/bestellung/bestaetigen"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
package/app/pages/bestellung.vue
CHANGED
|
@@ -1,95 +1,47 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import LoginOrRegister from "~/components/Checkout/LoginOrRegister.vue";
|
|
3
2
|
import type { StepperItem } from "@nuxt/ui";
|
|
4
3
|
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
4
|
+
const checkoutStore = useCheckoutStore();
|
|
5
|
+
const { step } = storeToRefs(checkoutStore);
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
slot: "shipping" as const,
|
|
16
|
-
title: "Zahlung & Versand",
|
|
17
|
-
icon: "i-lucide-truck",
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
slot: "checkout" as const,
|
|
21
|
-
title: "Prüfen & Bestellen",
|
|
22
|
-
icon: "i-lucide-check",
|
|
23
|
-
},
|
|
24
|
-
] satisfies StepperItem[];
|
|
7
|
+
const stepRoutes = [
|
|
8
|
+
"/bestellung/warenkorb",
|
|
9
|
+
"/bestellung/zahlung-versand",
|
|
10
|
+
"/bestellung/bestaetigen",
|
|
11
|
+
] as const;
|
|
25
12
|
|
|
26
|
-
const
|
|
13
|
+
const items = computed(
|
|
14
|
+
() =>
|
|
15
|
+
[
|
|
16
|
+
{
|
|
17
|
+
title: "Warenkorb",
|
|
18
|
+
icon: "i-lucide-shopping-cart",
|
|
19
|
+
disabled: false,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
title: "Zahlung & Versand",
|
|
23
|
+
icon: "i-lucide-truck",
|
|
24
|
+
disabled: step.value < 1,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: "Prüfen & Bestellen",
|
|
28
|
+
icon: "i-lucide-check",
|
|
29
|
+
disabled: step.value < 2,
|
|
30
|
+
},
|
|
31
|
+
] satisfies StepperItem[],
|
|
32
|
+
);
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
watch(step, (newStep) => {
|
|
35
|
+
navigateTo(stepRoutes[newStep]);
|
|
30
36
|
});
|
|
31
37
|
</script>
|
|
32
38
|
|
|
33
39
|
<template>
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
v-if="!isLoggedIn && !isGuestSession"
|
|
42
|
-
class="basis-1/2"
|
|
43
|
-
/>
|
|
44
|
-
<div v-else class="basis-1/2">
|
|
45
|
-
<UserDetail :with-edit-button="true" />
|
|
46
|
-
</div>
|
|
47
|
-
<div class="basis-1/2">
|
|
48
|
-
<h3 class="text-2xl mb-6 font-semibold">Warenkorb</h3>
|
|
49
|
-
<CartQuickView />
|
|
50
|
-
<UButton
|
|
51
|
-
v-if="!isEmpty && isCustomerAvailable"
|
|
52
|
-
:disabled="!isLoggedIn && !isGuestSession"
|
|
53
|
-
label="Zahlungs- und Versandart auswählen"
|
|
54
|
-
size="xl"
|
|
55
|
-
block
|
|
56
|
-
trailing-icon="i-lucide-arrow-right"
|
|
57
|
-
class="my-4"
|
|
58
|
-
@click="activeStep = 1"
|
|
59
|
-
/>
|
|
60
|
-
<UButton
|
|
61
|
-
v-if="isEmpty"
|
|
62
|
-
label="Zur Speisekarte"
|
|
63
|
-
size="xl"
|
|
64
|
-
block
|
|
65
|
-
icon="i-lucide-arrow-left"
|
|
66
|
-
class="my-4"
|
|
67
|
-
to="/speisekarte"
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
</template>
|
|
72
|
-
|
|
73
|
-
<template #shipping>
|
|
74
|
-
<div class="my-14">
|
|
75
|
-
<CheckoutPaymentAndDelivery />
|
|
76
|
-
<div class="w-full flex justify-end">
|
|
77
|
-
<UButton
|
|
78
|
-
label="Weiter zu Prüfen & Bestellen"
|
|
79
|
-
trailing-icon="i-lucide-arrow-right"
|
|
80
|
-
class="m-8 md:max-w-96"
|
|
81
|
-
size="xl"
|
|
82
|
-
block
|
|
83
|
-
@click="activeStep = 2"
|
|
84
|
-
/>
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
</template>
|
|
88
|
-
|
|
89
|
-
<template #checkout>
|
|
90
|
-
<CheckoutSummary />
|
|
91
|
-
</template>
|
|
92
|
-
</UStepper>
|
|
93
|
-
</UPageBody>
|
|
94
|
-
</UContainer>
|
|
40
|
+
<UPageSection>
|
|
41
|
+
<UStepper ref="stepper" v-model="step" :items="items" size="lg">
|
|
42
|
+
<template #content>
|
|
43
|
+
<NuxtPage />
|
|
44
|
+
</template>
|
|
45
|
+
</UStepper>
|
|
46
|
+
</UPageSection>
|
|
95
47
|
</template>
|
package/app/pages/index.vue
CHANGED
package/app/pages/kontakt.vue
CHANGED
|
@@ -13,7 +13,7 @@ if (config.public.shopBite.feature.contactForm !== true) {
|
|
|
13
13
|
|
|
14
14
|
<template>
|
|
15
15
|
<UPageSection
|
|
16
|
-
title="Sie haben eine Frage oder Anregung?Wir freuen uns auf Ihre Nachricht."
|
|
16
|
+
title="Sie haben eine Frage oder Anregung? Wir freuen uns auf Ihre Nachricht."
|
|
17
17
|
description="Bitte beachten Sie, dass wir Tischreservierung oder Tischstornierung nur telefonisch entgegennehmen können."
|
|
18
18
|
icon="i-lucide-mail"
|
|
19
19
|
orientation="horizontal"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const stepRoutes = [
|
|
2
|
+
"/bestellung/warenkorb",
|
|
3
|
+
"/bestellung/zahlung-versand",
|
|
4
|
+
"/bestellung/bestaetigen",
|
|
5
|
+
] as const;
|
|
6
|
+
|
|
7
|
+
export const useCheckoutStore = defineStore("checkout", () => {
|
|
8
|
+
const step = ref(0);
|
|
9
|
+
|
|
10
|
+
function setStep(index: number) {
|
|
11
|
+
step.value = index;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function navigateToStep(index: number) {
|
|
15
|
+
if (index < 0 || index >= stepRoutes.length) return;
|
|
16
|
+
if (index <= step.value) {
|
|
17
|
+
await navigateTo(stepRoutes[index]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { step, setStep, navigateToStep };
|
|
22
|
+
});
|
package/nuxt.config.ts
CHANGED
|
@@ -78,6 +78,9 @@ export default defineNuxtConfig({
|
|
|
78
78
|
"/registrierung/bestaetigen": {
|
|
79
79
|
ssr: false,
|
|
80
80
|
},
|
|
81
|
+
"/bestellung": {
|
|
82
|
+
redirect: "/bestellung/warenkorb",
|
|
83
|
+
},
|
|
81
84
|
},
|
|
82
85
|
|
|
83
86
|
css: ["~/assets/css/main.css"],
|
|
@@ -100,6 +103,7 @@ export default defineNuxtConfig({
|
|
|
100
103
|
"@nuxt/scripts",
|
|
101
104
|
"nuxt-vitalizer",
|
|
102
105
|
"@nuxt/eslint",
|
|
106
|
+
"@pinia/nuxt",
|
|
103
107
|
],
|
|
104
108
|
|
|
105
109
|
content: {
|
|
@@ -197,6 +201,7 @@ export default defineNuxtConfig({
|
|
|
197
201
|
"@nuxt/scripts",
|
|
198
202
|
"@nuxt/test-utils/module",
|
|
199
203
|
"@nuxt/eslint",
|
|
204
|
+
"@nuxt/hints",
|
|
200
205
|
],
|
|
201
206
|
},
|
|
202
207
|
$production: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopbite-de/storefront",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.3",
|
|
4
4
|
"main": "nuxt.config.ts",
|
|
5
5
|
"description": "Shopware storefront for food delivery shops",
|
|
6
6
|
"keywords": [
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@nuxt/scripts": "0.13.2",
|
|
28
28
|
"@nuxt/ui": "^4.5.1",
|
|
29
29
|
"@nuxtjs/robots": "^6.0.0",
|
|
30
|
+
"@pinia/nuxt": "0.11.3",
|
|
30
31
|
"@sentry/nuxt": "^10.43.0",
|
|
31
32
|
"@shopware/api-client": "^1.4.0",
|
|
32
33
|
"@shopware/api-gen": "^1.4.0",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"uuid": "^13.0.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
47
|
+
"@nuxt/hints": "1.0.2",
|
|
46
48
|
"@iconify-json/heroicons": "^1.2.3",
|
|
47
49
|
"@iconify-json/hugeicons": "^1.2.23",
|
|
48
50
|
"@nuxt/devtools-kit": "^3.2.3",
|
|
@@ -39,7 +39,7 @@ async function clearCart(page: Page) {
|
|
|
39
39
|
await expect(page.getByRole("heading", { name: /warenkorb/i })).toBeVisible();
|
|
40
40
|
|
|
41
41
|
const deleteButtons = page.getByRole("button", {
|
|
42
|
-
name: "
|
|
42
|
+
name: "Artikel entfernen",
|
|
43
43
|
});
|
|
44
44
|
const itemCount = await deleteButtons.count();
|
|
45
45
|
|
|
@@ -92,7 +92,7 @@ async function selectProductAndAddToCart(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
async function proceedToCheckoutAndLogin(page: Page) {
|
|
95
|
-
await page.goto("/bestellung", { waitUntil: "load" });
|
|
95
|
+
await page.goto("/bestellung/warenkorb", { waitUntil: "load" });
|
|
96
96
|
|
|
97
97
|
const loginTab = page.getByRole("tab", { name: "Einloggen" });
|
|
98
98
|
await expect(loginTab).toBeVisible();
|
|
@@ -129,7 +129,7 @@ async function proceedToCheckoutAndLogin(page: Page) {
|
|
|
129
129
|
|
|
130
130
|
// Wait for login to complete and navigate back to checkout
|
|
131
131
|
await page.waitForTimeout(2000);
|
|
132
|
-
await page.goto("/bestellung", { waitUntil: "load" });
|
|
132
|
+
await page.goto("/bestellung/zahlung-versand", { waitUntil: "load" });
|
|
133
133
|
await page.waitForTimeout(1000);
|
|
134
134
|
|
|
135
135
|
// Skip the rest of this function since we already logged in
|
|
@@ -159,15 +159,15 @@ async function proceedToCheckoutAndLogin(page: Page) {
|
|
|
159
159
|
|
|
160
160
|
async function verifyCheckoutQuantity(page: Page) {
|
|
161
161
|
// Ensure we're on the checkout page
|
|
162
|
-
if (!page.url().includes("/bestellung")) {
|
|
163
|
-
await page.goto("/bestellung", { waitUntil: "load" });
|
|
162
|
+
if (!page.url().includes("/bestellung/warenkorb")) {
|
|
163
|
+
await page.goto("/bestellung/warenkorb", { waitUntil: "load" });
|
|
164
164
|
await page.waitForTimeout(1000);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// Try to find the quantity input with multiple strategies
|
|
168
168
|
const checkoutQuantityInput = page
|
|
169
169
|
.getByRole("spinbutton", {
|
|
170
|
-
name: /
|
|
170
|
+
name: /menge/i,
|
|
171
171
|
})
|
|
172
172
|
.or(page.getByRole("spinbutton"))
|
|
173
173
|
.or(page.locator("input[type='number']"));
|
|
@@ -178,7 +178,7 @@ async function verifyCheckoutQuantity(page: Page) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
async function selectPaymentAndShipping(page: Page) {
|
|
181
|
-
const nextStepButton = page.getByRole("
|
|
181
|
+
const nextStepButton = page.getByRole("link", {
|
|
182
182
|
name: "Zahlungs- und Versandart auswählen",
|
|
183
183
|
});
|
|
184
184
|
await expect(nextStepButton).toBeVisible({ timeout: 10000 });
|
|
@@ -197,7 +197,7 @@ async function selectPaymentAndShipping(page: Page) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
async function proceedToOrderReview(page: Page) {
|
|
200
|
-
const lastStepButton = page.getByRole("
|
|
200
|
+
const lastStepButton = page.getByRole("link", {
|
|
201
201
|
name: "Weiter zu Prüfen & Bestellen",
|
|
202
202
|
});
|
|
203
203
|
await expect(lastStepButton).toBeVisible({ timeout: 10000 });
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
3
|
+
import { ref } from "vue";
|
|
3
4
|
import { useShopBiteConfig } from "~/composables/useShopBiteConfig";
|
|
4
5
|
|
|
5
|
-
const { mockInvoke
|
|
6
|
-
()
|
|
7
|
-
|
|
8
|
-
mockDeliveryTime: { value: 0 },
|
|
9
|
-
mockIsCheckoutEnabled: { value: false },
|
|
10
|
-
}),
|
|
11
|
-
);
|
|
6
|
+
const { mockInvoke } = vi.hoisted(() => ({
|
|
7
|
+
mockInvoke: vi.fn(),
|
|
8
|
+
}));
|
|
12
9
|
|
|
13
10
|
mockNuxtImport("useShopwareContext", () => () => ({
|
|
14
11
|
apiClient: {
|
|
@@ -16,25 +13,26 @@ mockNuxtImport("useShopwareContext", () => () => ({
|
|
|
16
13
|
},
|
|
17
14
|
}));
|
|
18
15
|
|
|
19
|
-
mockNuxtImport(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
mockNuxtImport(
|
|
17
|
+
"useAsyncData",
|
|
18
|
+
() => (_key: string, fetcher: () => Promise<unknown>) => {
|
|
19
|
+
const data = ref<unknown>(null);
|
|
20
|
+
const refresh = async () => {
|
|
21
|
+
data.value = await fetcher();
|
|
22
|
+
};
|
|
23
|
+
return { data, refresh };
|
|
24
|
+
},
|
|
25
|
+
);
|
|
24
26
|
|
|
25
27
|
describe("useShopBiteConfig", () => {
|
|
26
28
|
beforeEach(() => {
|
|
27
29
|
vi.clearAllMocks();
|
|
28
|
-
mockDeliveryTime.value = 0;
|
|
29
|
-
mockIsCheckoutEnabled.value = false;
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
it("should
|
|
33
|
-
mockDeliveryTime.value = 30;
|
|
34
|
-
mockIsCheckoutEnabled.value = true;
|
|
32
|
+
it("should return default values when no data is loaded", () => {
|
|
35
33
|
const { deliveryTime, isCheckoutEnabled } = useShopBiteConfig();
|
|
36
34
|
expect(deliveryTime.value).toBe(30);
|
|
37
|
-
expect(isCheckoutEnabled.value).toBe(
|
|
35
|
+
expect(isCheckoutEnabled.value).toBe(false);
|
|
38
36
|
});
|
|
39
37
|
|
|
40
38
|
it("should refresh values from API", async () => {
|
|
@@ -53,7 +51,5 @@ describe("useShopBiteConfig", () => {
|
|
|
53
51
|
);
|
|
54
52
|
expect(deliveryTime.value).toBe(45);
|
|
55
53
|
expect(isCheckoutEnabled.value).toBe(true);
|
|
56
|
-
expect(mockDeliveryTime.value).toBe(45);
|
|
57
|
-
expect(mockIsCheckoutEnabled.value).toBe(true);
|
|
58
54
|
});
|
|
59
55
|
});
|