@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.
Files changed (39) hide show
  1. package/.github/workflows/build.yaml +1 -1
  2. package/app/app.vue +10 -11
  3. package/app/components/Address/Detail.vue +40 -11
  4. package/app/components/Address/Fields.vue +1 -1
  5. package/app/components/Address/Form.vue +33 -25
  6. package/app/components/Category/Breadcrumb.vue +1 -1
  7. package/app/components/Category/Listing.vue +1 -7
  8. package/app/components/Checkout/LoginOrRegister.vue +0 -2
  9. package/app/components/Checkout/PaymentAndDelivery.vue +2 -2
  10. package/app/components/Checkout/Summary.vue +19 -4
  11. package/app/components/Footer.vue +32 -11
  12. package/app/components/Header/Body.vue +8 -20
  13. package/app/components/Header/Right.vue +77 -59
  14. package/app/components/Header.vue +3 -22
  15. package/app/components/Navigation/DesktopLeft2.vue +4 -9
  16. package/app/components/Navigation/MobileTop2.vue +3 -4
  17. package/app/components/Order/Detail.vue +44 -26
  18. package/app/components/SalesChannelSwitch.vue +2 -4
  19. package/app/components/User/Detail.vue +38 -5
  20. package/app/components/User/RegistrationForm.vue +9 -11
  21. package/app/composables/useCategory.ts +37 -0
  22. package/app/composables/useNavigation.ts +86 -0
  23. package/app/pages/anmelden.vue +1 -1
  24. package/app/pages/bestellung.vue +1 -9
  25. package/app/pages/konto/adressen.vue +10 -3
  26. package/app/pages/order/[id].vue +1 -1
  27. package/package.json +7 -6
  28. package/test/e2e/simple-checkout-as-recurring-customer.test.ts +67 -10
  29. package/test/nuxt/HeaderRight.test.ts +2 -14
  30. package/test/nuxt/PaymentAndDelivery.test.ts +1 -3
  31. package/test/nuxt/RegistrationForm.test.ts +90 -30
  32. package/app/components/Navigation/DesktopLeft.vue +0 -47
  33. package/app/components/Navigation/MobileTop.vue +0 -55
  34. package/app/layouts/listing.vue +0 -32
  35. package/app/pages/menu/[...all].vue +0 -52
  36. package/content/navigation.yml +0 -57
  37. /package/content/{unternehmen/agb.md → agb.md} +0 -0
  38. /package/content/{unternehmen/datenschutz.md → datenschutz.md} +0 -0
  39. /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: any;
10
- constructor(details: any) {
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
- // @ts-ignore
73
- wrapper.vm.state.accountType = "business";
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
- // @ts-ignore
92
- wrapper.vm.state.isShippingAddressDifferent = true;
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
- // Manually update state since USelectMenu is hard to interact with in simple tests
112
- // @ts-ignore
113
- wrapper.vm.state.billingAddress.street = "Musterstr 1";
114
- // Set zipcode and city directly since fields are removed
115
- // @ts-ignore
116
- wrapper.vm.state.billingAddress.zipcode = "12345";
117
- // @ts-ignore
118
- wrapper.vm.state.billingAddress.city = "Musterstadt";
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
- // @ts-ignore
160
- wrapper.vm.state.billingAddress.street = "Musterstr 1";
161
- // @ts-ignore
162
- wrapper.vm.state.billingAddress.city = "Musterstadt";
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
- // @ts-ignore
190
- wrapper.vm.state.billingAddress.street = "Musterstr 1";
191
- // @ts-ignore
192
- wrapper.vm.state.billingAddress.city = "Musterstadt";
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
- // @ts-ignore
227
- wrapper.vm.state.billingAddress.street = "Musterstr 1";
228
- // @ts-ignore
229
- wrapper.vm.state.billingAddress.zipcode = "12345";
230
- // @ts-ignore
231
- wrapper.vm.state.billingAddress.city = "Musterstadt";
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>
@@ -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>
@@ -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