@vtex/faststore-plugin-buyer-portal 1.1.114 → 1.1.116-poc

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.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/src/features/addresses/layouts/AddressDetailsLayout/AddressDetailsLayout.tsx +1 -0
  3. package/src/features/addresses/layouts/AddressesLayout/AddressesLayout.tsx +1 -0
  4. package/src/features/budgets/layouts/BudgetsDetailsLayout/BudgetsDetailsLayout.tsx +1 -0
  5. package/src/features/collections/layouts/CollectionsLayout/CollectionsLayout.tsx +1 -0
  6. package/src/features/contracts/hooks/index.ts +2 -0
  7. package/src/features/contracts/hooks/useContractDetails.ts +22 -0
  8. package/src/features/contracts/hooks/useContractsByOrgUnitId.ts +20 -0
  9. package/src/features/credit-cards/layouts/CreditCardsLayout/CreditCardLayout.tsx +1 -0
  10. package/src/features/custom-fields/layouts/CustomFieldsLayout/CustomFieldsLayout.tsx +1 -0
  11. package/src/features/org-units/hooks/index.ts +1 -0
  12. package/src/features/org-units/hooks/useOrgUnitBasicData.ts +20 -0
  13. package/src/features/org-units/layouts/OrgUnitDetailsLayout/OrgUnitDetailsLayout.tsx +31 -28
  14. package/src/features/payment-methods/layouts/PaymentMethodsLayout/PaymentMethodsLayout.tsx +1 -0
  15. package/src/features/profile/layouts/ProfileLayout/ProfileLayout.tsx +33 -16
  16. package/src/features/shared/components/withErrorBoundary/withErrorBoundary.tsx +6 -7
  17. package/src/features/shared/layouts/ContractTabsLayout/ContractTabsLayout.tsx +6 -4
  18. package/src/features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout.tsx +1 -0
  19. package/src/features/shared/layouts/LoadingTabsLayout/LoadingTabsLayout.tsx +2 -27
  20. package/src/features/shared/types/AuthRouteProps.ts +2 -3
  21. package/src/features/shared/utils/withAuth.tsx +7 -45
  22. package/src/features/shared/utils/withAuthLoader.ts +0 -28
  23. package/src/features/shared/utils/withAuthProvider.tsx +9 -59
  24. package/src/pages/org-unit-details.tsx +9 -61
  25. package/src/pages/profile.tsx +28 -71
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.1.114",
3
+ "version": "1.1.116-poc",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -124,6 +124,7 @@ export const AddressDetailsLayout = ({
124
124
  image: undefined,
125
125
  name: user?.name ?? "",
126
126
  role: user?.role ?? "",
127
+ id: user?.id ?? "",
127
128
  }}
128
129
  >
129
130
  <section data-fs-address-details-section>
@@ -114,6 +114,7 @@ export const AddressLayout = ({
114
114
  image: undefined,
115
115
  name: user?.name ?? "",
116
116
  role: user?.role ?? "",
117
+ id: user?.id ?? "",
117
118
  }}
118
119
  >
119
120
  <section data-fs-addresses-section>
@@ -151,6 +151,7 @@ export const BudgetsDetailsLayout = ({ budget }: BudgetsDetailsLayoutProps) => {
151
151
  image: undefined,
152
152
  name: user?.name ?? "",
153
153
  role: user?.role ?? "",
154
+ id: user?.id ?? "",
154
155
  }}
155
156
  >
156
157
  <div data-fs-bp-budgets-details-layout>
@@ -72,6 +72,7 @@ export const CollectionsLayout = ({
72
72
  image: undefined,
73
73
  name: user?.name ?? "",
74
74
  role: user?.role ?? "",
75
+ id: user?.id ?? "",
75
76
  }}
76
77
  >
77
78
  <section data-fs-bp-collections-container>
@@ -1 +1,3 @@
1
1
  export { useUpdateContractStatus } from "./useUpdateContractStatus";
2
+ export { useContractsByOrgUnitId } from "./useContractsByOrgUnitId";
3
+ export { useContractDetails } from "./useContractDetails";
@@ -0,0 +1,22 @@
1
+ import { type QueryOptions, useQuery } from "../../shared/hooks";
2
+ import { getContractDetailsService } from "../services";
3
+
4
+ export const useContractDetails = (
5
+ contractId: string,
6
+ unitId: string,
7
+ options?: QueryOptions<AwaitedType<typeof getContractDetailsService>>
8
+ ) => {
9
+ const { data, error, isLoading, refetch } = useQuery(
10
+ `api/contract-details/${unitId}`,
11
+ ({ cookie }) =>
12
+ getContractDetailsService({ contractId, cookie, unitId: unitId }),
13
+ options
14
+ );
15
+
16
+ return {
17
+ contract: data,
18
+ hasContractError: error,
19
+ isContractLoading: isLoading,
20
+ refetchContract: refetch,
21
+ };
22
+ };
@@ -0,0 +1,20 @@
1
+ import { type QueryOptions, useQuery } from "../../shared/hooks";
2
+ import { getContractsByOrgUnitIdService } from "../services";
3
+
4
+ export const useContractsByOrgUnitId = (
5
+ orgUnitId: string,
6
+ options?: QueryOptions<AwaitedType<typeof getContractsByOrgUnitIdService>>
7
+ ) => {
8
+ const { data, error, isLoading, refetch } = useQuery(
9
+ `api/contracts-by-org-unit-id/${orgUnitId}`,
10
+ ({ cookie }) => getContractsByOrgUnitIdService({ orgUnitId, cookie }),
11
+ options
12
+ );
13
+
14
+ return {
15
+ contracts: data ?? [],
16
+ hasContractsError: error,
17
+ isContractsLoading: isLoading,
18
+ refetchContracts: refetch,
19
+ };
20
+ };
@@ -126,6 +126,7 @@ export const CreditCardLayout = ({ data }: CreditCardsLayoutProps) => {
126
126
  image: undefined,
127
127
  name: user?.name ?? "",
128
128
  role: user?.role ?? "",
129
+ id: user?.id ?? "",
129
130
  }}
130
131
  >
131
132
  <section data-fs-credit-card-section>
@@ -214,6 +214,7 @@ export const CustomFieldsLayout = ({
214
214
  image: undefined,
215
215
  name: user?.name ?? "",
216
216
  role: user?.role ?? "",
217
+ id: user?.id ?? "",
217
218
  }}
218
219
  >
219
220
  <section data-fs-bp-custom-field-container>
@@ -5,3 +5,4 @@ export { useUpdateOrgUnit } from "./useUpdateOrgUnit";
5
5
  export { useChildrenOrgUnits } from "./useChildrenOrgUnits";
6
6
  export { useOrgUnitByUser } from "./useOrgUnitByUser";
7
7
  export { useSearchOrgUnits } from "./useSearchOrgUnits";
8
+ export { useOrgUnitBasicData } from "./useOrgUnitBasicData";
@@ -0,0 +1,20 @@
1
+ import { type QueryOptions, useQuery } from "../../shared/hooks";
2
+ import { getOrgUnitBasicDataService } from "../services";
3
+
4
+ export const useOrgUnitBasicData = (
5
+ id: string,
6
+ options?: QueryOptions<AwaitedType<typeof getOrgUnitBasicDataService>>
7
+ ) => {
8
+ const { data, error, isLoading, refetch } = useQuery(
9
+ `api/org-unit-basic-data/${id}`,
10
+ ({ cookie }) => getOrgUnitBasicDataService({ id, cookie }),
11
+ options
12
+ );
13
+
14
+ return {
15
+ orgUnit: data,
16
+ hasOrgUnitError: error,
17
+ isOrgUnitLoading: isLoading,
18
+ refetchOrgUnit: refetch,
19
+ };
20
+ };
@@ -5,6 +5,7 @@ import {
5
5
  Skeleton,
6
6
  } from "@faststore/ui";
7
7
 
8
+ import { useContractsByOrgUnitId } from "../../../contracts/hooks";
8
9
  import {
9
10
  BasicCard,
10
11
  BasicDropdownMenu,
@@ -21,13 +22,14 @@ import {
21
22
  getOrganizationSettingsLinks,
22
23
  } from "../../../shared/utils";
23
24
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
25
+ import { useGetUserById } from "../../../users/hooks";
24
26
  import {
25
27
  AddAllToOrgUnitDropdown,
26
28
  OrgUnitBreadcrumb,
27
29
  OrgUnitDetailsNavbar,
28
30
  OrgUnitsDropdownMenu,
29
31
  } from "../../components";
30
- import { useOrgUnitByUser } from "../../hooks";
32
+ import { useOrgUnitBasicData, useOrgUnitByUser } from "../../hooks";
31
33
 
32
34
  import {
33
35
  MAX_DESKTOP_BREADCRUMB_ITEMS,
@@ -35,28 +37,29 @@ import {
35
37
  QUERY_TABLET,
36
38
  } from "./utils";
37
39
 
38
- import type { ContractData } from "../../../contracts/types";
39
- import type { UserData } from "../../../users/types";
40
- import type { OrgUnitBasicData } from "../../types";
41
-
42
40
  export type OrgUnitsDetailsLayoutProps = {
43
- data: {
44
- orgUnit: OrgUnitBasicData;
45
- contracts: ContractData[];
46
- user: UserData | null;
47
- };
41
+ orgUnitId: string;
42
+ userId: string;
48
43
  loading?: boolean;
49
44
  };
50
45
 
51
46
  export const OrgUnitsDetailsLayout = ({
52
- data: { orgUnit, contracts, user },
53
- loading = false,
47
+ orgUnitId,
48
+ userId,
49
+ loading = true,
54
50
  }: OrgUnitsDetailsLayoutProps) => {
51
+ const { orgUnit, isOrgUnitLoading } = useOrgUnitBasicData(orgUnitId);
52
+
53
+ const { contracts, isContractsLoading } = useContractsByOrgUnitId(orgUnitId);
54
+
55
+ const { user, isUserLoading } = useGetUserById({ orgUnitId, userId });
56
+
57
+ loading = isOrgUnitLoading || isContractsLoading || isUserLoading;
55
58
  const isMobile = useMediaQuery(QUERY_TABLET);
56
59
 
57
60
  const isSingleContract = contracts.length <= 1;
58
61
 
59
- const { orgUnit: userUnit } = useOrgUnitByUser(user?.id ?? "");
62
+ const { orgUnit: userUnit } = useOrgUnitByUser(userId);
60
63
 
61
64
  const orgUnitList = orgUnit?.path.ids.split("/").slice(1) ?? [];
62
65
 
@@ -67,7 +70,7 @@ export const OrgUnitsDetailsLayout = ({
67
70
  const breadcrumbList = orgUnitList.map((orgUnitId, index) => ({
68
71
  unitId: orgUnitId,
69
72
  item: buyerPortalRoutes.orgUnitDetails({ orgUnitId }),
70
- name: orgUnit.path.names.split("/")[index],
73
+ name: orgUnit?.path.names.split("/")[index] ?? "",
71
74
  position: index + 1,
72
75
  enabled: index >= userUnitIndex,
73
76
  }));
@@ -76,7 +79,7 @@ export const OrgUnitsDetailsLayout = ({
76
79
  <GlobalLayout>
77
80
  {/* TODO: Add person here */}
78
81
  <OrgUnitDetailsNavbar
79
- orgName={orgUnit.name}
82
+ orgName={orgUnit?.name ?? ""}
80
83
  person={{
81
84
  name: user?.name ?? "",
82
85
  role: user?.roles?.[0] ?? "",
@@ -107,8 +110,8 @@ export const OrgUnitsDetailsLayout = ({
107
110
  <BasicDropdownMenu.Trigger />
108
111
  <OrgUnitsDropdownMenu
109
112
  isComplete={false}
110
- id={orgUnit.id}
111
- name={orgUnit.name}
113
+ id={orgUnitId}
114
+ name={orgUnit?.name ?? ""}
112
115
  />
113
116
  </Dropdown>
114
117
  <Dropdown>
@@ -118,8 +121,8 @@ export const OrgUnitsDetailsLayout = ({
118
121
  <AddAllToOrgUnitDropdown
119
122
  isSingleContract={isSingleContract}
120
123
  contractId={contracts[0]?.id}
121
- unitId={orgUnit?.id}
122
- unitName={orgUnit?.name}
124
+ unitId={orgUnitId}
125
+ unitName={orgUnit?.name ?? ""}
123
126
  />
124
127
  </Dropdown>
125
128
  </HeaderInside>
@@ -129,13 +132,13 @@ export const OrgUnitsDetailsLayout = ({
129
132
  data-fs-bp-contracts-settings-card
130
133
  footerMessage="Manage contract settings"
131
134
  footerLink={buyerPortalRoutes.profileDetails({
132
- orgUnitId: orgUnit.id,
135
+ orgUnitId: orgUnitId,
133
136
  contractId: contracts[0]?.id ?? "",
134
137
  })}
135
138
  enableFooter
136
139
  >
137
- <LetterHighlight letter={orgUnit.name[0]} />
138
- <VerticalNav.Menu title={orgUnit.name} loading={loading}>
140
+ <LetterHighlight letter={orgUnit?.name?.[0] ?? ""} />
141
+ <VerticalNav.Menu title={orgUnit?.name ?? ""} loading={loading}>
139
142
  {loading
140
143
  ? Array(8)
141
144
  .fill(null)
@@ -150,7 +153,7 @@ export const OrgUnitsDetailsLayout = ({
150
153
  </li>
151
154
  ))
152
155
  : getContractSettingsLinks({
153
- orgUnitId: orgUnit.id,
156
+ orgUnitId: orgUnitId,
154
157
  contractId: contracts[0]?.id ?? "",
155
158
  }).map(({ name, link }) => (
156
159
  <VerticalNav.Link key={name} link={link}>
@@ -177,7 +180,7 @@ export const OrgUnitsDetailsLayout = ({
177
180
  />
178
181
  }
179
182
  href={buyerPortalRoutes.profileDetails({
180
- orgUnitId: orgUnit.id,
183
+ orgUnitId: orgUnitId,
181
184
  contractId: contract.id,
182
185
  })}
183
186
  menu={
@@ -196,12 +199,12 @@ export const OrgUnitsDetailsLayout = ({
196
199
  data-fs-bp-organizations-settings-card
197
200
  footerMessage="Manage organization settings"
198
201
  footerLink={buyerPortalRoutes.users({
199
- orgUnitId: orgUnit.id,
202
+ orgUnitId: orgUnitId,
200
203
  })}
201
204
  enableFooter
202
205
  >
203
206
  <VerticalNav.Menu title="Organization">
204
- {getOrganizationSettingsLinks(orgUnit.id).map((option) => (
207
+ {getOrganizationSettingsLinks(orgUnitId).map((option) => (
205
208
  <VerticalNav.Link key={option.name} link={option.link}>
206
209
  {option.name}
207
210
  </VerticalNav.Link>
@@ -212,14 +215,14 @@ export const OrgUnitsDetailsLayout = ({
212
215
  data-fs-bp-compliance-settings-card
213
216
  footerMessage="Manage finance and compliance settings"
214
217
  footerLink={buyerPortalRoutes.buyingPolicies({
215
- orgUnitId: orgUnit.id,
218
+ orgUnitId: orgUnitId,
216
219
  contractId: contracts[0]?.id,
217
220
  })}
218
221
  enableFooter
219
222
  >
220
223
  <VerticalNav.Menu title="Finance and Compliance">
221
224
  {getFinanceSettingsLinks({
222
- orgUnitId: orgUnit.id,
225
+ orgUnitId: orgUnitId,
223
226
  contractId: contracts[0]?.id ?? "",
224
227
  }).map((option) => (
225
228
  <VerticalNav.Link key={option.name} link={option.link}>
@@ -168,6 +168,7 @@ export const PaymentMethodsLayout = ({
168
168
  image: undefined,
169
169
  name: user?.name ?? "",
170
170
  role: user?.role ?? "",
171
+ id: user?.id ?? "",
171
172
  }}
172
173
  >
173
174
  <section data-fs-payment-methods-section>
@@ -1,18 +1,33 @@
1
+ import { useContractDetails } from "../../../contracts/hooks";
2
+ import { useOrgUnitBasicData } from "../../../org-units/hooks";
1
3
  import { HeaderInside } from "../../../shared/components";
2
- import { useBuyerPortal } from "../../../shared/hooks";
3
4
  import { GlobalLayout, ContractTabsLayout } from "../../../shared/layouts";
4
-
5
- import type { ContractData } from "../../../contracts/types";
5
+ import { useGetUserById } from "../../../users/hooks";
6
6
 
7
7
  export type ProfileLayoutProps = {
8
- data: ContractData | null;
8
+ orgUnitId: string;
9
+ contractId: string;
10
+ userId: string;
9
11
  };
10
12
 
11
- export const ProfileLayout = ({ data }: ProfileLayoutProps) => {
12
- const { currentOrgUnit: orgUnit, currentUser: user } = useBuyerPortal();
13
+ export const ProfileLayout = ({
14
+ orgUnitId,
15
+ contractId,
16
+ userId,
17
+ }: ProfileLayoutProps) => {
18
+ const { orgUnit, isOrgUnitLoading } = useOrgUnitBasicData(orgUnitId);
19
+
20
+ const { contract, isContractLoading } = useContractDetails(
21
+ contractId,
22
+ orgUnitId
23
+ );
24
+
25
+ const { user, isUserLoading } = useGetUserById({ orgUnitId, userId });
26
+
27
+ const loading = isContractLoading || isOrgUnitLoading || isUserLoading;
13
28
 
14
- const creationDate = data
15
- ? new Date(data.creationDate).toLocaleDateString("en-US", {
29
+ const creationDate = contract
30
+ ? new Date(contract.creationDate).toLocaleDateString("en-US", {
16
31
  year: "numeric",
17
32
  month: "long",
18
33
  day: "numeric",
@@ -23,30 +38,32 @@ export const ProfileLayout = ({ data }: ProfileLayoutProps) => {
23
38
  <GlobalLayout>
24
39
  <ContractTabsLayout
25
40
  orgUnitName={orgUnit?.name ?? ""}
26
- orgUnitId={orgUnit?.id ?? ""}
27
- contractName={data?.name ?? ""}
28
- contractId={data?.id ?? ""}
41
+ orgUnitId={orgUnitId}
42
+ contractName={contract?.name ?? ""}
43
+ contractId={contract?.id ?? ""}
29
44
  pageName="Contract"
30
45
  person={{
31
46
  image: undefined,
32
47
  name: user?.name ?? "",
33
- role: user?.role ?? "",
48
+ role: user?.roles?.[0] ?? "",
49
+ id: userId,
34
50
  }}
51
+ loading={loading}
35
52
  >
36
53
  <section data-fs-bp-profile>
37
54
  <HeaderInside title="Profile" />
38
55
 
39
- {data && (
56
+ {contract && (
40
57
  <div data-fs-bp-profile-details>
41
58
  <span data-fs-bp-profile-details-title>Details</span>
42
59
 
43
- {!!data.name && (
60
+ {!!contract.name && (
44
61
  <>
45
62
  <hr data-fs-bp-profile-divider />
46
63
  <div data-fs-bp-profile-details-row>
47
64
  <span data-fs-bp-profile-details-row-label>Name</span>
48
65
  <span data-fs-bp-profile-details-row-value>
49
- {data.name}
66
+ {contract.name}
50
67
  </span>
51
68
  </div>
52
69
  </>
@@ -57,7 +74,7 @@ export const ProfileLayout = ({ data }: ProfileLayoutProps) => {
57
74
  <div data-fs-bp-profile-details-row>
58
75
  <span data-fs-bp-profile-details-row-label>Email</span>
59
76
  <span data-fs-bp-profile-details-row-value>
60
- <a href={`mailto:${data.email}`}>{data.email}</a>
77
+ <a href={`mailto:${contract.email}`}>{contract.email}</a>
61
78
  </span>
62
79
  </div>
63
80
 
@@ -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,15 @@ 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
+ options: WithErrorBoundaryOptions = {}
20
19
  ) {
21
- // const { onError, tags } = options;
20
+ const { onError, tags } = options;
22
21
 
23
22
  const ComponentWithErrorBoundary = (props: P) => {
24
23
  return (
25
- // <ErrorBoundary onError={onError} tags={tags}>
26
- <WrappedComponent {...props} />
27
- // </ErrorBoundary>
24
+ <ErrorBoundary onError={onError} tags={tags}>
25
+ <WrappedComponent {...props} />
26
+ </ErrorBoundary>
28
27
  );
29
28
  };
30
29
 
@@ -18,6 +18,7 @@ export type ContractTabsLayoutProps = {
18
18
  image?: ReactNode;
19
19
  name: string;
20
20
  role?: string;
21
+ id: string;
21
22
  };
22
23
  loading?: boolean;
23
24
  children?: ReactNode;
@@ -29,8 +30,9 @@ export const ContractTabsLayout = ({
29
30
  children,
30
31
  orgUnitId,
31
32
  contractId,
33
+ person,
32
34
  }: ContractTabsLayoutProps) => {
33
- const { currentOrgUnit, currentUser, currentContract } = useBuyerPortal();
35
+ const { currentOrgUnit, currentContract } = useBuyerPortal();
34
36
 
35
37
  const verticalLinks = getContractSettingsLinks({
36
38
  contractId: currentContract?.id ?? contractId ?? "",
@@ -43,9 +45,9 @@ export const ContractTabsLayout = ({
43
45
  orgUnit={currentOrgUnit}
44
46
  pageName={pageName}
45
47
  person={{
46
- name: currentUser?.name ?? "",
47
- role: currentUser?.role ?? "",
48
- id: currentUser?.id ?? "",
48
+ name: person.name,
49
+ role: person.role,
50
+ id: person.id,
49
51
  }}
50
52
  loading={loading}
51
53
  />
@@ -82,6 +82,7 @@ export const ErrorTabsLayout = ({ children, error }: ErrorTabsLayoutProps) => {
82
82
  image: undefined,
83
83
  name: user?.name ?? "",
84
84
  role: user?.role ?? "",
85
+ id: user?.id ?? "",
85
86
  }}
86
87
  >
87
88
  <ErrorTabsLayoutContent title={pageTitle} error={error} />
@@ -78,6 +78,7 @@ export const LoadingTabsLayout = ({ children }: LoadingTabsLayoutProps) => {
78
78
  image: undefined,
79
79
  name: "",
80
80
  role: "",
81
+ id: "",
81
82
  }}
82
83
  loading={true}
83
84
  >
@@ -100,33 +101,7 @@ export const LoadingTabsLayout = ({ children }: LoadingTabsLayoutProps) => {
100
101
  );
101
102
 
102
103
  case "OrgUnitDetailsLayout":
103
- return (
104
- <OrgUnitsDetailsLayout
105
- loading={true}
106
- data={{
107
- contracts: [],
108
- orgUnit: {
109
- contractMode: "Single",
110
- customerGroup: { customerIds: [] },
111
- id: routeParams.orgUnitId,
112
- name: "",
113
- path: {
114
- ids: "",
115
- names: "",
116
- },
117
- },
118
- user: {
119
- name: "",
120
- roles: [],
121
- id: "",
122
- orgUnit: {
123
- id: routeParams.orgUnitId,
124
- name: "",
125
- },
126
- },
127
- }}
128
- />
129
- );
104
+ return <OrgUnitsDetailsLayout loading={true} orgUnitId="" userId="" />;
130
105
 
131
106
  default:
132
107
  return <LoadingTabsLayoutContent />;
@@ -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,57 +8,31 @@ 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
38
  console.error("[withAuthLoader] Auth validation failed:", error);
@@ -69,7 +42,6 @@ export const withAuthLoader = async <T>(
69
42
 
70
43
  return {
71
44
  authorized: false,
72
- loading: false,
73
45
  };
74
46
  }
75
47
  };
@@ -1,84 +1,34 @@
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
 
31
+ // Se autorizado, renderiza o componente envolvido no provider
82
32
  return (
83
33
  <BuyerPortalProvider clientContext={props?.clientContext}>
84
34
  <Component {...props.data} clientContext={props?.clientContext} />
@@ -1,49 +1,25 @@
1
- import { getContractsByOrgUnitIdService } from "../features/contracts/services";
2
1
  import { OrgUnitsDetailsLayout } from "../features/org-units/layouts";
3
- import { getOrgUnitBasicDataService } from "../features/org-units/services";
4
2
  import { withErrorBoundary } from "../features/shared/components";
5
- import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
6
- import {
7
- type ClientContext,
8
- withLoaderErrorBoundary,
9
- withAuthLoader,
10
- withProviders,
11
- } from "../features/shared/utils";
12
- import { getUserByIdService } from "../features/users/services";
3
+ import { withAuthLoader, withProviders } from "../features/shared/utils";
4
+ import { ClientContext } from "../features/shared/utils/getClientContext";
13
5
 
14
- import type { ContractData } from "../features/contracts/types";
15
- import type { OrgUnitBasicData } from "../features/org-units/types";
16
6
  import type { AuthRouteProps, LoaderData } from "../features/shared/types";
17
- import type { UserData } from "../features/users/types";
18
7
 
19
8
  export type OrgUnitDetailsPageData = {
20
9
  data: {
21
- orgUnit: OrgUnitBasicData;
22
- contracts: ContractData[];
23
- user: UserData | null;
10
+ orgUnitId: string;
11
+ userId: string;
24
12
  };
25
13
  context: {
26
14
  clientContext: ClientContext;
27
15
  };
28
- hasError?: boolean;
29
- error?: {
30
- error: {
31
- message: string;
32
- name: string;
33
- stack?: string;
34
- };
35
- tags: {
36
- component: string;
37
- errorType: string;
38
- };
39
- };
40
16
  };
41
17
 
42
18
  type OrgUnitDetailsPageQuery = {
43
19
  orgUnitId: string;
44
20
  };
45
21
 
46
- const loaderFunction = async (
22
+ export const loader = async (
47
23
  data: LoaderData<OrgUnitDetailsPageQuery>
48
24
  ): Promise<AuthRouteProps<OrgUnitDetailsPageData>> => {
49
25
  const { orgUnitId } = data.query;
@@ -53,23 +29,10 @@ const loaderFunction = async (
53
29
  }
54
30
 
55
31
  return withAuthLoader(data, async ({ cookie, userId, ...clientContext }) => {
56
- const orgUnit = await getOrgUnitBasicDataService({
57
- id: orgUnitId,
58
- cookie,
59
- });
60
-
61
- const user = await getUserByIdService({ orgUnitId, userId, cookie });
62
-
63
- const contracts = await getContractsByOrgUnitIdService({
64
- orgUnitId,
65
- cookie,
66
- });
67
-
68
32
  return {
69
33
  data: {
70
- contracts,
71
- orgUnit,
72
- user,
34
+ orgUnitId,
35
+ userId,
73
36
  },
74
37
  context: {
75
38
  clientContext: { cookie, userId, ...clientContext },
@@ -78,24 +41,9 @@ const loaderFunction = async (
78
41
  });
79
42
  };
80
43
 
81
- export const loader = withLoaderErrorBoundary(loaderFunction, {
82
- componentName: "OrgUnitDetailsPage",
83
- redirectToError: true,
84
- });
85
-
86
- const OrgUnitDetailsPage = ({
87
- data,
88
- hasError,
89
- error,
90
- }: OrgUnitDetailsPageData) => {
44
+ const OrgUnitDetailsPage = ({ data }: OrgUnitDetailsPageData) => {
91
45
  return (
92
- <>
93
- {hasError ? (
94
- <ErrorTabsLayout error={error} />
95
- ) : (
96
- <OrgUnitsDetailsLayout data={data} />
97
- )}
98
- </>
46
+ <OrgUnitsDetailsLayout orgUnitId={data.orgUnitId} userId={data.userId} />
99
47
  );
100
48
  };
101
49
 
@@ -1,98 +1,55 @@
1
- import { getContractDetailsService } from "../features/contracts/services";
2
- import { getOrgUnitBasicDataService } from "../features/org-units/services";
3
1
  import { ProfileLayout } from "../features/profile/layouts";
4
2
  import { withErrorBoundary } from "../features/shared/components";
5
- import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/types";
6
- import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
7
3
  import {
8
4
  type ClientContext,
9
- withLoaderErrorBoundary,
10
- withAuthLoader,
11
5
  withProviders,
6
+ withAuthLoader,
12
7
  } from "../features/shared/utils";
13
- import { getUserByIdService } from "../features/users/services";
14
8
 
15
- import type { ContractData } from "../features/contracts/types";
16
- import type { OrgUnitBasicData } from "../features/org-units/types";
17
9
  import type { AuthRouteProps, LoaderData } from "../features/shared/types";
18
- import type { UserData } from "../features/users/types";
19
10
 
20
11
  export type ProfilePageData = {
21
- data: ContractData | null;
12
+ data: {
13
+ contractId: string;
14
+ orgUnitId: string;
15
+ userId: string;
16
+ };
22
17
  context: {
23
18
  clientContext: ClientContext;
24
- currentOrgUnit: OrgUnitBasicData | null;
25
- currentContract: ContractData | null;
26
- currentUser: UserData | null;
27
19
  };
28
- hasError?: boolean;
29
- error?: ErrorBoundaryProps;
30
20
  };
31
21
 
32
22
  export type ProfilePageQuery = {
33
23
  contractId: string;
34
24
  orgUnitId: string;
25
+ userId: string;
35
26
  };
36
27
 
37
- export async function loaderFunction(
28
+ export const loader = async (
38
29
  data: LoaderData<ProfilePageQuery>
39
- ): Promise<AuthRouteProps<ProfilePageData>> {
30
+ ): Promise<AuthRouteProps<ProfilePageData>> => {
40
31
  const { contractId, orgUnitId } = data.query;
41
32
 
42
- return withAuthLoader(
43
- data,
44
- async ({ customerId, cookie, userId, ...clientContext }) => {
45
- if (!contractId || !orgUnitId) {
46
- return {
47
- data: null,
48
- context: {
49
- clientContext: { customerId, cookie, userId, ...clientContext },
50
- currentOrgUnit: null,
51
- currentContract: null,
52
- currentUser: null,
53
- },
54
- };
55
- }
56
-
57
- const [orgUnit, contract, user] = await Promise.all([
58
- getOrgUnitBasicDataService({
59
- cookie,
60
- id: orgUnitId,
61
- }),
62
- getContractDetailsService({
63
- contractId,
64
- cookie,
65
- unitId: orgUnitId,
66
- }),
67
- getUserByIdService({ orgUnitId, userId, cookie }),
68
- ]);
69
-
70
- return {
71
- data: contract,
72
- context: {
73
- clientContext: { customerId, cookie, userId, ...clientContext },
74
- currentOrgUnit: orgUnit,
75
- currentContract: contract,
76
- currentUser: user,
77
- },
78
- };
79
- }
80
- );
81
- }
82
-
83
- export const loader = withLoaderErrorBoundary(loaderFunction, {
84
- componentName: "ProfilePage",
85
- redirectToError: true,
86
- });
33
+ return withAuthLoader(data, async ({ cookie, userId, ...clientContext }) => {
34
+ return {
35
+ data: {
36
+ contractId,
37
+ orgUnitId,
38
+ userId,
39
+ },
40
+ context: {
41
+ clientContext: { cookie, userId, ...clientContext },
42
+ },
43
+ };
44
+ });
45
+ };
87
46
 
88
- const ProfilePage = ({ data, hasError, error }: ProfilePageData) => (
89
- <>
90
- {hasError ? (
91
- <ErrorTabsLayout error={error} />
92
- ) : (
93
- <ProfileLayout data={data} />
94
- )}
95
- </>
47
+ const ProfilePage = ({ data }: ProfilePageData) => (
48
+ <ProfileLayout
49
+ orgUnitId={data.orgUnitId}
50
+ contractId={data.contractId}
51
+ userId={data.userId}
52
+ />
96
53
  );
97
54
 
98
55
  export default withProviders(