@bunnyapp/components 1.7.0-beta.26 → 1.7.0-beta.28

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/dist/cjs/index.js CHANGED
@@ -1283,7 +1283,7 @@ const DEFAULT_CONFIG = {
1283
1283
  };
1284
1284
 
1285
1285
  // This will be replaced at build time by rollup-plugin-replace
1286
- const PACKAGE_VERSION = '1.7.0-beta.25';
1286
+ const PACKAGE_VERSION = '1.7.0-beta.27';
1287
1287
  const createRequestHeaders = (token) => {
1288
1288
  const headers = createClientDevHeaders({ token });
1289
1289
  // Add the components version header
@@ -1435,7 +1435,12 @@ const createValueContext = () => {
1435
1435
 
1436
1436
  const [BrandProvider, useBrand] = createValueContext();
1437
1437
 
1438
- const IGNORE_ERRORS = ['Invalid or missing authorization', '["Not found"]'];
1438
+ const [BunnyProviderCallbacksProvider, useBunnyProviderCallbacks] = createValueContext();
1439
+
1440
+ const QUOTE_UNAVAILABLE_ERROR = 'This quote is currently unavailable';
1441
+ const INVALID_OR_MISSING_AUTHORIZATION_ERROR = 'Invalid or missing authorization';
1442
+ const IGNORE_ERRORS = [INVALID_OR_MISSING_AUTHORIZATION_ERROR, '["Not found"]', QUOTE_UNAVAILABLE_ERROR];
1443
+ const CONTACT_DOES_NOT_EXIST_ERROR = 'Contact does not exist within';
1439
1444
  /**
1440
1445
  * Extracts errors from nested data structure
1441
1446
  * @param data - The response data object
@@ -1467,13 +1472,24 @@ const extractNestedErrors = (data, mutationName) => {
1467
1472
  }
1468
1473
  return null;
1469
1474
  };
1470
- const useAllErrorFormats = () => {
1475
+ const useAllErrorFormats = ({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification } = {}) => {
1471
1476
  const showErrorNotification = useErrorNotification();
1472
1477
  return (error, mutationName) => {
1473
- var _a, _b, _c, _d, _e, _f, _g;
1478
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1474
1479
  // Fall back to existing error handling logic
1475
1480
  const responseErrors = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.errors) || null;
1476
- if (IGNORE_ERRORS.includes((_d = (_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.errors) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message)) {
1481
+ // also check error?.message, which is the structure provided by the api/plugins rest endpoint
1482
+ const errorMessage = (_e = (_d = (_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.errors) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message) !== null && _e !== void 0 ? _e : error === null || error === void 0 ? void 0 : error.message;
1483
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(INVALID_OR_MISSING_AUTHORIZATION_ERROR)) {
1484
+ onInvalidOrMissingAuthorization === null || onInvalidOrMissingAuthorization === void 0 ? void 0 : onInvalidOrMissingAuthorization();
1485
+ }
1486
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(CONTACT_DOES_NOT_EXIST_ERROR)) {
1487
+ onUserUnavailable === null || onUserUnavailable === void 0 ? void 0 : onUserUnavailable();
1488
+ if (suppressUserUnavailableErrorNotification) {
1489
+ return error;
1490
+ }
1491
+ }
1492
+ if (IGNORE_ERRORS.includes(errorMessage)) {
1477
1493
  return error;
1478
1494
  }
1479
1495
  // For errors like this:
@@ -1500,18 +1516,18 @@ const useAllErrorFormats = () => {
1500
1516
  }
1501
1517
  if (responseErrors) {
1502
1518
  for (let index in responseErrors) {
1503
- const error = (_e = responseErrors[index]) === null || _e === void 0 ? void 0 : _e.message;
1519
+ const error = (_f = responseErrors[index]) === null || _f === void 0 ? void 0 : _f.message;
1504
1520
  showErrorNotification(error, null);
1505
1521
  }
1506
1522
  }
1507
- else if ((_f = error === null || error === void 0 ? void 0 : error.response) === null || _f === void 0 ? void 0 : _f.error) {
1523
+ else if ((_g = error === null || error === void 0 ? void 0 : error.response) === null || _g === void 0 ? void 0 : _g.error) {
1508
1524
  showErrorNotification(error.response.exception, error.response.error);
1509
1525
  }
1510
1526
  else if ((error === null || error === void 0 ? void 0 : error.constructor) === Array) {
1511
1527
  for (let index in error)
1512
1528
  showErrorNotification(error[index].message, null);
1513
1529
  }
1514
- else if ((_g = error === null || error === void 0 ? void 0 : error.response) === null || _g === void 0 ? void 0 : _g.message) {
1530
+ else if ((_h = error === null || error === void 0 ? void 0 : error.response) === null || _h === void 0 ? void 0 : _h.message) {
1515
1531
  showErrorNotification(error.response.message);
1516
1532
  }
1517
1533
  else if (error === null || error === void 0 ? void 0 : error.message) {
@@ -1527,7 +1543,8 @@ const useAllErrorFormats = () => {
1527
1543
  };
1528
1544
 
1529
1545
  const useCreateQueryClient = () => {
1530
- const handleAllErrorFormats = useAllErrorFormats();
1546
+ const { onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification } = useBunnyProviderCallbacks();
1547
+ const handleAllErrorFormats = useAllErrorFormats({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification });
1531
1548
  const onError = (error, _query) => {
1532
1549
  // Extract mutation name from query string
1533
1550
  // const mutationName = query?.mutation?.match(/mutation\s+(\w+)/)?.[1];
@@ -2245,9 +2262,11 @@ const getBranding = async ({ token, apiHost }) => {
2245
2262
  };
2246
2263
 
2247
2264
  const BunnyContext = react.createContext({});
2248
- // const extraQueryClient = new QueryClient();
2249
2265
  // Every component shares similar props and functionality, which this wrapper handles.
2250
- function BunnyProvider({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, configProviderProps, }) {
2266
+ function BunnyProvider({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification = false, configProviderProps, }) {
2267
+ return (jsxRuntime.jsx(BunnyProviderCallbacksProvider, { value: { onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification }, children: jsxRuntime.jsx(BunnyProviderContent, { darkMode: darkMode, queryClient: queryClient, apiHost: apiHost, token: token, onTokenExpired: onTokenExpired, configProviderProps: configProviderProps, children: children }) }));
2268
+ }
2269
+ function BunnyProviderContent({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, configProviderProps, }) {
2251
2270
  const createQueryClient = useCreateQueryClient();
2252
2271
  const extraQueryClient = react.useMemo(() => createQueryClient(), []);
2253
2272
  return (jsxRuntime.jsx(BunnyContext.Provider, { value: {
@@ -20376,7 +20395,7 @@ function useApproveHold$1({ onApproveHoldSuccess, onApproveHoldError, }) {
20376
20395
  // Used for Signup component. Signup needs a custom checkout function
20377
20396
  const [CustomCheckoutFunctionProvider, useCustomCheckoutFunction] = createValueContext();
20378
20397
 
20379
- const showErrorNotification$9 = useErrorNotification();
20398
+ const showErrorNotification$a = useErrorNotification();
20380
20399
  function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20381
20400
  const { apiHost } = react.useContext(BunnyContext);
20382
20401
  const customCheckoutFunction = useCustomCheckoutFunction();
@@ -20387,7 +20406,7 @@ function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin,
20387
20406
  setIsPaying(true);
20388
20407
  if (customCheckoutFunction) {
20389
20408
  if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20390
- showErrorNotification$9('Plugin ID is required');
20409
+ showErrorNotification$a('Plugin ID is required');
20391
20410
  return;
20392
20411
  }
20393
20412
  const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
@@ -20446,7 +20465,7 @@ function useApproveHold({ onApproveHoldSuccess, onApproveHoldError, }) {
20446
20465
  return { approveHold, isApprovingHold };
20447
20466
  }
20448
20467
 
20449
- const showErrorNotification$8 = useErrorNotification();
20468
+ const showErrorNotification$9 = useErrorNotification();
20450
20469
  function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20451
20470
  // Hooks
20452
20471
  const { apiHost } = react.useContext(BunnyContext);
@@ -20458,7 +20477,7 @@ function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, })
20458
20477
  setIsPaying(true);
20459
20478
  if (customCheckoutFunction) {
20460
20479
  if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20461
- showErrorNotification$8('Plugin ID is required');
20480
+ showErrorNotification$9('Plugin ID is required');
20462
20481
  return;
20463
20482
  }
20464
20483
  const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
@@ -20466,7 +20485,7 @@ function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, })
20466
20485
  }
20467
20486
  else {
20468
20487
  if (!quoteId && !(invoice === null || invoice === void 0 ? void 0 : invoice.id)) {
20469
- showErrorNotification$8('Quote ID or Invoice ID is required');
20488
+ showErrorNotification$9('Quote ID or Invoice ID is required');
20470
20489
  return;
20471
20490
  }
20472
20491
  const response = await checkout({
@@ -20497,7 +20516,7 @@ var PaymentType;
20497
20516
  })(PaymentType || (PaymentType = {}));
20498
20517
 
20499
20518
  const handleAllErrorFormats$3 = useAllErrorFormats();
20500
- const showErrorNotification$7 = useErrorNotification();
20519
+ const showErrorNotification$8 = useErrorNotification();
20501
20520
  const useHandlePayment_QuoteFragment = t(`
20502
20521
  fragment useHandlePayment_QuoteFragment on Quote {
20503
20522
  id
@@ -20586,11 +20605,11 @@ const useHandlePayment = ({ quote: maskedQuote, invoice, onPaymentSuccess, onPay
20586
20605
  var _a, _b, _c, _d;
20587
20606
  const paymentMethodId = overridePaymentMethodId || (defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
20588
20607
  if (!plugin)
20589
- return showErrorNotification$7('plugin is undefined');
20608
+ return showErrorNotification$8('plugin is undefined');
20590
20609
  if (!(quote === null || quote === void 0 ? void 0 : quote.id) || !(quote === null || quote === void 0 ? void 0 : quote.amount))
20591
- return showErrorNotification$7('quote id and amount are required to approve hold');
20610
+ return showErrorNotification$8('quote id and amount are required to approve hold');
20592
20611
  if (!paymentMethodId)
20593
- return showErrorNotification$7('payment method id is undefined');
20612
+ return showErrorNotification$8('payment method id is undefined');
20594
20613
  switch ((_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) {
20595
20614
  case 'StripePayment':
20596
20615
  await approveHoldStripe({
@@ -20607,7 +20626,7 @@ const useHandlePayment = ({ quote: maskedQuote, invoice, onPaymentSuccess, onPay
20607
20626
  });
20608
20627
  break;
20609
20628
  default:
20610
- showErrorNotification$7(`Payment holds are not supported by ${(_d = (_c = plugin.components) === null || _c === void 0 ? void 0 : _c.frontend) === null || _d === void 0 ? void 0 : _d[0].name}`);
20629
+ showErrorNotification$8(`Payment holds are not supported by ${(_d = (_c = plugin.components) === null || _c === void 0 ? void 0 : _c.frontend) === null || _d === void 0 ? void 0 : _d[0].name}`);
20611
20630
  break;
20612
20631
  }
20613
20632
  };
@@ -20723,7 +20742,7 @@ const useShowPaymentDetailsState = (isOpen = false) => react.useState(isOpen);
20723
20742
  const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(useShowPaymentDetailsState);
20724
20743
 
20725
20744
  const handleAllErrorFormats$2 = useAllErrorFormats();
20726
- const showErrorNotification$6 = useErrorNotification();
20745
+ const showErrorNotification$7 = useErrorNotification();
20727
20746
  // Contexts
20728
20747
  const [FormattedAmountDueProvider, useFormattedAmountDue] = createValueContext();
20729
20748
  const [PaymentTypeProvider, usePaymentType] = createValueContext();
@@ -20803,7 +20822,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20803
20822
  return await saveDemoPay(demoPayCardDetails);
20804
20823
  }
20805
20824
  else {
20806
- showErrorNotification$6(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20825
+ showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20807
20826
  return undefined;
20808
20827
  }
20809
20828
  };
@@ -20816,7 +20835,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20816
20835
  await handleApproveHold(savedPaymentMethodId);
20817
20836
  }
20818
20837
  else {
20819
- showErrorNotification$6(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20838
+ showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20820
20839
  }
20821
20840
  };
20822
20841
  const handlePaymentFormSubmit = async (demoPayCardDetails) => {
@@ -20847,7 +20866,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20847
20866
  }
20848
20867
  else {
20849
20868
  if (!(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id)) {
20850
- showErrorNotification$6('Default payment method ID is undefined');
20869
+ showErrorNotification$7('Default payment method ID is undefined');
20851
20870
  return;
20852
20871
  }
20853
20872
  await handlePayment(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
@@ -21281,7 +21300,7 @@ function useSetDefaultPaymentMethod(onError, onSuccess) {
21281
21300
  return { setDefaultPaymentMethod, loading };
21282
21301
  }
21283
21302
 
21284
- const showErrorNotification$5 = useErrorNotification();
21303
+ const showErrorNotification$6 = useErrorNotification();
21285
21304
  const PaymentForm_QuoteFragment = t(`
21286
21305
  fragment PaymentForm_QuoteFragment on Quote {
21287
21306
  id
@@ -21330,7 +21349,7 @@ function PaymentFormContent({ onPaymentMethodRemoved, onSetDefaultPaymentMethod,
21330
21349
  const hasPaymentMethods = (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) && (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) > 0;
21331
21350
  // Custom hooks
21332
21351
  const { setDefaultPaymentMethod: handleSetDefault, loading: setDefaultPaymentMethodLoading } = useSetDefaultPaymentMethod(message => {
21333
- showErrorNotification$5(message, 'Error setting default payment method');
21352
+ showErrorNotification$6(message, 'Error setting default payment method');
21334
21353
  }, () => {
21335
21354
  onSetDefaultPaymentMethod === null || onSetDefaultPaymentMethod === void 0 ? void 0 : onSetDefaultPaymentMethod();
21336
21355
  });
@@ -21340,11 +21359,11 @@ function PaymentFormContent({ onPaymentMethodRemoved, onSetDefaultPaymentMethod,
21340
21359
  enabled: !!paymentPlugins,
21341
21360
  });
21342
21361
  const onClickRemove = useRemovePaymentMethod(onPaymentMethodRemoved, message => {
21343
- showErrorNotification$5(message, 'Error removing payment method');
21362
+ showErrorNotification$6(message, 'Error removing payment method');
21344
21363
  });
21345
21364
  function handleClickAddPaymentMethod() {
21346
21365
  if ((paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.length) === 0) {
21347
- showErrorNotification$5('No payment plugins available', 'Error adding payment method');
21366
+ showErrorNotification$6('No payment plugins available', 'Error adding payment method');
21348
21367
  }
21349
21368
  else {
21350
21369
  setShowPaymentMethodForm(true);
@@ -21424,23 +21443,26 @@ if (typeof window !== 'undefined') {
21424
21443
  const shadowRoot = originalAttachShadow.call(this, init);
21425
21444
  // Prevent watermark styles from being injected
21426
21445
  const originalInnerHTMLSetter = (_a = Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'innerHTML')) === null || _a === void 0 ? void 0 : _a.set;
21427
- console.log('originalInnerHTMLSetter', originalInnerHTMLSetter);
21428
21446
  if (originalInnerHTMLSetter) {
21429
- Object.defineProperty(shadowRoot, 'innerHTML', {
21430
- set: function (value) {
21431
- // Block watermark injection
21432
- if (typeof value === 'string' &&
21433
- value.includes('background-image: url(data:image/webp')) {
21434
- return; // Don't set the watermark HTML
21435
- }
21436
- console.log('value', value);
21437
- originalInnerHTMLSetter.call(this, value);
21438
- },
21439
- get: function () {
21440
- console.log('this.innerHTML', this.innerHTML);
21441
- return this.innerHTML;
21442
- },
21443
- });
21447
+ try {
21448
+ Object.defineProperty(shadowRoot, 'innerHTML', {
21449
+ configurable: true,
21450
+ set: function (value) {
21451
+ // Block watermark injection
21452
+ if (typeof value === 'string' &&
21453
+ value.includes('background-image: url(data:image/webp')) {
21454
+ return; // Don't set the watermark HTML
21455
+ }
21456
+ originalInnerHTMLSetter.call(this, value);
21457
+ },
21458
+ get: function () {
21459
+ return this.innerHTML;
21460
+ },
21461
+ });
21462
+ }
21463
+ catch (e) {
21464
+ // Property already defined, ignore error (happens during hot module reloading)
21465
+ }
21444
21466
  }
21445
21467
  return shadowRoot;
21446
21468
  };
@@ -21795,7 +21817,7 @@ function PaymentHoldDisplay({ paymentHold, currency, amount, }) {
21795
21817
 
21796
21818
  const { Title: Title$2, Text: Text$x } = antd.Typography;
21797
21819
  const showSuccessNotification$2 = useSuccessNotification();
21798
- const showErrorNotification$4 = useErrorNotification();
21820
+ const showErrorNotification$5 = useErrorNotification();
21799
21821
  const PaymentHoldModal_FormattedQuoteFragment = t(`
21800
21822
  fragment PaymentHoldModal_FormattedQuoteFragment on FormattedQuote {
21801
21823
  quote {
@@ -21823,7 +21845,7 @@ const PaymentHoldModal = ({ visible, setVisible, formattedQuote: maskedFormatted
21823
21845
  setVisible(false);
21824
21846
  showSuccessNotification$2(`Approved payment hold for ${formatCurrency(formattedQuote.amount, formattedQuote.currency || 'null')}`);
21825
21847
  if (!formattedQuote.quote.id) {
21826
- showErrorNotification$4('Quote ID is required for onPaymentHoldSuccess callback in PaymentForm');
21848
+ showErrorNotification$5('Quote ID is required for onPaymentHoldSuccess callback in PaymentForm');
21827
21849
  return;
21828
21850
  }
21829
21851
  queryClient.invalidateQueries({
@@ -22247,12 +22269,13 @@ const PandadocPollingModal = ({ isVisible, setVisible, id }) => {
22247
22269
  return (jsxRuntime.jsxs(antd.Modal, { title: "Uploading quote to Pandadoc", open: isVisible, closable: false, footer: null, children: [jsxRuntime.jsxs("div", { className: "bunny-py-4 bunny-text-center", children: ["This may take a few seconds", '.'.repeat(numberOfPolls)] }), jsxRuntime.jsx("div", { className: "bunny-text-center", children: infoMessage })] }));
22248
22270
  };
22249
22271
 
22272
+ const showErrorNotification$4 = useErrorNotification();
22250
22273
  defaultStyled.div `
22251
22274
  Text {
22252
22275
  width: 100%;
22253
22276
  }
22254
22277
  `;
22255
- function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, onQuoteLoaded, onQuoteAccepted, }) {
22278
+ function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, onQuoteLoaded, onQuoteAccepted, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification = false, }) {
22256
22279
  return (jsxRuntime.jsx(InvoiceQuoteContext.Provider, { value: {
22257
22280
  id,
22258
22281
  invoiceQuoteViewComponent,
@@ -22262,6 +22285,8 @@ function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymen
22262
22285
  className,
22263
22286
  hideDownloadButton,
22264
22287
  onQuoteLoaded,
22288
+ onQuoteUnavailableError,
22289
+ suppressQuoteUnavailableErrorNotification,
22265
22290
  }, children: jsxRuntime.jsx(ActualQuote, { onQuoteAccepted: onQuoteAccepted }) }));
22266
22291
  }
22267
22292
  const useQuotePaymentHold = (formattedQuote) => {
@@ -22282,9 +22307,9 @@ function ActualQuote({ onQuoteAccepted }) {
22282
22307
  // Context
22283
22308
  const { apiHost } = react.useContext(BunnyContext);
22284
22309
  const token = useToken();
22285
- const { className, id, hideDownloadButton, onQuoteLoaded } = react.useContext(InvoiceQuoteContext);
22310
+ const { className, id, hideDownloadButton, onQuoteLoaded, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification } = react.useContext(InvoiceQuoteContext);
22286
22311
  // Queries
22287
- const { data: formattedQuote, isLoading } = reactQuery.useQuery({
22312
+ const { data: formattedQuote, isLoading, error } = reactQuery.useQuery({
22288
22313
  queryKey: QueryKeyFactory.createQuoteKey({ id, token }),
22289
22314
  queryFn: async () => {
22290
22315
  return await getFormattedQuote({ token, apiHost, id });
@@ -22305,6 +22330,18 @@ function ActualQuote({ onQuoteAccepted }) {
22305
22330
  onQuoteLoaded === null || onQuoteLoaded === void 0 ? void 0 : onQuoteLoaded(formattedQuote); // TODO: fix to use the correct type
22306
22331
  }
22307
22332
  }, [formattedQuote]);
22333
+ react.useEffect(() => {
22334
+ var _a, _b, _c;
22335
+ if (error) {
22336
+ const errorMessage = (_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.errors) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message;
22337
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes("This quote is currently unavailable")) {
22338
+ onQuoteUnavailableError === null || onQuoteUnavailableError === void 0 ? void 0 : onQuoteUnavailableError();
22339
+ if (!suppressQuoteUnavailableErrorNotification) {
22340
+ showErrorNotification$4(errorMessage, null);
22341
+ }
22342
+ }
22343
+ }
22344
+ }, [error]);
22308
22345
  // Payment hold stuff here
22309
22346
  const { paymentHoldModalVisible, setPaymentHoldModalVisible, shouldDoPaymentHold, paymentHoldCompleted, paymentHold, } = useQuotePaymentHold(formattedQuote); // TODO: fix to use the correct type
22310
22347
  const handleClickAccept = () => {
@@ -7,13 +7,16 @@ export type BunnyContextValues = {
7
7
  onTokenExpired?: () => void;
8
8
  };
9
9
  export declare const BunnyContext: import("react").Context<BunnyContextValues>;
10
- declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, configProviderProps, }: {
10
+ declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification, configProviderProps, }: {
11
11
  children: React.ReactNode;
12
12
  darkMode?: boolean;
13
13
  queryClient?: QueryClient;
14
14
  apiHost: string;
15
15
  token?: string;
16
16
  onTokenExpired?: () => void;
17
+ onUserUnavailable?: () => void;
18
+ onInvalidOrMissingAuthorization?: () => void;
19
+ suppressUserUnavailableErrorNotification?: boolean;
17
20
  configProviderProps?: ThemeConfig | undefined;
18
21
  }): import("react/jsx-runtime").JSX.Element;
19
22
  export default BunnyProvider;
@@ -0,0 +1,13 @@
1
+ declare const BunnyProviderCallbacksProvider: ({ value, children }: {
2
+ value: {
3
+ onUserUnavailable?: () => void;
4
+ onInvalidOrMissingAuthorization?: () => void;
5
+ suppressUserUnavailableErrorNotification?: boolean;
6
+ };
7
+ children?: import("react").ReactNode;
8
+ }) => import("react/jsx-runtime").JSX.Element, useBunnyProviderCallbacks: () => {
9
+ onUserUnavailable?: () => void;
10
+ onInvalidOrMissingAuthorization?: () => void;
11
+ suppressUserUnavailableErrorNotification?: boolean;
12
+ };
13
+ export { BunnyProviderCallbacksProvider, useBunnyProviderCallbacks };
@@ -2,6 +2,8 @@ import { InvoiceQuoteContextProps } from '../../contexts/InvoiceQuoteContext';
2
2
  export declare const MarkupContainer: any;
3
3
  type QuoteProps = InvoiceQuoteContextProps & {
4
4
  onQuoteAccepted?: () => void;
5
+ onQuoteUnavailableError?: () => void;
6
+ suppressQuoteUnavailableErrorNotification?: boolean;
5
7
  };
6
- export default function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, }: QuoteProps): import("react/jsx-runtime").JSX.Element;
8
+ export default function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification, }: QuoteProps): import("react/jsx-runtime").JSX.Element;
7
9
  export {};
@@ -3,7 +3,7 @@ export { Quote } from './Quote';
3
3
  export { Quotes } from './Quotes';
4
4
  export { PaymentForm } from './PaymentForm';
5
5
  export { Signup } from './Signup';
6
- export { default as BunnyProvider } from './BunnyProvider';
6
+ export { default as BunnyProvider } from './BunnyProvider/BunnyProvider';
7
7
  export { default as Transactions } from './Transactions';
8
8
  export { Subscriptions } from './Subscriptions';
9
9
  export { BillingDetails } from './BillingDetails';
@@ -13,5 +13,7 @@ export type InvoiceQuoteContextProps = {
13
13
  hideDownloadButton?: boolean;
14
14
  onInvoiceLoaded?: (formattedInvoice: FormattedInvoice) => void;
15
15
  onQuoteLoaded?: (formattedQuote: FormattedQuote) => void;
16
+ onQuoteUnavailableError?: () => void;
17
+ suppressQuoteUnavailableErrorNotification?: boolean;
16
18
  };
17
19
  export declare const InvoiceQuoteContext: import("react").Context<InvoiceQuoteContextProps>;
@@ -1 +1,5 @@
1
- export declare const useAllErrorFormats: () => (error: any, mutationName?: string) => any;
1
+ export declare const useAllErrorFormats: ({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification }?: {
2
+ onUserUnavailable?: () => void;
3
+ onInvalidOrMissingAuthorization?: () => void;
4
+ suppressUserUnavailableErrorNotification?: boolean;
5
+ }) => (error: any, mutationName?: string) => any;
package/dist/esm/index.js CHANGED
@@ -1281,7 +1281,7 @@ const DEFAULT_CONFIG = {
1281
1281
  };
1282
1282
 
1283
1283
  // This will be replaced at build time by rollup-plugin-replace
1284
- const PACKAGE_VERSION = '1.7.0-beta.25';
1284
+ const PACKAGE_VERSION = '1.7.0-beta.27';
1285
1285
  const createRequestHeaders = (token) => {
1286
1286
  const headers = createClientDevHeaders({ token });
1287
1287
  // Add the components version header
@@ -1433,7 +1433,12 @@ const createValueContext = () => {
1433
1433
 
1434
1434
  const [BrandProvider, useBrand] = createValueContext();
1435
1435
 
1436
- const IGNORE_ERRORS = ['Invalid or missing authorization', '["Not found"]'];
1436
+ const [BunnyProviderCallbacksProvider, useBunnyProviderCallbacks] = createValueContext();
1437
+
1438
+ const QUOTE_UNAVAILABLE_ERROR = 'This quote is currently unavailable';
1439
+ const INVALID_OR_MISSING_AUTHORIZATION_ERROR = 'Invalid or missing authorization';
1440
+ const IGNORE_ERRORS = [INVALID_OR_MISSING_AUTHORIZATION_ERROR, '["Not found"]', QUOTE_UNAVAILABLE_ERROR];
1441
+ const CONTACT_DOES_NOT_EXIST_ERROR = 'Contact does not exist within';
1437
1442
  /**
1438
1443
  * Extracts errors from nested data structure
1439
1444
  * @param data - The response data object
@@ -1465,13 +1470,24 @@ const extractNestedErrors = (data, mutationName) => {
1465
1470
  }
1466
1471
  return null;
1467
1472
  };
1468
- const useAllErrorFormats = () => {
1473
+ const useAllErrorFormats = ({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification } = {}) => {
1469
1474
  const showErrorNotification = useErrorNotification();
1470
1475
  return (error, mutationName) => {
1471
- var _a, _b, _c, _d, _e, _f, _g;
1476
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1472
1477
  // Fall back to existing error handling logic
1473
1478
  const responseErrors = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.errors) || null;
1474
- if (IGNORE_ERRORS.includes((_d = (_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.errors) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message)) {
1479
+ // also check error?.message, which is the structure provided by the api/plugins rest endpoint
1480
+ const errorMessage = (_e = (_d = (_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.errors) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message) !== null && _e !== void 0 ? _e : error === null || error === void 0 ? void 0 : error.message;
1481
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(INVALID_OR_MISSING_AUTHORIZATION_ERROR)) {
1482
+ onInvalidOrMissingAuthorization === null || onInvalidOrMissingAuthorization === void 0 ? void 0 : onInvalidOrMissingAuthorization();
1483
+ }
1484
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(CONTACT_DOES_NOT_EXIST_ERROR)) {
1485
+ onUserUnavailable === null || onUserUnavailable === void 0 ? void 0 : onUserUnavailable();
1486
+ if (suppressUserUnavailableErrorNotification) {
1487
+ return error;
1488
+ }
1489
+ }
1490
+ if (IGNORE_ERRORS.includes(errorMessage)) {
1475
1491
  return error;
1476
1492
  }
1477
1493
  // For errors like this:
@@ -1498,18 +1514,18 @@ const useAllErrorFormats = () => {
1498
1514
  }
1499
1515
  if (responseErrors) {
1500
1516
  for (let index in responseErrors) {
1501
- const error = (_e = responseErrors[index]) === null || _e === void 0 ? void 0 : _e.message;
1517
+ const error = (_f = responseErrors[index]) === null || _f === void 0 ? void 0 : _f.message;
1502
1518
  showErrorNotification(error, null);
1503
1519
  }
1504
1520
  }
1505
- else if ((_f = error === null || error === void 0 ? void 0 : error.response) === null || _f === void 0 ? void 0 : _f.error) {
1521
+ else if ((_g = error === null || error === void 0 ? void 0 : error.response) === null || _g === void 0 ? void 0 : _g.error) {
1506
1522
  showErrorNotification(error.response.exception, error.response.error);
1507
1523
  }
1508
1524
  else if ((error === null || error === void 0 ? void 0 : error.constructor) === Array) {
1509
1525
  for (let index in error)
1510
1526
  showErrorNotification(error[index].message, null);
1511
1527
  }
1512
- else if ((_g = error === null || error === void 0 ? void 0 : error.response) === null || _g === void 0 ? void 0 : _g.message) {
1528
+ else if ((_h = error === null || error === void 0 ? void 0 : error.response) === null || _h === void 0 ? void 0 : _h.message) {
1513
1529
  showErrorNotification(error.response.message);
1514
1530
  }
1515
1531
  else if (error === null || error === void 0 ? void 0 : error.message) {
@@ -1525,7 +1541,8 @@ const useAllErrorFormats = () => {
1525
1541
  };
1526
1542
 
1527
1543
  const useCreateQueryClient = () => {
1528
- const handleAllErrorFormats = useAllErrorFormats();
1544
+ const { onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification } = useBunnyProviderCallbacks();
1545
+ const handleAllErrorFormats = useAllErrorFormats({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification });
1529
1546
  const onError = (error, _query) => {
1530
1547
  // Extract mutation name from query string
1531
1548
  // const mutationName = query?.mutation?.match(/mutation\s+(\w+)/)?.[1];
@@ -2243,9 +2260,11 @@ const getBranding = async ({ token, apiHost }) => {
2243
2260
  };
2244
2261
 
2245
2262
  const BunnyContext = createContext({});
2246
- // const extraQueryClient = new QueryClient();
2247
2263
  // Every component shares similar props and functionality, which this wrapper handles.
2248
- function BunnyProvider({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, configProviderProps, }) {
2264
+ function BunnyProvider({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification = false, configProviderProps, }) {
2265
+ return (jsx(BunnyProviderCallbacksProvider, { value: { onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification }, children: jsx(BunnyProviderContent, { darkMode: darkMode, queryClient: queryClient, apiHost: apiHost, token: token, onTokenExpired: onTokenExpired, configProviderProps: configProviderProps, children: children }) }));
2266
+ }
2267
+ function BunnyProviderContent({ children, darkMode = false, queryClient, apiHost, token, onTokenExpired, configProviderProps, }) {
2249
2268
  const createQueryClient = useCreateQueryClient();
2250
2269
  const extraQueryClient = useMemo(() => createQueryClient(), []);
2251
2270
  return (jsx(BunnyContext.Provider, { value: {
@@ -20374,7 +20393,7 @@ function useApproveHold$1({ onApproveHoldSuccess, onApproveHoldError, }) {
20374
20393
  // Used for Signup component. Signup needs a custom checkout function
20375
20394
  const [CustomCheckoutFunctionProvider, useCustomCheckoutFunction] = createValueContext();
20376
20395
 
20377
- const showErrorNotification$9 = useErrorNotification();
20396
+ const showErrorNotification$a = useErrorNotification();
20378
20397
  function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20379
20398
  const { apiHost } = useContext(BunnyContext);
20380
20399
  const customCheckoutFunction = useCustomCheckoutFunction();
@@ -20385,7 +20404,7 @@ function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin,
20385
20404
  setIsPaying(true);
20386
20405
  if (customCheckoutFunction) {
20387
20406
  if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20388
- showErrorNotification$9('Plugin ID is required');
20407
+ showErrorNotification$a('Plugin ID is required');
20389
20408
  return;
20390
20409
  }
20391
20410
  const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
@@ -20444,7 +20463,7 @@ function useApproveHold({ onApproveHoldSuccess, onApproveHoldError, }) {
20444
20463
  return { approveHold, isApprovingHold };
20445
20464
  }
20446
20465
 
20447
- const showErrorNotification$8 = useErrorNotification();
20466
+ const showErrorNotification$9 = useErrorNotification();
20448
20467
  function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20449
20468
  // Hooks
20450
20469
  const { apiHost } = useContext(BunnyContext);
@@ -20456,7 +20475,7 @@ function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, })
20456
20475
  setIsPaying(true);
20457
20476
  if (customCheckoutFunction) {
20458
20477
  if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20459
- showErrorNotification$8('Plugin ID is required');
20478
+ showErrorNotification$9('Plugin ID is required');
20460
20479
  return;
20461
20480
  }
20462
20481
  const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
@@ -20464,7 +20483,7 @@ function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, })
20464
20483
  }
20465
20484
  else {
20466
20485
  if (!quoteId && !(invoice === null || invoice === void 0 ? void 0 : invoice.id)) {
20467
- showErrorNotification$8('Quote ID or Invoice ID is required');
20486
+ showErrorNotification$9('Quote ID or Invoice ID is required');
20468
20487
  return;
20469
20488
  }
20470
20489
  const response = await checkout({
@@ -20495,7 +20514,7 @@ var PaymentType;
20495
20514
  })(PaymentType || (PaymentType = {}));
20496
20515
 
20497
20516
  const handleAllErrorFormats$3 = useAllErrorFormats();
20498
- const showErrorNotification$7 = useErrorNotification();
20517
+ const showErrorNotification$8 = useErrorNotification();
20499
20518
  const useHandlePayment_QuoteFragment = t(`
20500
20519
  fragment useHandlePayment_QuoteFragment on Quote {
20501
20520
  id
@@ -20584,11 +20603,11 @@ const useHandlePayment = ({ quote: maskedQuote, invoice, onPaymentSuccess, onPay
20584
20603
  var _a, _b, _c, _d;
20585
20604
  const paymentMethodId = overridePaymentMethodId || (defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
20586
20605
  if (!plugin)
20587
- return showErrorNotification$7('plugin is undefined');
20606
+ return showErrorNotification$8('plugin is undefined');
20588
20607
  if (!(quote === null || quote === void 0 ? void 0 : quote.id) || !(quote === null || quote === void 0 ? void 0 : quote.amount))
20589
- return showErrorNotification$7('quote id and amount are required to approve hold');
20608
+ return showErrorNotification$8('quote id and amount are required to approve hold');
20590
20609
  if (!paymentMethodId)
20591
- return showErrorNotification$7('payment method id is undefined');
20610
+ return showErrorNotification$8('payment method id is undefined');
20592
20611
  switch ((_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) {
20593
20612
  case 'StripePayment':
20594
20613
  await approveHoldStripe({
@@ -20605,7 +20624,7 @@ const useHandlePayment = ({ quote: maskedQuote, invoice, onPaymentSuccess, onPay
20605
20624
  });
20606
20625
  break;
20607
20626
  default:
20608
- showErrorNotification$7(`Payment holds are not supported by ${(_d = (_c = plugin.components) === null || _c === void 0 ? void 0 : _c.frontend) === null || _d === void 0 ? void 0 : _d[0].name}`);
20627
+ showErrorNotification$8(`Payment holds are not supported by ${(_d = (_c = plugin.components) === null || _c === void 0 ? void 0 : _c.frontend) === null || _d === void 0 ? void 0 : _d[0].name}`);
20609
20628
  break;
20610
20629
  }
20611
20630
  };
@@ -20721,7 +20740,7 @@ const useShowPaymentDetailsState = (isOpen = false) => useState(isOpen);
20721
20740
  const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(useShowPaymentDetailsState);
20722
20741
 
20723
20742
  const handleAllErrorFormats$2 = useAllErrorFormats();
20724
- const showErrorNotification$6 = useErrorNotification();
20743
+ const showErrorNotification$7 = useErrorNotification();
20725
20744
  // Contexts
20726
20745
  const [FormattedAmountDueProvider, useFormattedAmountDue] = createValueContext();
20727
20746
  const [PaymentTypeProvider, usePaymentType] = createValueContext();
@@ -20801,7 +20820,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20801
20820
  return await saveDemoPay(demoPayCardDetails);
20802
20821
  }
20803
20822
  else {
20804
- showErrorNotification$6(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20823
+ showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20805
20824
  return undefined;
20806
20825
  }
20807
20826
  };
@@ -20814,7 +20833,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20814
20833
  await handleApproveHold(savedPaymentMethodId);
20815
20834
  }
20816
20835
  else {
20817
- showErrorNotification$6(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20836
+ showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20818
20837
  }
20819
20838
  };
20820
20839
  const handlePaymentFormSubmit = async (demoPayCardDetails) => {
@@ -20845,7 +20864,7 @@ function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onP
20845
20864
  }
20846
20865
  else {
20847
20866
  if (!(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id)) {
20848
- showErrorNotification$6('Default payment method ID is undefined');
20867
+ showErrorNotification$7('Default payment method ID is undefined');
20849
20868
  return;
20850
20869
  }
20851
20870
  await handlePayment(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
@@ -21279,7 +21298,7 @@ function useSetDefaultPaymentMethod(onError, onSuccess) {
21279
21298
  return { setDefaultPaymentMethod, loading };
21280
21299
  }
21281
21300
 
21282
- const showErrorNotification$5 = useErrorNotification();
21301
+ const showErrorNotification$6 = useErrorNotification();
21283
21302
  const PaymentForm_QuoteFragment = t(`
21284
21303
  fragment PaymentForm_QuoteFragment on Quote {
21285
21304
  id
@@ -21328,7 +21347,7 @@ function PaymentFormContent({ onPaymentMethodRemoved, onSetDefaultPaymentMethod,
21328
21347
  const hasPaymentMethods = (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) && (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) > 0;
21329
21348
  // Custom hooks
21330
21349
  const { setDefaultPaymentMethod: handleSetDefault, loading: setDefaultPaymentMethodLoading } = useSetDefaultPaymentMethod(message => {
21331
- showErrorNotification$5(message, 'Error setting default payment method');
21350
+ showErrorNotification$6(message, 'Error setting default payment method');
21332
21351
  }, () => {
21333
21352
  onSetDefaultPaymentMethod === null || onSetDefaultPaymentMethod === void 0 ? void 0 : onSetDefaultPaymentMethod();
21334
21353
  });
@@ -21338,11 +21357,11 @@ function PaymentFormContent({ onPaymentMethodRemoved, onSetDefaultPaymentMethod,
21338
21357
  enabled: !!paymentPlugins,
21339
21358
  });
21340
21359
  const onClickRemove = useRemovePaymentMethod(onPaymentMethodRemoved, message => {
21341
- showErrorNotification$5(message, 'Error removing payment method');
21360
+ showErrorNotification$6(message, 'Error removing payment method');
21342
21361
  });
21343
21362
  function handleClickAddPaymentMethod() {
21344
21363
  if ((paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.length) === 0) {
21345
- showErrorNotification$5('No payment plugins available', 'Error adding payment method');
21364
+ showErrorNotification$6('No payment plugins available', 'Error adding payment method');
21346
21365
  }
21347
21366
  else {
21348
21367
  setShowPaymentMethodForm(true);
@@ -21422,23 +21441,26 @@ if (typeof window !== 'undefined') {
21422
21441
  const shadowRoot = originalAttachShadow.call(this, init);
21423
21442
  // Prevent watermark styles from being injected
21424
21443
  const originalInnerHTMLSetter = (_a = Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'innerHTML')) === null || _a === void 0 ? void 0 : _a.set;
21425
- console.log('originalInnerHTMLSetter', originalInnerHTMLSetter);
21426
21444
  if (originalInnerHTMLSetter) {
21427
- Object.defineProperty(shadowRoot, 'innerHTML', {
21428
- set: function (value) {
21429
- // Block watermark injection
21430
- if (typeof value === 'string' &&
21431
- value.includes('background-image: url(data:image/webp')) {
21432
- return; // Don't set the watermark HTML
21433
- }
21434
- console.log('value', value);
21435
- originalInnerHTMLSetter.call(this, value);
21436
- },
21437
- get: function () {
21438
- console.log('this.innerHTML', this.innerHTML);
21439
- return this.innerHTML;
21440
- },
21441
- });
21445
+ try {
21446
+ Object.defineProperty(shadowRoot, 'innerHTML', {
21447
+ configurable: true,
21448
+ set: function (value) {
21449
+ // Block watermark injection
21450
+ if (typeof value === 'string' &&
21451
+ value.includes('background-image: url(data:image/webp')) {
21452
+ return; // Don't set the watermark HTML
21453
+ }
21454
+ originalInnerHTMLSetter.call(this, value);
21455
+ },
21456
+ get: function () {
21457
+ return this.innerHTML;
21458
+ },
21459
+ });
21460
+ }
21461
+ catch (e) {
21462
+ // Property already defined, ignore error (happens during hot module reloading)
21463
+ }
21442
21464
  }
21443
21465
  return shadowRoot;
21444
21466
  };
@@ -21793,7 +21815,7 @@ function PaymentHoldDisplay({ paymentHold, currency, amount, }) {
21793
21815
 
21794
21816
  const { Title: Title$2, Text: Text$x } = Typography;
21795
21817
  const showSuccessNotification$2 = useSuccessNotification();
21796
- const showErrorNotification$4 = useErrorNotification();
21818
+ const showErrorNotification$5 = useErrorNotification();
21797
21819
  const PaymentHoldModal_FormattedQuoteFragment = t(`
21798
21820
  fragment PaymentHoldModal_FormattedQuoteFragment on FormattedQuote {
21799
21821
  quote {
@@ -21821,7 +21843,7 @@ const PaymentHoldModal = ({ visible, setVisible, formattedQuote: maskedFormatted
21821
21843
  setVisible(false);
21822
21844
  showSuccessNotification$2(`Approved payment hold for ${formatCurrency(formattedQuote.amount, formattedQuote.currency || 'null')}`);
21823
21845
  if (!formattedQuote.quote.id) {
21824
- showErrorNotification$4('Quote ID is required for onPaymentHoldSuccess callback in PaymentForm');
21846
+ showErrorNotification$5('Quote ID is required for onPaymentHoldSuccess callback in PaymentForm');
21825
21847
  return;
21826
21848
  }
21827
21849
  queryClient.invalidateQueries({
@@ -22245,12 +22267,13 @@ const PandadocPollingModal = ({ isVisible, setVisible, id }) => {
22245
22267
  return (jsxs(Modal, { title: "Uploading quote to Pandadoc", open: isVisible, closable: false, footer: null, children: [jsxs("div", { className: "bunny-py-4 bunny-text-center", children: ["This may take a few seconds", '.'.repeat(numberOfPolls)] }), jsx("div", { className: "bunny-text-center", children: infoMessage })] }));
22246
22268
  };
22247
22269
 
22270
+ const showErrorNotification$4 = useErrorNotification();
22248
22271
  defaultStyled.div `
22249
22272
  Text {
22250
22273
  width: 100%;
22251
22274
  }
22252
22275
  `;
22253
- function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, onQuoteLoaded, onQuoteAccepted, }) {
22276
+ function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, onQuoteLoaded, onQuoteAccepted, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification = false, }) {
22254
22277
  return (jsx(InvoiceQuoteContext.Provider, { value: {
22255
22278
  id,
22256
22279
  invoiceQuoteViewComponent,
@@ -22260,6 +22283,8 @@ function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymen
22260
22283
  className,
22261
22284
  hideDownloadButton,
22262
22285
  onQuoteLoaded,
22286
+ onQuoteUnavailableError,
22287
+ suppressQuoteUnavailableErrorNotification,
22263
22288
  }, children: jsx(ActualQuote, { onQuoteAccepted: onQuoteAccepted }) }));
22264
22289
  }
22265
22290
  const useQuotePaymentHold = (formattedQuote) => {
@@ -22280,9 +22305,9 @@ function ActualQuote({ onQuoteAccepted }) {
22280
22305
  // Context
22281
22306
  const { apiHost } = useContext(BunnyContext);
22282
22307
  const token = useToken();
22283
- const { className, id, hideDownloadButton, onQuoteLoaded } = useContext(InvoiceQuoteContext);
22308
+ const { className, id, hideDownloadButton, onQuoteLoaded, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification } = useContext(InvoiceQuoteContext);
22284
22309
  // Queries
22285
- const { data: formattedQuote, isLoading } = useQuery({
22310
+ const { data: formattedQuote, isLoading, error } = useQuery({
22286
22311
  queryKey: QueryKeyFactory.createQuoteKey({ id, token }),
22287
22312
  queryFn: async () => {
22288
22313
  return await getFormattedQuote({ token, apiHost, id });
@@ -22303,6 +22328,18 @@ function ActualQuote({ onQuoteAccepted }) {
22303
22328
  onQuoteLoaded === null || onQuoteLoaded === void 0 ? void 0 : onQuoteLoaded(formattedQuote); // TODO: fix to use the correct type
22304
22329
  }
22305
22330
  }, [formattedQuote]);
22331
+ useEffect(() => {
22332
+ var _a, _b, _c;
22333
+ if (error) {
22334
+ const errorMessage = (_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.errors) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message;
22335
+ if (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes("This quote is currently unavailable")) {
22336
+ onQuoteUnavailableError === null || onQuoteUnavailableError === void 0 ? void 0 : onQuoteUnavailableError();
22337
+ if (!suppressQuoteUnavailableErrorNotification) {
22338
+ showErrorNotification$4(errorMessage, null);
22339
+ }
22340
+ }
22341
+ }
22342
+ }, [error]);
22306
22343
  // Payment hold stuff here
22307
22344
  const { paymentHoldModalVisible, setPaymentHoldModalVisible, shouldDoPaymentHold, paymentHoldCompleted, paymentHold, } = useQuotePaymentHold(formattedQuote); // TODO: fix to use the correct type
22308
22345
  const handleClickAccept = () => {
@@ -7,13 +7,16 @@ export type BunnyContextValues = {
7
7
  onTokenExpired?: () => void;
8
8
  };
9
9
  export declare const BunnyContext: import("react").Context<BunnyContextValues>;
10
- declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, configProviderProps, }: {
10
+ declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification, configProviderProps, }: {
11
11
  children: React.ReactNode;
12
12
  darkMode?: boolean;
13
13
  queryClient?: QueryClient;
14
14
  apiHost: string;
15
15
  token?: string;
16
16
  onTokenExpired?: () => void;
17
+ onUserUnavailable?: () => void;
18
+ onInvalidOrMissingAuthorization?: () => void;
19
+ suppressUserUnavailableErrorNotification?: boolean;
17
20
  configProviderProps?: ThemeConfig | undefined;
18
21
  }): import("react/jsx-runtime").JSX.Element;
19
22
  export default BunnyProvider;
@@ -0,0 +1,13 @@
1
+ declare const BunnyProviderCallbacksProvider: ({ value, children }: {
2
+ value: {
3
+ onUserUnavailable?: () => void;
4
+ onInvalidOrMissingAuthorization?: () => void;
5
+ suppressUserUnavailableErrorNotification?: boolean;
6
+ };
7
+ children?: import("react").ReactNode;
8
+ }) => import("react/jsx-runtime").JSX.Element, useBunnyProviderCallbacks: () => {
9
+ onUserUnavailable?: () => void;
10
+ onInvalidOrMissingAuthorization?: () => void;
11
+ suppressUserUnavailableErrorNotification?: boolean;
12
+ };
13
+ export { BunnyProviderCallbacksProvider, useBunnyProviderCallbacks };
@@ -2,6 +2,8 @@ import { InvoiceQuoteContextProps } from '../../contexts/InvoiceQuoteContext';
2
2
  export declare const MarkupContainer: any;
3
3
  type QuoteProps = InvoiceQuoteContextProps & {
4
4
  onQuoteAccepted?: () => void;
5
+ onQuoteUnavailableError?: () => void;
6
+ suppressQuoteUnavailableErrorNotification?: boolean;
5
7
  };
6
- export default function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, }: QuoteProps): import("react/jsx-runtime").JSX.Element;
8
+ export default function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification, }: QuoteProps): import("react/jsx-runtime").JSX.Element;
7
9
  export {};
@@ -3,7 +3,7 @@ export { Quote } from './Quote';
3
3
  export { Quotes } from './Quotes';
4
4
  export { PaymentForm } from './PaymentForm';
5
5
  export { Signup } from './Signup';
6
- export { default as BunnyProvider } from './BunnyProvider';
6
+ export { default as BunnyProvider } from './BunnyProvider/BunnyProvider';
7
7
  export { default as Transactions } from './Transactions';
8
8
  export { Subscriptions } from './Subscriptions';
9
9
  export { BillingDetails } from './BillingDetails';
@@ -13,5 +13,7 @@ export type InvoiceQuoteContextProps = {
13
13
  hideDownloadButton?: boolean;
14
14
  onInvoiceLoaded?: (formattedInvoice: FormattedInvoice) => void;
15
15
  onQuoteLoaded?: (formattedQuote: FormattedQuote) => void;
16
+ onQuoteUnavailableError?: () => void;
17
+ suppressQuoteUnavailableErrorNotification?: boolean;
16
18
  };
17
19
  export declare const InvoiceQuoteContext: import("react").Context<InvoiceQuoteContextProps>;
@@ -1 +1,5 @@
1
- export declare const useAllErrorFormats: () => (error: any, mutationName?: string) => any;
1
+ export declare const useAllErrorFormats: ({ onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification }?: {
2
+ onUserUnavailable?: () => void;
3
+ onInvalidOrMissingAuthorization?: () => void;
4
+ suppressUserUnavailableErrorNotification?: boolean;
5
+ }) => (error: any, mutationName?: string) => any;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ type InvoiceQuoteContextProps = {
23
23
  hideDownloadButton?: boolean;
24
24
  onInvoiceLoaded?: (formattedInvoice: FormattedInvoice) => void;
25
25
  onQuoteLoaded?: (formattedQuote: FormattedQuote) => void;
26
+ onQuoteUnavailableError?: () => void;
27
+ suppressQuoteUnavailableErrorNotification?: boolean;
26
28
  };
27
29
 
28
30
  type InvoiceProps = InvoiceQuoteContextProps & {
@@ -41,8 +43,10 @@ declare global {
41
43
 
42
44
  type QuoteProps = InvoiceQuoteContextProps & {
43
45
  onQuoteAccepted?: () => void;
46
+ onQuoteUnavailableError?: () => void;
47
+ suppressQuoteUnavailableErrorNotification?: boolean;
44
48
  };
45
- declare function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, }: QuoteProps): react_jsx_runtime.JSX.Element;
49
+ declare function Quote({ id, invoiceQuoteViewComponent, onInvoiceDownloadError, onPaymentSuccess, shadow, className, hideDownloadButton, onQuoteLoaded, onQuoteAccepted, onQuoteUnavailableError, suppressQuoteUnavailableErrorNotification, }: QuoteProps): react_jsx_runtime.JSX.Element;
46
50
 
47
51
  type TransactionListColumnType = 'date' | 'title' | 'state' | 'amount' | 'download' | 'accountName';
48
52
 
@@ -152,13 +156,16 @@ declare function Signup({ companyName, priceListCode, returnUrl, couponCode, cla
152
156
  defaultBillingCountry?: string;
153
157
  }): react_jsx_runtime.JSX.Element;
154
158
 
155
- declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, configProviderProps, }: {
159
+ declare function BunnyProvider({ children, darkMode, queryClient, apiHost, token, onTokenExpired, onUserUnavailable, onInvalidOrMissingAuthorization, suppressUserUnavailableErrorNotification, configProviderProps, }: {
156
160
  children: React.ReactNode;
157
161
  darkMode?: boolean;
158
162
  queryClient?: QueryClient;
159
163
  apiHost: string;
160
164
  token?: string;
161
165
  onTokenExpired?: () => void;
166
+ onUserUnavailable?: () => void;
167
+ onInvalidOrMissingAuthorization?: () => void;
168
+ suppressUserUnavailableErrorNotification?: boolean;
162
169
  configProviderProps?: ThemeConfig | undefined;
163
170
  }): react_jsx_runtime.JSX.Element;
164
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnyapp/components",
3
- "version": "1.7.0-beta.26",
3
+ "version": "1.7.0-beta.28",
4
4
  "description": "Components from the Bunny portal to embed Bunny UI functionality into your application.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",