@shopbite-de/storefront 1.16.0 → 1.17.0
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/.claude/settings.local.json +21 -0
- package/.env.example +0 -4
- package/.github/workflows/build.yaml +14 -8
- package/.github/workflows/ci.yaml +125 -42
- package/.nuxtrc +1 -1
- package/CLAUDE.md +106 -0
- package/app/app.vue +53 -34
- package/app/components/AddToWishlist.vue +0 -3
- package/app/components/Address/Form.vue +5 -2
- package/app/components/Category/Listing.vue +5 -4
- package/app/components/Checkout/DeliveryTimeSelect.vue +4 -2
- package/app/components/Contact/Form.vue +3 -2
- package/app/components/Hero.vue +1 -1
- package/app/components/ImageGallery.vue +1 -1
- package/app/components/Navigation/DesktopLeft.vue +2 -1
- package/app/components/Order/Detail.vue +2 -2
- package/app/components/Product/Card.vue +14 -8
- package/app/components/SalesChannelSwitch.vue +5 -3
- package/app/components/User/LoginForm.vue +1 -4
- package/app/components/User/RegistrationForm.vue +5 -2
- package/app/composables/useBusinessHours.ts +11 -4
- package/app/composables/useCategory.ts +1 -0
- package/app/composables/useTopSellers.ts +2 -2
- package/app/layouts/account.vue +0 -1
- package/app/pages/anmelden.vue +8 -4
- package/app/pages/c/[...all].vue +2 -1
- package/app/pages/konto/adressen.vue +4 -1
- package/app/pages/konto/bestellung/[id].vue +14 -2
- package/app/pages/konto/profil.vue +5 -1
- package/app/utils/formatDate.ts +2 -1
- package/content.config.ts +1 -2
- package/eslint.config.mjs +10 -6
- package/node.dockerfile +7 -6
- package/nuxt.config.ts +17 -3
- package/package.json +38 -32
- package/renovate.json +9 -1
- package/server/api/address/autocomplete.get.ts +3 -2
- package/test/e2e/simple-checkout-as-recurring-customer.test.ts +1 -1
- package/test/nuxt/AddressFields.test.ts +12 -3
- package/test/nuxt/ContactForm.test.ts +31 -11
- package/test/nuxt/HeaderRight.test.ts +15 -6
- package/test/nuxt/LoginForm.test.ts +41 -23
- package/test/nuxt/PaymentAndDelivery.test.ts +2 -2
- package/test/nuxt/RegistrationForm.test.ts +6 -3
- package/test/nuxt/registrationSchema.test.ts +8 -8
- package/test/nuxt/useAddToCart.test.ts +5 -4
- package/test/nuxt/useAddressAutocomplete.test.ts +2 -0
- package/test/nuxt/useBusinessHours.test.ts +5 -5
- package/test/nuxt/useDeliveryTime.test.ts +17 -9
- package/test/nuxt/useProductConfigurator.test.ts +6 -4
- package/test/nuxt/useProductVariants.test.ts +16 -10
- package/test/nuxt/useProductVariantsZwei.test.ts +6 -2
- package/test/nuxt/useScrollAnimation.test.ts +16 -13
- package/test/nuxt/useTopSellers.test.ts +2 -2
- package/test/nuxt/useWishlistActions.test.ts +4 -3
- package/test/unit/useCategorySeo.spec.ts +26 -12
- package/tsconfig.json +21 -1
- package/app/middleware/trailing-slash.global.ts +0 -19
- package/server/utils/shopware/adminApiClient.ts +0 -24
- package/test/unit/sales-channels.test.ts +0 -66
|
@@ -26,13 +26,17 @@ const isVegi = computed<boolean>(() => {
|
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
return
|
|
30
|
-
(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
return (
|
|
30
|
+
(
|
|
31
|
+
product.value?.sortedProperties as Schemas["PropertyGroup"][] | undefined
|
|
32
|
+
)?.some(
|
|
33
|
+
(propertyGroup: Schemas["PropertyGroup"]) =>
|
|
34
|
+
propertyGroup.translated.name === "Vegetarisch" &&
|
|
35
|
+
propertyGroup.options?.some(
|
|
36
|
+
(option: Schemas["PropertyGroupOption"]) =>
|
|
37
|
+
option.translated.name === "Ja",
|
|
38
|
+
),
|
|
39
|
+
) ?? false
|
|
36
40
|
);
|
|
37
41
|
});
|
|
38
42
|
const openDetails = ref(false);
|
|
@@ -45,7 +49,9 @@ const MAIN_INGREDIENTS_PROPERTY_LABEL = "Hauptzutaten";
|
|
|
45
49
|
|
|
46
50
|
const mainIngredients = computed<Schemas["PropertyGroupOption"][]>(() => {
|
|
47
51
|
const sortedProps =
|
|
48
|
-
product.value?.sortedProperties
|
|
52
|
+
(product.value?.sortedProperties as
|
|
53
|
+
| Schemas["PropertyGroup"][]
|
|
54
|
+
| undefined) ?? ([] as Schemas["PropertyGroup"][]);
|
|
49
55
|
const mainIngredientsProperty = sortedProps.find(
|
|
50
56
|
(propertyGroup: Schemas["PropertyGroup"]) =>
|
|
51
57
|
propertyGroup.translated.name === MAIN_INGREDIENTS_PROPERTY_LABEL,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import type { SelectMenuItem } from "@nuxt/ui";
|
|
3
3
|
import type { Schemas } from "#shopware";
|
|
4
4
|
|
|
5
|
+
type StoreSelectItem = SelectMenuItem & { value: string };
|
|
6
|
+
|
|
5
7
|
const { apiClient } = useShopwareContext();
|
|
6
8
|
|
|
7
9
|
const config = useRuntimeConfig();
|
|
@@ -29,7 +31,7 @@ const { data: salesChannels, pending: status } = useAsyncData(
|
|
|
29
31
|
|
|
30
32
|
function transform(
|
|
31
33
|
multiChannelGroups: Schemas["MultiChannelGroupStruct"],
|
|
32
|
-
):
|
|
34
|
+
): StoreSelectItem[] {
|
|
33
35
|
const group = multiChannelGroups.multiChannelGroup?.[0];
|
|
34
36
|
if (!group) return [];
|
|
35
37
|
|
|
@@ -55,10 +57,10 @@ function transform(
|
|
|
55
57
|
}));
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
const selectedStore = ref<
|
|
60
|
+
const selectedStore = ref<StoreSelectItem>();
|
|
59
61
|
|
|
60
62
|
watchEffect(() => {
|
|
61
|
-
const scValue = salesChannels?.value as
|
|
63
|
+
const scValue = salesChannels?.value as StoreSelectItem[] | undefined;
|
|
62
64
|
if (Array.isArray(scValue) && storeUrl.value && !selectedStore.value) {
|
|
63
65
|
const matchingChannel = scValue.find(
|
|
64
66
|
(channel) => channel?.value === storeUrl.value,
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import * as z from "zod";
|
|
3
|
-
import { useWishlist, useUser } from "@shopware/composables";
|
|
4
3
|
import { ApiClientError } from "@shopware/api-client";
|
|
5
4
|
import type { FormSubmitEvent } from "@nuxt/ui";
|
|
6
5
|
|
|
7
6
|
const { isLoggedIn, login, user } = useUser();
|
|
8
|
-
const { mergeWishlistProducts } = useWishlist();
|
|
9
7
|
const toast = useToast();
|
|
10
8
|
|
|
11
9
|
const props = withDefaults(
|
|
@@ -64,10 +62,9 @@ async function onSubmit(payload: FormSubmitEvent<Schema>) {
|
|
|
64
62
|
toast.add({
|
|
65
63
|
title:
|
|
66
64
|
"Hallo " + user.value?.firstName + " " + user.value?.lastName + "!",
|
|
67
|
-
description: "
|
|
65
|
+
description: "Erfolgreich angemeldet.",
|
|
68
66
|
color: "success",
|
|
69
67
|
});
|
|
70
|
-
mergeWishlistProducts();
|
|
71
68
|
emit("login-success", payload.data.email);
|
|
72
69
|
} catch (error) {
|
|
73
70
|
console.error("Login failed:", error);
|
|
@@ -18,7 +18,7 @@ watch(isLoggedIn, (isLoggedIn) => {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
const state = reactive({
|
|
21
|
-
accountType: "private",
|
|
21
|
+
accountType: "private" as "private" | "business",
|
|
22
22
|
salutationId: "",
|
|
23
23
|
firstName: "",
|
|
24
24
|
lastName: "",
|
|
@@ -118,7 +118,10 @@ async function onSubmit(event: FormSubmitEvent<RegistrationSchema>) {
|
|
|
118
118
|
title: "Erfolgreich Kundendaten erfasst",
|
|
119
119
|
color: "success",
|
|
120
120
|
});
|
|
121
|
-
emit(
|
|
121
|
+
emit(
|
|
122
|
+
"registration-success",
|
|
123
|
+
registrationData as unknown as RegistrationSchema,
|
|
124
|
+
);
|
|
122
125
|
} catch (error) {
|
|
123
126
|
console.error("Registration failed:", error);
|
|
124
127
|
let description = "Bitte versuchen Sie es erneut.";
|
|
@@ -30,15 +30,22 @@ export function useBusinessHours() {
|
|
|
30
30
|
return dayBusinessHours
|
|
31
31
|
.map((bh) => {
|
|
32
32
|
if (!bh.openingTime || !bh.closingTime) return null;
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const startParts = bh.openingTime.split(":").map(Number);
|
|
34
|
+
const endParts = bh.closingTime.split(":").map(Number);
|
|
35
|
+
const startH = startParts[0] ?? 0;
|
|
36
|
+
const startM = startParts[1] ?? 0;
|
|
37
|
+
const endH = endParts[0] ?? 0;
|
|
38
|
+
const endM = endParts[1] ?? 0;
|
|
35
39
|
|
|
36
40
|
return {
|
|
37
41
|
start: setTime(date, startH, startM),
|
|
38
42
|
end: setTime(date, endH, endM),
|
|
39
43
|
};
|
|
40
44
|
})
|
|
41
|
-
.sort((a, b) =>
|
|
45
|
+
.sort((a, b) => {
|
|
46
|
+
if (!a || !b) return 0;
|
|
47
|
+
return a.start.getTime() - b.start.getTime();
|
|
48
|
+
})
|
|
42
49
|
.filter((interval): interval is ServiceInterval => interval !== null);
|
|
43
50
|
};
|
|
44
51
|
|
|
@@ -109,7 +116,7 @@ export function useBusinessHours() {
|
|
|
109
116
|
continue; // Today's openings have passed, check next days
|
|
110
117
|
}
|
|
111
118
|
|
|
112
|
-
const nextOpen = intervals[0]
|
|
119
|
+
const nextOpen = intervals[0]!.start;
|
|
113
120
|
const day = nextOpen.getDate().toString().padStart(2, "0");
|
|
114
121
|
const month = (nextOpen.getMonth() + 1).toString().padStart(2, "0");
|
|
115
122
|
const dayName = [
|
|
@@ -21,6 +21,7 @@ export async function useCategory(categoryId: Ref<string>) {
|
|
|
21
21
|
const response = await apiClient.invoke(
|
|
22
22
|
"readCategoryGet get /category/{navigationId}",
|
|
23
23
|
{
|
|
24
|
+
// @ts-expect-error: _criteria is not in the type definition
|
|
24
25
|
query: { _criteria: criteria },
|
|
25
26
|
pathParams: {
|
|
26
27
|
navigationId: categoryId.value,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { Schemas } from "#shopware";
|
|
2
2
|
|
|
3
3
|
export type useTopSellersReturn = {
|
|
4
|
-
loadTopSellers(): Promise<Schemas["
|
|
4
|
+
loadTopSellers(): Promise<Schemas["Product"][]>;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
export function useTopSellers(): useTopSellersReturn {
|
|
8
8
|
const { apiClient } = useShopwareContext();
|
|
9
9
|
async function loadTopSellers() {
|
|
10
10
|
try {
|
|
11
|
-
const result = await apiClient.invoke("
|
|
11
|
+
const result = await apiClient.invoke("readProduct post /product", {
|
|
12
12
|
body: {
|
|
13
13
|
filter: [{ type: "equals", field: "markAsTopseller", value: true }],
|
|
14
14
|
includes: {
|
package/app/layouts/account.vue
CHANGED
package/app/pages/anmelden.vue
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useWishlist } from "@shopware/composables";
|
|
3
|
+
|
|
2
4
|
const { isLoggedIn } = useUser();
|
|
5
|
+
const { mergeWishlistProducts } = useWishlist();
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
if (isLoggedIn.value) {
|
|
6
|
-
navigateTo("/konto");
|
|
7
|
+
onBeforeMount(async () => {
|
|
8
|
+
if (import.meta.client && isLoggedIn.value) {
|
|
9
|
+
navigateTo({ path: "/konto" });
|
|
7
10
|
}
|
|
8
11
|
});
|
|
9
12
|
|
|
10
13
|
watch(isLoggedIn, (newValue) => {
|
|
11
14
|
if (newValue) {
|
|
12
|
-
navigateTo("/konto");
|
|
15
|
+
navigateTo({ path: "/konto" });
|
|
13
16
|
}
|
|
14
17
|
});
|
|
15
18
|
|
|
16
19
|
function handleLoginSuccess() {
|
|
20
|
+
mergeWishlistProducts();
|
|
17
21
|
navigateTo("/");
|
|
18
22
|
}
|
|
19
23
|
</script>
|
package/app/pages/c/[...all].vue
CHANGED
|
@@ -133,7 +133,10 @@ const openNewModal = ref(false);
|
|
|
133
133
|
|
|
134
134
|
<template #body>
|
|
135
135
|
<div class="p-8">
|
|
136
|
-
<AddressForm
|
|
136
|
+
<AddressForm
|
|
137
|
+
:address="undefined"
|
|
138
|
+
@submit-success="reloadCustomerData"
|
|
139
|
+
/>
|
|
137
140
|
</div>
|
|
138
141
|
</template>
|
|
139
142
|
</UModal>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useOrderDetails } from "@shopware/composables";
|
|
3
3
|
import { formatDate } from "~/utils/formatDate";
|
|
4
|
+
import type { Schemas } from "#shopware";
|
|
4
5
|
|
|
5
6
|
import type { RouteParams } from "vue-router";
|
|
6
7
|
|
|
@@ -14,7 +15,14 @@ interface OrderRouteParams extends RouteParams {
|
|
|
14
15
|
|
|
15
16
|
const route = useRoute();
|
|
16
17
|
const { id } = route.params as OrderRouteParams;
|
|
17
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
order: orderRaw,
|
|
20
|
+
loadOrderDetails,
|
|
21
|
+
status,
|
|
22
|
+
} = useOrderDetails(id as string);
|
|
23
|
+
const order = computed(
|
|
24
|
+
() => orderRaw.value as Schemas["Order"] | undefined | null,
|
|
25
|
+
);
|
|
18
26
|
|
|
19
27
|
const isLoadingData = ref(true);
|
|
20
28
|
|
|
@@ -35,7 +43,11 @@ onMounted(async () => {
|
|
|
35
43
|
:description="formatDate(order?.createdAt)"
|
|
36
44
|
/>
|
|
37
45
|
<UPageBody>
|
|
38
|
-
<OrderDetail
|
|
46
|
+
<OrderDetail
|
|
47
|
+
v-if="order"
|
|
48
|
+
:order="order as Schemas['Order']"
|
|
49
|
+
:status="status ?? 'laden...'"
|
|
50
|
+
/>
|
|
39
51
|
</UPageBody>
|
|
40
52
|
</UContainer>
|
|
41
53
|
</template>
|
|
@@ -33,7 +33,11 @@ const state = initializeFormState();
|
|
|
33
33
|
|
|
34
34
|
async function handleEmailUpdate(newEmail: string) {
|
|
35
35
|
if (newEmail !== user.value?.email) {
|
|
36
|
-
await updateEmail(
|
|
36
|
+
await updateEmail({
|
|
37
|
+
email: newEmail,
|
|
38
|
+
emailConfirmation: newEmail,
|
|
39
|
+
password: "",
|
|
40
|
+
});
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
|
package/app/utils/formatDate.ts
CHANGED
package/content.config.ts
CHANGED
package/eslint.config.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import withNuxt from './.nuxt/eslint.config.mjs'
|
|
3
|
+
import eslintConfigPrettier from 'eslint-config-prettier'
|
|
3
4
|
|
|
4
|
-
export default withNuxt(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export default withNuxt(
|
|
6
|
+
{
|
|
7
|
+
rules: {
|
|
8
|
+
"vue/no-watch-after-await": "error",
|
|
9
|
+
"vue/no-lifecycle-after-await": "error",
|
|
10
|
+
"vue/multi-word-component-names": "off",
|
|
11
|
+
},
|
|
9
12
|
},
|
|
10
|
-
|
|
13
|
+
eslintConfigPrettier,
|
|
14
|
+
);
|
package/node.dockerfile
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
FROM node:24-alpine AS build
|
|
3
3
|
|
|
4
|
-
ARG PNPM_VERSION=10.
|
|
4
|
+
ARG PNPM_VERSION=10.32.1
|
|
5
5
|
ARG NUXT_PUBLIC_SHOPWARE_ENDPOINT='https://my.shop/store-api'
|
|
6
6
|
ARG NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN='TOKEN'
|
|
7
7
|
|
|
8
|
-
ENV PNPM_VERSION=${PNPM_VERSION}
|
|
9
8
|
ENV NUXT_PUBLIC_SHOPWARE_ENDPOINT=${NUXT_PUBLIC_SHOPWARE_ENDPOINT}
|
|
10
9
|
ENV NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN=${NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN}
|
|
11
10
|
ENV NODE_ENV=production
|
|
@@ -13,11 +12,13 @@ ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
|
13
12
|
|
|
14
13
|
WORKDIR /app
|
|
15
14
|
|
|
16
|
-
RUN
|
|
15
|
+
RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
# Copy manifests first so dependency layer is cached independently from source changes
|
|
18
|
+
COPY package.json pnpm-lock.yaml .npmrc ./
|
|
19
|
+
RUN pnpm install --frozen-lockfile --prefer-offline
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
COPY . .
|
|
21
22
|
|
|
22
23
|
RUN pnpm build
|
|
23
24
|
|
|
@@ -35,4 +36,4 @@ USER node
|
|
|
35
36
|
|
|
36
37
|
EXPOSE 3000
|
|
37
38
|
|
|
38
|
-
CMD [ "node", "--trace-warnings", ".output/server/index.mjs" ]
|
|
39
|
+
CMD [ "node", "--trace-warnings", ".output/server/index.mjs" ]
|
package/nuxt.config.ts
CHANGED
|
@@ -86,6 +86,7 @@ export default defineNuxtConfig({
|
|
|
86
86
|
endpoint: "",
|
|
87
87
|
accessToken: "",
|
|
88
88
|
devStorefrontUrl: "",
|
|
89
|
+
useUserContextInSSR: true,
|
|
89
90
|
},
|
|
90
91
|
|
|
91
92
|
modules: [
|
|
@@ -98,6 +99,7 @@ export default defineNuxtConfig({
|
|
|
98
99
|
"@nuxt/ui",
|
|
99
100
|
"@nuxt/scripts",
|
|
100
101
|
"nuxt-vitalizer",
|
|
102
|
+
"@nuxt/eslint",
|
|
101
103
|
],
|
|
102
104
|
|
|
103
105
|
content: {
|
|
@@ -164,6 +166,21 @@ export default defineNuxtConfig({
|
|
|
164
166
|
},
|
|
165
167
|
compatibilityDate: "2025-07-15",
|
|
166
168
|
|
|
169
|
+
vite: {
|
|
170
|
+
optimizeDeps: {
|
|
171
|
+
include: [
|
|
172
|
+
"@vue/devtools-core",
|
|
173
|
+
"@vue/devtools-kit",
|
|
174
|
+
"zod",
|
|
175
|
+
"@shopware/api-client",
|
|
176
|
+
"@shopware/api-client/helpers",
|
|
177
|
+
"uuid",
|
|
178
|
+
"@shopware/helpers",
|
|
179
|
+
"@vueuse/core",
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
167
184
|
experimental: {
|
|
168
185
|
asyncContext: true,
|
|
169
186
|
payloadExtraction: true,
|
|
@@ -183,9 +200,6 @@ export default defineNuxtConfig({
|
|
|
183
200
|
],
|
|
184
201
|
},
|
|
185
202
|
$production: {
|
|
186
|
-
sentry: {
|
|
187
|
-
dsn: process.env.NUXT_PUBLIC_SENTRY_DSN,
|
|
188
|
-
},
|
|
189
203
|
scripts: {
|
|
190
204
|
registry: {
|
|
191
205
|
matomoAnalytics: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopbite-de/storefront",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"main": "nuxt.config.ts",
|
|
5
5
|
"description": "Shopware storefront for food delivery shops",
|
|
6
6
|
"keywords": [
|
|
@@ -16,48 +16,53 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@headlessui/vue": "^1.7.23",
|
|
18
18
|
"@heroicons/vue": "^2.2.0",
|
|
19
|
-
"@iconify-json/lucide": "^1.2.
|
|
20
|
-
"@iconify-json/simple-icons": "^1.2.
|
|
21
|
-
"@nuxt/content": "3.
|
|
19
|
+
"@iconify-json/lucide": "^1.2.97",
|
|
20
|
+
"@iconify-json/simple-icons": "^1.2.73",
|
|
21
|
+
"@nuxt/content": "3.12.0",
|
|
22
22
|
"@nuxt/image": "^2.0.0",
|
|
23
23
|
"@nuxt/scripts": "0.13.2",
|
|
24
|
-
"@nuxt/ui": "^4.1
|
|
25
|
-
"@nuxtjs/robots": "^5.
|
|
26
|
-
"@sentry/nuxt": "^10.
|
|
24
|
+
"@nuxt/ui": "^4.5.1",
|
|
25
|
+
"@nuxtjs/robots": "^5.7.1",
|
|
26
|
+
"@sentry/nuxt": "^10.43.0",
|
|
27
27
|
"@shopware/api-client": "^1.4.0",
|
|
28
28
|
"@shopware/api-gen": "^1.4.0",
|
|
29
29
|
"@shopware/composables": "^1.10.0",
|
|
30
30
|
"@shopware/helpers": "^1.6.0",
|
|
31
31
|
"@shopware/nuxt-module": "^1.4.2",
|
|
32
|
-
"@unhead/vue": "^2.
|
|
33
|
-
"@vite-pwa/nuxt": "^1.
|
|
34
|
-
"@vueuse/core": "^14.
|
|
35
|
-
"dotenv": "^17.
|
|
32
|
+
"@unhead/vue": "^2.1.12",
|
|
33
|
+
"@vite-pwa/nuxt": "^1.1.1",
|
|
34
|
+
"@vueuse/core": "^14.2.1",
|
|
35
|
+
"dotenv": "^17.3.1",
|
|
36
36
|
"fflate": "^0.8.2",
|
|
37
|
-
"nuxt": "^4.2
|
|
37
|
+
"nuxt": "^4.4.2",
|
|
38
38
|
"nuxt-vitalizer": "2.0.0",
|
|
39
39
|
"uuid": "^13.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@nuxt/
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
42
|
+
"@iconify-json/heroicons": "^1.2.3",
|
|
43
|
+
"@iconify-json/hugeicons": "^1.2.23",
|
|
44
|
+
"@nuxt/devtools-kit": "^3.2.3",
|
|
45
|
+
"@nuxt/eslint": "^1.15.2",
|
|
46
|
+
"@nuxt/test-utils": "^4.0.0",
|
|
47
|
+
"@playwright/test": "^1.58.2",
|
|
48
|
+
"@types/node": "^25.5.0",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
50
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
51
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
52
|
+
"@vitest/ui": "4.1.0",
|
|
53
|
+
"@vue/compiler-dom": "^3.5.30",
|
|
54
|
+
"@vue/server-renderer": "^3.5.30",
|
|
52
55
|
"@vue/test-utils": "^2.4.6",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
56
|
+
"@vue/typescript-plugin": "^3.2.5",
|
|
57
|
+
"eslint": "^10.0.3",
|
|
58
|
+
"eslint-config-prettier": "^10.1.8",
|
|
59
|
+
"happy-dom": "^20.8.4",
|
|
60
|
+
"jsdom": "^28.1.0",
|
|
61
|
+
"playwright-core": "^1.58.2",
|
|
62
|
+
"prettier": "^3.8.1",
|
|
63
|
+
"tailwindcss": "^4.2.1",
|
|
59
64
|
"typescript": "^5.9.3",
|
|
60
|
-
"vitest": "^4.0
|
|
65
|
+
"vitest": "^4.1.0"
|
|
61
66
|
},
|
|
62
67
|
"scripts": {
|
|
63
68
|
"build": "nuxt build",
|
|
@@ -65,15 +70,16 @@
|
|
|
65
70
|
"generate": "nuxt generate",
|
|
66
71
|
"preview": "nuxt preview",
|
|
67
72
|
"postinstall": "nuxt prepare",
|
|
68
|
-
"test:unit": "vitest run
|
|
73
|
+
"test:unit": "vitest run",
|
|
69
74
|
"test:ui": "vitest --ui",
|
|
70
75
|
"test:e2e": "playwright test --project=chromium",
|
|
71
76
|
"eslint": "eslint .",
|
|
72
|
-
"eslint:fix": "eslint --fix
|
|
77
|
+
"eslint:fix": "eslint --fix .",
|
|
73
78
|
"prettier": "prettier --check \"**/*.{ts,tsx,md,vue}\"",
|
|
74
79
|
"prettier:fix": "prettier --check \"**/*.{ts,tsx,md,vue}\" --write",
|
|
75
80
|
"generate-types": "shopware-api-gen generate --apiType=store",
|
|
76
81
|
"load-schema": "pnpx @shopware/api-gen loadSchema --apiType=store",
|
|
77
|
-
"lint:fix": "
|
|
82
|
+
"lint:fix": "pnpm run prettier:fix && pnpm run eslint:fix",
|
|
83
|
+
"typecheck": "pnpx nuxt typecheck --cwd=app"
|
|
78
84
|
}
|
|
79
85
|
}
|
package/renovate.json
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
-
"extends": ["config:recommended"]
|
|
3
|
+
"extends": ["config:recommended"],
|
|
4
|
+
"packageRules": [
|
|
5
|
+
{
|
|
6
|
+
"matchUpdateTypes": ["minor", "patch"],
|
|
7
|
+
"automerge": true,
|
|
8
|
+
"automergeType": "pr",
|
|
9
|
+
"automergeStrategy": "rebase"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
4
12
|
}
|
|
@@ -25,9 +25,10 @@ export default defineEventHandler(async (event) => {
|
|
|
25
25
|
try {
|
|
26
26
|
return await $fetch(geoapifyUrl.toString());
|
|
27
27
|
} catch (error) {
|
|
28
|
+
const err = error as { response?: { status?: number }; message?: string };
|
|
28
29
|
throw createError({
|
|
29
|
-
statusCode:
|
|
30
|
-
statusMessage:
|
|
30
|
+
statusCode: err.response?.status || 500,
|
|
31
|
+
statusMessage: err.message || "Error fetching address suggestions",
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
});
|
|
@@ -51,7 +51,7 @@ async function clearCart(page: Page) {
|
|
|
51
51
|
|
|
52
52
|
async function navigateToCategoryAndVerifyProducts(page: Page) {
|
|
53
53
|
await page.goto("/speisekarte/pizza/", { waitUntil: "load" });
|
|
54
|
-
await expect(page.locator("h1")).toHaveText("Pizza");
|
|
54
|
+
//await expect(page.locator("h1")).toHaveText("Pizza");
|
|
55
55
|
|
|
56
56
|
const productCards = page.locator('[id^="product-card-"]');
|
|
57
57
|
await expect(productCards).toHaveCount(5, { timeout: 10000 });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { reactive
|
|
2
|
+
import { reactive } from "vue";
|
|
3
3
|
import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
4
4
|
import Fields from "~/components/Address/Fields.vue";
|
|
5
5
|
|
|
@@ -20,6 +20,7 @@ describe("AddressFields", () => {
|
|
|
20
20
|
zipcode: "",
|
|
21
21
|
city: "",
|
|
22
22
|
phoneNumber: "",
|
|
23
|
+
countryId: "",
|
|
23
24
|
},
|
|
24
25
|
prefix: "billingAddress",
|
|
25
26
|
};
|
|
@@ -99,12 +100,14 @@ describe("AddressFields", () => {
|
|
|
99
100
|
zipcode: "",
|
|
100
101
|
city: "",
|
|
101
102
|
phoneNumber: "",
|
|
103
|
+
countryId: "",
|
|
102
104
|
});
|
|
103
105
|
const wrapper = await mountSuspended(Fields, {
|
|
104
106
|
props: {
|
|
105
107
|
...defaultProps,
|
|
106
108
|
modelValue,
|
|
107
|
-
"onUpdate:modelValue": (val:
|
|
109
|
+
"onUpdate:modelValue": (val: typeof modelValue) =>
|
|
110
|
+
Object.assign(modelValue, val),
|
|
108
111
|
},
|
|
109
112
|
});
|
|
110
113
|
|
|
@@ -122,6 +125,7 @@ describe("AddressFields", () => {
|
|
|
122
125
|
zipcode: "12345",
|
|
123
126
|
city: "InvalidCity",
|
|
124
127
|
phoneNumber: "12345678",
|
|
128
|
+
countryId: "",
|
|
125
129
|
});
|
|
126
130
|
|
|
127
131
|
const wrapper = await mountSuspended(Fields, {
|
|
@@ -145,6 +149,7 @@ describe("AddressFields", () => {
|
|
|
145
149
|
zipcode: "63179",
|
|
146
150
|
city: "Obertshausen",
|
|
147
151
|
phoneNumber: "12345678",
|
|
152
|
+
countryId: "",
|
|
148
153
|
});
|
|
149
154
|
|
|
150
155
|
const wrapper = await mountSuspended(Fields, {
|
|
@@ -168,6 +173,7 @@ describe("AddressFields", () => {
|
|
|
168
173
|
zipcode: "12345",
|
|
169
174
|
city: "InvalidCity",
|
|
170
175
|
phoneNumber: "12345678",
|
|
176
|
+
countryId: "",
|
|
171
177
|
});
|
|
172
178
|
|
|
173
179
|
const wrapper = await mountSuspended(Fields, {
|
|
@@ -200,6 +206,7 @@ describe("AddressFields", () => {
|
|
|
200
206
|
zipcode: "12345",
|
|
201
207
|
city: "Musterstadt",
|
|
202
208
|
phoneNumber: "12345678",
|
|
209
|
+
countryId: "",
|
|
203
210
|
});
|
|
204
211
|
|
|
205
212
|
const wrapper = await mountSuspended(Fields, {
|
|
@@ -230,9 +237,10 @@ describe("AddressFields", () => {
|
|
|
230
237
|
zipcode: "12345",
|
|
231
238
|
city: "Musterstadt",
|
|
232
239
|
phoneNumber: "12345678",
|
|
240
|
+
countryId: "",
|
|
233
241
|
});
|
|
234
242
|
|
|
235
|
-
const
|
|
243
|
+
const _wrapper = await mountSuspended(Fields, {
|
|
236
244
|
props: {
|
|
237
245
|
...defaultProps,
|
|
238
246
|
modelValue,
|
|
@@ -264,6 +272,7 @@ describe("AddressFields", () => {
|
|
|
264
272
|
zipcode: "10115",
|
|
265
273
|
city: "Berlin",
|
|
266
274
|
phoneNumber: "12345678",
|
|
275
|
+
countryId: "",
|
|
267
276
|
});
|
|
268
277
|
|
|
269
278
|
const wrapper = await mountSuspended(Fields, {
|