@vtex/faststore-plugin-buyer-portal 1.1.116 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.1.116",
3
+ "version": "1.2.1",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -38,7 +38,6 @@ export const OrgUnitBreadcrumbLink = ({
38
38
  ref={linkRef as React.RefObject<HTMLAnchorElement>}
39
39
  data-fs-bp-breadcrumb-text
40
40
  href={item}
41
- data-fs-bp-breadcrumb-link
42
41
  >
43
42
  {name}
44
43
  </Link>
@@ -76,4 +76,3 @@ export { ErrorBoundary } from "./ErrorBoundary/ErrorBoundary";
76
76
  export { withErrorBoundary } from "./withErrorBoundary/withErrorBoundary";
77
77
  export { default as Error } from "./Error/Error";
78
78
  export { IconBookmarked } from "./IconBookmarked/IconBookmarked";
79
- export { PageLoader } from "./PageLoader";
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React from "react";
4
4
 
5
- // import { ErrorBoundary } from "../ErrorBoundary/ErrorBoundary";
5
+ import { ErrorBoundary } from "../ErrorBoundary/ErrorBoundary";
6
6
 
7
7
  type WithErrorBoundaryOptions = {
8
8
  fallback?: React.ReactNode;
@@ -15,16 +15,16 @@ type WithErrorBoundaryOptions = {
15
15
 
16
16
  export function withErrorBoundary<P extends Record<string, unknown>>(
17
17
  WrappedComponent: React.ComponentType<P>,
18
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
- _options: WithErrorBoundaryOptions = {}
18
+
19
+ options: WithErrorBoundaryOptions = {}
20
20
  ) {
21
- // const { onError, tags } = options;
21
+ const { onError, tags } = options;
22
22
 
23
23
  const ComponentWithErrorBoundary = (props: P) => {
24
24
  return (
25
- // <ErrorBoundary onError={onError} tags={tags}>
26
- <WrappedComponent {...props} />
27
- // </ErrorBoundary>
25
+ <ErrorBoundary onError={onError} tags={tags}>
26
+ <WrappedComponent {...props} />
27
+ </ErrorBoundary>
28
28
  );
29
29
  };
30
30
 
@@ -1,5 +1,4 @@
1
1
  export { useBuyerPortal } from "./useBuyerPortal";
2
- export { useCookieMonitor } from "./useCookieMonitor";
3
2
  export { useDrawerProps, type DrawerProps } from "./useDrawerProps";
4
3
  export {
5
4
  useMutation,
@@ -1,6 +1,5 @@
1
1
  import { ClientContext } from "../utils";
2
2
 
3
3
  export type AuthRouteProps<T> =
4
- | { authorized: true; data: T; clientContext: ClientContext; loading: false }
5
- | { authorized: false; loading: false }
6
- | { authorized: false; loading: true };
4
+ | { authorized: true; data: T; clientContext: ClientContext }
5
+ | { authorized: false };
@@ -1,65 +1,27 @@
1
- import { useEffect, useRef, type ComponentType } from "react";
1
+ import { useEffect, type ComponentType } from "react";
2
2
 
3
3
  import { useRouter } from "next/router";
4
4
 
5
- import { PageLoader } from "../components";
6
- import { useCookieMonitor } from "../hooks/useCookieMonitor";
7
-
8
5
  import type { AuthRouteProps } from "../types";
9
6
 
10
7
  /**
11
- * HOC that checks only for authorization.
12
- * Now handles loading state for pre-fetch scenarios.
8
+ * HOC that checks only for authorization
13
9
  */
14
10
  export const withAuth = <T extends Record<string, unknown>>(
15
11
  Component: ComponentType<T>
16
12
  ) => {
17
13
  return function AuthenticatedComponent(props: AuthRouteProps<T>) {
18
14
  const router = useRouter();
19
- const hasCookie = useCookieMonitor();
20
- const reloadAttemptedRef = useRef(false);
21
-
22
- // Debug logging
23
- useEffect(() => {
24
- console.log("[withAuth] Props state:", {
25
- authorized: props?.authorized,
26
- loading: props?.loading,
27
- hasCookie,
28
- reloadAttempted: reloadAttemptedRef.current,
29
- });
30
- }, [props?.authorized, props?.loading, hasCookie]);
31
15
 
32
16
  useEffect(() => {
33
- // If not authorized and not loading, reload the page
34
- if (
35
- !props?.authorized &&
36
- !props?.loading &&
37
- !reloadAttemptedRef.current
38
- ) {
39
- console.log("[withAuth] Reloading: not authorized and not loading");
40
- reloadAttemptedRef.current = true;
41
- // Use replace to bypass Next.js data cache
42
- router.replace(router.asPath);
17
+ // If not authorized, reload the page
18
+ if (!props?.authorized) {
19
+ router.reload();
43
20
  }
44
- }, [props?.authorized, props?.loading, router]);
45
-
46
- useEffect(() => {
47
- if (props?.loading && hasCookie && !reloadAttemptedRef.current) {
48
- console.log(
49
- "[withAuth] Cookie available, performing hard reload to bypass cache"
50
- );
51
- reloadAttemptedRef.current = true;
52
- // Hard reload to completely bypass Next.js cache
53
- if (typeof window !== "undefined") {
54
- window.location.href = router.asPath;
55
- }
56
- }
57
- }, [props?.loading, hasCookie, router]);
21
+ }, [props?.authorized, router]);
58
22
 
23
+ // If not authorized, render nothing
59
24
  if (!props?.authorized) {
60
- if (props?.loading) {
61
- return <PageLoader />;
62
- }
63
25
  return null;
64
26
  }
65
27
 
@@ -1,6 +1,5 @@
1
1
  import { validateAccessService } from "../services";
2
2
 
3
- import { getAuthCookie } from "./cookie";
4
3
  import { ClientContext, getClientContext } from "./getClientContext";
5
4
 
6
5
  import type { AuthRouteProps } from "../types";
@@ -9,67 +8,40 @@ import type { LoaderData } from "../types";
9
8
  /**
10
9
  * Utility function to encapsulate access validation in loaders.
11
10
  * Similar to withAuthProvider but for server-side usage.
12
- *
13
- * Now supports a loading state for when cookies are not yet available during pre-fetch.
14
11
  */
15
12
  export const withAuthLoader = async <T>(
16
13
  data: LoaderData,
17
14
  dataLoader: (clientContext: ClientContext) => Promise<T>
18
15
  ): Promise<AuthRouteProps<T>> => {
19
16
  try {
20
- // Check if auth cookie exists before proceeding
21
- const authCookie = getAuthCookie(data);
22
-
23
- console.log("[withAuthLoader] Cookie check:", {
24
- hasCookie: !!authCookie,
25
- cookieLength: authCookie?.length,
26
- });
27
-
28
- if (!authCookie || authCookie === "") {
29
- console.log("[withAuthLoader] Returning loading state (no cookie)");
30
- return {
31
- authorized: false,
32
- loading: true,
33
- };
34
- }
35
-
36
17
  const { cookie, userId, ...clientContext } = await getClientContext(data);
37
18
 
38
19
  const hasAccess = await validateAccessService({ cookie });
39
20
 
40
- console.log("[withAuthLoader] Access validation result:", hasAccess);
41
-
42
21
  if (!hasAccess) {
43
- console.log("[withAuthLoader] Access denied, redirecting to /");
44
22
  data.res?.writeHead(302, { Location: "/" });
45
23
  data.res?.end();
46
24
 
47
25
  return {
48
26
  authorized: false,
49
- loading: false,
50
27
  };
51
28
  }
52
29
 
53
30
  const pageData = await dataLoader({ cookie, userId, ...clientContext });
54
31
 
55
- console.log(
56
- "[withAuthLoader] Successfully loaded data, returning authorized"
57
- );
58
32
  return {
59
33
  authorized: true,
60
34
  data: pageData,
61
35
  clientContext: { cookie, userId, ...clientContext },
62
- loading: false,
63
36
  };
64
37
  } catch (error) {
65
- console.error("[withAuthLoader] Auth validation failed:", error);
38
+ console.error("Auth validation failed:", error);
66
39
 
67
40
  data.res?.writeHead(302, { Location: "/" });
68
41
  data.res?.end();
69
42
 
70
43
  return {
71
44
  authorized: false,
72
- loading: false,
73
45
  };
74
46
  }
75
47
  };
@@ -1,81 +1,30 @@
1
- import { useEffect, useRef, type ComponentType } from "react";
1
+ import { useEffect, type ComponentType } from "react";
2
2
 
3
3
  import { useRouter } from "next/router";
4
4
 
5
- import { BuyerPortalProvider, PageLoader } from "../components";
6
- import { useCookieMonitor } from "../hooks/useCookieMonitor";
5
+ import { BuyerPortalProvider } from "../components";
7
6
 
8
7
  import type { AuthRouteProps } from "../types";
9
8
  import type { ClientContext } from "./getClientContext";
10
9
 
11
10
  /**
12
- * HOC que encapsula lógica de autenticação e provider.
13
- * Now handles loading state for pre-fetch scenarios.
14
- * Monitors cookie availability and reloads when cookie becomes available.
11
+ * HOC que encapsula lógica de autenticação e provider
15
12
  */
16
13
  export const withAuthProvider = <T,>(
17
14
  Component: ComponentType<T & { clientContext: ClientContext }>
18
15
  ) => {
19
16
  return function WrappedPage(props: AuthRouteProps<T>) {
20
17
  const router = useRouter();
21
- const hasCookie = useCookieMonitor();
22
- const reloadAttemptedRef = useRef(false);
23
- const isProduction = process.env.NODE_ENV === "production";
24
18
 
25
- // Debug logging (only in development or when needed)
26
19
  useEffect(() => {
27
- if (!isProduction) {
28
- console.log("[withAuthProvider] Props state:", {
29
- authorized: props?.authorized,
30
- loading: props?.loading,
31
- hasCookie,
32
- reloadAttempted: reloadAttemptedRef.current,
33
- routerPath: router.asPath,
34
- });
20
+ // Se não está autorizado, recarrega a página
21
+ if (!props?.authorized) {
22
+ router.reload();
35
23
  }
36
- }, [
37
- props?.authorized,
38
- props?.loading,
39
- hasCookie,
40
- router.asPath,
41
- isProduction,
42
- ]);
43
-
44
- useEffect(() => {
45
- // Se não está autorizado e não está carregando, recarrega a página
46
- if (
47
- !props?.authorized &&
48
- !props?.loading &&
49
- !reloadAttemptedRef.current
50
- ) {
51
- console.log(
52
- "[withAuthProvider] Reloading: not authorized and not loading"
53
- );
54
- reloadAttemptedRef.current = true;
55
- // Use replace to bypass Next.js data cache
56
- router.replace(router.asPath);
57
- }
58
- }, [props?.authorized, props?.loading, router]);
59
-
60
- useEffect(() => {
61
- // If we're in loading state and cookie becomes available, do hard reload
62
- if (props?.loading && hasCookie && !reloadAttemptedRef.current) {
63
- console.log(
64
- "[withAuthProvider] Cookie available, performing hard reload to bypass cache"
65
- );
66
- reloadAttemptedRef.current = true;
67
- // Hard reload to completely bypass Next.js cache
68
- if (typeof window !== "undefined") {
69
- window.location.href = router.asPath;
70
- }
71
- }
72
- }, [props?.loading, hasCookie, router]);
24
+ }, [props?.authorized, router]);
73
25
 
26
+ // Se não está autorizado, não renderiza nada
74
27
  if (!props?.authorized) {
75
- if (props?.loading) {
76
- console.log("[withAuthProvider] Loading state");
77
- return <PageLoader />;
78
- }
79
28
  return null;
80
29
  }
81
30
 
@@ -1,4 +1,4 @@
1
- // import { getClientContext } from "./getClientContext";
1
+ import { getClientContext } from "./getClientContext";
2
2
  import { serializeLoaderData } from "./serializeLoaderData";
3
3
 
4
4
  import type { LoaderData } from "../types";
@@ -11,85 +11,77 @@ interface WithLoaderErrorBoundaryOptions {
11
11
 
12
12
  export function withLoaderErrorBoundary<TQuery, TReturn>(
13
13
  loaderFn: (data: LoaderData<TQuery>) => Promise<TReturn>,
14
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
- _options: WithLoaderErrorBoundaryOptions = {}
14
+ options: WithLoaderErrorBoundaryOptions = {}
16
15
  ) {
17
- // Bypass: Just call the loader function and serialize the result
16
+ const { componentName, onError, redirectToError = true } = options;
17
+
18
18
  return async (data: LoaderData<TQuery>): Promise<TReturn> => {
19
- const result = await loaderFn(data);
20
- return serializeLoaderData(result);
21
- };
19
+ try {
20
+ const result = await loaderFn(data);
21
+ return serializeLoaderData(result);
22
+ } catch (error) {
23
+ console.error(`[${componentName || "Loader"}] Error:`, {
24
+ message: (error as Error).message,
25
+ query: data.query,
26
+ });
27
+
28
+ if (onError) {
29
+ onError(error as Error, data.query);
30
+ }
31
+
32
+ if (redirectToError) {
33
+ let clientContext;
34
+ try {
35
+ clientContext = await getClientContext(data);
36
+ } catch (contextError) {
37
+ console.error(
38
+ `[${componentName || "Loader"}] Failed to get client context:`,
39
+ contextError
40
+ );
41
+ clientContext = {
42
+ cookie: "",
43
+ customerId: "",
44
+ userId: "",
45
+ vtexIdclientAutCookie: "",
46
+ };
47
+ }
22
48
 
23
- // Old error handling code - commented out for bypass
24
- // const { componentName, onError, redirectToError = true } = options;
25
- //
26
- // return async (data: LoaderData<TQuery>): Promise<TReturn> => {
27
- // try {
28
- // const result = await loaderFn(data);
29
- // return serializeLoaderData(result);
30
- // } catch (error) {
31
- // console.error(`[${componentName || "Loader"}] Error:`, {
32
- // message: (error as Error).message,
33
- // query: data.query,
34
- // });
35
- //
36
- // if (onError) {
37
- // onError(error as Error, data.query);
38
- // }
39
- //
40
- // if (redirectToError) {
41
- // let clientContext;
42
- // try {
43
- // clientContext = await getClientContext(data);
44
- // } catch (contextError) {
45
- // console.error(
46
- // `[${componentName || "Loader"}] Failed to get client context:`,
47
- // contextError
48
- // );
49
- // clientContext = {
50
- // cookie: "",
51
- // customerId: "",
52
- // userId: "",
53
- // vtexIdclientAutCookie: "",
54
- // };
55
- // }
56
- //
57
- // // Serialize query to ensure it's JSON-safe (use null instead of undefined)
58
- // const serializedQuery = data.query
59
- // ? JSON.parse(JSON.stringify(data.query))
60
- // : null;
61
- //
62
- // const errorResponse = {
63
- // error: {
64
- // error: {
65
- // message: (error as Error).message,
66
- // name: (error as Error).name,
67
- // stack: (error as Error).stack || null,
68
- // query: serializedQuery,
69
- // },
70
- // tags: {
71
- // component: componentName || "Unknown",
72
- // errorType: "loader_error",
73
- // },
74
- // extra: {
75
- // query: serializedQuery,
76
- // },
77
- // },
78
- // hasError: true,
79
- // context: {
80
- // clientContext,
81
- // currentOrgUnit: null,
82
- // currentContract: null,
83
- // currentUser: null,
84
- // },
85
- // data: null,
86
- // };
87
- //
88
- // // Ensure error response is also JSON-serializable
89
- // return serializeLoaderData(errorResponse) as TReturn;
90
- // }
91
- //
92
- // throw error;
93
- // }
94
- // };
49
+ // Serialize query to ensure it's JSON-safe (use null instead of undefined)
50
+ const serializedQuery = data.query
51
+ ? JSON.parse(JSON.stringify(data.query))
52
+ : null;
53
+
54
+ const errorResponse = {
55
+ error: {
56
+ error: {
57
+ message: (error as Error).message,
58
+ name: (error as Error).name,
59
+ stack: (error as Error).stack || null,
60
+ query: serializedQuery,
61
+ },
62
+ tags: {
63
+ component: componentName || "Unknown",
64
+ errorType: "loader_error",
65
+ },
66
+ extra: {
67
+ query: serializedQuery,
68
+ },
69
+ },
70
+ hasError: true,
71
+ context: {
72
+ clientContext,
73
+ currentOrgUnit: null,
74
+ currentContract: null,
75
+ currentUser: null,
76
+ },
77
+ data: null,
78
+ };
79
+
80
+ // Ensure error response is also JSON-serializable
81
+ return serializeLoaderData(errorResponse) as TReturn;
82
+ }
83
+
84
+ throw error;
85
+ }
86
+ };
95
87
  }
@@ -1,5 +1,4 @@
1
1
  @import "../features/profile/components/ProfileSummary/profile-summary.scss";
2
- @import "../features/shared/components/PageLoader/page-loader.scss";
3
2
  @import "./layouts";
4
3
 
5
4
  // ----------------------------------------------------------
@@ -1,22 +0,0 @@
1
- import { Skeleton } from "@faststore/ui";
2
-
3
- /**
4
- * PageLoader component displays skeleton loaders while page data is being fetched.
5
- * Used when cookies are not yet available during pre-fetch scenarios.
6
- */
7
- export const PageLoader = () => {
8
- return (
9
- <div data-fs-bp-page-loader>
10
- <div data-fs-bp-page-loader-header>
11
- <Skeleton size={{ width: "12rem", height: "2rem" }} />
12
- <Skeleton size={{ width: "8rem", height: "1.5rem" }} />
13
- </div>
14
-
15
- <div data-fs-bp-page-loader-content>
16
- <Skeleton size={{ width: "100%", height: "4rem" }} />
17
- <Skeleton size={{ width: "100%", height: "8rem" }} />
18
- <Skeleton size={{ width: "100%", height: "8rem" }} />
19
- </div>
20
- </div>
21
- );
22
- };
@@ -1 +0,0 @@
1
- export { PageLoader } from "./PageLoader";
@@ -1,22 +0,0 @@
1
- @import "@faststore/ui/src/components/atoms/Skeleton/styles.scss";
2
-
3
- [data-fs-bp-page-loader] {
4
- padding: var(--fs-spacing-3);
5
- width: 100%;
6
- min-height: 100vh;
7
- display: flex;
8
- flex-direction: column;
9
- gap: var(--fs-spacing-4);
10
- }
11
-
12
- [data-fs-bp-page-loader-header] {
13
- display: flex;
14
- flex-direction: column;
15
- gap: var(--fs-spacing-2);
16
- }
17
-
18
- [data-fs-bp-page-loader-content] {
19
- display: flex;
20
- flex-direction: column;
21
- gap: var(--fs-spacing-3);
22
- }
@@ -1,73 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- import storeConfig from "discovery.config";
4
-
5
- import { AUT_COOKIE_KEY } from "../utils/constants";
6
-
7
- /**
8
- * Hook to monitor when authentication cookie becomes available.
9
- * Used to detect when cookies load after pre-fetch scenarios.
10
- *
11
- * @returns boolean indicating if auth cookie is present
12
- */
13
- export const useCookieMonitor = () => {
14
- const [hasCookie, setHasCookie] = useState(false);
15
-
16
- useEffect(() => {
17
- const checkCookie = () => {
18
- if (typeof document === "undefined") return false;
19
-
20
- const cookieName = `${AUT_COOKIE_KEY}_${storeConfig?.api.storeId}`;
21
- const cookies = document.cookie.split(";");
22
-
23
- const authCookie = cookies.find((cookie) =>
24
- cookie.trim().startsWith(`${cookieName}=`)
25
- );
26
-
27
- const hasCookieValue =
28
- !!authCookie && authCookie.split("=")[1]?.trim() !== "";
29
-
30
- console.log("[useCookieMonitor] Checking cookie:", {
31
- cookieName,
32
- hasCookie: hasCookieValue,
33
- cookieValue: authCookie
34
- ? authCookie.split("=")[1]?.substring(0, 10) + "..."
35
- : "none",
36
- });
37
-
38
- return hasCookieValue;
39
- };
40
-
41
- // Check immediately
42
- const initialCheck = checkCookie();
43
- console.log("[useCookieMonitor] Initial check:", initialCheck);
44
- setHasCookie(initialCheck);
45
-
46
- // If no cookie yet, poll for it
47
- if (!initialCheck) {
48
- console.log("[useCookieMonitor] Starting cookie polling...");
49
- const interval = setInterval(() => {
50
- const hasAuthCookie = checkCookie();
51
-
52
- if (hasAuthCookie) {
53
- console.log("[useCookieMonitor] Cookie found! Stopping poll.");
54
- setHasCookie(true);
55
- clearInterval(interval);
56
- }
57
- }, 100); // Check every 100ms
58
-
59
- // Cleanup after 5 seconds max
60
- const timeout = setTimeout(() => {
61
- console.log("[useCookieMonitor] Polling timeout reached (5s)");
62
- clearInterval(interval);
63
- }, 5000);
64
-
65
- return () => {
66
- clearInterval(interval);
67
- clearTimeout(timeout);
68
- };
69
- }
70
- }, []);
71
-
72
- return hasCookie;
73
- };