@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.
- package/package.json +1 -1
- package/public/buyer-portal-icons.svg +1 -1
- package/src/features/addresses/services/get-addresses-by-unit-id.service.ts +13 -1
- package/src/features/addresses/services/get-addresses.service.ts +15 -2
- package/src/features/buying-policies/services/get-buying-policies.service.ts +19 -21
- package/src/features/contracts/services/get-contract-details.service.ts +13 -1
- package/src/features/contracts/services/get-contracts-org-by-unit-id.service.ts +18 -11
- package/src/features/contracts/services/update-contract-status.service.ts +18 -11
- package/src/features/profile/layouts/ProfileLayout/profile-layout.scss +1 -0
- package/src/features/shared/clients/Client.ts +84 -8
- package/src/features/shared/components/BuyerPortalProvider/BuyerPortalProvider.tsx +4 -1
- package/src/features/shared/components/Error/Error.tsx +31 -0
- package/src/features/shared/components/Error/error.scss +71 -0
- package/src/features/shared/components/ErrorBoundary/ErrorBoundary.tsx +63 -0
- package/src/features/shared/components/ErrorBoundary/types.ts +14 -0
- package/src/features/shared/components/index.ts +3 -0
- package/src/features/shared/components/withErrorBoundary/withErrorBoundary.tsx +35 -0
- package/src/features/shared/layouts/BaseTabsLayout/SidebarMenu.tsx +9 -2
- package/src/features/shared/layouts/ContractTabsLayout/ContractTabsLayout.tsx +4 -2
- package/src/features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout.tsx +119 -0
- package/src/features/shared/layouts/ErrorTabsLayout/error-tabs-layout.scss +1 -0
- package/src/features/shared/utils/environment.ts +41 -0
- package/src/features/shared/utils/extractErrorMessage.ts +22 -0
- package/src/features/shared/utils/getHome.tsx +5 -0
- package/src/features/shared/utils/index.ts +2 -0
- package/src/features/shared/utils/withClientErrorBoundary.ts +61 -0
- package/src/features/shared/utils/withLoaderErrorBoundary.ts +46 -0
- package/src/features/users/clients/UsersClient.ts +0 -1
- package/src/pages/address-details.tsx +38 -11
- package/src/pages/addresses.tsx +35 -6
- package/src/pages/budgets-details.tsx +38 -8
- package/src/pages/budgets.tsx +33 -8
- package/src/pages/buying-policies.tsx +36 -12
- package/src/pages/buying-policy-details.tsx +38 -8
- package/src/pages/collections.tsx +36 -12
- package/src/pages/cost-centers.tsx +38 -8
- package/src/pages/credit-cards.tsx +38 -8
- package/src/pages/home.tsx +22 -5
- package/src/pages/org-unit-details.tsx +43 -7
- package/src/pages/org-units.tsx +39 -8
- package/src/pages/payment-methods.tsx +38 -8
- package/src/pages/po-numbers.tsx +38 -8
- package/src/pages/profile.tsx +31 -6
- package/src/pages/releases.tsx +33 -8
- package/src/pages/role-details.tsx +39 -7
- package/src/pages/roles.tsx +28 -7
- package/src/pages/user-details.tsx +39 -8
- package/src/pages/users.tsx +25 -7
|
@@ -27,12 +27,14 @@ export const ContractTabsLayout = ({
|
|
|
27
27
|
pageName,
|
|
28
28
|
loading = false,
|
|
29
29
|
children,
|
|
30
|
+
orgUnitId,
|
|
31
|
+
contractId,
|
|
30
32
|
}: ContractTabsLayoutProps) => {
|
|
31
33
|
const { currentOrgUnit, currentUser, currentContract } = useBuyerPortal();
|
|
32
34
|
|
|
33
35
|
const verticalLinks = getContractSettingsLinks({
|
|
34
|
-
contractId: currentContract?.id ?? "",
|
|
35
|
-
orgUnitId: currentOrgUnit?.id ?? "",
|
|
36
|
+
contractId: currentContract?.id ?? contractId ?? "",
|
|
37
|
+
orgUnitId: currentOrgUnit?.id ?? orgUnitId ?? "",
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
return (
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { useRouter } from "next/router";
|
|
4
|
+
|
|
5
|
+
import { OrgUnitDetailsNavbar } from "../../../org-units/components";
|
|
6
|
+
import { HeaderInside } from "../../components";
|
|
7
|
+
import Error from "../../components/Error/Error";
|
|
8
|
+
import { useBuyerPortal } from "../../hooks";
|
|
9
|
+
import { getTabsLayoutConfigFromRoute } from "../../utils/routeLayoutMapping";
|
|
10
|
+
import { ContractTabsLayout } from "../ContractTabsLayout/ContractTabsLayout";
|
|
11
|
+
import { FinanceTabsLayout } from "../FinanceTabsLayout/FinanceTabsLayout";
|
|
12
|
+
import { OrgUnitTabsLayout } from "../OrgUnitTabsLayout/OrgUnitTabLayout";
|
|
13
|
+
|
|
14
|
+
export type ErrorTabsLayoutContentProps = {
|
|
15
|
+
title?: string;
|
|
16
|
+
error?: {
|
|
17
|
+
error: Error;
|
|
18
|
+
tags: {
|
|
19
|
+
component: string;
|
|
20
|
+
errorType: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ErrorTabsLayoutProps = {
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
error?: {
|
|
28
|
+
error: Error;
|
|
29
|
+
tags: {
|
|
30
|
+
component: string;
|
|
31
|
+
errorType: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const ErrorTabsLayoutContent = ({
|
|
37
|
+
title,
|
|
38
|
+
error,
|
|
39
|
+
}: ErrorTabsLayoutContentProps) => (
|
|
40
|
+
<div>
|
|
41
|
+
<section data-fs-bp-profile>
|
|
42
|
+
<HeaderInside title={title} />
|
|
43
|
+
<Error error={error?.error} tags={error?.tags} />
|
|
44
|
+
</section>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export const ErrorTabsLayout = ({ children, error }: ErrorTabsLayoutProps) => {
|
|
49
|
+
const router = useRouter();
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
currentOrgUnit: orgUnit,
|
|
53
|
+
currentUser: user,
|
|
54
|
+
currentContract: contract,
|
|
55
|
+
} = useBuyerPortal();
|
|
56
|
+
|
|
57
|
+
const orgUnitId = router.query.orgUnitId as string;
|
|
58
|
+
const customerId = router.query.contractId as string;
|
|
59
|
+
|
|
60
|
+
const layoutConfig = getTabsLayoutConfigFromRoute(router.route);
|
|
61
|
+
|
|
62
|
+
if (!layoutConfig) {
|
|
63
|
+
return <>{children}</>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const pageTitle = layoutConfig.pageTitle;
|
|
67
|
+
|
|
68
|
+
switch (layoutConfig.layout) {
|
|
69
|
+
case "ContractTabsLayout":
|
|
70
|
+
return (
|
|
71
|
+
<ContractTabsLayout
|
|
72
|
+
orgUnitName={orgUnit?.name ?? ""}
|
|
73
|
+
orgUnitId={orgUnitId}
|
|
74
|
+
contractId={customerId}
|
|
75
|
+
contractName={contract?.name ?? ""}
|
|
76
|
+
pageName="Contract"
|
|
77
|
+
person={{
|
|
78
|
+
image: undefined,
|
|
79
|
+
name: user?.name ?? "",
|
|
80
|
+
role: user?.role ?? "",
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<ErrorTabsLayoutContent title={pageTitle} error={error} />
|
|
84
|
+
</ContractTabsLayout>
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
case "FinanceTabsLayout":
|
|
88
|
+
return (
|
|
89
|
+
<FinanceTabsLayout pageName={layoutConfig.pageName}>
|
|
90
|
+
<ErrorTabsLayoutContent title={pageTitle} error={error} />
|
|
91
|
+
</FinanceTabsLayout>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
case "OrgUnitTabsLayout":
|
|
95
|
+
return (
|
|
96
|
+
<OrgUnitTabsLayout pageName={layoutConfig.pageName}>
|
|
97
|
+
<ErrorTabsLayoutContent title={pageTitle} error={error} />
|
|
98
|
+
</OrgUnitTabsLayout>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
case "OrgUnitDetailsLayout":
|
|
102
|
+
return (
|
|
103
|
+
<div data-fs-bp-create-user-error-state>
|
|
104
|
+
<OrgUnitDetailsNavbar
|
|
105
|
+
orgName={orgUnit?.name ?? ""}
|
|
106
|
+
person={{
|
|
107
|
+
name: user?.name ?? "",
|
|
108
|
+
role: user?.role ?? "",
|
|
109
|
+
}}
|
|
110
|
+
loading={false}
|
|
111
|
+
/>
|
|
112
|
+
<ErrorTabsLayoutContent title={pageTitle} error={error} />
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
default:
|
|
117
|
+
return <ErrorTabsLayoutContent error={error} />;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "../../components//Error/error.scss";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for environment detection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if the application is running on localhost
|
|
7
|
+
* @returns {boolean} True if running on localhost, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
export const isLocalhost = (): boolean => {
|
|
10
|
+
if (typeof window === "undefined") {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const hostname = window.location.hostname;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
hostname === "localhost" ||
|
|
18
|
+
hostname === "127.0.0.1" ||
|
|
19
|
+
hostname === "0.0.0.0" ||
|
|
20
|
+
hostname.startsWith("192.168.") ||
|
|
21
|
+
hostname.startsWith("10.") ||
|
|
22
|
+
hostname.startsWith("172.") ||
|
|
23
|
+
hostname.endsWith(".local")
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks if the application is running in development mode
|
|
29
|
+
* @returns {boolean} True if in development mode, false otherwise
|
|
30
|
+
*/
|
|
31
|
+
export const isDevelopment = (): boolean => {
|
|
32
|
+
return process.env.NODE_ENV === "development";
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if detailed error information should be shown
|
|
37
|
+
* @returns {boolean} True if error details should be shown, false otherwise
|
|
38
|
+
*/
|
|
39
|
+
export const shouldShowErrorDetails = (): boolean => {
|
|
40
|
+
return isLocalhost() && isDevelopment();
|
|
41
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const extractErrorMessage = (
|
|
2
|
+
response: Response,
|
|
3
|
+
responseData: unknown
|
|
4
|
+
): string => {
|
|
5
|
+
if (
|
|
6
|
+
responseData &&
|
|
7
|
+
typeof responseData === "object" &&
|
|
8
|
+
"message" in responseData
|
|
9
|
+
) {
|
|
10
|
+
return String((responseData as { message: unknown }).message);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (responseData) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.stringify(responseData);
|
|
16
|
+
} catch {
|
|
17
|
+
return String(responseData);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return `HTTP ${response.status}: ${response.statusText}`;
|
|
22
|
+
};
|
|
@@ -33,3 +33,5 @@ export {
|
|
|
33
33
|
} from "./search";
|
|
34
34
|
export { toQueryParams } from "./toQueryParams";
|
|
35
35
|
export { getValidPage } from "./getValidPage";
|
|
36
|
+
export { withLoaderErrorBoundary } from "./withLoaderErrorBoundary";
|
|
37
|
+
export { withClientErrorBoundary } from "./withClientErrorBoundary";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { goHome } from "./getHome";
|
|
2
|
+
|
|
3
|
+
import type { ClientError } from "../clients/Client";
|
|
4
|
+
|
|
5
|
+
interface WithClientErrorBoundaryOptions {
|
|
6
|
+
componentName?: string;
|
|
7
|
+
onError?: (
|
|
8
|
+
error: ClientError,
|
|
9
|
+
requestInfo: {
|
|
10
|
+
url: string;
|
|
11
|
+
method: string;
|
|
12
|
+
data?: unknown;
|
|
13
|
+
}
|
|
14
|
+
) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const handleUnauthorizedRedirect = (error: ClientError) => {
|
|
18
|
+
if (error.status === 401) {
|
|
19
|
+
if (typeof window !== "undefined") {
|
|
20
|
+
goHome();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function withClientErrorBoundary<TArgs extends unknown[], TReturn>(
|
|
26
|
+
clientFunction: (...args: TArgs) => Promise<TReturn>,
|
|
27
|
+
options: WithClientErrorBoundaryOptions = {}
|
|
28
|
+
) {
|
|
29
|
+
const { componentName, onError } = options;
|
|
30
|
+
|
|
31
|
+
return async (...args: TArgs): Promise<TReturn> => {
|
|
32
|
+
try {
|
|
33
|
+
return await clientFunction(...args);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (onError && error instanceof Error) {
|
|
36
|
+
const clientError = error as Partial<ClientError>;
|
|
37
|
+
const requestInfo = {
|
|
38
|
+
url:
|
|
39
|
+
typeof clientError.url === "string" ? clientError.url : "unknown",
|
|
40
|
+
method:
|
|
41
|
+
typeof clientError.method === "string"
|
|
42
|
+
? clientError.method
|
|
43
|
+
: "unknown",
|
|
44
|
+
data:
|
|
45
|
+
clientError.responseData !== undefined
|
|
46
|
+
? clientError.responseData
|
|
47
|
+
: undefined,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
onError(error as ClientError, requestInfo);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (error instanceof Error && (error as ClientError).status === 401) {
|
|
54
|
+
return null as TReturn;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.error(`Error in ${componentName || "client function"}:`, error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { LoaderData } from "../types";
|
|
2
|
+
|
|
3
|
+
interface WithLoaderErrorBoundaryOptions {
|
|
4
|
+
componentName?: string;
|
|
5
|
+
onError?: (error: Error, query: unknown) => void;
|
|
6
|
+
redirectToError?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function withLoaderErrorBoundary<TQuery, TReturn>(
|
|
10
|
+
loaderFn: (data: LoaderData<TQuery>) => Promise<TReturn>,
|
|
11
|
+
options: WithLoaderErrorBoundaryOptions = {}
|
|
12
|
+
) {
|
|
13
|
+
const { componentName, onError, redirectToError = true } = options;
|
|
14
|
+
|
|
15
|
+
return async (data: LoaderData<TQuery>): Promise<TReturn> => {
|
|
16
|
+
try {
|
|
17
|
+
return await loaderFn(data);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (onError) {
|
|
20
|
+
onError(error as Error, data.query);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (redirectToError) {
|
|
24
|
+
return {
|
|
25
|
+
error: {
|
|
26
|
+
error: {
|
|
27
|
+
message: (error as Error).message,
|
|
28
|
+
name: (error as Error).name,
|
|
29
|
+
stack: (error as Error).stack,
|
|
30
|
+
},
|
|
31
|
+
tags: {
|
|
32
|
+
component: componentName || "Unknown",
|
|
33
|
+
errorType: "loader_error",
|
|
34
|
+
},
|
|
35
|
+
extra: {
|
|
36
|
+
query: data.query,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
hasError: true,
|
|
40
|
+
} as TReturn;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -4,15 +4,21 @@ import { getAddressLocationService } from "../features/addresses/services/locati
|
|
|
4
4
|
import { getAddressRecipientsService } from "../features/addresses/services/recipients/get-address-recipients.service";
|
|
5
5
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
6
6
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BuyerPortalProvider,
|
|
9
|
+
withErrorBoundary,
|
|
10
|
+
} from "../features/shared/components";
|
|
11
|
+
import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/types";
|
|
8
12
|
import {
|
|
9
13
|
PAGE_PARAMS,
|
|
10
14
|
SEARCH_PARAMS,
|
|
11
15
|
} from "../features/shared/hooks/usePageItems";
|
|
16
|
+
import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
|
|
12
17
|
import {
|
|
13
18
|
type ClientContext,
|
|
14
19
|
getClientContext,
|
|
15
20
|
getValidPage,
|
|
21
|
+
withLoaderErrorBoundary,
|
|
16
22
|
} from "../features/shared/utils";
|
|
17
23
|
import { getUserByIdService } from "../features/users/services";
|
|
18
24
|
|
|
@@ -57,11 +63,13 @@ export type AddressDetailsPageData = {
|
|
|
57
63
|
currentContract: ContractData | null;
|
|
58
64
|
currentUser: UserData | null;
|
|
59
65
|
};
|
|
66
|
+
hasError?: boolean;
|
|
67
|
+
error?: ErrorBoundaryProps;
|
|
60
68
|
};
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
const loaderFunction = async (
|
|
63
71
|
data: LoaderData<AddressDetailsPageQuery>
|
|
64
|
-
): Promise<AddressDetailsPageData> {
|
|
72
|
+
): Promise<AddressDetailsPageData> => {
|
|
65
73
|
const {
|
|
66
74
|
contractId,
|
|
67
75
|
orgUnitId,
|
|
@@ -135,16 +143,35 @@ export async function loader(
|
|
|
135
143
|
currentUser: user,
|
|
136
144
|
},
|
|
137
145
|
};
|
|
138
|
-
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const loader = withLoaderErrorBoundary(loaderFunction, {
|
|
149
|
+
componentName: "AddressDetailsPage",
|
|
150
|
+
redirectToError: true,
|
|
151
|
+
});
|
|
139
152
|
|
|
140
|
-
const AddressDetailsPage = ({
|
|
153
|
+
const AddressDetailsPage = ({
|
|
154
|
+
data,
|
|
155
|
+
context,
|
|
156
|
+
hasError,
|
|
157
|
+
error,
|
|
158
|
+
}: AddressDetailsPageData) => (
|
|
141
159
|
<BuyerPortalProvider {...context}>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
{hasError ? (
|
|
161
|
+
<ErrorTabsLayout error={error} />
|
|
162
|
+
) : (
|
|
163
|
+
<AddressDetailsLayout
|
|
164
|
+
addressDetails={data.addressDetails}
|
|
165
|
+
recipientsData={data.recipientsData ?? DEFAULT_ADDRESS_PAGINATED_DATA}
|
|
166
|
+
locationsData={data.locationsData ?? DEFAULT_ADDRESS_PAGINATED_DATA}
|
|
167
|
+
/>
|
|
168
|
+
)}
|
|
147
169
|
</BuyerPortalProvider>
|
|
148
170
|
);
|
|
149
171
|
|
|
150
|
-
export default AddressDetailsPage
|
|
172
|
+
export default withErrorBoundary(AddressDetailsPage, {
|
|
173
|
+
tags: {
|
|
174
|
+
component: "AddressDetailsPage",
|
|
175
|
+
errorType: "address_details_error",
|
|
176
|
+
},
|
|
177
|
+
});
|
package/src/pages/addresses.tsx
CHANGED
|
@@ -5,11 +5,17 @@ import {
|
|
|
5
5
|
} from "../features/addresses/services";
|
|
6
6
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
7
7
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
BuyerPortalProvider,
|
|
10
|
+
withErrorBoundary,
|
|
11
|
+
} from "../features/shared/components";
|
|
12
|
+
import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/types";
|
|
13
|
+
import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
|
|
9
14
|
import {
|
|
10
15
|
type ClientContext,
|
|
11
16
|
getClientContext,
|
|
12
17
|
getValidPage,
|
|
18
|
+
withLoaderErrorBoundary,
|
|
13
19
|
} from "../features/shared/utils";
|
|
14
20
|
import { getUserByIdService } from "../features/users/services";
|
|
15
21
|
|
|
@@ -30,6 +36,8 @@ export type AddressesPageData = {
|
|
|
30
36
|
currentOrgUnit: OrgUnitBasicData | null;
|
|
31
37
|
currentUser: UserData | null;
|
|
32
38
|
};
|
|
39
|
+
hasError?: boolean;
|
|
40
|
+
error?: ErrorBoundaryProps;
|
|
33
41
|
};
|
|
34
42
|
|
|
35
43
|
export type AddressesPageQuery = GetAddressesServiceProps & {
|
|
@@ -38,9 +46,9 @@ export type AddressesPageQuery = GetAddressesServiceProps & {
|
|
|
38
46
|
page?: string;
|
|
39
47
|
};
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
const loaderFunction = async (
|
|
42
50
|
data: LoaderData<AddressesPageQuery>
|
|
43
|
-
): Promise<AddressesPageData> {
|
|
51
|
+
): Promise<AddressesPageData> => {
|
|
44
52
|
const { contractId, orgUnitId, search, page: pageString } = data.query;
|
|
45
53
|
|
|
46
54
|
const page = getValidPage(pageString);
|
|
@@ -81,7 +89,12 @@ export async function loader(
|
|
|
81
89
|
currentContract: contract,
|
|
82
90
|
},
|
|
83
91
|
};
|
|
84
|
-
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const loader = withLoaderErrorBoundary(loaderFunction, {
|
|
95
|
+
componentName: "AddressesPage",
|
|
96
|
+
redirectToError: true,
|
|
97
|
+
});
|
|
85
98
|
|
|
86
99
|
const AddressPage = ({
|
|
87
100
|
data,
|
|
@@ -89,10 +102,26 @@ const AddressPage = ({
|
|
|
89
102
|
page,
|
|
90
103
|
total,
|
|
91
104
|
context,
|
|
105
|
+
hasError,
|
|
106
|
+
error,
|
|
92
107
|
}: AddressesPageData) => (
|
|
93
108
|
<BuyerPortalProvider {...context}>
|
|
94
|
-
|
|
109
|
+
{hasError ? (
|
|
110
|
+
<ErrorTabsLayout error={error} />
|
|
111
|
+
) : (
|
|
112
|
+
<AddressLayout
|
|
113
|
+
addresses={data}
|
|
114
|
+
search={search}
|
|
115
|
+
page={page}
|
|
116
|
+
total={total}
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
95
119
|
</BuyerPortalProvider>
|
|
96
120
|
);
|
|
97
121
|
|
|
98
|
-
export default AddressPage
|
|
122
|
+
export default withErrorBoundary(AddressPage, {
|
|
123
|
+
tags: {
|
|
124
|
+
component: "AddressesPage",
|
|
125
|
+
errorType: "addresses_error",
|
|
126
|
+
},
|
|
127
|
+
});
|
|
@@ -3,8 +3,17 @@ import { getBudgetByIdService } from "../features/budgets/services";
|
|
|
3
3
|
import { Budget } from "../features/budgets/types";
|
|
4
4
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
5
5
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
BuyerPortalProvider,
|
|
8
|
+
withErrorBoundary,
|
|
9
|
+
} from "../features/shared/components";
|
|
10
|
+
import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/types";
|
|
11
|
+
import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
|
|
12
|
+
import {
|
|
13
|
+
type ClientContext,
|
|
14
|
+
getClientContext,
|
|
15
|
+
withLoaderErrorBoundary,
|
|
16
|
+
} from "../features/shared/utils";
|
|
8
17
|
import { getUserByIdService } from "../features/users/services";
|
|
9
18
|
|
|
10
19
|
import type { ContractData } from "../features/contracts/types";
|
|
@@ -20,6 +29,8 @@ export type BudgetsDetailsData = {
|
|
|
20
29
|
currentContract: ContractData | null;
|
|
21
30
|
currentUser: UserData | null;
|
|
22
31
|
};
|
|
32
|
+
hasError?: boolean;
|
|
33
|
+
error?: ErrorBoundaryProps;
|
|
23
34
|
};
|
|
24
35
|
|
|
25
36
|
export type BudgetsDetailsQuery = {
|
|
@@ -28,9 +39,9 @@ export type BudgetsDetailsQuery = {
|
|
|
28
39
|
budgetId: string;
|
|
29
40
|
};
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
const loaderFunction = async (
|
|
32
43
|
data: LoaderData<BudgetsDetailsQuery>
|
|
33
|
-
): Promise<BudgetsDetailsData> {
|
|
44
|
+
): Promise<BudgetsDetailsData> => {
|
|
34
45
|
const { customerId, cookie, userId, ...clientContext } =
|
|
35
46
|
await getClientContext(data);
|
|
36
47
|
|
|
@@ -61,12 +72,31 @@ export async function loader(
|
|
|
61
72
|
currentUser: user,
|
|
62
73
|
},
|
|
63
74
|
};
|
|
64
|
-
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const loader = withLoaderErrorBoundary(loaderFunction, {
|
|
78
|
+
componentName: "BudgetsDetailsPage",
|
|
79
|
+
redirectToError: true,
|
|
80
|
+
});
|
|
65
81
|
|
|
66
|
-
const
|
|
82
|
+
const BudgetsDetailsPage = ({
|
|
83
|
+
budget,
|
|
84
|
+
context,
|
|
85
|
+
hasError,
|
|
86
|
+
error,
|
|
87
|
+
}: BudgetsDetailsData) => (
|
|
67
88
|
<BuyerPortalProvider {...context}>
|
|
68
|
-
|
|
89
|
+
{hasError ? (
|
|
90
|
+
<ErrorTabsLayout error={error} />
|
|
91
|
+
) : (
|
|
92
|
+
<BudgetsDetailsLayout budget={budget} />
|
|
93
|
+
)}
|
|
69
94
|
</BuyerPortalProvider>
|
|
70
95
|
);
|
|
71
96
|
|
|
72
|
-
export default
|
|
97
|
+
export default withErrorBoundary(BudgetsDetailsPage, {
|
|
98
|
+
tags: {
|
|
99
|
+
component: "BudgetsDetailsPage",
|
|
100
|
+
errorType: "budgets_details_error",
|
|
101
|
+
},
|
|
102
|
+
});
|
package/src/pages/budgets.tsx
CHANGED
|
@@ -3,8 +3,17 @@ import { listBudgetsService } from "../features/budgets/services";
|
|
|
3
3
|
import { BudgetListResponse } from "../features/budgets/types";
|
|
4
4
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
5
5
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
BuyerPortalProvider,
|
|
8
|
+
withErrorBoundary,
|
|
9
|
+
} from "../features/shared/components";
|
|
10
|
+
import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/types";
|
|
11
|
+
import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
|
|
12
|
+
import {
|
|
13
|
+
type ClientContext,
|
|
14
|
+
getClientContext,
|
|
15
|
+
withLoaderErrorBoundary,
|
|
16
|
+
} from "../features/shared/utils";
|
|
8
17
|
import { getUserByIdService } from "../features/users/services";
|
|
9
18
|
|
|
10
19
|
import type { ContractData } from "../features/contracts/types";
|
|
@@ -20,6 +29,8 @@ export type BudgetPageData = {
|
|
|
20
29
|
currentOrgUnit: OrgUnitBasicData | null;
|
|
21
30
|
currentUser: UserData | null;
|
|
22
31
|
};
|
|
32
|
+
hasError?: boolean;
|
|
33
|
+
error?: ErrorBoundaryProps;
|
|
23
34
|
};
|
|
24
35
|
|
|
25
36
|
export type BudgetPageQuery = {
|
|
@@ -27,9 +38,9 @@ export type BudgetPageQuery = {
|
|
|
27
38
|
orgUnitId: string;
|
|
28
39
|
};
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
const loaderFunction = async (
|
|
31
42
|
data: LoaderData<BudgetPageQuery>
|
|
32
|
-
): Promise<BudgetPageData> {
|
|
43
|
+
): Promise<BudgetPageData> => {
|
|
33
44
|
const { customerId, cookie, userId, ...clientContext } =
|
|
34
45
|
await getClientContext(data);
|
|
35
46
|
|
|
@@ -62,12 +73,26 @@ export async function loader(
|
|
|
62
73
|
currentUser: user,
|
|
63
74
|
},
|
|
64
75
|
};
|
|
65
|
-
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const loader = withLoaderErrorBoundary(loaderFunction, {
|
|
79
|
+
componentName: "BudgetsPage",
|
|
80
|
+
redirectToError: true,
|
|
81
|
+
});
|
|
66
82
|
|
|
67
|
-
const ContractsPage = ({ data, context }: BudgetPageData) => (
|
|
83
|
+
const ContractsPage = ({ data, context, hasError, error }: BudgetPageData) => (
|
|
68
84
|
<BuyerPortalProvider {...context}>
|
|
69
|
-
|
|
85
|
+
{hasError ? (
|
|
86
|
+
<ErrorTabsLayout error={error} />
|
|
87
|
+
) : (
|
|
88
|
+
<BudgetsLayout data={data} />
|
|
89
|
+
)}
|
|
70
90
|
</BuyerPortalProvider>
|
|
71
91
|
);
|
|
72
92
|
|
|
73
|
-
export default ContractsPage
|
|
93
|
+
export default withErrorBoundary(ContractsPage, {
|
|
94
|
+
tags: {
|
|
95
|
+
component: "BudgetsPage",
|
|
96
|
+
errorType: "budgets_error",
|
|
97
|
+
},
|
|
98
|
+
});
|