@alma/widgets 2.3.4 → 2.4.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.
@@ -2019,11 +2019,33 @@ async function fetchFromApi(url = '', data, headers) {
2019
2019
  return response.json();
2020
2020
  }
2021
2021
 
2022
+ const isPlanEligible = (plan, configPlan) => {
2023
+ if (!plan.eligible) {
2024
+ return false;
2025
+ }
2026
+
2027
+ return configPlan ? plan.purchase_amount >= (configPlan == null ? void 0 : configPlan.minAmount) && plan.purchase_amount <= (configPlan == null ? void 0 : configPlan.maxAmount) : false;
2028
+ };
2029
+
2030
+ const getPaymentPlanBoundaries = (plan, configPlan) => {
2031
+ var _plan$constraints;
2032
+
2033
+ // When the plan is not eligible, the purchase amount constraints is given from the merchant config
2034
+ const purchaseAmountConstraints = (_plan$constraints = plan.constraints) == null ? void 0 : _plan$constraints.purchase_amount;
2035
+
2036
+ if (purchaseAmountConstraints && configPlan) {
2037
+ return {
2038
+ minAmount: Math.max(configPlan.minAmount, purchaseAmountConstraints == null ? void 0 : purchaseAmountConstraints.minimum),
2039
+ maxAmount: Math.min(configPlan.maxAmount, purchaseAmountConstraints == null ? void 0 : purchaseAmountConstraints.maximum)
2040
+ };
2041
+ }
2042
+
2043
+ return configPlan != null ? configPlan : {};
2044
+ };
2045
+
2022
2046
  const filterELigibility = (eligibilities, configPlans) => {
2023
2047
  // Remove p1x
2024
- const filteredEligibilityPlans = eligibilities.filter(plan => !(plan.installments_count === 1 && plan.deferred_days === 0 && plan.deferred_months === 0)) // Keeps the plans that have a payment_plan property
2025
- .filter(plan => plan.payment_plan) // Remove plans that have a reasons property
2026
- .filter(plan => !plan.reasons); // If no configPlans was provided, return eligibility response
2048
+ const filteredEligibilityPlans = eligibilities.filter(plan => !(plan.installments_count === 1 && plan.deferred_days === 0 && plan.deferred_months === 0)); // If no configPlans was provided, return eligibility response
2027
2049
 
2028
2050
  if (!configPlans) {
2029
2051
  return filteredEligibilityPlans;
@@ -2038,10 +2060,8 @@ const filterELigibility = (eligibilities, configPlans) => {
2038
2060
  return plan.installments_count === configPlan.installmentsCount && eligibilityDeferredDays === configPlanDeferredDays;
2039
2061
  });
2040
2062
  return _extends({}, plan, {
2041
- eligible: relatedConfigPlan ? plan.purchase_amount >= (relatedConfigPlan == null ? void 0 : relatedConfigPlan.minAmount) && plan.purchase_amount <= (relatedConfigPlan == null ? void 0 : relatedConfigPlan.maxAmount) : false,
2042
- minAmount: relatedConfigPlan == null ? void 0 : relatedConfigPlan.minAmount,
2043
- maxAmount: relatedConfigPlan == null ? void 0 : relatedConfigPlan.maxAmount
2044
- });
2063
+ eligible: isPlanEligible(plan, relatedConfigPlan)
2064
+ }, getPaymentPlanBoundaries(plan, relatedConfigPlan));
2045
2065
  });
2046
2066
  };
2047
2067
 
@@ -2108,6 +2128,29 @@ function CrossIcon({
2108
2128
  }));
2109
2129
  }
2110
2130
 
2131
+ /**
2132
+ * Prefix classes to avoid name collisions.
2133
+ */
2134
+ const prefix = 'alma-eligibility-modal';
2135
+ /**
2136
+ * Class names for the **eligibility modale** widget.
2137
+ * Those classes are intended to be used by the **merchant developer**.
2138
+ */
2139
+
2140
+ const STATIC_CUSTOMISATION_CLASSES = {
2141
+ leftSide: prefix + '-left-side',
2142
+ rightSide: prefix + '-right-side',
2143
+ title: prefix + '-title',
2144
+ info: prefix + '-info',
2145
+ infoMessage: prefix + '-info-message',
2146
+ eligibilityOptions: prefix + '-eligibility-options',
2147
+ activeOption: prefix + '-active-option',
2148
+ closeButton: prefix + '-close-button',
2149
+ scheduleDetails: prefix + '-schedule-details',
2150
+ scheduleTotal: prefix + '-schedule-total',
2151
+ scheduleCredit: prefix + '-schedule-credit'
2152
+ };
2153
+
2111
2154
  const _excluded = ["children", "isOpen", "onClose", "className", "contentClassName", "scrollable"];
2112
2155
 
2113
2156
  const ControlledModal = _ref => {
@@ -2139,7 +2182,7 @@ const ControlledModal = _ref => {
2139
2182
  className: s$1.header
2140
2183
  }, /*#__PURE__*/React.createElement("button", {
2141
2184
  onClick: onClose,
2142
- className: s$1.closeButton,
2185
+ className: cx(s$1.closeButton, STATIC_CUSTOMISATION_CLASSES.closeButton),
2143
2186
  "data-testid": "modal-close-button"
2144
2187
  }, /*#__PURE__*/React.createElement(CrossIcon, null))), /*#__PURE__*/React.createElement("div", {
2145
2188
  className: cx(s$1.content, contentClassName, {
@@ -2168,6 +2211,18 @@ const paymentPlanShorthandName = payment => {
2168
2211
  return `${installmentsCount}x`;
2169
2212
  }
2170
2213
  };
2214
+
2215
+ const withNoFee = payment => {
2216
+ var _payment$payment_plan;
2217
+
2218
+ if ((_payment$payment_plan = payment.payment_plan) != null && _payment$payment_plan.every(plan => plan.customer_fee === 0 && plan.customer_interest === 0)) {
2219
+ return /*#__PURE__*/React.createElement(React.Fragment, null, ' ', /*#__PURE__*/React.createElement(FormattedMessage, {
2220
+ id: "payment-plan-strings.no-fee",
2221
+ defaultMessage: '(sans frais)'
2222
+ }));
2223
+ }
2224
+ };
2225
+
2171
2226
  const paymentPlanInfoText = payment => {
2172
2227
  const {
2173
2228
  deferred_days,
@@ -2176,19 +2231,11 @@ const paymentPlanInfoText = payment => {
2176
2231
  eligible,
2177
2232
  purchase_amount: purchaseAmount,
2178
2233
  minAmount = 0,
2179
- maxAmount = 0
2234
+ maxAmount = 0,
2235
+ payment_plan
2180
2236
  } = payment;
2181
2237
  const deferredDaysCount = deferred_days + deferred_months * 30;
2182
2238
 
2183
- const withNoFee = () => {
2184
- if (payment.payment_plan.every(plan => plan.customer_fee === 0 && plan.customer_interest === 0)) {
2185
- return /*#__PURE__*/React.createElement(React.Fragment, null, ' ', /*#__PURE__*/React.createElement(FormattedMessage, {
2186
- id: "payment-plan-strings.no-fee",
2187
- defaultMessage: '(sans frais)'
2188
- }));
2189
- }
2190
- };
2191
-
2192
2239
  if (!eligible) {
2193
2240
  return purchaseAmount > maxAmount ? /*#__PURE__*/React.createElement(FormattedMessage, {
2194
2241
  id: "payment-plan-strings.ineligible-greater-than-max",
@@ -2211,26 +2258,31 @@ const paymentPlanInfoText = payment => {
2211
2258
  })
2212
2259
  }
2213
2260
  });
2261
+ } else if (!payment_plan) {
2262
+ /* This error should never happen. We added this condition to avoid a typescript warning on
2263
+ payment_plan possibly undefined. As far as we know, it only happens when the plan is not
2264
+ eligible, which is checked above. */
2265
+ throw Error(`No payment plan provided for payment in ${installmentsCount} installments. Please contact us if you see this error.`);
2214
2266
  } else if (deferredDaysCount !== 0 && installmentsCount === 1) {
2215
2267
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormattedMessage, {
2216
2268
  id: "payment-plan-strings.deferred",
2217
2269
  defaultMessage: "{totalAmount} \u00E0 payer le {dueDate}",
2218
2270
  values: {
2219
2271
  totalAmount: /*#__PURE__*/React.createElement(FormattedNumber, {
2220
- value: priceFromCents(payment.payment_plan[0].total_amount),
2272
+ value: priceFromCents(payment_plan[0].total_amount),
2221
2273
  style: "currency",
2222
2274
  currency: "EUR"
2223
2275
  }),
2224
2276
  dueDate: /*#__PURE__*/React.createElement(FormattedDate, {
2225
- value: secondsToMilliseconds(payment.payment_plan[0].due_date),
2277
+ value: secondsToMilliseconds(payment_plan[0].due_date),
2226
2278
  day: "numeric",
2227
2279
  month: "long",
2228
2280
  year: "numeric"
2229
2281
  })
2230
2282
  }
2231
- }), withNoFee());
2283
+ }), withNoFee(payment));
2232
2284
  } else if (installmentsCount > 0) {
2233
- const areInstallmentsOfSameAmount = payment.payment_plan.every((installment, index) => index === 0 || installment.total_amount === payment.payment_plan[0].total_amount);
2285
+ const areInstallmentsOfSameAmount = payment_plan == null ? void 0 : payment_plan.every((installment, index) => index === 0 || installment.total_amount === payment_plan[0].total_amount);
2234
2286
 
2235
2287
  if (areInstallmentsOfSameAmount) {
2236
2288
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormattedMessage, {
@@ -2238,13 +2290,13 @@ const paymentPlanInfoText = payment => {
2238
2290
  defaultMessage: "{installmentsCount} x {totalAmount}",
2239
2291
  values: {
2240
2292
  totalAmount: /*#__PURE__*/React.createElement(FormattedNumber, {
2241
- value: priceFromCents(payment.payment_plan[0].total_amount),
2293
+ value: priceFromCents(payment_plan[0].total_amount),
2242
2294
  style: "currency",
2243
2295
  currency: "EUR"
2244
2296
  }),
2245
2297
  installmentsCount
2246
2298
  }
2247
- }), withNoFee());
2299
+ }), withNoFee(payment));
2248
2300
  }
2249
2301
 
2250
2302
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormattedMessage, {
@@ -2252,18 +2304,18 @@ const paymentPlanInfoText = payment => {
2252
2304
  defaultMessage: "{numberOfRemainingInstallments, plural, one {{firstInstallmentAmount} puis {numberOfRemainingInstallments} x {othersInstallmentAmount}} other {{firstInstallmentAmount} puis {numberOfRemainingInstallments} x {othersInstallmentAmount}}}",
2253
2305
  values: {
2254
2306
  firstInstallmentAmount: /*#__PURE__*/React.createElement(FormattedNumber, {
2255
- value: priceFromCents(payment.payment_plan[0].total_amount),
2307
+ value: priceFromCents(payment_plan[0].total_amount),
2256
2308
  style: "currency",
2257
2309
  currency: "EUR"
2258
2310
  }),
2259
2311
  numberOfRemainingInstallments: installmentsCount - 1,
2260
2312
  othersInstallmentAmount: /*#__PURE__*/React.createElement(FormattedNumber, {
2261
- value: priceFromCents(payment.payment_plan[1].total_amount),
2313
+ value: priceFromCents(payment_plan[1].total_amount),
2262
2314
  style: "currency",
2263
2315
  currency: "EUR"
2264
2316
  })
2265
2317
  }
2266
- }), withNoFee());
2318
+ }), withNoFee(payment));
2267
2319
  }
2268
2320
 
2269
2321
  return /*#__PURE__*/React.createElement(FormattedMessage, {
@@ -2279,11 +2331,11 @@ const EligibilityPlansButtons = ({
2279
2331
  currentPlanIndex,
2280
2332
  setCurrentPlanIndex
2281
2333
  }) => /*#__PURE__*/React.createElement("div", {
2282
- className: s$2.buttons
2334
+ className: cx(s$2.buttons, STATIC_CUSTOMISATION_CLASSES.eligibilityOptions)
2283
2335
  }, eligibilityPlans.map((eligibilityPlan, index) => /*#__PURE__*/React.createElement("button", {
2284
2336
  key: index,
2285
2337
  className: cx({
2286
- [s$2.active]: index === currentPlanIndex
2338
+ [cx(s$2.active, STATIC_CUSTOMISATION_CLASSES.activeOption)]: index === currentPlanIndex
2287
2339
  }),
2288
2340
  onClick: () => setCurrentPlanIndex(index)
2289
2341
  }, paymentPlanShorthandName(eligibilityPlan))));
@@ -2300,10 +2352,10 @@ const Schedule = ({
2300
2352
  const isCredit = currentPlan && currentPlan.installments_count > 4;
2301
2353
  const intl = useIntl();
2302
2354
  return /*#__PURE__*/React.createElement("div", {
2303
- className: s$3.schedule,
2355
+ className: cx(s$3.schedule, STATIC_CUSTOMISATION_CLASSES.scheduleDetails),
2304
2356
  "data-testid": "modal-installments-element"
2305
2357
  }, /*#__PURE__*/React.createElement("div", {
2306
- className: cx(s$3.scheduleLine, s$3.total)
2358
+ className: cx(s$3.scheduleLine, s$3.total, STATIC_CUSTOMISATION_CLASSES.scheduleTotal)
2307
2359
  }, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, {
2308
2360
  id: "eligibility-modal.total",
2309
2361
  defaultMessage: "Total"
@@ -2312,7 +2364,7 @@ const Schedule = ({
2312
2364
  style: "currency",
2313
2365
  currency: "EUR"
2314
2366
  }))), /*#__PURE__*/React.createElement("div", {
2315
- className: cx(s$3.scheduleLine, s$3.creditCost)
2367
+ className: cx(s$3.scheduleLine, s$3.creditCost, STATIC_CUSTOMISATION_CLASSES.scheduleCredit)
2316
2368
  }, isCredit ? /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, {
2317
2369
  id: "eligibility-modal.credit-cost",
2318
2370
  defaultMessage: "Dont co\u00FBt du cr\u00E9dit"
@@ -2362,13 +2414,15 @@ const Schedule = ({
2362
2414
  var s$4 = {"list":"_180ro","listItem":"_1HqCO","bullet":"_3B8wx"};
2363
2415
 
2364
2416
  const Info = () => /*#__PURE__*/React.createElement("div", {
2365
- className: s$4.list,
2417
+ className: cx(s$4.list, STATIC_CUSTOMISATION_CLASSES.info),
2366
2418
  "data-testid": "modal-info-element"
2367
2419
  }, /*#__PURE__*/React.createElement("div", {
2368
2420
  className: s$4.listItem
2369
2421
  }, /*#__PURE__*/React.createElement("div", {
2370
2422
  className: s$4.bullet
2371
- }, "1"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(FormattedMessage, {
2423
+ }, "1"), /*#__PURE__*/React.createElement("div", {
2424
+ className: STATIC_CUSTOMISATION_CLASSES.infoMessage
2425
+ }, /*#__PURE__*/React.createElement(FormattedMessage, {
2372
2426
  id: "eligibility-modal.bullet-1",
2373
2427
  defaultMessage: "Choisissez <strong>Alma</strong> au moment du paiement.",
2374
2428
  values: {
@@ -2378,7 +2432,9 @@ const Info = () => /*#__PURE__*/React.createElement("div", {
2378
2432
  className: s$4.listItem
2379
2433
  }, /*#__PURE__*/React.createElement("div", {
2380
2434
  className: s$4.bullet
2381
- }, "2"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(FormattedMessage, {
2435
+ }, "2"), /*#__PURE__*/React.createElement("div", {
2436
+ className: STATIC_CUSTOMISATION_CLASSES.infoMessage
2437
+ }, /*#__PURE__*/React.createElement(FormattedMessage, {
2382
2438
  id: "eligibility-modal.bullet-2",
2383
2439
  defaultMessage: "Renseignez les <strong>informations</strong> demand\u00E9es.",
2384
2440
  values: {
@@ -2388,7 +2444,9 @@ const Info = () => /*#__PURE__*/React.createElement("div", {
2388
2444
  className: s$4.listItem
2389
2445
  }, /*#__PURE__*/React.createElement("div", {
2390
2446
  className: s$4.bullet
2391
- }, "3"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(FormattedMessage, {
2447
+ }, "3"), /*#__PURE__*/React.createElement("div", {
2448
+ className: STATIC_CUSTOMISATION_CLASSES.infoMessage
2449
+ }, /*#__PURE__*/React.createElement(FormattedMessage, {
2392
2450
  id: "eligibility-modal.bullet-3",
2393
2451
  defaultMessage: "La validation de votre paiement <strong>instantan\u00E9e</strong> !",
2394
2452
  values: {
@@ -2439,7 +2497,7 @@ const Logo = () => /*#__PURE__*/React.createElement("div", {
2439
2497
  var s$6 = {"title":"_3ERx-"};
2440
2498
 
2441
2499
  const Title = () => /*#__PURE__*/React.createElement("div", {
2442
- className: s$6.title,
2500
+ className: cx(s$6.title, STATIC_CUSTOMISATION_CLASSES.title),
2443
2501
  "data-testid": "modal-title-element"
2444
2502
  }, /*#__PURE__*/React.createElement(FormattedMessage, {
2445
2503
  id: "eligibility-modal.title",
@@ -2457,9 +2515,9 @@ const DesktopModal = ({
2457
2515
  className: s$7.container,
2458
2516
  "data-testid": "modal-container"
2459
2517
  }, /*#__PURE__*/React.createElement("div", {
2460
- className: cx([s$7.block, s$7.left])
2518
+ className: cx([s$7.block, s$7.left, STATIC_CUSTOMISATION_CLASSES.leftSide])
2461
2519
  }, /*#__PURE__*/React.createElement(Title, null), /*#__PURE__*/React.createElement(Info, null), /*#__PURE__*/React.createElement(Logo, null)), /*#__PURE__*/React.createElement("div", {
2462
- className: s$7.block
2520
+ className: cx(s$7.block, STATIC_CUSTOMISATION_CLASSES.rightSide)
2463
2521
  }, children));
2464
2522
 
2465
2523
  var s$8 = {"noEligibility":"_17qNJ","loader":"_2oTJq"};
@@ -2606,6 +2664,24 @@ const getIndexOfActivePlan = ({
2606
2664
  return 0;
2607
2665
  };
2608
2666
 
2667
+ /**
2668
+ * Prefix classes to avoid name collisions.
2669
+ */
2670
+ const prefix$1 = 'alma-payment-plans';
2671
+ /**
2672
+ * Class names for the **payment plans** widget.
2673
+ * Those classes are intended to be used by the **merchant developer**.
2674
+ */
2675
+
2676
+ const STATIC_CUSTOMISATION_CLASSES$1 = {
2677
+ container: prefix$1 + '-container',
2678
+ eligibilityLine: prefix$1 + '-eligibility-line',
2679
+ eligibilityOptions: prefix$1 + '-eligibility-options',
2680
+ notEligibleOption: prefix$1 + '-not-eligible-option',
2681
+ paymentInfo: prefix$1 + '-payment-info',
2682
+ activeOption: prefix$1 + '-active-option'
2683
+ };
2684
+
2609
2685
  var s$b = {"widgetButton":"_TSkFv","logo":"_LJ4nZ","primaryContainer":"_bMClc","paymentPlans":"_17c_S","plan":"_2Kqjn","active":"_3dG_J","notEligible":"_3O1bg","info":"_25GrF","loader":"_30j1O","error":"_R0YlN","errorText":"_2kGhu","errorButton":"_73d_Y","pending":"_1ZDMS","clickable":"_UksZa","unClickable":"_1lr-q"};
2610
2686
 
2611
2687
  const VERY_LONG_TIME_IN_MS = 1000 * 3600 * 24 * 365;
@@ -2625,7 +2701,7 @@ const PaymentPlanWidget = ({
2625
2701
  eligibilityPlans,
2626
2702
  suggestedPaymentPlan: suggestedPaymentPlan != null ? suggestedPaymentPlan : 0
2627
2703
  });
2628
- const isSuggestedPaymentPlanSpecified = suggestedPaymentPlan !== undefined; // 👈 The merchant decided to focus a tab and remove animated transition.
2704
+ const isSuggestedPaymentPlanSpecified = suggestedPaymentPlan !== undefined; // 👈 The merchant decided to focus a tab
2629
2705
 
2630
2706
  const isTransitionSpecified = transitionDelay !== undefined; // 👈 The merchant has specified a transition time
2631
2707
 
@@ -2703,28 +2779,28 @@ const PaymentPlanWidget = ({
2703
2779
  className: cx(s$b.widgetButton, {
2704
2780
  [s$b.clickable]: eligiblePlans.length > 0,
2705
2781
  [s$b.unClickable]: eligiblePlans.length === 0
2706
- }),
2782
+ }, STATIC_CUSTOMISATION_CLASSES$1.container),
2707
2783
  "data-testid": "widget-button"
2708
2784
  }, /*#__PURE__*/React.createElement("div", {
2709
- className: s$b.primaryContainer
2785
+ className: cx(s$b.primaryContainer, STATIC_CUSTOMISATION_CLASSES$1.eligibilityLine)
2710
2786
  }, /*#__PURE__*/React.createElement(LogoIcon, {
2711
2787
  className: s$b.logo
2712
2788
  }), /*#__PURE__*/React.createElement("div", {
2713
- className: s$b.paymentPlans
2789
+ className: cx(s$b.paymentPlans, STATIC_CUSTOMISATION_CLASSES$1.eligibilityOptions)
2714
2790
  }, eligibilityPlans.map((eligibilityPlan, key) => {
2715
2791
  return /*#__PURE__*/React.createElement("div", {
2716
2792
  key: key,
2717
2793
  onMouseEnter: () => onHover(key),
2718
2794
  onMouseOut: onLeave,
2719
2795
  className: cx(s$b.plan, {
2720
- [s$b.active]: current === key,
2721
- [s$b.notEligible]: !eligibilityPlan.eligible
2796
+ [cx(s$b.active, STATIC_CUSTOMISATION_CLASSES$1.activeOption)]: current === key,
2797
+ [cx(s$b.notEligible, STATIC_CUSTOMISATION_CLASSES$1.notEligibleOption)]: !eligibilityPlan.eligible
2722
2798
  })
2723
2799
  }, paymentPlanShorthandName(eligibilityPlan));
2724
2800
  }))), /*#__PURE__*/React.createElement("div", {
2725
2801
  className: cx(s$b.info, {
2726
- [s$b.notEligible]: eligibilityPlans[current] && !eligibilityPlans[current].eligible
2727
- })
2802
+ [cx(s$b.notEligible, STATIC_CUSTOMISATION_CLASSES$1.notEligibleOption)]: eligibilityPlans[current] && !eligibilityPlans[current].eligible
2803
+ }, STATIC_CUSTOMISATION_CLASSES$1.paymentInfo)
2728
2804
  }, eligibilityPlans.length !== 0 && paymentPlanInfoText(eligibilityPlans[current]))), isOpen && /*#__PURE__*/React.createElement(EligibilityModal, {
2729
2805
  initialPlanIndex: getIndexWithinEligiblePlans(current),
2730
2806
  onClose: closeModal,