@vtex/faststore-plugin-buyer-portal 1.1.104 → 1.1.106
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/src/features/shared/components/Error/Error.tsx +17 -9
- package/src/features/shared/components/ErrorBoundary/types.ts +1 -0
- package/src/features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout.tsx +5 -1
- package/src/features/shared/utils/index.ts +1 -0
- package/src/features/shared/utils/serializeLoaderData.ts +10 -0
- package/src/features/shared/utils/withLoaderErrorBoundary.ts +46 -5
- package/src/pages/address-details.tsx +16 -0
- package/src/pages/addresses.tsx +15 -0
- package/src/pages/budgets.tsx +12 -0
- package/src/pages/buying-policies.tsx +15 -0
- package/src/pages/cost-centers.tsx +14 -1
- package/src/pages/org-unit-details.tsx +5 -1
- package/src/pages/profile.tsx +13 -1
- package/src/pages/users.tsx +18 -0
package/package.json
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
import { Icon } from "../Icon";
|
|
3
3
|
|
|
4
4
|
export type ErrorProps = {
|
|
5
|
-
error?:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
error?: {
|
|
6
|
+
error: Error;
|
|
7
|
+
query?: unknown;
|
|
8
|
+
tags?: {
|
|
9
|
+
component: string;
|
|
10
|
+
errorType: string;
|
|
11
|
+
};
|
|
9
12
|
};
|
|
10
13
|
};
|
|
11
14
|
|
|
12
|
-
export default function Error({ error
|
|
15
|
+
export default function Error({ error }: ErrorProps) {
|
|
13
16
|
return (
|
|
14
17
|
<div data-fs-bp-error>
|
|
15
18
|
<Icon name="Warning" width={48} height={48} data-fs-icon-loading="true" />
|
|
@@ -18,11 +21,16 @@ export default function Error({ error, tags }: ErrorProps) {
|
|
|
18
21
|
Try again
|
|
19
22
|
</button>
|
|
20
23
|
<div data-fs-bp-error-details>
|
|
21
|
-
<span data-fs-bp-error-details-type>{tags?.errorType}</span>
|
|
24
|
+
<span data-fs-bp-error-details-type>{error?.tags?.errorType}</span>
|
|
22
25
|
<h2 data-fs-bp-error-details-title>Error Details</h2>
|
|
23
|
-
<p data-fs-bp-error-details-message>{error?.message}</p>
|
|
24
|
-
<p data-fs-bp-error-details-stack>Stack: {error?.stack}</p>
|
|
25
|
-
<p data-fs-bp-error-details-component>
|
|
26
|
+
<p data-fs-bp-error-details-message>{error?.error.message}</p>
|
|
27
|
+
<p data-fs-bp-error-details-stack>Stack: {error?.error.stack}</p>
|
|
28
|
+
<p data-fs-bp-error-details-component>
|
|
29
|
+
Component: {error?.tags?.component}
|
|
30
|
+
</p>
|
|
31
|
+
<p data-fs-bp-error-details-query>
|
|
32
|
+
Query: {JSON.stringify(error?.query)}
|
|
33
|
+
</p>
|
|
26
34
|
</div>
|
|
27
35
|
</div>
|
|
28
36
|
);
|
|
@@ -15,6 +15,8 @@ export type ErrorTabsLayoutContentProps = {
|
|
|
15
15
|
title?: string;
|
|
16
16
|
error?: {
|
|
17
17
|
error: Error;
|
|
18
|
+
query?: unknown;
|
|
19
|
+
rawData?: unknown;
|
|
18
20
|
tags: {
|
|
19
21
|
component: string;
|
|
20
22
|
errorType: string;
|
|
@@ -26,6 +28,8 @@ export type ErrorTabsLayoutProps = {
|
|
|
26
28
|
children?: ReactNode;
|
|
27
29
|
error?: {
|
|
28
30
|
error: Error;
|
|
31
|
+
query?: unknown;
|
|
32
|
+
rawData?: unknown;
|
|
29
33
|
tags: {
|
|
30
34
|
component: string;
|
|
31
35
|
errorType: string;
|
|
@@ -40,7 +44,7 @@ export const ErrorTabsLayoutContent = ({
|
|
|
40
44
|
<div>
|
|
41
45
|
<section data-fs-bp-profile>
|
|
42
46
|
<HeaderInside title={title} />
|
|
43
|
-
<Error error={error
|
|
47
|
+
<Error error={error} />
|
|
44
48
|
</section>
|
|
45
49
|
</div>
|
|
46
50
|
);
|
|
@@ -34,5 +34,6 @@ export {
|
|
|
34
34
|
export { toQueryParams } from "./toQueryParams";
|
|
35
35
|
export { getValidPage } from "./getValidPage";
|
|
36
36
|
export { isPluginError } from "./isPluginError";
|
|
37
|
+
export { serializeLoaderData } from "./serializeLoaderData";
|
|
37
38
|
export { withLoaderErrorBoundary } from "./withLoaderErrorBoundary";
|
|
38
39
|
export { withClientErrorBoundary } from "./withClientErrorBoundary";
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { getClientContext } from "./getClientContext";
|
|
2
|
+
import { serializeLoaderData } from "./serializeLoaderData";
|
|
3
|
+
|
|
1
4
|
import type { LoaderData } from "../types";
|
|
2
5
|
|
|
3
6
|
interface WithLoaderErrorBoundaryOptions {
|
|
@@ -14,30 +17,68 @@ export function withLoaderErrorBoundary<TQuery, TReturn>(
|
|
|
14
17
|
|
|
15
18
|
return async (data: LoaderData<TQuery>): Promise<TReturn> => {
|
|
16
19
|
try {
|
|
17
|
-
|
|
20
|
+
const result = await loaderFn(data);
|
|
21
|
+
return serializeLoaderData(result);
|
|
18
22
|
} catch (error) {
|
|
23
|
+
console.error(`[${componentName || "Loader"}] Error:`, {
|
|
24
|
+
message: (error as Error).message,
|
|
25
|
+
query: data.query,
|
|
26
|
+
});
|
|
27
|
+
|
|
19
28
|
if (onError) {
|
|
20
29
|
onError(error as Error, data.query);
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
if (redirectToError) {
|
|
24
|
-
|
|
33
|
+
let clientContext;
|
|
34
|
+
try {
|
|
35
|
+
clientContext = await getClientContext(data);
|
|
36
|
+
} catch (contextError) {
|
|
37
|
+
console.error(
|
|
38
|
+
`[${componentName || "Loader"}] Failed to get client context:`,
|
|
39
|
+
contextError
|
|
40
|
+
);
|
|
41
|
+
clientContext = {
|
|
42
|
+
cookie: "",
|
|
43
|
+
customerId: "",
|
|
44
|
+
userId: "",
|
|
45
|
+
vtexIdclientAutCookie: "",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Serialize query to ensure it's JSON-safe (use null instead of undefined)
|
|
50
|
+
const serializedQuery = data.query
|
|
51
|
+
? JSON.parse(JSON.stringify(data.query))
|
|
52
|
+
: null;
|
|
53
|
+
|
|
54
|
+
const errorResponse = {
|
|
25
55
|
error: {
|
|
26
56
|
error: {
|
|
27
57
|
message: (error as Error).message,
|
|
28
58
|
name: (error as Error).name,
|
|
29
|
-
stack: (error as Error).stack,
|
|
59
|
+
stack: (error as Error).stack || null,
|
|
60
|
+
query: serializedQuery,
|
|
30
61
|
},
|
|
31
62
|
tags: {
|
|
32
63
|
component: componentName || "Unknown",
|
|
33
64
|
errorType: "loader_error",
|
|
34
65
|
},
|
|
35
66
|
extra: {
|
|
36
|
-
query:
|
|
67
|
+
query: serializedQuery,
|
|
37
68
|
},
|
|
38
69
|
},
|
|
39
70
|
hasError: true,
|
|
40
|
-
|
|
71
|
+
context: {
|
|
72
|
+
clientContext,
|
|
73
|
+
currentOrgUnit: null,
|
|
74
|
+
currentContract: null,
|
|
75
|
+
currentUser: null,
|
|
76
|
+
},
|
|
77
|
+
data: null,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Ensure error response is also JSON-serializable
|
|
81
|
+
return serializeLoaderData(errorResponse) as TReturn;
|
|
41
82
|
}
|
|
42
83
|
|
|
43
84
|
throw error;
|
|
@@ -85,6 +85,22 @@ const loaderFunction = async (
|
|
|
85
85
|
const { customerId, cookie, userId, ...clientContext } =
|
|
86
86
|
await getClientContext(data);
|
|
87
87
|
|
|
88
|
+
if (!contractId || !orgUnitId) {
|
|
89
|
+
return {
|
|
90
|
+
data: {
|
|
91
|
+
addressDetails: null,
|
|
92
|
+
recipientsData: null,
|
|
93
|
+
locationsData: null,
|
|
94
|
+
},
|
|
95
|
+
context: {
|
|
96
|
+
clientContext: { customerId, cookie, userId, ...clientContext },
|
|
97
|
+
currentOrgUnit: null,
|
|
98
|
+
currentContract: null,
|
|
99
|
+
currentUser: null,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
88
104
|
const orgUnit = await getOrgUnitBasicDataService({ cookie, id: orgUnitId });
|
|
89
105
|
|
|
90
106
|
const contract = await getContractDetailsService({
|
package/src/pages/addresses.tsx
CHANGED
|
@@ -56,6 +56,21 @@ const loaderFunction = async (
|
|
|
56
56
|
const { cookie, userId, customerId, ...clientContext } =
|
|
57
57
|
await getClientContext(data);
|
|
58
58
|
|
|
59
|
+
if (!contractId || !orgUnitId) {
|
|
60
|
+
return {
|
|
61
|
+
data: [],
|
|
62
|
+
search: search ?? "",
|
|
63
|
+
total: 0,
|
|
64
|
+
page: page ?? 1,
|
|
65
|
+
context: {
|
|
66
|
+
clientContext: { customerId, cookie, userId, ...clientContext },
|
|
67
|
+
currentOrgUnit: null,
|
|
68
|
+
currentContract: null,
|
|
69
|
+
currentUser: null,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
const [currentOrgUnit, user, contract, addressesResponse] = await Promise.all(
|
|
60
75
|
[
|
|
61
76
|
getOrgUnitBasicDataService({
|
package/src/pages/budgets.tsx
CHANGED
|
@@ -46,6 +46,18 @@ const loaderFunction = async (
|
|
|
46
46
|
|
|
47
47
|
const { contractId, orgUnitId } = data.query;
|
|
48
48
|
|
|
49
|
+
if (!contractId || !orgUnitId) {
|
|
50
|
+
return {
|
|
51
|
+
data: { data: [], total: 0 },
|
|
52
|
+
context: {
|
|
53
|
+
clientContext: { cookie, userId, customerId, ...clientContext },
|
|
54
|
+
currentOrgUnit: null,
|
|
55
|
+
currentContract: null,
|
|
56
|
+
currentUser: null,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
const [orgUnit, contract, user, budgetsData] = await Promise.all([
|
|
50
62
|
getOrgUnitBasicDataService({
|
|
51
63
|
cookie,
|
|
@@ -57,6 +57,21 @@ const loaderFunction = async (
|
|
|
57
57
|
|
|
58
58
|
const { cookie, userId, ...clientContext } = await getClientContext(data);
|
|
59
59
|
|
|
60
|
+
if (!contractId || !orgUnitId) {
|
|
61
|
+
return {
|
|
62
|
+
buyingPolicies: [],
|
|
63
|
+
total: 0,
|
|
64
|
+
search: search ?? "",
|
|
65
|
+
page: page ?? 1,
|
|
66
|
+
context: {
|
|
67
|
+
clientContext: { cookie, userId, ...clientContext },
|
|
68
|
+
currentOrgUnit: null,
|
|
69
|
+
currentContract: null,
|
|
70
|
+
currentUser: null,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
const [currentOrgUnit, user, contract, budgetData, buyingPoliciesResponse] =
|
|
61
76
|
await Promise.all([
|
|
62
77
|
getOrgUnitBasicDataService({
|
|
@@ -23,7 +23,7 @@ export type CostCentersPageData = {
|
|
|
23
23
|
data: ContractData | null;
|
|
24
24
|
context: {
|
|
25
25
|
clientContext: ClientContext;
|
|
26
|
-
currentOrgUnit: OrgUnitBasicData;
|
|
26
|
+
currentOrgUnit: OrgUnitBasicData | null;
|
|
27
27
|
currentContract: ContractData | null;
|
|
28
28
|
currentUser: UserData | null;
|
|
29
29
|
search: string;
|
|
@@ -46,6 +46,19 @@ const loaderFunction = async (
|
|
|
46
46
|
|
|
47
47
|
const { contractId, orgUnitId, search } = data.query;
|
|
48
48
|
|
|
49
|
+
if (!contractId || !orgUnitId) {
|
|
50
|
+
return {
|
|
51
|
+
data: null,
|
|
52
|
+
context: {
|
|
53
|
+
clientContext: { cookie, userId, customerId, ...clientContext },
|
|
54
|
+
currentContract: null,
|
|
55
|
+
currentUser: null,
|
|
56
|
+
currentOrgUnit: null,
|
|
57
|
+
search: "",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
const [orgUnit, contract, user] = await Promise.all([
|
|
50
63
|
getOrgUnitBasicDataService({ id: orgUnitId, cookie }),
|
|
51
64
|
getContractDetailsService({ contractId, unitId: orgUnitId, cookie }),
|
|
@@ -46,7 +46,11 @@ type OrgUnitDetailsPageQuery = {
|
|
|
46
46
|
const loaderFunction = async (
|
|
47
47
|
data: LoaderData<OrgUnitDetailsPageQuery>
|
|
48
48
|
): Promise<OrgUnitDetailsPageData> => {
|
|
49
|
-
const { orgUnitId } = data.query;
|
|
49
|
+
const { orgUnitId } = data.query ?? {};
|
|
50
|
+
|
|
51
|
+
if (!orgUnitId) {
|
|
52
|
+
throw new Error(`Missing required query param: orgUnitId=${orgUnitId}`);
|
|
53
|
+
}
|
|
50
54
|
|
|
51
55
|
const { cookie, userId, ...clientContext } = await getClientContext(data);
|
|
52
56
|
|
package/src/pages/profile.tsx
CHANGED
|
@@ -42,7 +42,19 @@ export async function loaderFunction(
|
|
|
42
42
|
const { customerId, cookie, userId, ...clientContext } =
|
|
43
43
|
await getClientContext(data);
|
|
44
44
|
|
|
45
|
-
const { contractId, orgUnitId } = data.query;
|
|
45
|
+
const { contractId, orgUnitId } = data.query ?? {};
|
|
46
|
+
|
|
47
|
+
if (!contractId || !orgUnitId) {
|
|
48
|
+
return {
|
|
49
|
+
data: null,
|
|
50
|
+
context: {
|
|
51
|
+
clientContext: { customerId, cookie, userId, ...clientContext },
|
|
52
|
+
currentOrgUnit: null,
|
|
53
|
+
currentContract: null,
|
|
54
|
+
currentUser: null,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
46
58
|
|
|
47
59
|
const [orgUnit, contract, user] = await Promise.all([
|
|
48
60
|
getOrgUnitBasicDataService({
|
package/src/pages/users.tsx
CHANGED
|
@@ -58,6 +58,24 @@ const loaderFunction = async (
|
|
|
58
58
|
const { orgUnitId, search = "", page = "1" } = data.query;
|
|
59
59
|
const validPage = getValidPage(page);
|
|
60
60
|
|
|
61
|
+
if (!orgUnitId) {
|
|
62
|
+
return {
|
|
63
|
+
data: {
|
|
64
|
+
users: [],
|
|
65
|
+
total: 0,
|
|
66
|
+
search: "",
|
|
67
|
+
page: 1,
|
|
68
|
+
rolesOptions: [],
|
|
69
|
+
},
|
|
70
|
+
context: {
|
|
71
|
+
clientContext: { cookie, userId, ...clientContext },
|
|
72
|
+
currentUser: null,
|
|
73
|
+
currentOrgUnit: null,
|
|
74
|
+
rolesOptions: [],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
const [currentOrgUnit, usersResponse, user, rolesOptions] = await Promise.all(
|
|
62
80
|
[
|
|
63
81
|
getOrgUnitBasicDataService({
|