@shopbite-de/storefront 1.16.0 → 1.16.1
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 +8 -0
- package/app/app.vue +53 -34
- package/app/components/AddToWishlist.vue +0 -3
- package/app/components/User/LoginForm.vue +1 -4
- package/app/layouts/account.vue +0 -1
- package/app/pages/anmelden.vue +8 -4
- package/nuxt.config.ts +1 -0
- package/package.json +1 -1
- package/test/nuxt/LoginForm.test.ts +25 -15
- package/app/middleware/trailing-slash.global.ts +0 -19
package/app/app.vue
CHANGED
|
@@ -1,37 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { Schemas } from "#shopware";
|
|
3
2
|
import type { Toast } from "#ui/composables/useToast";
|
|
4
3
|
|
|
5
|
-
// Composables
|
|
6
|
-
const toast = useToast();
|
|
7
|
-
const appConfig = useAppConfig();
|
|
8
|
-
const { apiClient } = useShopwareContext();
|
|
9
|
-
const { refresh: refreshToppings } = useShopBiteConfig();
|
|
10
|
-
const { refreshCart } = useCart();
|
|
11
|
-
const { getWishlistProducts } = useWishlist();
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
getNextOpeningTime,
|
|
15
|
-
isStoreOpen,
|
|
16
|
-
refresh: refreshBusinessHours,
|
|
17
|
-
} = useBusinessHours();
|
|
18
|
-
const { isClosedHoliday, refresh: refreshHolidays } = useHolidays();
|
|
19
|
-
|
|
20
|
-
await Promise.all([refreshBusinessHours(), refreshHolidays()]);
|
|
21
|
-
|
|
22
|
-
const sessionContextData = ref<Schemas["SalesChannelContext"]>();
|
|
23
|
-
|
|
24
|
-
const contextResponse = await apiClient
|
|
25
|
-
.invoke("readContext get /context")
|
|
26
|
-
.catch((error) => {
|
|
27
|
-
console.error("Error fetching session context data:", error);
|
|
28
|
-
return { data: undefined };
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
sessionContextData.value = contextResponse.data;
|
|
32
|
-
useSessionContext(sessionContextData.value);
|
|
33
|
-
|
|
34
|
-
// Toast configuration
|
|
35
4
|
const TOAST_CONFIG = {
|
|
36
5
|
open: {
|
|
37
6
|
id: "currently-open",
|
|
@@ -59,6 +28,41 @@ const TOAST_CONFIG = {
|
|
|
59
28
|
} as Partial<Toast>,
|
|
60
29
|
} as const;
|
|
61
30
|
|
|
31
|
+
const { apiClient } = useShopwareContext();
|
|
32
|
+
const appConfig = useAppConfig();
|
|
33
|
+
const router = useRouter();
|
|
34
|
+
const toast = useToast();
|
|
35
|
+
|
|
36
|
+
const { data: sessionContextData } = await useAsyncData(
|
|
37
|
+
"sessionContext",
|
|
38
|
+
async () => {
|
|
39
|
+
try {
|
|
40
|
+
const { data } = await apiClient.invoke("readContext get /context");
|
|
41
|
+
return data;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Failed to load session context", error);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
default: () => null,
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (sessionContextData.value) {
|
|
53
|
+
usePrice({
|
|
54
|
+
currencyCode: sessionContextData.value.currency?.isoCode || "",
|
|
55
|
+
});
|
|
56
|
+
useSessionContext(sessionContextData.value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
getNextOpeningTime,
|
|
61
|
+
isStoreOpen,
|
|
62
|
+
refresh: refreshBusinessHours,
|
|
63
|
+
} = useBusinessHours();
|
|
64
|
+
const { isClosedHoliday, refresh: refreshHolidays } = useHolidays();
|
|
65
|
+
|
|
62
66
|
function displayStoreStatus() {
|
|
63
67
|
const isOpen = isStoreOpen(undefined, isClosedHoliday);
|
|
64
68
|
|
|
@@ -75,10 +79,25 @@ function displayStoreStatus() {
|
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
const { refreshCart } = useCart();
|
|
83
|
+
const { getWishlistProducts } = useWishlist();
|
|
84
|
+
|
|
85
|
+
if (import.meta.client) {
|
|
86
|
+
// getting the wishlist products should not block SSR
|
|
87
|
+
if (!(router.currentRoute.value.name as string).includes("wishlist")) {
|
|
88
|
+
getWishlistProducts(); // initial page loading
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { refresh: refreshToppings } = useShopBiteConfig();
|
|
93
|
+
|
|
79
94
|
onMounted(async () => {
|
|
80
|
-
await
|
|
81
|
-
|
|
95
|
+
await Promise.all([
|
|
96
|
+
refreshHolidays(),
|
|
97
|
+
refreshBusinessHours(),
|
|
98
|
+
refreshToppings(),
|
|
99
|
+
]);
|
|
100
|
+
refreshCart();
|
|
82
101
|
displayStoreStatus();
|
|
83
102
|
});
|
|
84
103
|
|
|
@@ -8,17 +8,14 @@ const props = defineProps<{
|
|
|
8
8
|
const { addToWishlist, isInWishlist, removeFromWishlist } = useProductWishlist(
|
|
9
9
|
props.product.id,
|
|
10
10
|
);
|
|
11
|
-
const { getWishlistProducts } = useWishlist();
|
|
12
11
|
const { trackAddToWishlist } = useTrackEvent();
|
|
13
12
|
|
|
14
13
|
const toggleWishlistProduct = async () => {
|
|
15
14
|
try {
|
|
16
15
|
if (isInWishlist.value) {
|
|
17
16
|
await removeFromWishlist();
|
|
18
|
-
await getWishlistProducts();
|
|
19
17
|
} else {
|
|
20
18
|
await addToWishlist();
|
|
21
|
-
await getWishlistProducts();
|
|
22
19
|
trackAddToWishlist(props.product);
|
|
23
20
|
}
|
|
24
21
|
} catch (error) {
|
|
@@ -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);
|
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/nuxt.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
2
|
+
import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
3
|
+
import { ref } from "vue";
|
|
3
4
|
import LoginForm from "@/components/User/LoginForm.vue";
|
|
4
|
-
import { useUser } from "@shopware/composables";
|
|
5
5
|
import { ApiClientError } from "@shopware/api-client";
|
|
6
6
|
|
|
7
7
|
vi.mock("@shopware/api-client", () => ({
|
|
@@ -16,9 +16,7 @@ vi.mock("@shopware/api-client", () => ({
|
|
|
16
16
|
|
|
17
17
|
vi.mock("@shopware/composables", () => ({
|
|
18
18
|
useUser: vi.fn(),
|
|
19
|
-
useWishlist: vi.fn(
|
|
20
|
-
mergeWishlistProducts: vi.fn(),
|
|
21
|
-
})),
|
|
19
|
+
useWishlist: vi.fn(),
|
|
22
20
|
}));
|
|
23
21
|
|
|
24
22
|
const mockToastAdd = vi.fn();
|
|
@@ -28,19 +26,30 @@ mockNuxtImport("useToast", () => {
|
|
|
28
26
|
});
|
|
29
27
|
});
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const loginMock = vi.fn();
|
|
30
|
+
const userMock = ref({ firstName: "John", lastName: "Doe" });
|
|
31
|
+
const isLoggedInMock = ref(false);
|
|
34
32
|
|
|
33
|
+
mockNuxtImport("useUser", () => {
|
|
34
|
+
return () => ({
|
|
35
|
+
isLoggedIn: isLoggedInMock,
|
|
36
|
+
login: loginMock,
|
|
37
|
+
user: userMock,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
mockNuxtImport("useWishlist", () => {
|
|
42
|
+
return () => ({
|
|
43
|
+
mergeWishlistProducts: vi.fn(),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("LoginForm", () => {
|
|
35
48
|
beforeEach(() => {
|
|
36
49
|
vi.clearAllMocks();
|
|
37
|
-
loginMock
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
isLoggedIn: { value: false },
|
|
41
|
-
login: loginMock,
|
|
42
|
-
user: userMock,
|
|
43
|
-
});
|
|
50
|
+
loginMock.mockReset();
|
|
51
|
+
isLoggedInMock.value = false;
|
|
52
|
+
userMock.value = { firstName: "John", lastName: "Doe" };
|
|
44
53
|
});
|
|
45
54
|
|
|
46
55
|
it("should show success toast on successful login", async () => {
|
|
@@ -63,6 +72,7 @@ describe("LoginForm", () => {
|
|
|
63
72
|
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
64
73
|
expect.objectContaining({
|
|
65
74
|
title: "Hallo John Doe!",
|
|
75
|
+
description: "Erfolgreich angemeldet.",
|
|
66
76
|
color: "success",
|
|
67
77
|
}),
|
|
68
78
|
);
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineNuxtRouteMiddleware, navigateTo } from "#app";
|
|
2
|
-
|
|
3
|
-
export default defineNuxtRouteMiddleware((to) => {
|
|
4
|
-
// Only handle category pages
|
|
5
|
-
if (!to.path.startsWith("/c/")) return;
|
|
6
|
-
|
|
7
|
-
// Skip if it already has a trailing slash or it's just "/c/"
|
|
8
|
-
if (to.path.endsWith("/") || to.path === "/c/") return;
|
|
9
|
-
|
|
10
|
-
// Preserve query and hash while adding the trailing slash
|
|
11
|
-
return navigateTo(
|
|
12
|
-
{
|
|
13
|
-
path: `${to.path}/`,
|
|
14
|
-
query: to.query,
|
|
15
|
-
hash: to.hash,
|
|
16
|
-
},
|
|
17
|
-
{ redirectCode: 301 },
|
|
18
|
-
);
|
|
19
|
-
});
|