@moneylion/react-native-offer-carousel 1.7.1 → 1.8.0
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/lib/commonjs/capabilities/errors/types/index.js +20 -0
- package/lib/commonjs/capabilities/errors/types/index.js.map +1 -0
- package/lib/commonjs/capabilities/offer-catalog/src/api/offerCatalogApi.js +41 -2
- package/lib/commonjs/capabilities/offer-catalog/src/api/offerCatalogApi.js.map +1 -1
- package/lib/commonjs/components/ErrorBoundary/index.js +1 -4
- package/lib/commonjs/components/ErrorBoundary/index.js.map +1 -1
- package/lib/commonjs/components/Modal/AllOffersModal.js +15 -6
- package/lib/commonjs/components/Modal/AllOffersModal.js.map +1 -1
- package/lib/commonjs/components/MoneyLionOfferCarousel.js +95 -43
- package/lib/commonjs/components/MoneyLionOfferCarousel.js.map +1 -1
- package/lib/commonjs/components/utils/errorUtils.js +71 -0
- package/lib/commonjs/components/utils/errorUtils.js.map +1 -0
- package/lib/commonjs/context/ThemeProvider.js +6 -26
- package/lib/commonjs/context/ThemeProvider.js.map +1 -1
- package/lib/commonjs/pageData.js +6 -3
- package/lib/commonjs/pageData.js.map +1 -1
- package/lib/commonjs/services/getDynamicOffers.js +6 -3
- package/lib/commonjs/services/getDynamicOffers.js.map +1 -1
- package/lib/commonjs/services/getProductTypes.js +4 -2
- package/lib/commonjs/services/getProductTypes.js.map +1 -1
- package/lib/commonjs/services/getProductTypesByQuery.js +19 -2
- package/lib/commonjs/services/getProductTypesByQuery.js.map +1 -1
- package/lib/commonjs/utils/getOffersByProductTypes.js +4 -3
- package/lib/commonjs/utils/getOffersByProductTypes.js.map +1 -1
- package/lib/commonjs/utils/resolveProductTypes.js.map +1 -1
- package/lib/module/capabilities/errors/types/index.js +14 -0
- package/lib/module/capabilities/errors/types/index.js.map +1 -0
- package/lib/module/capabilities/offer-catalog/src/api/offerCatalogApi.js +41 -2
- package/lib/module/capabilities/offer-catalog/src/api/offerCatalogApi.js.map +1 -1
- package/lib/module/components/ErrorBoundary/index.js +1 -4
- package/lib/module/components/ErrorBoundary/index.js.map +1 -1
- package/lib/module/components/Modal/AllOffersModal.js +16 -7
- package/lib/module/components/Modal/AllOffersModal.js.map +1 -1
- package/lib/module/components/MoneyLionOfferCarousel.js +96 -44
- package/lib/module/components/MoneyLionOfferCarousel.js.map +1 -1
- package/lib/module/components/utils/errorUtils.js +63 -0
- package/lib/module/components/utils/errorUtils.js.map +1 -0
- package/lib/module/context/ThemeProvider.js +7 -27
- package/lib/module/context/ThemeProvider.js.map +1 -1
- package/lib/module/pageData.js +6 -3
- package/lib/module/pageData.js.map +1 -1
- package/lib/module/services/getDynamicOffers.js +6 -3
- package/lib/module/services/getDynamicOffers.js.map +1 -1
- package/lib/module/services/getProductTypes.js +4 -2
- package/lib/module/services/getProductTypes.js.map +1 -1
- package/lib/module/services/getProductTypesByQuery.js +19 -2
- package/lib/module/services/getProductTypesByQuery.js.map +1 -1
- package/lib/module/utils/getOffersByProductTypes.js +4 -3
- package/lib/module/utils/getOffersByProductTypes.js.map +1 -1
- package/lib/module/utils/resolveProductTypes.js.map +1 -1
- package/lib/typescript/src/capabilities/errors/types/index.d.ts +26 -0
- package/lib/typescript/src/capabilities/errors/types/index.d.ts.map +1 -0
- package/lib/typescript/src/capabilities/offer-catalog/src/api/offerCatalogApi.d.ts +3 -2
- package/lib/typescript/src/capabilities/offer-catalog/src/api/offerCatalogApi.d.ts.map +1 -1
- package/lib/typescript/src/components/ErrorBoundary/index.d.ts +1 -2
- package/lib/typescript/src/components/ErrorBoundary/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Modal/AllOffersModal.d.ts.map +1 -1
- package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts +12 -3
- package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts.map +1 -1
- package/lib/typescript/src/components/utils/errorUtils.d.ts +22 -0
- package/lib/typescript/src/components/utils/errorUtils.d.ts.map +1 -0
- package/lib/typescript/src/context/ThemeProvider.d.ts +0 -3
- package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/pageData.d.ts +3 -1
- package/lib/typescript/src/pageData.d.ts.map +1 -1
- package/lib/typescript/src/services/getDynamicOffers.d.ts +3 -1
- package/lib/typescript/src/services/getDynamicOffers.d.ts.map +1 -1
- package/lib/typescript/src/services/getProductTypes.d.ts +3 -1
- package/lib/typescript/src/services/getProductTypes.d.ts.map +1 -1
- package/lib/typescript/src/services/getProductTypesByQuery.d.ts +3 -1
- package/lib/typescript/src/services/getProductTypesByQuery.d.ts.map +1 -1
- package/lib/typescript/src/utils/getOffersByProductTypes.d.ts +3 -1
- package/lib/typescript/src/utils/getOffersByProductTypes.d.ts.map +1 -1
- package/lib/typescript/src/utils/resolveProductTypes.d.ts +2 -0
- package/lib/typescript/src/utils/resolveProductTypes.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/capabilities/errors/types/index.ts +39 -0
- package/src/capabilities/offer-catalog/src/api/offerCatalogApi.ts +49 -6
- package/src/components/ErrorBoundary/index.tsx +2 -6
- package/src/components/Modal/AllOffersModal.tsx +18 -8
- package/src/components/MoneyLionOfferCarousel.tsx +130 -49
- package/src/components/utils/errorUtils.ts +70 -0
- package/src/context/ThemeProvider.tsx +16 -36
- package/src/pageData.ts +5 -0
- package/src/services/getDynamicOffers.ts +5 -0
- package/src/services/getProductTypes.ts +4 -0
- package/src/services/getProductTypesByQuery.ts +24 -1
- package/src/utils/getOffersByProductTypes.ts +6 -2
- package/src/utils/resolveProductTypes.ts +2 -0
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState,
|
|
6
|
+
type Dispatch,
|
|
7
|
+
type SetStateAction,
|
|
8
|
+
} from "react";
|
|
2
9
|
import { localCnfContext } from "../config/mocks/cnfContext";
|
|
3
10
|
import { Text } from "react-native";
|
|
4
11
|
import { getPageData, type GetPageDataParams } from "../pageData";
|
|
@@ -19,6 +26,15 @@ import { getThemeColors } from "../utils/getThemeColors";
|
|
|
19
26
|
import type { UserData } from "../capabilities/configuration/src/userData/types";
|
|
20
27
|
import type { ReshapedThemeColors } from "../capabilities/core/src";
|
|
21
28
|
import ErrorBoundary from "./ErrorBoundary";
|
|
29
|
+
import { VERSION } from "../version";
|
|
30
|
+
import type {
|
|
31
|
+
ApiError,
|
|
32
|
+
InternalMoneyLionOfferCarouselError,
|
|
33
|
+
InternalOnErrorCallback,
|
|
34
|
+
OnErrorCallback,
|
|
35
|
+
} from "../capabilities/errors/types";
|
|
36
|
+
import { ErrorCodes } from "../capabilities/errors/types";
|
|
37
|
+
import { getApiErrorLogProperties } from "./utils/errorUtils";
|
|
22
38
|
|
|
23
39
|
export type CustomError = {
|
|
24
40
|
code?: number;
|
|
@@ -44,10 +60,19 @@ export type MoneyLionOfferCarouselProps = {
|
|
|
44
60
|
subtitle?: string;
|
|
45
61
|
isDarkTheme?: boolean;
|
|
46
62
|
fallbackUI?: React.ReactNode;
|
|
47
|
-
onError?:
|
|
63
|
+
onError?: OnErrorCallback;
|
|
48
64
|
onLoad?: (numOffers: number) => void;
|
|
49
65
|
userData?: UserData;
|
|
50
|
-
}
|
|
66
|
+
} & Omit<EventHandlerContextType, "rateTableUuid" | "leadUuid">;
|
|
67
|
+
|
|
68
|
+
export interface InternalMoneyLionOfferCarouselProps
|
|
69
|
+
extends Omit<MoneyLionOfferCarouselProps, "fallbackUI"> {
|
|
70
|
+
context: CnfContext | null;
|
|
71
|
+
pageData: any;
|
|
72
|
+
setContext: Dispatch<SetStateAction<CnfContext | null>>;
|
|
73
|
+
setPageData: Dispatch<SetStateAction<any>>;
|
|
74
|
+
onError?: InternalOnErrorCallback;
|
|
75
|
+
}
|
|
51
76
|
|
|
52
77
|
const getConfiguration = async ({
|
|
53
78
|
channel,
|
|
@@ -56,7 +81,7 @@ const getConfiguration = async ({
|
|
|
56
81
|
isDev,
|
|
57
82
|
onError,
|
|
58
83
|
}: Pick<
|
|
59
|
-
|
|
84
|
+
InternalMoneyLionOfferCarouselProps,
|
|
60
85
|
"channel" | "zone" | "subAccountToken" | "isDev" | "onError"
|
|
61
86
|
>) => {
|
|
62
87
|
const url = `${getConfigApiBaseUrl(isDev)}/network/${channel}/${zone}/api/configuration`;
|
|
@@ -67,17 +92,11 @@ const getConfiguration = async ({
|
|
|
67
92
|
try {
|
|
68
93
|
const response = await fetch(url, { headers });
|
|
69
94
|
if (!response.ok) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
onError?.({
|
|
73
|
-
code: errorCode,
|
|
74
|
-
message: `Configuration request failed with status: ${response.status}`,
|
|
75
|
-
timestamp: new Date().toISOString(),
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
throw new Error(
|
|
95
|
+
const error = new Error(
|
|
79
96
|
`Configuration request failed with status: ${response.status}`
|
|
80
|
-
);
|
|
97
|
+
) as ApiError;
|
|
98
|
+
error.code = response.status;
|
|
99
|
+
throw error;
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
const data = await response.json();
|
|
@@ -86,21 +105,26 @@ const getConfiguration = async ({
|
|
|
86
105
|
} catch (error) {
|
|
87
106
|
console.error("Error fetching configuration", error);
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
onError?.({
|
|
109
|
+
message: "Failed to fetch configuration",
|
|
110
|
+
error,
|
|
111
|
+
additionalInfo: {
|
|
112
|
+
channel,
|
|
113
|
+
zone,
|
|
114
|
+
isDev,
|
|
115
|
+
},
|
|
116
|
+
...(getApiErrorLogProperties(error) || {
|
|
117
|
+
code: ErrorCodes.FLOW_ERROR,
|
|
118
|
+
severity: "error",
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
96
121
|
|
|
97
122
|
return localCnfContext;
|
|
98
123
|
}
|
|
99
124
|
};
|
|
100
125
|
|
|
101
126
|
const InternalMoneyLionOfferCarousel = (
|
|
102
|
-
props:
|
|
103
|
-
Omit<EventHandlerContextType, "rateTableUuid" | "leadUuid">
|
|
127
|
+
props: InternalMoneyLionOfferCarouselProps
|
|
104
128
|
) => {
|
|
105
129
|
const {
|
|
106
130
|
channel,
|
|
@@ -121,6 +145,10 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
121
145
|
showProductTypeLabel: _showProductTypeLabel,
|
|
122
146
|
showCardBorder: _showCardBorder,
|
|
123
147
|
userData,
|
|
148
|
+
context,
|
|
149
|
+
setContext,
|
|
150
|
+
pageData,
|
|
151
|
+
setPageData,
|
|
124
152
|
} = props;
|
|
125
153
|
|
|
126
154
|
const {
|
|
@@ -166,11 +194,9 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
166
194
|
]
|
|
167
195
|
);
|
|
168
196
|
|
|
169
|
-
const [context, setContext] = useState<CnfContext | null>(null);
|
|
170
197
|
const [isFetchingData, setIsFetchingData] = useState(true);
|
|
171
198
|
const [isFetchingConfig, setIsFetchingConfig] = useState(true);
|
|
172
199
|
const [error, setError] = useState<Error | null>(null);
|
|
173
|
-
const [pageData, setPageData] = useState<any>(null);
|
|
174
200
|
|
|
175
201
|
const isLoading = isFetchingData || isFetchingConfig;
|
|
176
202
|
|
|
@@ -194,13 +220,19 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
194
220
|
|
|
195
221
|
setError(errorObj);
|
|
196
222
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
onError?.({
|
|
224
|
+
message: "Failed to fetch configuration",
|
|
225
|
+
error: errorObj,
|
|
226
|
+
additionalInfo: {
|
|
227
|
+
channel,
|
|
228
|
+
zone,
|
|
229
|
+
isDev,
|
|
230
|
+
},
|
|
231
|
+
...(getApiErrorLogProperties(errorObj) || {
|
|
232
|
+
code: ErrorCodes.FLOW_ERROR,
|
|
233
|
+
severity: "error",
|
|
234
|
+
}),
|
|
235
|
+
});
|
|
204
236
|
} finally {
|
|
205
237
|
setIsFetchingConfig(false);
|
|
206
238
|
}
|
|
@@ -221,6 +253,23 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
221
253
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
222
254
|
}, [isFetchingConfig]);
|
|
223
255
|
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (!channel || !zone || !subAccountToken || !searchAPIToken) {
|
|
258
|
+
const missingParams: string[] = [];
|
|
259
|
+
if (!channel) missingParams.push("channel");
|
|
260
|
+
if (!zone) missingParams.push("zone");
|
|
261
|
+
if (!subAccountToken) missingParams.push("subAccountToken");
|
|
262
|
+
if (!searchAPIToken) missingParams.push("searchAPIToken");
|
|
263
|
+
|
|
264
|
+
onError?.({
|
|
265
|
+
code: ErrorCodes.MISSING_CONFIG,
|
|
266
|
+
message: `Missing required parameters: ${missingParams.join(", ")}`,
|
|
267
|
+
severity: "error",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
271
|
+
}, [channel, zone, subAccountToken, searchAPIToken]);
|
|
272
|
+
|
|
224
273
|
useEffect(() => {
|
|
225
274
|
const fetchPageData = async () => {
|
|
226
275
|
// Should only fetch data if context is done fetching
|
|
@@ -231,17 +280,9 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
231
280
|
context: { ...context, isDev },
|
|
232
281
|
params: getPageDataParams,
|
|
233
282
|
onRateTableSubmit,
|
|
283
|
+
onError,
|
|
234
284
|
});
|
|
235
285
|
|
|
236
|
-
// Check for API errors in the response
|
|
237
|
-
if (data.isError) {
|
|
238
|
-
onError?.({
|
|
239
|
-
code: data.errorCode,
|
|
240
|
-
message: "Rate table error occurred",
|
|
241
|
-
timestamp: new Date().toISOString(),
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
286
|
onRateTableResponse?.({
|
|
246
287
|
timestamp: new Date().toISOString(),
|
|
247
288
|
isError: Boolean(data.isError),
|
|
@@ -257,8 +298,16 @@ const InternalMoneyLionOfferCarousel = (
|
|
|
257
298
|
err instanceof Error ? err : new Error("Failed to fetch page data");
|
|
258
299
|
|
|
259
300
|
onError?.({
|
|
260
|
-
message:
|
|
261
|
-
|
|
301
|
+
message: "Failed to fetch page data",
|
|
302
|
+
error: errorObj,
|
|
303
|
+
additionalInfo: {
|
|
304
|
+
context,
|
|
305
|
+
pageDataParams: getPageDataParams,
|
|
306
|
+
},
|
|
307
|
+
...(getApiErrorLogProperties(errorObj) || {
|
|
308
|
+
code: ErrorCodes.FLOW_ERROR,
|
|
309
|
+
severity: "error",
|
|
310
|
+
}),
|
|
262
311
|
});
|
|
263
312
|
} finally {
|
|
264
313
|
setIsFetchingData(false);
|
|
@@ -343,15 +392,47 @@ export const MoneyLionOfferCarousel = (
|
|
|
343
392
|
props: MoneyLionOfferCarouselProps &
|
|
344
393
|
Omit<EventHandlerContextType, "rateTableUuid" | "leadUuid">
|
|
345
394
|
) => {
|
|
346
|
-
const { fallbackUI, onError } = props;
|
|
395
|
+
const { fallbackUI, onError: _onError } = props;
|
|
396
|
+
const [context, setContext] = useState<CnfContext | null>(null);
|
|
397
|
+
const [pageData, setPageData] = useState<any>(null);
|
|
347
398
|
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
399
|
+
const onError = useCallback(
|
|
400
|
+
(err: InternalMoneyLionOfferCarouselError): void => {
|
|
401
|
+
_onError?.({
|
|
402
|
+
timestamp: new Date().toISOString(),
|
|
403
|
+
sdkVersion: VERSION,
|
|
404
|
+
...err,
|
|
405
|
+
});
|
|
406
|
+
},
|
|
407
|
+
[_onError]
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const logErrorBoundary = useCallback(
|
|
411
|
+
(error: Error) => {
|
|
412
|
+
onError?.({
|
|
413
|
+
code: ErrorCodes.UI_CRASH,
|
|
414
|
+
severity: "error",
|
|
415
|
+
message: "Error boundary crash",
|
|
416
|
+
error,
|
|
417
|
+
additionalInfo: {
|
|
418
|
+
context,
|
|
419
|
+
pageData,
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
},
|
|
423
|
+
[onError, pageData, context]
|
|
424
|
+
);
|
|
351
425
|
|
|
352
426
|
return (
|
|
353
|
-
<ErrorBoundary fallbackUI={fallbackUI} onError={
|
|
354
|
-
<InternalMoneyLionOfferCarousel
|
|
427
|
+
<ErrorBoundary fallbackUI={fallbackUI} onError={logErrorBoundary}>
|
|
428
|
+
<InternalMoneyLionOfferCarousel
|
|
429
|
+
{...props}
|
|
430
|
+
context={context}
|
|
431
|
+
setContext={setContext}
|
|
432
|
+
pageData={pageData}
|
|
433
|
+
setPageData={setPageData}
|
|
434
|
+
onError={onError}
|
|
435
|
+
/>
|
|
355
436
|
</ErrorBoundary>
|
|
356
437
|
);
|
|
357
438
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ErrorCodes } from "../../capabilities/errors/types";
|
|
2
|
+
import type { InternalMoneyLionOfferCarouselError } from "../../capabilities/errors/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts HTTP status code from the custom Api Error object.
|
|
6
|
+
* Api Error object contains code field which is the HTTP status code.
|
|
7
|
+
*
|
|
8
|
+
* @param error - The error object (can be any type)
|
|
9
|
+
* @returns The HTTP status code if available, null otherwise
|
|
10
|
+
*/
|
|
11
|
+
export const getCustomApiErrorStatusCode = (error: unknown): number | null => {
|
|
12
|
+
// Check if error is an object with code property (custom ApiError pattern)
|
|
13
|
+
if (error && typeof error === "object") {
|
|
14
|
+
if ("code" in error && typeof (error as any).code === "number") {
|
|
15
|
+
return (error as any).code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Builds API error log properties from an error object.
|
|
23
|
+
* Checks if the error is a network error, and categorizes it as:
|
|
24
|
+
* - Client-side error (4xx status codes)
|
|
25
|
+
* - Server-side error (5xx status codes)
|
|
26
|
+
* - Other network error
|
|
27
|
+
*
|
|
28
|
+
* @param error - The error object (can be any type)
|
|
29
|
+
* @param message - The error message to include in the log properties
|
|
30
|
+
* @returns InternalMoneyLionOfferCarouselError object if it's a network error, null otherwise
|
|
31
|
+
*/
|
|
32
|
+
export const getApiErrorLogProperties = (
|
|
33
|
+
error: Error | unknown
|
|
34
|
+
): Omit<InternalMoneyLionOfferCarouselError, "message"> | null => {
|
|
35
|
+
const statusCode = getCustomApiErrorStatusCode(error);
|
|
36
|
+
const logProperties: Omit<
|
|
37
|
+
InternalMoneyLionOfferCarouselError,
|
|
38
|
+
"code" | "message"
|
|
39
|
+
> = {
|
|
40
|
+
severity: "error",
|
|
41
|
+
error,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (statusCode) {
|
|
45
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
46
|
+
// 4xx - Request error (client-side)
|
|
47
|
+
return {
|
|
48
|
+
...logProperties,
|
|
49
|
+
statusCode,
|
|
50
|
+
code: ErrorCodes.NETWORK_REQUEST_ERROR,
|
|
51
|
+
};
|
|
52
|
+
} else if (statusCode >= 500 && statusCode < 600) {
|
|
53
|
+
// 5xx - Server error
|
|
54
|
+
return {
|
|
55
|
+
...logProperties,
|
|
56
|
+
statusCode,
|
|
57
|
+
code: ErrorCodes.NETWORK_SERVER_ERROR,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Other status codes
|
|
61
|
+
return {
|
|
62
|
+
...logProperties,
|
|
63
|
+
statusCode,
|
|
64
|
+
code: ErrorCodes.NETWORK_OTHER_ERROR,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Not a network error (no status code and not a timeout)
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
@@ -3,7 +3,7 @@ import React, {
|
|
|
3
3
|
type FC,
|
|
4
4
|
type ReactNode,
|
|
5
5
|
useContext,
|
|
6
|
-
|
|
6
|
+
useMemo,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { Platform } from "react-native";
|
|
9
9
|
|
|
@@ -69,9 +69,6 @@ interface ThemeContextType {
|
|
|
69
69
|
theme: ThemeColors;
|
|
70
70
|
fontFamily: FontFamily;
|
|
71
71
|
isDarkTheme: boolean;
|
|
72
|
-
updateTheme: (newTheme: Partial<ThemeColors>) => void;
|
|
73
|
-
updateFontFamily: (newFontFamily: Partial<FontFamily>) => void;
|
|
74
|
-
setIsDarkTheme: (isDark: boolean) => void;
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
export const ThemeContext = createContext<ThemeContextType | null>(null);
|
|
@@ -97,39 +94,22 @@ export const ThemeProvider: FC<ThemeProviderProps> = ({
|
|
|
97
94
|
isDarkTheme = false,
|
|
98
95
|
children,
|
|
99
96
|
}) => {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const updateTheme = (newTheme: Partial<ThemeColors>) => {
|
|
108
|
-
setTheme((prevTheme) => ({
|
|
109
|
-
...prevTheme,
|
|
110
|
-
...newTheme,
|
|
111
|
-
}));
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const updateFontFamily = (newFontFamily: Partial<FontFamily>) => {
|
|
115
|
-
setFonts((prevFonts) => ({
|
|
116
|
-
...prevFonts,
|
|
117
|
-
...newFontFamily,
|
|
118
|
-
}));
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const setIsDarkTheme = (isDark: boolean) => {
|
|
122
|
-
setDarkTheme(isDark);
|
|
123
|
-
};
|
|
97
|
+
const fonts = useMemo<FontFamily>(
|
|
98
|
+
() => ({
|
|
99
|
+
...defaultFontFamily,
|
|
100
|
+
...fontFamily,
|
|
101
|
+
}),
|
|
102
|
+
[fontFamily]
|
|
103
|
+
);
|
|
124
104
|
|
|
125
|
-
const contextValue =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
105
|
+
const contextValue = useMemo<ThemeContextType>(
|
|
106
|
+
() => ({
|
|
107
|
+
theme: themeColors,
|
|
108
|
+
fontFamily: fonts,
|
|
109
|
+
isDarkTheme,
|
|
110
|
+
}),
|
|
111
|
+
[themeColors, fonts, isDarkTheme]
|
|
112
|
+
);
|
|
133
113
|
|
|
134
114
|
return (
|
|
135
115
|
<ThemeContext.Provider value={contextValue}>
|
package/src/pageData.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { UserData } from "./capabilities/configuration/src/userData/types";
|
|
2
2
|
import type { CnfContext } from "./capabilities/core/src/system/cnfContext/CnfContext";
|
|
3
|
+
import type { InternalOnErrorCallback } from "./capabilities/errors/types";
|
|
3
4
|
import { getIsDevEnvironment } from "./capabilities/offer-catalog/src/utils/contextUtil";
|
|
4
5
|
|
|
5
6
|
import type { onRateTableSubmitProps } from "./context/EventHandlerProvider";
|
|
@@ -26,12 +27,14 @@ type GetPageDataProps = {
|
|
|
26
27
|
context: CnfContext;
|
|
27
28
|
params: GetPageDataParams;
|
|
28
29
|
onRateTableSubmit?: (props: onRateTableSubmitProps) => void;
|
|
30
|
+
onError?: InternalOnErrorCallback;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export async function getPageData({
|
|
32
34
|
context,
|
|
33
35
|
params,
|
|
34
36
|
onRateTableSubmit,
|
|
37
|
+
onError,
|
|
35
38
|
}: GetPageDataProps) {
|
|
36
39
|
const {
|
|
37
40
|
productType,
|
|
@@ -69,6 +72,7 @@ export async function getPageData({
|
|
|
69
72
|
query,
|
|
70
73
|
searchAPIToken,
|
|
71
74
|
isDev,
|
|
75
|
+
onError,
|
|
72
76
|
});
|
|
73
77
|
|
|
74
78
|
onRateTableSubmit?.({
|
|
@@ -90,6 +94,7 @@ export async function getPageData({
|
|
|
90
94
|
context,
|
|
91
95
|
defaultProductType,
|
|
92
96
|
userData,
|
|
97
|
+
onError,
|
|
93
98
|
});
|
|
94
99
|
|
|
95
100
|
return {
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
} from "../capabilities/offer-catalog/src";
|
|
18
18
|
import type { UserData } from "../capabilities/configuration/src/userData/types";
|
|
19
19
|
import type { CnfContext } from "../capabilities/core/src/system/cnfContext/CnfContext";
|
|
20
|
+
import type { InternalOnErrorCallback } from "../capabilities/errors/types";
|
|
20
21
|
|
|
21
22
|
export type GetDynamicOffersProps = {
|
|
22
23
|
tags: string;
|
|
@@ -27,6 +28,7 @@ export type GetDynamicOffersProps = {
|
|
|
27
28
|
partnersOverrideDefinition: PartnerOverride[];
|
|
28
29
|
context: CnfContext;
|
|
29
30
|
userData?: UserData;
|
|
31
|
+
onError?: InternalOnErrorCallback;
|
|
30
32
|
} & Omit<
|
|
31
33
|
GetProductTypesProps,
|
|
32
34
|
| "context"
|
|
@@ -53,6 +55,7 @@ export const getDynamicOffers = async ({
|
|
|
53
55
|
partnersOverrideDefinition,
|
|
54
56
|
context,
|
|
55
57
|
userData,
|
|
58
|
+
onError,
|
|
56
59
|
}: Omit<GetDynamicOffersProps, "isDev">) => {
|
|
57
60
|
// Get the initial offers
|
|
58
61
|
let { offers, rateTableUuid, isError, leadUuid, errorCode } =
|
|
@@ -63,6 +66,7 @@ export const getDynamicOffers = async ({
|
|
|
63
66
|
context,
|
|
64
67
|
userData,
|
|
65
68
|
enablePolling: true,
|
|
69
|
+
onError,
|
|
66
70
|
});
|
|
67
71
|
|
|
68
72
|
// If no offers found, fall back to the default product type
|
|
@@ -73,6 +77,7 @@ export const getDynamicOffers = async ({
|
|
|
73
77
|
tags,
|
|
74
78
|
context,
|
|
75
79
|
userData,
|
|
80
|
+
onError,
|
|
76
81
|
});
|
|
77
82
|
|
|
78
83
|
// Update the offers, rateTableUuid, isError, leafUuid and productTypes
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InternalOnErrorCallback } from "../capabilities/errors/types";
|
|
1
2
|
import { getProductTypesByQuery } from "./getProductTypesByQuery";
|
|
2
3
|
|
|
3
4
|
export type GetProductTypesProps = {
|
|
@@ -6,6 +7,7 @@ export type GetProductTypesProps = {
|
|
|
6
7
|
defaultProductType: string;
|
|
7
8
|
searchAPIToken: string;
|
|
8
9
|
isDev: boolean;
|
|
10
|
+
onError?: InternalOnErrorCallback;
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
export type ResultType = "STATIC" | "QUERY" | "PRODUCT_TYPE" | "DEFAULT";
|
|
@@ -32,12 +34,14 @@ export const getProductTypes = async ({
|
|
|
32
34
|
query,
|
|
33
35
|
searchAPIToken,
|
|
34
36
|
isDev,
|
|
37
|
+
onError,
|
|
35
38
|
}: GetProductTypesProps): Promise<ProductTypesResult> => {
|
|
36
39
|
if (query && query.trim().length > 0) {
|
|
37
40
|
const productTypes = await getProductTypesByQuery({
|
|
38
41
|
query,
|
|
39
42
|
searchAPIToken,
|
|
40
43
|
isDev,
|
|
44
|
+
onError,
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
return { productTypes, resultType: "QUERY" };
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { pipe } from "effect";
|
|
2
2
|
import { getEwsSearchApiBaseUrl } from "../apiEnvironment";
|
|
3
3
|
import { orderProductTypesByRank } from "../capabilities/core/src/domain/third-party-search";
|
|
4
|
+
import {
|
|
5
|
+
ErrorCodes,
|
|
6
|
+
type ApiError,
|
|
7
|
+
type InternalOnErrorCallback,
|
|
8
|
+
} from "../capabilities/errors/types";
|
|
9
|
+
import { getApiErrorLogProperties } from "../components/utils/errorUtils";
|
|
4
10
|
|
|
5
11
|
type GetProductTypesByQueryProps = {
|
|
6
12
|
query: string;
|
|
7
13
|
searchAPIToken: string;
|
|
8
14
|
isDev: boolean;
|
|
15
|
+
onError?: InternalOnErrorCallback;
|
|
9
16
|
};
|
|
10
17
|
|
|
11
18
|
/**
|
|
@@ -33,6 +40,7 @@ export const getProductTypesByQuery = async ({
|
|
|
33
40
|
query,
|
|
34
41
|
searchAPIToken,
|
|
35
42
|
isDev,
|
|
43
|
+
onError,
|
|
36
44
|
}: GetProductTypesByQueryProps) => {
|
|
37
45
|
const url = `${getEwsSearchApiBaseUrl(isDev)}/search/product-types?query=${encodeURIComponent(query)}`;
|
|
38
46
|
const headers = {
|
|
@@ -41,13 +49,28 @@ export const getProductTypesByQuery = async ({
|
|
|
41
49
|
try {
|
|
42
50
|
const response = await fetch(url, { headers });
|
|
43
51
|
if (!response.ok) {
|
|
44
|
-
|
|
52
|
+
const error = new Error() as ApiError;
|
|
53
|
+
error.code = response.status;
|
|
54
|
+
throw error;
|
|
45
55
|
}
|
|
46
56
|
const productTypes = await response.json();
|
|
47
57
|
|
|
48
58
|
return pipe(productTypes, orderProductTypesByRank);
|
|
49
59
|
} catch (error) {
|
|
50
60
|
console.error("Error fetching ProductTypesByQuery", JSON.stringify(error));
|
|
61
|
+
onError?.({
|
|
62
|
+
message: "Failed to fetch product types by query",
|
|
63
|
+
error,
|
|
64
|
+
additionalInfo: {
|
|
65
|
+
query,
|
|
66
|
+
isDev,
|
|
67
|
+
},
|
|
68
|
+
...(getApiErrorLogProperties(error) || {
|
|
69
|
+
code: ErrorCodes.FLOW_ERROR,
|
|
70
|
+
severity: "error",
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
|
|
51
74
|
return [];
|
|
52
75
|
}
|
|
53
76
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { UserData } from "../capabilities/configuration/src/userData/types";
|
|
2
2
|
import type { CnfContext } from "../capabilities/core/src/system/cnfContext/CnfContext";
|
|
3
|
+
import type { InternalOnErrorCallback } from "../capabilities/errors/types";
|
|
3
4
|
import {
|
|
4
5
|
getCachedOffersByProductTypes,
|
|
5
6
|
getOffersForProductTypes,
|
|
@@ -23,6 +24,7 @@ export const getOffersByProductTypes = async ({
|
|
|
23
24
|
context,
|
|
24
25
|
userData,
|
|
25
26
|
enablePolling = false,
|
|
27
|
+
onError,
|
|
26
28
|
}: {
|
|
27
29
|
isCachedOffersRequest: boolean;
|
|
28
30
|
productTypes: string[];
|
|
@@ -30,10 +32,11 @@ export const getOffersByProductTypes = async ({
|
|
|
30
32
|
context: CnfContext;
|
|
31
33
|
userData?: UserData;
|
|
32
34
|
enablePolling?: boolean;
|
|
35
|
+
onError?: InternalOnErrorCallback;
|
|
33
36
|
}) => {
|
|
34
37
|
if (isCachedOffersRequest) {
|
|
35
38
|
const { offers, uuid, isError, leadUuid, errorCode } =
|
|
36
|
-
await getCachedOffersByProductTypes(context)(productTypes);
|
|
39
|
+
await getCachedOffersByProductTypes(context)(productTypes, onError);
|
|
37
40
|
|
|
38
41
|
return { offers, rateTableUuid: uuid, isError, leadUuid, errorCode };
|
|
39
42
|
}
|
|
@@ -43,7 +46,8 @@ export const getOffersByProductTypes = async ({
|
|
|
43
46
|
productTypes,
|
|
44
47
|
parseClientTags(tags),
|
|
45
48
|
userData,
|
|
46
|
-
enablePolling
|
|
49
|
+
enablePolling,
|
|
50
|
+
onError
|
|
47
51
|
);
|
|
48
52
|
|
|
49
53
|
return { offers, rateTableUuid: uuid, isError, leadUuid, errorCode };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InternalOnErrorCallback } from "../capabilities/errors/types";
|
|
1
2
|
import { getProductTypes, type ResultType } from "../services/getProductTypes";
|
|
2
3
|
|
|
3
4
|
type ResolveProductTypesProps = {
|
|
@@ -6,6 +7,7 @@ type ResolveProductTypesProps = {
|
|
|
6
7
|
defaultProductType: string;
|
|
7
8
|
searchAPIToken: string;
|
|
8
9
|
isDev: boolean;
|
|
10
|
+
onError?: InternalOnErrorCallback;
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
export const resolveProductTypes = async (
|