@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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find:*)",
5
+ "Bash(grep:*)"
6
+ ]
7
+ }
8
+ }
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
- // Lifecycle
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 refreshToppings();
81
- await Promise.all([refreshCart(), getWishlistProducts()]);
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: "Erfolreich angemeldet.",
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);
@@ -68,7 +68,6 @@ const items = ref<NavigationMenuItem[][]>([
68
68
  />
69
69
  </template>
70
70
  <slot />
71
- <BottomNavi />
72
71
  </UPage>
73
72
  </UContainer>
74
73
  </template>
@@ -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
- onMounted(() => {
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
@@ -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: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopbite-de/storefront",
3
- "version": "1.16.0",
3
+ "version": "1.16.1",
4
4
  "main": "nuxt.config.ts",
5
5
  "description": "Shopware storefront for food delivery shops",
6
6
  "keywords": [
@@ -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
- describe("LoginForm", () => {
32
- let loginMock: any;
33
- let userMock: any;
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 = vi.fn();
38
- userMock = { value: { firstName: "John", lastName: "Doe" } };
39
- (useUser as any).mockReturnValue({
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
- });