@shopbite-de/storefront 1.12.0 → 1.14.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/.github/workflows/build.yaml +1 -1
- package/app/app.vue +10 -11
- package/app/components/Address/Detail.vue +40 -11
- package/app/components/Address/Fields.vue +1 -1
- package/app/components/Address/Form.vue +33 -25
- package/app/components/Category/Breadcrumb.vue +1 -1
- package/app/components/Category/Listing.vue +1 -7
- package/app/components/Checkout/LoginOrRegister.vue +0 -2
- package/app/components/Checkout/PaymentAndDelivery.vue +2 -2
- package/app/components/Checkout/Summary.vue +19 -4
- package/app/components/Footer.vue +32 -11
- package/app/components/Header/Body.vue +8 -20
- package/app/components/Header/Right.vue +77 -59
- package/app/components/Header.vue +3 -22
- package/app/components/Navigation/DesktopLeft2.vue +4 -9
- package/app/components/Navigation/MobileTop2.vue +3 -4
- package/app/components/Order/Detail.vue +44 -26
- package/app/components/SalesChannelSwitch.vue +2 -4
- package/app/components/User/Detail.vue +38 -5
- package/app/components/User/RegistrationForm.vue +9 -11
- package/app/composables/useCategory.ts +37 -0
- package/app/composables/useNavigation.ts +86 -0
- package/app/pages/anmelden.vue +1 -1
- package/app/pages/bestellung.vue +1 -9
- package/app/pages/konto/adressen.vue +10 -3
- package/app/pages/order/[id].vue +1 -1
- package/package.json +7 -6
- package/test/e2e/simple-checkout-as-recurring-customer.test.ts +67 -10
- package/test/nuxt/HeaderRight.test.ts +2 -14
- package/test/nuxt/PaymentAndDelivery.test.ts +1 -3
- package/test/nuxt/RegistrationForm.test.ts +90 -30
- package/app/components/Navigation/DesktopLeft.vue +0 -47
- package/app/components/Navigation/MobileTop.vue +0 -55
- package/app/layouts/listing.vue +0 -32
- package/app/pages/menu/[...all].vue +0 -52
- package/content/navigation.yml +0 -57
- /package/content/{unternehmen/agb.md → agb.md} +0 -0
- /package/content/{unternehmen/datenschutz.md → datenschutz.md} +0 -0
- /package/content/{unternehmen/zahlung-und-versand.md → zahlung-und-versand.md} +0 -0
|
@@ -6,8 +6,8 @@ import { ApiClientError } from "@shopware/api-client";
|
|
|
6
6
|
|
|
7
7
|
vi.mock("@shopware/api-client", () => ({
|
|
8
8
|
ApiClientError: class extends Error {
|
|
9
|
-
details:
|
|
10
|
-
constructor(details:
|
|
9
|
+
details: unknown;
|
|
10
|
+
constructor(details: unknown) {
|
|
11
11
|
super("ApiClientError");
|
|
12
12
|
this.details = details;
|
|
13
13
|
}
|
|
@@ -68,9 +68,10 @@ describe("RegistrationForm", () => {
|
|
|
68
68
|
false,
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
-
// Switch to business
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
// Switch to business (without using ts-ignore)
|
|
72
|
+
(
|
|
73
|
+
wrapper.vm as unknown as { state: { accountType: string } }
|
|
74
|
+
).state.accountType = "business";
|
|
74
75
|
await nextTick();
|
|
75
76
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
76
77
|
|
|
@@ -88,8 +89,11 @@ describe("RegistrationForm", () => {
|
|
|
88
89
|
'input[name="billingAddress.street"]',
|
|
89
90
|
).length;
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
(
|
|
93
|
+
wrapper.vm as unknown as {
|
|
94
|
+
state: { isShippingAddressDifferent: boolean };
|
|
95
|
+
}
|
|
96
|
+
).state.isShippingAddressDifferent = true;
|
|
93
97
|
await nextTick();
|
|
94
98
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
95
99
|
|
|
@@ -102,20 +106,37 @@ describe("RegistrationForm", () => {
|
|
|
102
106
|
it("submits the form with correct data", async () => {
|
|
103
107
|
const wrapper = await mountSuspended(RegistrationForm);
|
|
104
108
|
|
|
109
|
+
// Register as guest to avoid password requirements
|
|
110
|
+
(wrapper.vm as unknown as { state: { guest: boolean } }).state.guest = true;
|
|
111
|
+
await nextTick();
|
|
112
|
+
|
|
105
113
|
// Fill required fields
|
|
106
114
|
await wrapper.find('input[name="firstName"]').setValue("John");
|
|
107
115
|
await wrapper.find('input[name="lastName"]').setValue("Doe");
|
|
108
116
|
await wrapper.find('input[name="email"]').setValue("john@example.com");
|
|
109
117
|
|
|
110
|
-
// Billing address fields (AddressFields component)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
// Billing address fields (AddressFields component): set values directly on state
|
|
119
|
+
(
|
|
120
|
+
wrapper.vm as unknown as {
|
|
121
|
+
state: {
|
|
122
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
).state.billingAddress.street = "Musterstr 1";
|
|
126
|
+
(
|
|
127
|
+
wrapper.vm as unknown as {
|
|
128
|
+
state: {
|
|
129
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
).state.billingAddress.zipcode = "12345";
|
|
133
|
+
(
|
|
134
|
+
wrapper.vm as unknown as {
|
|
135
|
+
state: {
|
|
136
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
).state.billingAddress.city = "Musterstadt";
|
|
119
140
|
|
|
120
141
|
await wrapper
|
|
121
142
|
.find('input[name="billingAddress.phoneNumber"]')
|
|
@@ -152,14 +173,24 @@ describe("RegistrationForm", () => {
|
|
|
152
173
|
mockRegister.mockRejectedValueOnce(apiClientError);
|
|
153
174
|
const wrapper = await mountSuspended(RegistrationForm);
|
|
154
175
|
|
|
176
|
+
// Register as guest to avoid password requirements
|
|
177
|
+
(wrapper.vm as unknown as { state: { guest: boolean } }).state.guest = true;
|
|
178
|
+
await nextTick();
|
|
179
|
+
|
|
155
180
|
// Fill minimum required fields to trigger onSubmit
|
|
156
181
|
await wrapper.find('input[name="firstName"]').setValue("John");
|
|
157
182
|
await wrapper.find('input[name="lastName"]').setValue("Doe");
|
|
158
183
|
await wrapper.find('input[name="email"]').setValue("lirim@veliu.net");
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
184
|
+
(
|
|
185
|
+
wrapper.vm as unknown as {
|
|
186
|
+
state: { billingAddress: { street: string; city: string } };
|
|
187
|
+
}
|
|
188
|
+
).state.billingAddress.street = "Musterstr 1";
|
|
189
|
+
(
|
|
190
|
+
wrapper.vm as unknown as {
|
|
191
|
+
state: { billingAddress: { street: string; city: string } };
|
|
192
|
+
}
|
|
193
|
+
).state.billingAddress.city = "Musterstadt";
|
|
163
194
|
await wrapper
|
|
164
195
|
.find('input[name="billingAddress.phoneNumber"]')
|
|
165
196
|
.setValue("12345678");
|
|
@@ -182,14 +213,24 @@ describe("RegistrationForm", () => {
|
|
|
182
213
|
mockRegister.mockRejectedValueOnce(apiClientError);
|
|
183
214
|
const wrapper = await mountSuspended(RegistrationForm);
|
|
184
215
|
|
|
216
|
+
// Register as guest to avoid password requirements
|
|
217
|
+
(wrapper.vm as unknown as { state: { guest: boolean } }).state.guest = true;
|
|
218
|
+
await nextTick();
|
|
219
|
+
|
|
185
220
|
// Fill minimum required fields to trigger onSubmit
|
|
186
221
|
await wrapper.find('input[name="firstName"]').setValue("John");
|
|
187
222
|
await wrapper.find('input[name="lastName"]').setValue("Doe");
|
|
188
223
|
await wrapper.find('input[name="email"]').setValue("lirim@veliu.net");
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
224
|
+
(
|
|
225
|
+
wrapper.vm as unknown as {
|
|
226
|
+
state: { billingAddress: { street: string; city: string } };
|
|
227
|
+
}
|
|
228
|
+
).state.billingAddress.street = "Musterstr 1";
|
|
229
|
+
(
|
|
230
|
+
wrapper.vm as unknown as {
|
|
231
|
+
state: { billingAddress: { street: string; city: string } };
|
|
232
|
+
}
|
|
233
|
+
).state.billingAddress.city = "Musterstadt";
|
|
193
234
|
await wrapper
|
|
194
235
|
.find('input[name="billingAddress.phoneNumber"]')
|
|
195
236
|
.setValue("12345678");
|
|
@@ -219,16 +260,35 @@ describe("RegistrationForm", () => {
|
|
|
219
260
|
|
|
220
261
|
const wrapper = await mountSuspended(RegistrationForm);
|
|
221
262
|
|
|
263
|
+
// Register as guest to avoid password requirements
|
|
264
|
+
(wrapper.vm as unknown as { state: { guest: boolean } }).state.guest = true;
|
|
265
|
+
await nextTick();
|
|
266
|
+
|
|
222
267
|
// Fill required fields
|
|
223
268
|
await wrapper.find('input[name="firstName"]').setValue("John");
|
|
224
269
|
await wrapper.find('input[name="lastName"]').setValue("Doe");
|
|
225
270
|
await wrapper.find('input[name="email"]').setValue("john@example.com");
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
271
|
+
(
|
|
272
|
+
wrapper.vm as unknown as {
|
|
273
|
+
state: {
|
|
274
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
).state.billingAddress.street = "Musterstr 1";
|
|
278
|
+
(
|
|
279
|
+
wrapper.vm as unknown as {
|
|
280
|
+
state: {
|
|
281
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
).state.billingAddress.zipcode = "12345";
|
|
285
|
+
(
|
|
286
|
+
wrapper.vm as unknown as {
|
|
287
|
+
state: {
|
|
288
|
+
billingAddress: { street: string; zipcode: string; city: string };
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
).state.billingAddress.city = "Musterstadt";
|
|
232
292
|
await wrapper
|
|
233
293
|
.find('input[name="billingAddress.phoneNumber"]')
|
|
234
294
|
.setValue("12345678");
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { NavigationMenuItem } from "@nuxt/ui";
|
|
3
|
-
import type { Schemas } from "#shopware";
|
|
4
|
-
|
|
5
|
-
const { loadNavigationElements, navigationElements } = useNavigation();
|
|
6
|
-
|
|
7
|
-
loadNavigationElements({ depth: 1 });
|
|
8
|
-
|
|
9
|
-
const scrollToElement = (elementId: string, margin = 0) => {
|
|
10
|
-
const element = document.getElementById(elementId);
|
|
11
|
-
if (element) {
|
|
12
|
-
const elementPosition =
|
|
13
|
-
element.getBoundingClientRect().top + window.scrollY;
|
|
14
|
-
const offsetPosition = elementPosition - margin;
|
|
15
|
-
window.scrollTo({
|
|
16
|
-
top: offsetPosition,
|
|
17
|
-
behavior: "smooth",
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const navItems = computed<NavigationMenuItem[][]>(() => {
|
|
23
|
-
return navigationElements.value.map((item: Schemas["Category"]) => {
|
|
24
|
-
return {
|
|
25
|
-
label: item.translated?.name,
|
|
26
|
-
onSelect: () => scrollToElement(item.name ?? "#", 90),
|
|
27
|
-
children: item.children?.map((child: Schemas["Category"]) => {
|
|
28
|
-
return {
|
|
29
|
-
label: child.translated?.name,
|
|
30
|
-
onSelect: () => scrollToElement(child.name ?? "#", 90),
|
|
31
|
-
};
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
</script>
|
|
37
|
-
|
|
38
|
-
<template>
|
|
39
|
-
<div>
|
|
40
|
-
<h2>Navigation</h2>
|
|
41
|
-
<UNavigationMenu
|
|
42
|
-
orientation="vertical"
|
|
43
|
-
:items="navItems"
|
|
44
|
-
class="data-[orientation=vertical]"
|
|
45
|
-
/>
|
|
46
|
-
</div>
|
|
47
|
-
</template>
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { NavigationMenuItem } from "@nuxt/ui";
|
|
3
|
-
import type { Schemas } from "#shopware";
|
|
4
|
-
|
|
5
|
-
const { loadNavigationElements, navigationElements } = useNavigation();
|
|
6
|
-
|
|
7
|
-
loadNavigationElements({ depth: 1 });
|
|
8
|
-
|
|
9
|
-
const scrollToElement = (elementId: string, margin = 0) => {
|
|
10
|
-
const element = document.getElementById(elementId);
|
|
11
|
-
if (element) {
|
|
12
|
-
const elementPosition =
|
|
13
|
-
element.getBoundingClientRect().top + window.scrollY;
|
|
14
|
-
const offsetPosition = elementPosition - margin;
|
|
15
|
-
window.scrollTo({
|
|
16
|
-
top: offsetPosition,
|
|
17
|
-
behavior: "smooth",
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const navItems = computed<NavigationMenuItem[][]>(() => {
|
|
23
|
-
return navigationElements.value.map((item: Schemas["Category"]) => {
|
|
24
|
-
return {
|
|
25
|
-
label: item.translated?.name,
|
|
26
|
-
onSelect: () => scrollToElement(item.name ?? "#", 90),
|
|
27
|
-
children: item.children?.map((child: Schemas["Category"]) => {
|
|
28
|
-
return {
|
|
29
|
-
label: child.translated?.name,
|
|
30
|
-
onSelect: () => scrollToElement(child.name ?? "#", 90),
|
|
31
|
-
};
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
</script>
|
|
37
|
-
|
|
38
|
-
<template>
|
|
39
|
-
<UNavigationMenu
|
|
40
|
-
class="lg:hidden"
|
|
41
|
-
orientation="horizontal"
|
|
42
|
-
:items="navItems"
|
|
43
|
-
:ui="{
|
|
44
|
-
list: 'overflow-x-auto',
|
|
45
|
-
item: 'flex-shrink-0',
|
|
46
|
-
}"
|
|
47
|
-
>
|
|
48
|
-
<template #list-leading>
|
|
49
|
-
<UIcon name="i-lucide-chevron-left" class="size-8" />
|
|
50
|
-
</template>
|
|
51
|
-
<template #list-trailing>
|
|
52
|
-
<UIcon name="i-lucide-chevron-right" class="size-8" />
|
|
53
|
-
</template>
|
|
54
|
-
</UNavigationMenu>
|
|
55
|
-
</template>
|
package/app/layouts/listing.vue
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const { count } = useWishlist();
|
|
3
|
-
const { refresh } = useShopBiteConfig();
|
|
4
|
-
|
|
5
|
-
onMounted(() => {
|
|
6
|
-
refresh();
|
|
7
|
-
});
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<template>
|
|
11
|
-
<div>
|
|
12
|
-
<UContainer>
|
|
13
|
-
<UPage>
|
|
14
|
-
<template #left>
|
|
15
|
-
<UPageAside>
|
|
16
|
-
<NavigationDesktopLeft />
|
|
17
|
-
</UPageAside>
|
|
18
|
-
</template>
|
|
19
|
-
|
|
20
|
-
<UPageBody>
|
|
21
|
-
<slot />
|
|
22
|
-
</UPageBody>
|
|
23
|
-
|
|
24
|
-
<template #right>
|
|
25
|
-
<UPageAside>
|
|
26
|
-
<Wishlist v-if="count > 0" />
|
|
27
|
-
</UPageAside>
|
|
28
|
-
</template>
|
|
29
|
-
</UPage>
|
|
30
|
-
</UContainer>
|
|
31
|
-
</div>
|
|
32
|
-
</template>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { Schemas } from "#shopware";
|
|
3
|
-
|
|
4
|
-
definePageMeta({
|
|
5
|
-
layout: "listing2",
|
|
6
|
-
});
|
|
7
|
-
const { clearBreadcrumbs } = useBreadcrumbs();
|
|
8
|
-
const { resolvePath } = useNavigationSearch();
|
|
9
|
-
const route = useRoute();
|
|
10
|
-
const routePath = route.path;
|
|
11
|
-
|
|
12
|
-
const { data: seoResult, error } = await useAsyncData(
|
|
13
|
-
`cmsResponse${routePath}`,
|
|
14
|
-
async () => {
|
|
15
|
-
// For client links if the history state contains seo url information we can omit the api call
|
|
16
|
-
if (import.meta.client) {
|
|
17
|
-
if (history.state?.routeName) {
|
|
18
|
-
return {
|
|
19
|
-
routeName: history.state?.routeName,
|
|
20
|
-
foreignKey: history.state?.foreignKey,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const seoUrl = await resolvePath(routePath);
|
|
25
|
-
|
|
26
|
-
if (!seoUrl?.foreignKey) {
|
|
27
|
-
throw createError({
|
|
28
|
-
statusCode: 404,
|
|
29
|
-
statusMessage: `No data fetched from API for ${routePath}`,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return seoUrl;
|
|
34
|
-
},
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (error.value) {
|
|
38
|
-
throw error.value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { foreignKey } = useNavigationContext(
|
|
42
|
-
seoResult as Ref<Schemas["SeoUrl"]>,
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
onBeforeRouteLeave(() => {
|
|
46
|
-
clearBreadcrumbs();
|
|
47
|
-
});
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<template>
|
|
51
|
-
<CategoryListing v-if="foreignKey" :id="foreignKey" :key="foreignKey" />
|
|
52
|
-
</template>
|
package/content/navigation.yml
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
main:
|
|
2
|
-
- label: Speisekarte
|
|
3
|
-
icon: i-lucide-utensils
|
|
4
|
-
to: /speisekarte/
|
|
5
|
-
- label: Routenplaner
|
|
6
|
-
icon: i-lucide-map-pinned
|
|
7
|
-
to: https://www.openstreetmap.org/directions?from=&to=50.080610%2C8.863783#map=19/50.080323/8.864079
|
|
8
|
-
target: _blank
|
|
9
|
-
- label: Merkliste
|
|
10
|
-
icon: i-lucide-book-heart
|
|
11
|
-
to: /merkliste
|
|
12
|
-
|
|
13
|
-
account:
|
|
14
|
-
loggedIn:
|
|
15
|
-
- - label: Mein Konto
|
|
16
|
-
type: label
|
|
17
|
-
- - label: Konto
|
|
18
|
-
icon: i-lucide-user
|
|
19
|
-
to: /konto
|
|
20
|
-
- label: Bestellungen
|
|
21
|
-
icon: i-lucide-pizza
|
|
22
|
-
to: /konto/bestellungen
|
|
23
|
-
- label: Adressen
|
|
24
|
-
icon: i-lucide-house
|
|
25
|
-
to: /konto/adressen
|
|
26
|
-
- - label: Abmelden
|
|
27
|
-
icon: i-lucide-log-out
|
|
28
|
-
action: logout
|
|
29
|
-
loggedOut:
|
|
30
|
-
- - label: Jetzt anmelden
|
|
31
|
-
type: label
|
|
32
|
-
- - label: Zur Anmeldung
|
|
33
|
-
icon: i-lucide-user
|
|
34
|
-
to: /anmelden
|
|
35
|
-
|
|
36
|
-
footer:
|
|
37
|
-
withGithubLink: true
|
|
38
|
-
withColorModeSwitch: true
|
|
39
|
-
text: Alle Preise inkl. gesetzlicher Mehrwertsteuer zzgl. Versandkosten, wenn nicht anders beschrieben
|
|
40
|
-
columns:
|
|
41
|
-
- label: Informationen
|
|
42
|
-
children:
|
|
43
|
-
- label: Impressum
|
|
44
|
-
to: /impressum
|
|
45
|
-
- label: Datenschutz
|
|
46
|
-
to: /datenschutz
|
|
47
|
-
- label: AGB
|
|
48
|
-
to: /agb
|
|
49
|
-
- label: Top Kategorien
|
|
50
|
-
children:
|
|
51
|
-
- label: Pizza
|
|
52
|
-
to: /speisekarte/pizza/
|
|
53
|
-
- label: Unternehmen
|
|
54
|
-
children:
|
|
55
|
-
- label: 'Tel: 06104 71427'
|
|
56
|
-
- label: Kantstraße 6
|
|
57
|
-
- label: 631679 Oberthsuasen
|
|
File without changes
|
|
File without changes
|
|
File without changes
|