@vtex/faststore-plugin-buyer-portal 1.1.109 → 1.1.110

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.109",
3
+ "version": "1.1.110",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,4 +1,4 @@
1
- import Link from "next/link";
1
+ // import Link from "next/link";
2
2
 
3
3
  import { Dropdown } from "@faststore/ui";
4
4
 
@@ -23,7 +23,7 @@ export const AddressLine = ({
23
23
  currentAddress,
24
24
  }: AddressLineProps) => (
25
25
  <li data-fs-addresses-line>
26
- <Link href={href} data-fs-addresses-line-link>
26
+ <a href={href} data-fs-addresses-line-link>
27
27
  <Icon
28
28
  name="LocalPostOffice"
29
29
  width={20}
@@ -37,7 +37,7 @@ export const AddressLine = ({
37
37
  {types.map((type) => (
38
38
  <Tag key={type}>{type}</Tag>
39
39
  ))}
40
- </Link>
40
+ </a>
41
41
 
42
42
  <Dropdown>
43
43
  <BasicDropdownMenu.Trigger />
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import { Tooltip } from "@faststore/ui";
6
6
 
@@ -34,13 +34,14 @@ export const OrgUnitBreadcrumbLink = ({
34
34
  {name}
35
35
  </p>
36
36
  ) : (
37
- <Link
37
+ <a
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
41
42
  >
42
43
  {name}
43
- </Link>
44
+ </a>
44
45
  )}
45
46
  </div>
46
47
  );
@@ -1,6 +1,6 @@
1
1
  import storeConfig from "discovery.config";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import {
6
6
  Dropdown,
@@ -30,13 +30,13 @@ export const OrgUnitDetailsNavbar = ({
30
30
  }: OrgUnitDetailsNavbarProps) => {
31
31
  return (
32
32
  <header data-fs-bp-org-unit-details-navbar>
33
- <Link href="/" data-fs-bp-org-unit-details-navbar-link>
33
+ <a href="/" data-fs-bp-org-unit-details-navbar-link>
34
34
  {storeConfig.seo.title}
35
- </Link>
35
+ </a>
36
36
  <div data-fs-bp-org-unit-details-navbar-actions>
37
- <Link href="/" data-fs-bp-org-unit-details-navbar-start-shopping-link>
37
+ <a href="/" data-fs-bp-org-unit-details-navbar-start-shopping-link>
38
38
  Start shopping
39
- </Link>
39
+ </a>
40
40
  {loading ? (
41
41
  <Skeleton size={{ width: "2.5rem", height: "2.5rem" }} />
42
42
  ) : (
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import { Button } from "@faststore/ui";
6
6
 
@@ -35,9 +35,9 @@ export const ProfileSummary = ({
35
35
  <div data-fs-self-profile-summary-header>
36
36
  <h2 data-fs-self-profile-summary-org-name>{orgName}</h2>
37
37
  {showManageLink && (
38
- <Link data-fs-self-profile-summary-org-link href="/buyer-portal">
38
+ <a data-fs-self-profile-summary-org-link href="/buyer-portal">
39
39
  Manage Organization <Icon name="OpenInNew" width={23} height={23} />
40
- </Link>
40
+ </a>
41
41
  )}
42
42
  </div>
43
43
  <div data-fs-self-profile-summary-person-actions>
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
  import { useRouter } from "next/router";
5
5
 
6
6
  import { Icon as UIIcon, IconButton } from "@faststore/ui";
@@ -58,9 +58,9 @@ export const BasicCard = ({
58
58
  <CardBody>{children}</CardBody>
59
59
  {enableFooter && (
60
60
  <CardFooter>
61
- <Link href={footerLink} data-fs-card-footer-link>
61
+ <a href={footerLink} data-fs-card-footer-link>
62
62
  <span>{footerMessage}</span>
63
- </Link>
63
+ </a>
64
64
  <IconButton
65
65
  onClick={() => {
66
66
  push(footerLink);
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import { Dropdown } from "@faststore/ui";
6
6
 
@@ -21,7 +21,7 @@ export const BasicCardLine = ({
21
21
  }: BasicCardLineProps) => {
22
22
  return (
23
23
  <span data-fs-basic-card-line title={title}>
24
- {href && <Link data-fs-basic-card-line-link href={href} />}
24
+ {href && <a data-fs-basic-card-line-link href={href} />}
25
25
  {!!icon && <span data-fs-basic-card-line-icon>{icon}</span>}
26
26
  <span data-fs-basic-card-line-title>{title}</span>
27
27
  {!!menu && (
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import { Icon } from "../Icon";
6
6
 
@@ -21,9 +21,9 @@ export const HeaderInside = ({
21
21
  return (
22
22
  <header data-fs-bp-header-inside {...otherProps}>
23
23
  {!!backLink && (
24
- <Link href={backLink} data-fs-bp-back-link>
24
+ <a href={backLink} data-fs-bp-back-link>
25
25
  <Icon name="Back" />
26
- </Link>
26
+ </a>
27
27
  )}
28
28
  {title && <h1 data-fs-bp-header-inside-title>{title}</h1>}
29
29
  {children}
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { HTMLAttributes } from "react";
3
3
 
4
- import Link from "next/link";
4
+ // import Link from "next/link";
5
5
 
6
6
  import { IconButton, Dropdown, DropdownButton } from "@faststore/ui";
7
7
 
@@ -32,7 +32,7 @@ export const InternalTopBar = ({
32
32
  return (
33
33
  <header data-fs-buyer-portal-topbar>
34
34
  <div data-fs-buyer-portal-topbar-navigation>
35
- <Link href={href}>
35
+ <a href={href}>
36
36
  <IconButton
37
37
  size="small"
38
38
  icon={
@@ -45,7 +45,7 @@ export const InternalTopBar = ({
45
45
  }
46
46
  aria-label={"topbar goback"}
47
47
  />
48
- </Link>
48
+ </a>
49
49
  <div data-fs-internal-topbar-icon-wrapper>
50
50
  <Icon name="StarFolder" height={24} width={24} />
51
51
  </div>
@@ -0,0 +1,22 @@
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
+ };
@@ -0,0 +1 @@
1
+ export { PageLoader } from "./PageLoader";
@@ -0,0 +1,22 @@
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,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
  import { useRouter } from "next/router";
5
5
 
6
6
  export type VerticalNavLinkProps = {
@@ -24,13 +24,13 @@ export const VerticalNavLink = ({ link, children }: VerticalNavLinkProps) => {
24
24
 
25
25
  return (
26
26
  <li key={link} data-fs-vertical-nav-menu-item>
27
- <Link
27
+ <a
28
28
  href={link}
29
29
  data-fs-vertical-nav-menu-link
30
30
  data-fs-vertical-nav-menu-link-is-active={isActive}
31
31
  >
32
32
  {children}
33
- </Link>
33
+ </a>
34
34
  </li>
35
35
  );
36
36
  };
@@ -76,3 +76,4 @@ 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";
@@ -1,6 +1,6 @@
1
1
  import storeConfig from "discovery.config";
2
2
 
3
- import Link from "next/link";
3
+ // import Link from "next/link";
4
4
 
5
5
  import { BasicDrawer, type BasicDrawerProps } from "../../../components";
6
6
  import { SidebarMenu } from "../SidebarMenu";
@@ -26,9 +26,9 @@ export const SidebarDrawer = ({
26
26
  >
27
27
  <BasicDrawer.Heading
28
28
  title={
29
- <Link href="/" data-fs-bp-org-unit-details-navbar-link>
29
+ <a href="/" data-fs-bp-org-unit-details-navbar-link>
30
30
  {storeConfig.seo.title}
31
- </Link>
31
+ </a>
32
32
  }
33
33
  onClose={close}
34
34
  />
@@ -1,5 +1,6 @@
1
1
  import { ClientContext } from "../utils";
2
2
 
3
3
  export type AuthRouteProps<T> =
4
- | { authorized: true; data: T; clientContext: ClientContext }
5
- | { authorized: false };
4
+ | { authorized: true; data: T; clientContext: ClientContext; loading: false }
5
+ | { authorized: false; loading: false }
6
+ | { authorized: false; loading: true };
@@ -2,10 +2,13 @@ import { useEffect, type ComponentType } from "react";
2
2
 
3
3
  import { useRouter } from "next/router";
4
4
 
5
+ import { PageLoader } from "../components";
6
+
5
7
  import type { AuthRouteProps } from "../types";
6
8
 
7
9
  /**
8
- * HOC that checks only for authorization
10
+ * HOC that checks only for authorization.
11
+ * Now handles loading state for pre-fetch scenarios.
9
12
  */
10
13
  export const withAuth = <T extends Record<string, unknown>>(
11
14
  Component: ComponentType<T>
@@ -14,13 +17,18 @@ export const withAuth = <T extends Record<string, unknown>>(
14
17
  const router = useRouter();
15
18
 
16
19
  useEffect(() => {
17
- // If not authorized, reload the page
18
- if (!props?.authorized) {
20
+ // If not authorized and not loading, reload the page
21
+ if (!props?.authorized && !props?.loading) {
19
22
  router.reload();
20
23
  }
21
- }, [props?.authorized, router]);
24
+ }, [props?.authorized, props?.loading, router]);
25
+
26
+ // If loading (cookie not yet available), show skeleton
27
+ if (props?.loading) {
28
+ return <PageLoader />;
29
+ }
22
30
 
23
- // If not authorized, render nothing
31
+ // If not authorized and not loading, render nothing
24
32
  if (!props?.authorized) {
25
33
  return null;
26
34
  }
@@ -1,5 +1,6 @@
1
1
  import { validateAccessService } from "../services";
2
2
 
3
+ import { getAuthCookie } from "./cookie";
3
4
  import { ClientContext, getClientContext } from "./getClientContext";
4
5
 
5
6
  import type { AuthRouteProps } from "../types";
@@ -8,12 +9,24 @@ import type { LoaderData } from "../types";
8
9
  /**
9
10
  * Utility function to encapsulate access validation in loaders.
10
11
  * 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.
11
14
  */
12
15
  export const withAuthLoader = async <T>(
13
16
  data: LoaderData,
14
17
  dataLoader: (clientContext: ClientContext) => Promise<T>
15
18
  ): Promise<AuthRouteProps<T>> => {
16
19
  try {
20
+ // Check if auth cookie exists before proceeding
21
+ const authCookie = getAuthCookie(data);
22
+
23
+ if (!authCookie || authCookie === "") {
24
+ return {
25
+ authorized: false,
26
+ loading: true,
27
+ };
28
+ }
29
+
17
30
  const { cookie, userId, ...clientContext } = await getClientContext(data);
18
31
 
19
32
  const hasAccess = await validateAccessService({ cookie });
@@ -24,6 +37,7 @@ export const withAuthLoader = async <T>(
24
37
 
25
38
  return {
26
39
  authorized: false,
40
+ loading: false,
27
41
  };
28
42
  }
29
43
 
@@ -33,6 +47,7 @@ export const withAuthLoader = async <T>(
33
47
  authorized: true,
34
48
  data: pageData,
35
49
  clientContext: { cookie, userId, ...clientContext },
50
+ loading: false,
36
51
  };
37
52
  } catch (error) {
38
53
  console.error("Auth validation failed:", error);
@@ -42,6 +57,7 @@ export const withAuthLoader = async <T>(
42
57
 
43
58
  return {
44
59
  authorized: false,
60
+ loading: false,
45
61
  };
46
62
  }
47
63
  };
@@ -2,13 +2,14 @@ import { useEffect, type ComponentType } from "react";
2
2
 
3
3
  import { useRouter } from "next/router";
4
4
 
5
- import { BuyerPortalProvider } from "../components";
5
+ import { BuyerPortalProvider, PageLoader } from "../components";
6
6
 
7
7
  import type { AuthRouteProps } from "../types";
8
8
  import type { ClientContext } from "./getClientContext";
9
9
 
10
10
  /**
11
- * HOC que encapsula lógica de autenticação e provider
11
+ * HOC que encapsula lógica de autenticação e provider.
12
+ * Now handles loading state for pre-fetch scenarios.
12
13
  */
13
14
  export const withAuthProvider = <T,>(
14
15
  Component: ComponentType<T & { clientContext: ClientContext }>
@@ -17,13 +18,18 @@ export const withAuthProvider = <T,>(
17
18
  const router = useRouter();
18
19
 
19
20
  useEffect(() => {
20
- // Se não está autorizado, recarrega a página
21
- if (!props?.authorized) {
21
+ // Se não está autorizado e não está carregando, recarrega a página
22
+ if (!props?.authorized && !props?.loading) {
22
23
  router.reload();
23
24
  }
24
- }, [props?.authorized, router]);
25
+ }, [props?.authorized, props?.loading, router]);
25
26
 
26
- // Se não está autorizado, não renderiza nada
27
+ // Se está carregando (cookie ainda não disponível), mostra skeleton
28
+ if (props?.loading) {
29
+ return <PageLoader />;
30
+ }
31
+
32
+ // Se não está autorizado e não está carregando, não renderiza nada
27
33
  if (!props?.authorized) {
28
34
  return null;
29
35
  }
@@ -1,4 +1,5 @@
1
1
  @import "../features/profile/components/ProfileSummary/profile-summary.scss";
2
+ @import "../features/shared/components/PageLoader/page-loader.scss";
2
3
  @import "./layouts";
3
4
 
4
5
  // ----------------------------------------------------------