@vtex/faststore-plugin-buyer-portal 1.1.92 → 1.1.93

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 (48) hide show
  1. package/package.json +1 -1
  2. package/public/buyer-portal-icons.svg +1 -1
  3. package/src/features/addresses/services/get-addresses-by-unit-id.service.ts +13 -1
  4. package/src/features/addresses/services/get-addresses.service.ts +15 -2
  5. package/src/features/buying-policies/services/get-buying-policies.service.ts +19 -21
  6. package/src/features/contracts/services/get-contract-details.service.ts +13 -1
  7. package/src/features/contracts/services/get-contracts-org-by-unit-id.service.ts +18 -11
  8. package/src/features/contracts/services/update-contract-status.service.ts +18 -11
  9. package/src/features/profile/layouts/ProfileLayout/profile-layout.scss +1 -0
  10. package/src/features/shared/clients/Client.ts +84 -8
  11. package/src/features/shared/components/BuyerPortalProvider/BuyerPortalProvider.tsx +4 -1
  12. package/src/features/shared/components/Error/Error.tsx +31 -0
  13. package/src/features/shared/components/Error/error.scss +71 -0
  14. package/src/features/shared/components/ErrorBoundary/ErrorBoundary.tsx +63 -0
  15. package/src/features/shared/components/ErrorBoundary/types.ts +14 -0
  16. package/src/features/shared/components/index.ts +3 -0
  17. package/src/features/shared/components/withErrorBoundary/withErrorBoundary.tsx +35 -0
  18. package/src/features/shared/layouts/BaseTabsLayout/SidebarMenu.tsx +9 -2
  19. package/src/features/shared/layouts/ContractTabsLayout/ContractTabsLayout.tsx +4 -2
  20. package/src/features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout.tsx +119 -0
  21. package/src/features/shared/layouts/ErrorTabsLayout/error-tabs-layout.scss +1 -0
  22. package/src/features/shared/utils/environment.ts +41 -0
  23. package/src/features/shared/utils/extractErrorMessage.ts +22 -0
  24. package/src/features/shared/utils/getHome.tsx +5 -0
  25. package/src/features/shared/utils/index.ts +2 -0
  26. package/src/features/shared/utils/withClientErrorBoundary.ts +61 -0
  27. package/src/features/shared/utils/withLoaderErrorBoundary.ts +46 -0
  28. package/src/features/users/clients/UsersClient.ts +0 -1
  29. package/src/pages/address-details.tsx +38 -11
  30. package/src/pages/addresses.tsx +35 -6
  31. package/src/pages/budgets-details.tsx +38 -8
  32. package/src/pages/budgets.tsx +33 -8
  33. package/src/pages/buying-policies.tsx +36 -12
  34. package/src/pages/buying-policy-details.tsx +38 -8
  35. package/src/pages/collections.tsx +36 -12
  36. package/src/pages/cost-centers.tsx +38 -8
  37. package/src/pages/credit-cards.tsx +38 -8
  38. package/src/pages/home.tsx +22 -5
  39. package/src/pages/org-unit-details.tsx +43 -7
  40. package/src/pages/org-units.tsx +39 -8
  41. package/src/pages/payment-methods.tsx +38 -8
  42. package/src/pages/po-numbers.tsx +38 -8
  43. package/src/pages/profile.tsx +31 -6
  44. package/src/pages/releases.tsx +33 -8
  45. package/src/pages/role-details.tsx +39 -7
  46. package/src/pages/roles.tsx +28 -7
  47. package/src/pages/user-details.tsx +39 -8
  48. package/src/pages/users.tsx +25 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.1.92",
3
+ "version": "1.1.93",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -537,4 +537,4 @@
537
537
  fill="currentColor"
538
538
  />
539
539
  </symbol>
540
- </svg>
540
+ </svg>
@@ -1,4 +1,8 @@
1
1
  import { compareItems, statusFilters } from "../../shared/utils";
2
+ import {
3
+ handleUnauthorizedRedirect,
4
+ withClientErrorBoundary,
5
+ } from "../../shared/utils/withClientErrorBoundary";
2
6
  import { addressesClient } from "../clients/AddressesClient";
3
7
 
4
8
  export type GetAddressesByUnitIdServiceProps = Partial<{
@@ -13,7 +17,7 @@ export type GetAddressesByUnitIdServiceProps = Partial<{
13
17
  cookie: string;
14
18
  };
15
19
 
16
- export const getAddressesByUnitIdService = async ({
20
+ export const getAddressesByUnitIdServiceFn = async ({
17
21
  orgUnitId,
18
22
  status,
19
23
  type,
@@ -46,3 +50,11 @@ export const getAddressesByUnitIdService = async ({
46
50
 
47
51
  return { data: formattedAddress, total };
48
52
  };
53
+
54
+ export const getAddressesByUnitIdService = withClientErrorBoundary(
55
+ getAddressesByUnitIdServiceFn,
56
+ {
57
+ componentName: "AddressesByUnitId",
58
+ onError: handleUnauthorizedRedirect,
59
+ }
60
+ );
@@ -1,4 +1,9 @@
1
- import { compareItems, statusFilters } from "../../shared/utils";
1
+ import {
2
+ compareItems,
3
+ statusFilters,
4
+ withClientErrorBoundary,
5
+ } from "../../shared/utils";
6
+ import { handleUnauthorizedRedirect } from "../../shared/utils/withClientErrorBoundary";
2
7
  import { addressesClient } from "../clients/AddressesClient";
3
8
 
4
9
  import type { AddressData } from "../types/AddressData";
@@ -15,7 +20,7 @@ export type GetAddressesServiceProps = Partial<{
15
20
  };
16
21
 
17
22
  //TODO: Check if deprecated so we can remove this
18
- export const getAddressesService = async ({
23
+ export const getAddressesServiceFn = async ({
19
24
  search = "",
20
25
  customerId,
21
26
  unitId,
@@ -54,3 +59,11 @@ export const getAddressesService = async ({
54
59
  })
55
60
  .sort((a, b) => compareItems(a, b, sort));
56
61
  };
62
+
63
+ export const getAddressesService = withClientErrorBoundary(
64
+ getAddressesServiceFn,
65
+ {
66
+ componentName: "AddressesService",
67
+ onError: handleUnauthorizedRedirect,
68
+ }
69
+ );
@@ -1,3 +1,7 @@
1
+ import {
2
+ withClientErrorBoundary,
3
+ handleUnauthorizedRedirect,
4
+ } from "../../shared/utils/withClientErrorBoundary";
1
5
  import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
2
6
  import { BuyingPolicy } from "../types";
3
7
 
@@ -9,7 +13,7 @@ export type GetBuyingPoliciesServiceProps = {
9
13
  page?: number;
10
14
  };
11
15
 
12
- export const getBuyingPoliciesService = async ({
16
+ const getBuyingPoliciesFunction = async ({
13
17
  search,
14
18
  page = 1,
15
19
  contractId,
@@ -19,25 +23,19 @@ export const getBuyingPoliciesService = async ({
19
23
  data: BuyingPolicy[];
20
24
  total: number;
21
25
  }> => {
22
- try {
23
- return await buyingPoliciesClient.getBuyingPoliciesByOrgUnitId(
24
- contractId,
25
- orgUnitId,
26
- cookie,
27
- search,
28
- page
29
- );
30
- } catch (error) {
31
- console.error("Error in getBuyingPoliciesService:", {
32
- error,
33
- orgUnitId,
34
- contractId,
35
- errorMessage: (error as { message?: string })?.message,
36
- });
26
+ return await buyingPoliciesClient.getBuyingPoliciesByOrgUnitId(
27
+ contractId,
28
+ orgUnitId,
29
+ cookie,
30
+ search,
31
+ page
32
+ );
33
+ };
37
34
 
38
- return {
39
- data: [],
40
- total: 0,
41
- };
35
+ export const getBuyingPoliciesService = withClientErrorBoundary(
36
+ getBuyingPoliciesFunction,
37
+ {
38
+ componentName: "BuyingPoliciesService",
39
+ onError: handleUnauthorizedRedirect,
42
40
  }
43
- };
41
+ );
@@ -1,3 +1,7 @@
1
+ import {
2
+ withClientErrorBoundary,
3
+ handleUnauthorizedRedirect,
4
+ } from "../../shared/utils/withClientErrorBoundary";
1
5
  import { contractsClient } from "../clients/ContractsClient";
2
6
 
3
7
  type GetContractDetailsServiceProps = {
@@ -6,7 +10,7 @@ type GetContractDetailsServiceProps = {
6
10
  unitId: string;
7
11
  };
8
12
 
9
- export const getContractDetailsService = async ({
13
+ const getContractDetailsFunction = async ({
10
14
  contractId,
11
15
  cookie,
12
16
  unitId,
@@ -19,3 +23,11 @@ export const getContractDetailsService = async ({
19
23
 
20
24
  return contract;
21
25
  };
26
+
27
+ export const getContractDetailsService = withClientErrorBoundary(
28
+ getContractDetailsFunction,
29
+ {
30
+ componentName: "ContractDetails",
31
+ onError: handleUnauthorizedRedirect,
32
+ }
33
+ );
@@ -1,3 +1,7 @@
1
+ import {
2
+ withClientErrorBoundary,
3
+ handleUnauthorizedRedirect,
4
+ } from "../../shared/utils/withClientErrorBoundary";
1
5
  import { contractsClient } from "../clients/ContractsClient";
2
6
 
3
7
  import type { ContractData } from "../types";
@@ -10,7 +14,7 @@ export type GetContractsByOrgUnitIdServiceProps = {
10
14
  cookie: string;
11
15
  };
12
16
 
13
- export const getContractsByOrgUnitIdService = async ({
17
+ const getContractsByOrgUnitIdFunction = async ({
14
18
  orgUnitId,
15
19
  cookie,
16
20
  }: GetContractsByOrgUnitIdServiceProps): Promise<ContractData[]> => {
@@ -20,16 +24,19 @@ export const getContractsByOrgUnitIdService = async ({
20
24
  return contractsData;
21
25
  }
22
26
 
23
- try {
24
- const { contracts } = await contractsClient.getContractsByOrgUnitId(
25
- orgUnitId,
26
- cookie
27
- );
28
- contractsData.push(...contracts);
29
- } catch (err) {
30
- console.error("Failed to fetch contracts", err);
31
- return contractsData;
32
- }
27
+ const { contracts } = await contractsClient.getContractsByOrgUnitId(
28
+ orgUnitId,
29
+ cookie
30
+ );
31
+ contractsData.push(...contracts);
33
32
 
34
33
  return contractsData;
35
34
  };
35
+
36
+ export const getContractsByOrgUnitIdService = withClientErrorBoundary(
37
+ getContractsByOrgUnitIdFunction,
38
+ {
39
+ componentName: "GetContractsByOrgUnitId",
40
+ onError: handleUnauthorizedRedirect,
41
+ }
42
+ );
@@ -1,3 +1,7 @@
1
+ import {
2
+ withClientErrorBoundary,
3
+ handleUnauthorizedRedirect,
4
+ } from "../../shared/utils/withClientErrorBoundary";
1
5
  import { contractsClient } from "../clients/ContractsClient";
2
6
 
3
7
  export type UpdateContractStatusServiceProps = {
@@ -7,21 +11,24 @@ export type UpdateContractStatusServiceProps = {
7
11
  unitId: string;
8
12
  };
9
13
 
10
- export const updateContractStatusService = async ({
14
+ const updateContractStatusFunction = async ({
11
15
  contractId,
12
16
  isActive,
13
17
  cookie,
14
18
  unitId,
15
19
  }: UpdateContractStatusServiceProps): Promise<{ isActive: boolean } | null> => {
16
- try {
17
- const data = await contractsClient.updateContractStatus(
18
- { unitId: unitId, contractId, isActive },
19
- cookie
20
- );
20
+ const data = await contractsClient.updateContractStatus(
21
+ { unitId: unitId, contractId, isActive },
22
+ cookie
23
+ );
21
24
 
22
- return data;
23
- } catch (err) {
24
- console.error("Failed to update contract status", err);
25
- return null;
26
- }
25
+ return data;
27
26
  };
27
+
28
+ export const updateContractStatusService = withClientErrorBoundary(
29
+ updateContractStatusFunction,
30
+ {
31
+ componentName: "UpdateContractStatus",
32
+ onError: handleUnauthorizedRedirect,
33
+ }
34
+ );
@@ -5,6 +5,7 @@
5
5
  @import "@faststore/ui/src/components/molecules/Toggle/styles.scss";
6
6
  @import "../../../shared/components/InternalTopbar/internal-top-bar.scss";
7
7
  @import "../../../shared/components/HeaderInside/header-inside.scss";
8
+ @import "../../../shared/components/Error/error.scss";
8
9
 
9
10
  width: 100%;
10
11
 
@@ -3,8 +3,9 @@ import {
3
3
  getAuthFromCookie,
4
4
  getCookieWithoutSessionObjects,
5
5
  } from "../utils/cookie";
6
+ import { extractErrorMessage } from "../utils/extractErrorMessage";
6
7
 
7
- interface RequestConfig<Input = never> {
8
+ interface RequestConfig<Input = unknown> {
8
9
  url: string;
9
10
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
10
11
  data?: Input;
@@ -12,6 +13,37 @@ interface RequestConfig<Input = never> {
12
13
  params?: Record<string, string | number | boolean>;
13
14
  ignoreContentType?: boolean;
14
15
  cache?: RequestCache;
16
+ componentName?: string; // For error tracking
17
+ }
18
+
19
+ export class ClientError extends Error {
20
+ public status?: number;
21
+ public statusText?: string;
22
+ public url?: string;
23
+ public method?: string;
24
+ public responseData?: unknown;
25
+ public componentName?: string;
26
+
27
+ constructor(
28
+ message: string,
29
+ options: {
30
+ status?: number;
31
+ statusText?: string;
32
+ url?: string;
33
+ method?: string;
34
+ responseData?: unknown;
35
+ componentName?: string;
36
+ } = {}
37
+ ) {
38
+ super(message);
39
+ this.name = "ClientError";
40
+ this.status = options.status;
41
+ this.statusText = options.statusText;
42
+ this.url = options.url;
43
+ this.method = options.method;
44
+ this.responseData = options.responseData;
45
+ this.componentName = options.componentName;
46
+ }
15
47
  }
16
48
 
17
49
  class Client {
@@ -21,6 +53,22 @@ class Client {
21
53
  this.baseURL = baseURL;
22
54
  }
23
55
 
56
+ private createClientError(
57
+ message: string,
58
+ response: Response,
59
+ responseData: unknown,
60
+ requestConfig: RequestConfig
61
+ ): ClientError {
62
+ return new ClientError(message, {
63
+ status: response.status,
64
+ statusText: response.statusText,
65
+ url: this.baseURL + requestConfig.url,
66
+ method: requestConfig.method,
67
+ responseData,
68
+ componentName: requestConfig.componentName,
69
+ });
70
+ }
71
+
24
72
  async request<Return, Input = never>({
25
73
  url,
26
74
  method,
@@ -29,6 +77,7 @@ class Client {
29
77
  params = {},
30
78
  ignoreContentType = false,
31
79
  cache = "no-cache",
80
+ componentName,
32
81
  }: RequestConfig<Input>): Promise<Return> {
33
82
  const config: RequestInit = {
34
83
  method,
@@ -59,24 +108,51 @@ class Client {
59
108
  }
60
109
 
61
110
  if (!response.ok) {
62
- console.error(`Request to ${url} failed:`, responseData);
63
- console.error(response.status, response.statusText);
64
- throw new Error(
65
- responseData ? JSON.stringify(responseData) : "Request failed"
111
+ const errorMessage = extractErrorMessage(response, responseData);
112
+
113
+ const clientError = this.createClientError(
114
+ errorMessage,
115
+ response,
116
+ responseData,
117
+ {
118
+ url,
119
+ method,
120
+ data,
121
+ headers,
122
+ params,
123
+ ignoreContentType,
124
+ cache,
125
+ componentName,
126
+ }
66
127
  );
128
+
129
+ throw clientError;
67
130
  }
68
131
 
69
132
  return responseData as Return;
70
133
  } catch (error) {
71
- return Promise.reject(error);
134
+ if (error instanceof ClientError) {
135
+ throw error;
136
+ }
137
+
138
+ const clientError = new ClientError(
139
+ error instanceof Error ? error.message : "Network error",
140
+ {
141
+ url: this.baseURL + url,
142
+ method,
143
+ componentName,
144
+ }
145
+ );
146
+
147
+ throw clientError;
72
148
  }
73
149
  }
74
150
 
75
151
  get<Return>(
76
152
  url: string,
77
- config: Partial<RequestConfig> = {}
153
+ config: Partial<RequestConfig<never>> = {}
78
154
  ): Promise<Return> {
79
- return this.request<Return>({ url, method: "GET", ...config });
155
+ return this.request<Return, never>({ url, method: "GET", ...config });
80
156
  }
81
157
 
82
158
  post<Return, Input>(
@@ -1,6 +1,7 @@
1
1
  import { createContext, type ReactNode } from "react";
2
2
 
3
3
  import { LoadingTabsLayout } from "../../layouts";
4
+ import { ErrorBoundary } from "../ErrorBoundary/ErrorBoundary";
4
5
 
5
6
  import type { ContractData } from "../../../contracts/types";
6
7
  import type { OrgUnitBasicData } from "../../../org-units/types";
@@ -31,7 +32,9 @@ export const BuyerPortalProvider = ({
31
32
  }: BuyerPortalProviderProps) => {
32
33
  return (
33
34
  <BuyerPortalContext.Provider value={value}>
34
- <LoadingTabsLayout>{children}</LoadingTabsLayout>
35
+ <ErrorBoundary>
36
+ <LoadingTabsLayout>{children}</LoadingTabsLayout>
37
+ </ErrorBoundary>
35
38
  </BuyerPortalContext.Provider>
36
39
  );
37
40
  };
@@ -0,0 +1,31 @@
1
+ import { isDevelopment } from "../../utils/environment";
2
+ import { Icon } from "../Icon";
3
+
4
+ export type ErrorProps = {
5
+ error?: Error;
6
+ tags?: {
7
+ component: string;
8
+ errorType: string;
9
+ };
10
+ };
11
+
12
+ export default function Error({ error, tags }: ErrorProps) {
13
+ return (
14
+ <div data-fs-bp-error>
15
+ <Icon name="Warning" width={48} height={48} data-fs-icon-loading="true" />
16
+ <h2 data-fs-bp-error-title>Something went wrong!</h2>
17
+ <button data-fs-bp-error-button onClick={() => window.location.reload()}>
18
+ Try again
19
+ </button>
20
+ {isDevelopment() ? (
21
+ <div data-fs-bp-error-details>
22
+ <span data-fs-bp-error-details-type>{tags?.errorType}</span>
23
+ <h2 data-fs-bp-error-details-title>Error Details</h2>
24
+ <p data-fs-bp-error-details-message>{error?.message}</p>
25
+ <p data-fs-bp-error-details-stack>Stack: {error?.stack}</p>
26
+ <p data-fs-bp-error-details-component>Component: {tags?.component}</p>
27
+ </div>
28
+ ) : null}
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,71 @@
1
+ [data-fs-bp-error] {
2
+ @import "@faststore/ui/src/components/atoms/Icon/styles.scss";
3
+
4
+ flex: 1;
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ justify-content: center;
9
+ gap: var(--fs-spacing-4);
10
+ height: 100%;
11
+ min-height: calc(100vh / 2);
12
+
13
+ [data-fs-bp-error-title] {
14
+ font-size: var(--fs-text-size-3);
15
+ font-weight: var(--fs-text-weight-semibold);
16
+ line-height: calc(var(--fs-text-size-4) + var(--fs-scale));
17
+ }
18
+
19
+ [data-fs-bp-error-button] {
20
+ font-size: var(--fs-text-size-1);
21
+ font-weight: var(--fs-text-weight-semibold);
22
+ color: #1f1f1f;
23
+ cursor: pointer;
24
+ }
25
+
26
+ [data-fs-icon="true"] {
27
+ color: #d31a15;
28
+ }
29
+
30
+ [data-fs-bp-error-details] {
31
+ background-color: #1f1f1f;
32
+ border-top: calc(var(--fs-border-width) * 5) solid #d31a15;
33
+ padding: var(--fs-spacing-4);
34
+ border-radius: var(--fs-border-radius-medium);
35
+ position: relative;
36
+
37
+ [data-fs-bp-error-details-type] {
38
+ position: absolute;
39
+ right: var(--fs-spacing-4);
40
+ top: var(--fs-spacing-4);
41
+ background: #f1f1f1;
42
+ padding: var(--fs-spacing-1);
43
+ border-radius: var(--fs-border-radius-medium);
44
+ font-size: var(--fs-text-size-0);
45
+ }
46
+
47
+ [data-fs-bp-error-details-title] {
48
+ font-size: var(--fs-text-size-3);
49
+ color: #e2e2e2;
50
+ font-weight: var(--fs-text-weight-semibold);
51
+ }
52
+
53
+ [data-fs-bp-error-details-message] {
54
+ margin: var(--fs-spacing-4) 0;
55
+ font-size: var(--fs-text-size-2);
56
+ font-weight: var(--fs-text-weight-medium);
57
+ color: #e2e2e2;
58
+ }
59
+
60
+ [data-fs-bp-error-details-stack] {
61
+ font-size: var(--fs-text-size-2);
62
+ margin: var(--fs-spacing-4) 0;
63
+ font-weight: var(--fs-text-weight-medium);
64
+ }
65
+
66
+ [data-fs-bp-error-details-component] {
67
+ font-size: var(--fs-text-size-2);
68
+ font-weight: var(--fs-text-weight-medium);
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import React, { Component, type ReactNode } from "react";
4
+
5
+ import { ErrorTabsLayout } from "../../layouts/ErrorTabsLayout/ErrorTabsLayout";
6
+
7
+ interface Props {
8
+ children: ReactNode;
9
+ fallback?: ReactNode;
10
+ tags?: {
11
+ component: string;
12
+ errorType: string;
13
+ };
14
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
15
+ }
16
+
17
+ interface State {
18
+ hasError: boolean;
19
+ error?: Error;
20
+ }
21
+
22
+ export class ErrorBoundary extends Component<Props, State> {
23
+ constructor(props: Props) {
24
+ super(props);
25
+ this.state = { hasError: false };
26
+ }
27
+
28
+ static getDerivedStateFromError(error: Error): State {
29
+ return { hasError: true, error };
30
+ }
31
+
32
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
33
+ if (this.props.onError) {
34
+ this.props.onError(error, errorInfo);
35
+ }
36
+ }
37
+
38
+ render() {
39
+ if (this.state.hasError) {
40
+ if (this.props.fallback) {
41
+ return this.props.fallback;
42
+ }
43
+
44
+ return (
45
+ <ErrorTabsLayout
46
+ error={{
47
+ error: this.state.error ?? {
48
+ message: "Unknown error",
49
+ name: "Unknown error",
50
+ stack: "Unknown error",
51
+ },
52
+ tags: this.props.tags ?? {
53
+ component: "Unknown",
54
+ errorType: "Unknown",
55
+ },
56
+ }}
57
+ />
58
+ );
59
+ }
60
+
61
+ return this.props.children;
62
+ }
63
+ }
@@ -0,0 +1,14 @@
1
+ export type ErrorBoundaryProps = {
2
+ error: {
3
+ message: string;
4
+ name: string;
5
+ stack?: string;
6
+ };
7
+ tags: {
8
+ component: string;
9
+ errorType: string;
10
+ };
11
+ extra?: {
12
+ query: unknown;
13
+ };
14
+ };
@@ -72,3 +72,6 @@ export type { CounterProps as PaginatorCounterProps } from "./Paginator/Counter"
72
72
  export type { NextPageButtonProps as PaginatorNextPageButtonProps } from "./Paginator/NextPageButton";
73
73
 
74
74
  export { EmptyState, type EmptyStateProps } from "./EmptyState/EmptyState";
75
+ export { ErrorBoundary } from "./ErrorBoundary/ErrorBoundary";
76
+ export { withErrorBoundary } from "./withErrorBoundary/withErrorBoundary";
77
+ export { default as Error } from "./Error/Error";
@@ -0,0 +1,35 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+
5
+ import { ErrorBoundary } from "../ErrorBoundary/ErrorBoundary";
6
+
7
+ type WithErrorBoundaryOptions = {
8
+ fallback?: React.ReactNode;
9
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
10
+ tags?: {
11
+ component: string;
12
+ errorType: string;
13
+ };
14
+ };
15
+
16
+ export function withErrorBoundary<P extends Record<string, unknown>>(
17
+ WrappedComponent: React.ComponentType<P>,
18
+ options: WithErrorBoundaryOptions = {}
19
+ ) {
20
+ const { onError, tags } = options;
21
+
22
+ const ComponentWithErrorBoundary = (props: P) => {
23
+ return (
24
+ <ErrorBoundary onError={onError} tags={tags}>
25
+ <WrappedComponent {...props} />
26
+ </ErrorBoundary>
27
+ );
28
+ };
29
+
30
+ ComponentWithErrorBoundary.displayName = `WithErrorBoundary(${
31
+ WrappedComponent.displayName || WrappedComponent.name || "Component"
32
+ })`;
33
+
34
+ return ComponentWithErrorBoundary;
35
+ }
@@ -1,5 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
+ import { useRouter } from "next/router";
4
+
3
5
  import { VerticalNav } from "../../components";
4
6
  import { useBuyerPortal } from "../../hooks";
5
7
  import {
@@ -22,6 +24,11 @@ export type SidebarMenuProps = {
22
24
  export const SidebarMenu = ({ ...otherProps }: SidebarMenuProps) => {
23
25
  const { currentOrgUnit, currentContract } = useBuyerPortal();
24
26
 
27
+ const router = useRouter();
28
+
29
+ const orgUnitId = router.query.orgUnitId as string;
30
+ const customerId = router.query.contractId as string;
31
+
25
32
  const idContract =
26
33
  (currentContract?.id
27
34
  ? currentContract?.id
@@ -31,8 +38,8 @@ export const SidebarMenu = ({ ...otherProps }: SidebarMenuProps) => {
31
38
  <aside data-fs-bp-sidebar-menu {...otherProps}>
32
39
  <VerticalNav.Menu title="Contract">
33
40
  {getContractSettingsLinks({
34
- orgUnitId: currentOrgUnit?.id ?? "",
35
- contractId: idContract,
41
+ orgUnitId: orgUnitId ?? currentOrgUnit?.id ?? "",
42
+ contractId: customerId ?? idContract,
36
43
  }).map((option) => (
37
44
  <VerticalNav.Link key={option.name} link={option.link}>
38
45
  {option.name}