@moneylion/react-native-offer-carousel 1.0.9 → 1.1.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.
Files changed (39) hide show
  1. package/lib/commonjs/capabilities/offer-catalog/src/api/offerCatalogApi.js +22 -6
  2. package/lib/commonjs/capabilities/offer-catalog/src/api/offerCatalogApi.js.map +1 -1
  3. package/lib/commonjs/components/MoneyLionOfferCarousel.js +68 -24
  4. package/lib/commonjs/components/MoneyLionOfferCarousel.js.map +1 -1
  5. package/lib/commonjs/pageData.js +5 -3
  6. package/lib/commonjs/pageData.js.map +1 -1
  7. package/lib/commonjs/services/getDynamicOffers.js +30 -19
  8. package/lib/commonjs/services/getDynamicOffers.js.map +1 -1
  9. package/lib/commonjs/utils/getOffersByProductTypes.js +31 -6
  10. package/lib/commonjs/utils/getOffersByProductTypes.js.map +1 -1
  11. package/lib/module/capabilities/offer-catalog/src/api/offerCatalogApi.js +22 -6
  12. package/lib/module/capabilities/offer-catalog/src/api/offerCatalogApi.js.map +1 -1
  13. package/lib/module/components/MoneyLionOfferCarousel.js +68 -24
  14. package/lib/module/components/MoneyLionOfferCarousel.js.map +1 -1
  15. package/lib/module/pageData.js +5 -3
  16. package/lib/module/pageData.js.map +1 -1
  17. package/lib/module/services/getDynamicOffers.js +30 -19
  18. package/lib/module/services/getDynamicOffers.js.map +1 -1
  19. package/lib/module/utils/getOffersByProductTypes.js +31 -6
  20. package/lib/module/utils/getOffersByProductTypes.js.map +1 -1
  21. package/lib/typescript/src/capabilities/offer-catalog/src/api/offerCatalogApi.d.ts +14 -1
  22. package/lib/typescript/src/capabilities/offer-catalog/src/api/offerCatalogApi.d.ts.map +1 -1
  23. package/lib/typescript/src/capabilities/offer-catalog/src/types/offerCatalogSchema.d.ts +1 -0
  24. package/lib/typescript/src/capabilities/offer-catalog/src/types/offerCatalogSchema.d.ts.map +1 -1
  25. package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts +6 -0
  26. package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts.map +1 -1
  27. package/lib/typescript/src/pageData.d.ts +1 -0
  28. package/lib/typescript/src/pageData.d.ts.map +1 -1
  29. package/lib/typescript/src/services/getDynamicOffers.d.ts +3 -2
  30. package/lib/typescript/src/services/getDynamicOffers.d.ts.map +1 -1
  31. package/lib/typescript/src/utils/getOffersByProductTypes.d.ts +10 -4
  32. package/lib/typescript/src/utils/getOffersByProductTypes.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/capabilities/offer-catalog/src/api/offerCatalogApi.ts +24 -6
  35. package/src/capabilities/offer-catalog/src/types/offerCatalogSchema.ts +1 -0
  36. package/src/components/MoneyLionOfferCarousel.tsx +91 -21
  37. package/src/pageData.ts +14 -12
  38. package/src/services/getDynamicOffers.ts +31 -27
  39. package/src/utils/getOffersByProductTypes.ts +15 -10
@@ -32,6 +32,12 @@ export type MoneyLionOfferCarouselProps = {
32
32
  isDev: boolean;
33
33
  showCardBorder?: boolean;
34
34
  showDescriptionPoints?: boolean;
35
+ onError?: (error: {
36
+ code?: number;
37
+ message: string;
38
+ timestamp: string;
39
+ }) => void;
40
+ onLoad?: (numOffers: number) => void;
35
41
  };
36
42
 
37
43
  const getConfiguration = async ({
@@ -39,9 +45,10 @@ const getConfiguration = async ({
39
45
  zone,
40
46
  subAccountToken,
41
47
  isDev,
48
+ onError,
42
49
  }: Pick<
43
50
  MoneyLionOfferCarouselProps,
44
- "channel" | "zone" | "subAccountToken" | "isDev"
51
+ "channel" | "zone" | "subAccountToken" | "isDev" | "onError"
45
52
  >) => {
46
53
  const url = `${getConfigApiBaseUrl(isDev)}/network/${channel}/${zone}/api/configuration`;
47
54
 
@@ -51,13 +58,33 @@ const getConfiguration = async ({
51
58
  try {
52
59
  const response = await fetch(url, { headers });
53
60
  if (!response.ok) {
54
- throw new Error();
61
+ const errorCode = response.status;
62
+
63
+ onError?.({
64
+ code: errorCode,
65
+ message: `Configuration request failed with status: ${response.status}`,
66
+ timestamp: new Date().toISOString(),
67
+ });
68
+
69
+ throw new Error(
70
+ `Configuration request failed with status: ${response.status}`
71
+ );
55
72
  }
73
+
56
74
  const data = await response.json();
57
75
 
58
76
  return data;
59
77
  } catch (error) {
60
78
  console.error("Error fetching configuration", error);
79
+
80
+ if (!(error instanceof Error && error.message.includes("status"))) {
81
+ // Call if network error not already reported
82
+ onError?.({
83
+ message: "Network request failed",
84
+ timestamp: new Date().toISOString(),
85
+ });
86
+ }
87
+
61
88
  return localCnfContext;
62
89
  }
63
90
  };
@@ -73,6 +100,8 @@ export const MoneyLionOfferCarousel = (
73
100
  fontFamily,
74
101
  isDev,
75
102
  showDescriptionPoints = true,
103
+ onError,
104
+ onLoad,
76
105
  } = props;
77
106
 
78
107
  const {
@@ -109,21 +138,31 @@ export const MoneyLionOfferCarousel = (
109
138
  zone,
110
139
  subAccountToken,
111
140
  isDev,
141
+ onError,
112
142
  });
113
143
  setContext(data.serializableContext as CnfContext);
114
144
  } catch (err) {
115
- setError(
145
+ const errorObj =
116
146
  err instanceof Error
117
147
  ? err
118
- : new Error("Failed to fetch configuration")
119
- );
148
+ : new Error("Failed to fetch configuration");
149
+
150
+ setError(errorObj);
151
+
152
+ // Only call onError if not already called in getConfiguration
153
+ if (!errorObj.message.includes("status")) {
154
+ onError?.({
155
+ message: errorObj.message,
156
+ timestamp: new Date().toISOString(),
157
+ });
158
+ }
120
159
  } finally {
121
160
  setIsLoading(false);
122
161
  }
123
162
  };
124
163
 
125
164
  fetchConfiguration();
126
- }, [channel, zone, subAccountToken, isDev]);
165
+ }, [channel, zone, subAccountToken, isDev, onError]);
127
166
 
128
167
  useEffect(() => {
129
168
  if (isLoading) {
@@ -134,24 +173,55 @@ export const MoneyLionOfferCarousel = (
134
173
  useEffect(() => {
135
174
  const fetchPageData = async () => {
136
175
  if (context) {
137
- const data = await getPageData({
138
- context: { ...context, isDev },
139
- params: props,
140
- onRateTableSubmit,
141
- });
142
- onRateTableResponse?.({
143
- timestamp: new Date().toISOString(),
144
- isError: Boolean(data.isError),
145
- offers: data.offers,
146
- rateTableUuid: data.rateTableUuid,
147
- leadUuid: data.leadUuid,
148
- });
149
- setPageData(data);
176
+ try {
177
+ const data = await getPageData({
178
+ context: { ...context, isDev },
179
+ params: props,
180
+ onRateTableSubmit,
181
+ });
182
+
183
+ // Check for API errors in the response
184
+ if (data.isError) {
185
+ onError?.({
186
+ code: data.errorCode,
187
+ message: "Rate table error occurred",
188
+ timestamp: new Date().toISOString(),
189
+ });
190
+ }
191
+
192
+ onRateTableResponse?.({
193
+ timestamp: new Date().toISOString(),
194
+ isError: Boolean(data.isError),
195
+ offers: data.offers,
196
+ rateTableUuid: data.rateTableUuid,
197
+ leadUuid: data.leadUuid,
198
+ });
199
+
200
+ setPageData(data);
201
+ onLoad?.(data.offers.length);
202
+ } catch (err) {
203
+ const errorObj =
204
+ err instanceof Error ? err : new Error("Failed to fetch page data");
205
+
206
+ onError?.({
207
+ message: errorObj.message,
208
+ timestamp: new Date().toISOString(),
209
+ });
210
+ }
150
211
  }
151
212
  };
152
213
 
153
214
  fetchPageData();
154
- }, [context, isDev, onRateTableResponse, onRateTableSubmit, props]);
215
+ }, [
216
+ context,
217
+ isDev,
218
+ onRateTableResponse,
219
+ onRateTableSubmit,
220
+ props,
221
+ isLoading,
222
+ onError,
223
+ onLoad,
224
+ ]);
155
225
 
156
226
  if (isLoading) {
157
227
  return <DynamicOfferSkeleton displayLayout={"fixed"} />;
@@ -162,7 +232,7 @@ export const MoneyLionOfferCarousel = (
162
232
  }
163
233
 
164
234
  if (!context) {
165
- return <Text>{"Context is null"}</Text>; // Replace with your error component
235
+ return <Text>{"Context is null"}</Text>;
166
236
  }
167
237
 
168
238
  if (!pageData) {
package/src/pageData.ts CHANGED
@@ -37,7 +37,7 @@ export async function getPageData({
37
37
  // Resolve Tags
38
38
  const resolvedTags = `${tags?.trim()}`;
39
39
 
40
- const enableUseCachedOffers = getEnableUseCachedOffers(context);
40
+ const isCachedOffersRequest = getEnableUseCachedOffers(context);
41
41
 
42
42
  const defaultProductType = getDefaultProductType(context);
43
43
 
@@ -72,20 +72,22 @@ export async function getPageData({
72
72
  });
73
73
 
74
74
  // Get offers
75
- const { offers, isError, leadUuid, rateTableUuid } = await getDynamicOffers({
76
- tags: safeTags,
77
- displayLayout: safeDisplayLayout,
78
- productTypes,
79
- resultType,
80
- enableUseCachedOffers,
81
- productTypesDefinition: [...productTypesDefinition],
82
- partnersOverrideDefinition: [...partnersOverrideDefinition],
83
- context,
84
- defaultProductType,
85
- });
75
+ const { offers, isError, leadUuid, rateTableUuid, errorCode } =
76
+ await getDynamicOffers({
77
+ tags: safeTags,
78
+ displayLayout: safeDisplayLayout,
79
+ productTypes,
80
+ resultType,
81
+ isCachedOffersRequest,
82
+ productTypesDefinition: [...productTypesDefinition],
83
+ partnersOverrideDefinition: [...partnersOverrideDefinition],
84
+ context,
85
+ defaultProductType,
86
+ });
86
87
 
87
88
  return {
88
89
  isError,
90
+ errorCode,
89
91
  leadUuid,
90
92
  rateTableUuid,
91
93
  // title,
@@ -24,7 +24,7 @@ export type GetDynamicOffersProps = {
24
24
  displayLayout: Layout;
25
25
  productTypes: string[];
26
26
  resultType: ProductTypesResult["resultType"];
27
- enableUseCachedOffers: boolean;
27
+ isCachedOffersRequest: boolean;
28
28
  productTypesDefinition: ProductTypeDefinition[];
29
29
  partnersOverrideDefinition: PartnerOverride[];
30
30
  context: CnfContext;
@@ -50,49 +50,53 @@ export const getDynamicOffers = async ({
50
50
  defaultProductType,
51
51
  productTypes,
52
52
  resultType,
53
- enableUseCachedOffers,
53
+ isCachedOffersRequest,
54
54
  productTypesDefinition,
55
55
  partnersOverrideDefinition,
56
56
  context,
57
57
  }: Omit<GetDynamicOffersProps, "isDev">) => {
58
58
  // Get the initial offers
59
- const initialOffersData = await getOffersByProductTypes({
60
- enableUseCachedOffers,
61
- productTypes,
62
- tags,
63
- context,
64
- });
59
+ let { offers, rateTableUuid, isError, leadUuid, errorCode } =
60
+ await getOffersByProductTypes({
61
+ isCachedOffersRequest,
62
+ productTypes,
63
+ tags,
64
+ context,
65
+ });
65
66
 
66
- // if no offers are returned, we need to get the offers for the default product type
67
- if (isEmpty(initialOffersData?.offers)) {
68
- // Set the productTypes to be defaultProductType
67
+ // If no offers found, fall back to the default product type
68
+ if (isEmpty(offers)) {
69
+ const fallbackResult = await getOffersByProductTypes({
70
+ isCachedOffersRequest,
71
+ productTypes: [defaultProductType],
72
+ tags,
73
+ context,
74
+ });
75
+
76
+ // Update the offers, rateTableUuid, isError, leafUuid and productTypes
77
+ offers = fallbackResult.offers;
78
+ rateTableUuid = fallbackResult.rateTableUuid;
79
+ isError = fallbackResult.isError;
80
+ leadUuid = fallbackResult.leadUuid;
69
81
  productTypes = [defaultProductType];
82
+ errorCode = fallbackResult.errorCode;
70
83
  }
71
84
 
72
- // if initial offers are returned, return them
73
- const offersData = isEmpty(initialOffersData?.offers)
74
- ? await getOffersByProductTypes({
75
- enableUseCachedOffers,
76
- productTypes,
77
- tags,
78
- context,
79
- })
80
- : initialOffersData;
81
-
82
85
  const sortedOffers =
83
86
  resultType === "QUERY"
84
- ? selectDynamicOffers(offersData.offers, productTypes)
85
- : sortOffersByRecommendationScore(offersData.offers);
87
+ ? selectDynamicOffers(offers, productTypes)
88
+ : sortOffersByRecommendationScore(offers);
86
89
 
87
90
  return {
88
91
  offers: pipe(
89
92
  sortedOffers,
90
93
  formatOfferUrl(productTypesDefinition, partnersOverrideDefinition),
91
- addClientTagsToOfferLinks(enableUseCachedOffers, tags),
94
+ addClientTagsToOfferLinks(isCachedOffersRequest, tags),
92
95
  getOffersBasedOnLayout(displayLayout)
93
96
  ),
94
- isError: offersData.isError,
95
- leadUuid: offersData.leadUuid,
96
- rateTableUuid: offersData.uuid,
97
+ isError,
98
+ errorCode,
99
+ leadUuid: leadUuid,
100
+ rateTableUuid,
97
101
  };
98
102
  };
@@ -9,29 +9,34 @@ import { parseClientTags } from "../capabilities/offer-catalog/src/utils/context
9
9
  * Retrieves offers by product types.
10
10
  *
11
11
  * @param {Object} params - The parameters for retrieving offers by product types.
12
- * @param {boolean} params.enableUseCachedOffers - Indicates if the offers should be retrieved from the cache.
12
+ * @param {boolean} params.isCachedOffersRequest - Indicates if the offers should be retrieved from the cache.
13
13
  * @param {string[]} params.productTypes - The product types to retrieve offers for.
14
14
  * @param {string} params.tags - The tags to use for retrieving offers.
15
15
  * @returns {Promise<OffersData>} A promise that resolves to an array of offers or undefined if no offers are found.
16
16
  */
17
17
  export const getOffersByProductTypes = async ({
18
- enableUseCachedOffers,
18
+ isCachedOffersRequest,
19
19
  productTypes,
20
20
  tags,
21
21
  context,
22
22
  }: {
23
- enableUseCachedOffers: boolean;
23
+ isCachedOffersRequest: boolean;
24
24
  productTypes: string[];
25
25
  tags: string;
26
26
  context: CnfContext;
27
27
  }) => {
28
- // TODO it should be just enableUseCachedOffers and not !enableUseCachedOffers
29
- if (enableUseCachedOffers) {
30
- return await getCachedOffersByProductTypes(context)(productTypes);
28
+ if (isCachedOffersRequest) {
29
+ const { offers, uuid, isError, leadUuid, errorCode } =
30
+ await getCachedOffersByProductTypes(context)(productTypes);
31
+
32
+ return { offers, rateTableUuid: uuid, isError, leadUuid, errorCode };
31
33
  }
32
34
 
33
- return await getOffersForProductTypes(context)(
34
- productTypes,
35
- parseClientTags(tags)
36
- );
35
+ const { offers, uuid, isError, leadUuid, errorCode } =
36
+ await getOffersForProductTypes(context)(
37
+ productTypes,
38
+ parseClientTags(tags)
39
+ );
40
+
41
+ return { offers, rateTableUuid: uuid, isError, leadUuid, errorCode };
37
42
  };