@bunnyapp/components 1.6.0-beta.20 → 1.6.0-beta.21

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
@@ -143,7 +143,7 @@ const useAllErrorFormats = () => {
143
143
  };
144
144
 
145
145
  // This will be replaced at build time by rollup-plugin-replace
146
- const PACKAGE_VERSION = '1.6.0-beta.19';
146
+ const PACKAGE_VERSION = '1.6.0-beta.20';
147
147
  const createRequestHeaders = (token) => {
148
148
  const headers = createClientDevHeaders({ token });
149
149
  // Add the components version header
@@ -929,7 +929,7 @@ function readFragment(...r) {
929
929
 
930
930
  var t = initGraphQLTada();
931
931
 
932
- const query$6 = t(`
932
+ const query$7 = t(`
933
933
  query entityBranding {
934
934
  entityBranding {
935
935
  accentColor
@@ -939,7 +939,7 @@ const query$6 = t(`
939
939
  }
940
940
  `);
941
941
  const getBranding = async ({ token, apiHost }) => {
942
- return await execute(query$6, { apiHost, token }, {});
942
+ return await execute(query$7, { apiHost, token }, {});
943
943
  };
944
944
 
945
945
  const BunnyContext = react.createContext({});
@@ -1146,7 +1146,7 @@ const InvoiceQuoteView = ({ children, formattedInvoice, html, backButtonName, on
1146
1146
  }, children: [targetUrl ? (jsxRuntime.jsx(DocumentTemplatePreview, { targetUrl: targetUrl })) : (jsxRuntime.jsx(interweave.Markup, { content: html })), children] }))] }));
1147
1147
  };
1148
1148
 
1149
- const MUTATION$a = `
1149
+ const MUTATION$9 = `
1150
1150
  query FormattedInvoice($id: ID) {
1151
1151
  formattedInvoice(id: $id) {
1152
1152
  amount
@@ -1214,7 +1214,7 @@ query FormattedInvoice($id: ID) {
1214
1214
  const getFormattedInvoice = async ({ id, token, apiHost, }) => {
1215
1215
  const vars = { id };
1216
1216
  const response = await gqlRequest({
1217
- query: MUTATION$a,
1217
+ query: MUTATION$9,
1218
1218
  token,
1219
1219
  vars,
1220
1220
  apiHost,
@@ -18732,7 +18732,7 @@ const QueryKeyFactory = {
18732
18732
  paymentPluginsKey: (token) => ['paymentPlugins', ...(token ? [token] : [])],
18733
18733
  };
18734
18734
 
18735
- const query$5 = t(`
18735
+ const query$6 = t(`
18736
18736
  query PaymentMethods($accountId: ID) {
18737
18737
  paymentMethods(accountId: $accountId) {
18738
18738
  nodes {
@@ -18761,7 +18761,7 @@ const query$5 = t(`
18761
18761
  `, [PaymentForm_PaymentMethodsFragment]);
18762
18762
  const getPaymentMethods = async ({ apiHost, token, accountId, }) => {
18763
18763
  var _a, _b, _c;
18764
- const response = await execute(query$5, { apiHost, token }, { accountId });
18764
+ const response = await execute(query$6, { apiHost, token }, { accountId });
18765
18765
  // Filter out null values that are technically possible due to api schema
18766
18766
  return (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.paymentMethods) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter(paymentMethod => paymentMethod !== null)) !== null && _c !== void 0 ? _c : [];
18767
18767
  };
@@ -19007,7 +19007,7 @@ function useSave$1({ onSaveSuccess, onSaveError, accountId, }) {
19007
19007
  return { save, isSaving };
19008
19008
  }
19009
19009
 
19010
- const MUTATION$9 = `
19010
+ const MUTATION$8 = `
19011
19011
  mutation checkout(
19012
19012
  $invoiceId: ID,
19013
19013
  $quoteId: ID,
@@ -19056,7 +19056,7 @@ const checkout = async ({ quoteId, invoiceId, paymentMethodId, paymentMethodData
19056
19056
  };
19057
19057
  }
19058
19058
  const response = await gqlRequest({
19059
- query: MUTATION$9,
19059
+ query: MUTATION$8,
19060
19060
  token,
19061
19061
  vars: mutationVars,
19062
19062
  apiHost: apiHost,
@@ -19344,14 +19344,20 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19344
19344
  const { defaultPaymentMethod } = usePaymentMethod({ accountId });
19345
19345
  const { apiHost } = react.useContext(BunnyContext);
19346
19346
  const token = useToken();
19347
+ // State
19348
+ const [isPaid, setIsPaid] = react.useState(false);
19347
19349
  const formattedAmountDue = () => {
19348
19350
  const amountDue = quote ? getQuoteAmountDue(quote) : invoice === null || invoice === void 0 ? void 0 : invoice.amountDue;
19349
19351
  const currencyId = (quote === null || quote === void 0 ? void 0 : quote.currencyId) || (invoice === null || invoice === void 0 ? void 0 : invoice.currencyId);
19350
19352
  return amountDue && currencyId ? common.formatCurrency(amountDue, currencyId).toString() : undefined;
19351
19353
  };
19354
+ // Validation checks
19355
+ if (quote !== undefined && invoice !== undefined) {
19356
+ throw new Error('Either quote or invoice must be provided, not both');
19357
+ }
19352
19358
  // Payment hooks
19353
19359
  const { pay: payDemoPay, isPaying: isPayingDemoPay } = usePay$1({
19354
- onPaymentSuccess,
19360
+ onPaymentSuccess: handlePaymentSuccess,
19355
19361
  onPaymentError: error => {
19356
19362
  handleAllErrorFormats$2(error);
19357
19363
  },
@@ -19360,7 +19366,7 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19360
19366
  plugin,
19361
19367
  });
19362
19368
  const { pay: payStripe, isPaying: isPayingStripe } = usePay({
19363
- onPaymentSuccess,
19369
+ onPaymentSuccess: handlePaymentSuccess,
19364
19370
  onPaymentError: error => {
19365
19371
  handleAllErrorFormats$2(error);
19366
19372
  },
@@ -19432,6 +19438,10 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19432
19438
  break;
19433
19439
  }
19434
19440
  };
19441
+ function handlePaymentSuccess(response) {
19442
+ setIsPaid(true);
19443
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
19444
+ }
19435
19445
  const handlePayment = async (overridePaymentMethodId) => {
19436
19446
  var _a, _b;
19437
19447
  if (!plugin)
@@ -19454,6 +19464,7 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19454
19464
  isPayingStripe ||
19455
19465
  isApprovingHoldDemoPay ||
19456
19466
  isApprovingHoldStripe,
19467
+ isPaid,
19457
19468
  paymentType: paymentHoldOptions.payToAccept ? PaymentType.APPROVE_HOLD : PaymentType.PAY,
19458
19469
  formattedAmountDue: formattedAmountDue(),
19459
19470
  };
@@ -19541,11 +19552,13 @@ const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(u
19541
19552
  const handleAllErrorFormats$1 = common.useAllErrorFormats();
19542
19553
  const showErrorNotification$5 = common.useErrorNotification();
19543
19554
  // Contexts
19544
- const [AmountDueProvider, useAmountDue] = createValueContext();
19555
+ const [FormattedAmountDueProvider, useFormattedAmountDue] = createValueContext();
19545
19556
  const [PaymentTypeProvider, usePaymentType] = createValueContext();
19546
19557
  const [IsPayingProvider, useIsPaying] = createValueContext();
19547
19558
  const [HandlePaymentFormSubmitProvider, useHandlePaymentFormSubmit] = createValueContext();
19548
19559
  const [IsSavingProvider, useIsSaving] = createValueContext();
19560
+ // Used for PaymentMethodFooter to disable pay button if payment is already successful
19561
+ const [IsPaidProvider, useIsPaid] = createValueContext();
19549
19562
  // Used for Signup component. Signup uses an apiClient token which api can't infer an account from, so accountId must be used
19550
19563
  const [AccountIdProvider, useAccountId] = createValueContext();
19551
19564
  function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldOptions, onSavePaymentMethod, }) {
@@ -19556,7 +19569,7 @@ function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess
19556
19569
  // Hooks
19557
19570
  const queryClient = reactQuery.useQueryClient();
19558
19571
  const token = useToken();
19559
- const { handlePayment, handleApproveHold, handleCheckoutNoPayment, isPaying, paymentType, formattedAmountDue, } = useHandlePayment({
19572
+ const { handlePayment, handleApproveHold, handleCheckoutNoPayment, isPaying, isPaid, paymentType, formattedAmountDue, } = useHandlePayment({
19560
19573
  quote,
19561
19574
  invoice,
19562
19575
  onPaymentSuccess: (response) => {
@@ -19662,11 +19675,12 @@ function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess
19662
19675
  }
19663
19676
  };
19664
19677
  const providers = [
19665
- [AmountDueProvider, { value: formattedAmountDue }],
19678
+ [FormattedAmountDueProvider, { value: formattedAmountDue }],
19666
19679
  [PaymentTypeProvider, { value: paymentType }],
19667
19680
  [IsPayingProvider, { value: isPaying }],
19668
19681
  [HandlePaymentFormSubmitProvider, { value: handlePaymentFormSubmit }],
19669
19682
  [IsSavingProvider, { value: isSavingDemoPay || isSavingStripe }],
19683
+ [IsPaidProvider, { value: isPaid }],
19670
19684
  [AccountIdProvider, { value: accountId }],
19671
19685
  ];
19672
19686
  return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: providers.reduceRight((acc, [Provider, props]) => react.createElement(Provider, props, acc), children) }));
@@ -19677,33 +19691,49 @@ function StripeWrapper({ children, currencyId, accountId, }) {
19677
19691
  return (jsxRuntime.jsx(reactStripeJs.Elements, { options: options, stripe: stripe, children: children }));
19678
19692
  }
19679
19693
 
19694
+ function usePaymentButtonText() {
19695
+ const formattedAmountDue = useFormattedAmountDue();
19696
+ const paymentType = usePaymentType();
19697
+ const isPaid = useIsPaid();
19698
+ const isSaving = useIsSaving();
19699
+ const isPaying = useIsPaying();
19700
+ const isProcessing = isSaving || isPaying;
19701
+ // If payment is already successful, show the amount paid
19702
+ if (isPaid) {
19703
+ return 'Paid ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
19704
+ }
19705
+ const paymentTypeText = isProcessing
19706
+ ? paymentType === PaymentType.APPROVE_HOLD
19707
+ ? 'Approving hold for'
19708
+ : 'Paying'
19709
+ : paymentType === PaymentType.APPROVE_HOLD
19710
+ ? 'Approve hold for'
19711
+ : 'Pay';
19712
+ return paymentTypeText + ' ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
19713
+ }
19714
+
19680
19715
  const CheckoutFooter = () => {
19716
+ const isPaid = useIsPaid();
19681
19717
  const isMobile = useIsMobile();
19682
- const paymentType = usePaymentType();
19683
19718
  const isPaying = useIsPaying();
19684
- const formattedAmountDue = useAmountDue();
19719
+ const paymentButtonText = usePaymentButtonText();
19685
19720
  const onPaymentFormSubmit = useHandlePaymentFormSubmit();
19686
- const buttonText = paymentType === PaymentType.APPROVE_HOLD ? 'Approve hold for' : 'Pay';
19687
- const isPayingButtonText = paymentType === PaymentType.APPROVE_HOLD ? 'Approving hold for' : 'Paying';
19688
- return (jsxRuntime.jsx(antd.Button, { className: "bunny-w-full", disabled: isPaying, onClick: () => onPaymentFormSubmit(), size: isMobile ? 'large' : 'middle', type: "primary", children: `${isPaying ? isPayingButtonText : buttonText} ${formattedAmountDue}` }));
19721
+ return (jsxRuntime.jsx(antd.Button, { className: "bunny-w-full", disabled: isPaying || isPaid, onClick: () => onPaymentFormSubmit(), size: isMobile ? 'large' : 'middle', type: "primary", children: paymentButtonText }));
19689
19722
  };
19690
19723
 
19691
19724
  function PaymentMethodFooter({ onSubmit, noPadding, }) {
19692
19725
  const isPaying = useIsPaying();
19693
19726
  const isSaving = useIsSaving();
19694
- const buttonText = useSubmitButtonText();
19727
+ const formattedAmountDue = useFormattedAmountDue();
19728
+ const paymentButtonText = usePaymentButtonText();
19695
19729
  const isMobile = common.useIsMobile();
19696
19730
  const [, setShowPaymentMethodForm] = useShowPaymentDetails();
19731
+ // If amount due is undefined, show the save button
19732
+ const payableAvailable = formattedAmountDue !== undefined;
19733
+ const saveButtonText = isSaving ? 'Saving' : 'Save';
19734
+ const buttonText = payableAvailable ? paymentButtonText : saveButtonText;
19697
19735
  return (jsxRuntime.jsxs("div", { className: `bunny-flex bunny-justify-end bunny-gap-2 ${noPadding ? '' : 'pt-6'}`, children: [jsxRuntime.jsx(antd.Button, { className: "bunny-w-full", size: isMobile ? 'large' : 'middle', onClick: () => setShowPaymentMethodForm(false), children: "Cancel" }), jsxRuntime.jsx(antd.Button, { className: "bunny-w-full", disabled: isSaving || isPaying, loading: isSaving || isPaying, onClick: onSubmit, size: isMobile ? 'large' : 'middle', type: "primary", children: buttonText })] }));
19698
19736
  }
19699
- function useSubmitButtonText() {
19700
- const amountDue = useAmountDue();
19701
- const paymentType = usePaymentType();
19702
- const payableAvailable = amountDue !== undefined;
19703
- const paymentTypeText = paymentType === PaymentType.APPROVE_HOLD ? 'approve hold for' : 'pay';
19704
- const buttonTextPrefix = payableAvailable ? paymentTypeText : 'save';
19705
- return lodashExports.capitalize(buttonTextPrefix) + ' ' + (amountDue !== null && amountDue !== void 0 ? amountDue : '');
19706
- }
19707
19737
 
19708
19738
  const DemoPayCardCvc = ({ autoFocus, onChange, placeholder, value, }) => {
19709
19739
  const onKeyPress = (event) => {
@@ -19904,7 +19934,7 @@ const useAutoSetDefaultPaymentMethod = ({ handleSetDefault, setDefaultPaymentMet
19904
19934
  ]);
19905
19935
  };
19906
19936
 
19907
- const query$4 = t(`
19937
+ const query$5 = t(`
19908
19938
  query GetCurrentUserData {
19909
19939
  company {
19910
19940
  name
@@ -19927,7 +19957,7 @@ const query$4 = t(`
19927
19957
  `);
19928
19958
  const getCurrentUserData = async ({ token, apiHost }) => {
19929
19959
  var _a, _b, _c, _d, _e, _f, _g, _h;
19930
- const response = await execute(query$4, { apiHost, token }, {});
19960
+ const response = await execute(query$5, { apiHost, token }, {});
19931
19961
  return {
19932
19962
  authObjectName: (_a = response === null || response === void 0 ? void 0 : response.currentUser) === null || _a === void 0 ? void 0 : _a.authObjectName,
19933
19963
  account: (_b = response === null || response === void 0 ? void 0 : response.currentUser) === null || _b === void 0 ? void 0 : _b.account,
@@ -20086,7 +20116,7 @@ function PaymentFormContent({ onRemovePaymentMethod, onSetDefaultPaymentMethod,
20086
20116
  const [showPaymentMethodForm, setShowPaymentMethodForm] = useShowPaymentDetails();
20087
20117
  const paymentType = usePaymentType();
20088
20118
  const isPaying = useIsPaying();
20089
- const amountDue = useAmountDue();
20119
+ const formattedAmountDue = useFormattedAmountDue();
20090
20120
  const selectedPlugin = useSelectedPlugin();
20091
20121
  const onPaymentFormSubmit = useHandlePaymentFormSubmit();
20092
20122
  const accountId = useAccountId();
@@ -20133,7 +20163,7 @@ function PaymentFormContent({ onRemovePaymentMethod, onSetDefaultPaymentMethod,
20133
20163
  label: !showPaymentMethodForm ? (jsxRuntime.jsx("div", { className: "bunny-pt-2", children: jsxRuntime.jsx(antd.Button, { onClick: handleClickAddPaymentMethod, type: "default", className: "bunny-w-full", id: "addPaymentMethod", children: "Add payment method" }) })) : null,
20134
20164
  children: (jsxRuntime.jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2 bunny-mt-2", children: [jsxRuntime.jsx(PaymentMethodSelector, {}), selectedPlugin && (jsxRuntime.jsx("div", { className: "bunny-flex bunny-flex-col", children: jsxRuntime.jsx(PaymentMethodDetails, {}) }))] })),
20135
20165
  },
20136
- ] }), amountDue !== undefined && !showPaymentMethodForm && (jsxRuntime.jsx("div", { className: "bunny-px-4", children: jsxRuntime.jsx(CheckoutFooter, {}) }))] })) : (jsxRuntime.jsx(antd.Button, { style: { margin: '0 16px' }, loading: isPaying, onClick: () => onPaymentFormSubmit(), type: "primary", children: isPaying ? 'Processing...' : 'Complete Order' })) }));
20166
+ ] }), formattedAmountDue !== undefined && !showPaymentMethodForm && (jsxRuntime.jsx("div", { className: "bunny-px-4", children: jsxRuntime.jsx(CheckoutFooter, {}) }))] })) : (jsxRuntime.jsx(antd.Button, { style: { margin: '0 16px' }, loading: isPaying, onClick: () => onPaymentFormSubmit(), type: "primary", children: isPaying ? 'Processing...' : 'Complete Order' })) }));
20137
20167
  }
20138
20168
 
20139
20169
  function Invoice({ id, invoiceQuoteViewComponent, backButtonName, onBackButtonClick, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, hidePaymentForm = false, onInvoiceLoaded, }) {
@@ -20168,7 +20198,7 @@ function ActualInvoice({ hidePaymentForm }) {
20168
20198
  const isInvoicePayable = common.PAYABLE_INVOICE_STATES.includes((formattedInvoice === null || formattedInvoice === void 0 ? void 0 : formattedInvoice.state) || '');
20169
20199
  // Local state
20170
20200
  const isMobile = common.useIsMobile(isInvoicePayable ? common.BreakpointNumbers.lg : undefined);
20171
- const onSuccess = () => {
20201
+ const handlePaymentSuccess = () => {
20172
20202
  queryClient.invalidateQueries({
20173
20203
  queryKey: common.QueryKeyFactory.default.transactionsKey({ token }),
20174
20204
  });
@@ -20188,15 +20218,21 @@ function ActualInvoice({ hidePaymentForm }) {
20188
20218
  }, [formattedInvoice]);
20189
20219
  if (!formattedInvoice)
20190
20220
  return jsxRuntime.jsx(jsxRuntime.Fragment, {});
20191
- return (jsxRuntime.jsx("div", { className: "bunny-invoice-container", children: jsxRuntime.jsxs("div", { className: `bunny-flex bunny-gap-6 ${isMobile ? 'bunny-flex-col bunny-w-full' : ''} ${className}`, children: [formattedInvoice.isLegacy ? (jsxRuntime.jsx("div", { className: "bunny-flex bunny-justify-center bunny-w-full", children: jsxRuntime.jsx(LegacyDocument, { documentUuid: formattedInvoice.uuid, documentType: "invoice" }) })) : (invoiceQuoteViewComponent || (jsxRuntime.jsx(InvoiceQuoteView, { html: formattedInvoice.html, formattedInvoice: formattedInvoice, backButtonName: backButtonName, onBackButtonClick: onBackButtonClick }))), isInvoicePayable && !hidePaymentForm && (jsxRuntime.jsx("div", { className: `bunny-w-full ${hideDownloadButton || formattedInvoice.isLegacy ? '' : 'pt-12'}`, children: jsxRuntime.jsx(PaymentForm, { onPaymentSuccess: onSuccess, invoice: formattedInvoice }) }))] }) }));
20221
+ return (jsxRuntime.jsx("div", { className: "bunny-invoice-container", children: jsxRuntime.jsxs("div", { className: `bunny-flex bunny-gap-6 ${isMobile ? 'bunny-flex-col bunny-w-full' : ''} ${className}`, children: [formattedInvoice.isLegacy ? (jsxRuntime.jsx("div", { className: "bunny-flex bunny-justify-center bunny-w-full", children: jsxRuntime.jsx(LegacyDocument, { documentUuid: formattedInvoice.uuid, documentType: "invoice" }) })) : (invoiceQuoteViewComponent || (jsxRuntime.jsx(InvoiceQuoteView, { html: formattedInvoice.html, formattedInvoice: formattedInvoice, backButtonName: backButtonName, onBackButtonClick: onBackButtonClick }))), isInvoicePayable && !hidePaymentForm && (jsxRuntime.jsx("div", { className: `bunny-w-full ${hideDownloadButton || formattedInvoice.isLegacy ? '' : 'pt-12'}`, children: jsxRuntime.jsx(PaymentForm, { onPaymentSuccess: handlePaymentSuccess, invoice: formattedInvoice }) }))] }) }));
20192
20222
  }
20193
20223
 
20194
- const MUTATION$8 = () => `
20195
- query formattedQuote ($id: ID) {
20196
- formattedQuote (id: $id) {
20224
+ const query$4 = t(`
20225
+ query formattedQuote($id: ID) {
20226
+ formattedQuote(id: $id) {
20197
20227
  quote {
20198
20228
  documentTemplateId
20199
- documents { id filename size date url }
20229
+ documents {
20230
+ id
20231
+ filename
20232
+ size
20233
+ date
20234
+ url
20235
+ }
20200
20236
  firstInvoice {
20201
20237
  id
20202
20238
  state
@@ -20326,17 +20362,10 @@ const MUTATION$8 = () => `
20326
20362
  taxNumberRequired
20327
20363
  vendorName
20328
20364
  }
20329
- }`;
20365
+ }
20366
+ `);
20330
20367
  const getFormattedQuote = async ({ token, apiHost, id, }) => {
20331
- const response = await gqlRequest({
20332
- query: MUTATION$8(),
20333
- token,
20334
- apiHost,
20335
- vars: { id },
20336
- });
20337
- if (response.errors && response.errors.length > 0) {
20338
- throw new Error(response.errors[0].message);
20339
- }
20368
+ const response = await execute(query$4, { apiHost, token }, { id });
20340
20369
  return response === null || response === void 0 ? void 0 : response.formattedQuote;
20341
20370
  };
20342
20371
 
@@ -20671,7 +20700,7 @@ const PaymentHoldModal = ({ visible, setVisible, quote, }) => {
20671
20700
  const token = useToken();
20672
20701
  return (jsxRuntime.jsxs(StyledModal$2, { centered: true, onCancel: () => {
20673
20702
  setVisible(false);
20674
- }, footer: null, open: visible, width: 600, children: [jsxRuntime.jsxs("div", { className: "bunny-mt-5 bunny-pb-4 bunny-mx-4", children: [jsxRuntime.jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay now" }), jsxRuntime.jsxs(Text$w, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', common.formatCurrency(quote.amount, quote.currency), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsxRuntime.jsx("div", { className: "bunny-mb-3", children: jsxRuntime.jsx(PaymentForm, { quote: {
20703
+ }, footer: null, open: visible, width: 600, children: [jsxRuntime.jsxs("div", { className: "bunny-mt-5 bunny-pb-4 bunny-mx-4", children: [jsxRuntime.jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay and sign" }), jsxRuntime.jsxs(Text$w, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', common.formatCurrency(quote.amount, quote.currency), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsxRuntime.jsx("div", { className: "bunny-mb-3", children: jsxRuntime.jsx(PaymentForm, { quote: {
20675
20704
  amount: quote.amount,
20676
20705
  currencyId: quote.currency,
20677
20706
  id: quote.quote.id,
@@ -20766,7 +20795,7 @@ function ActualQuote({ onQuoteAccepted }) {
20766
20795
  },
20767
20796
  placeholderData: reactQuery.keepPreviousData,
20768
20797
  });
20769
- const formattedQuote = data;
20798
+ const formattedQuote = data; // TODO: fix to use the correct type
20770
20799
  // Hooks
20771
20800
  const { acceptBoxVisible, isAccepting, sendAccept, setAcceptBoxVisible, setIsAccepting, startAcceptance, pandadocPollingModalVisible, setPandadocPollingModalVisible, isSendAcceptPending, } = useSendAcceptQuote({
20772
20801
  token,
@@ -20813,7 +20842,7 @@ function QuoteButtons({ isAccepted, formattedQuote, isMobile, hideDownloadButton
20813
20842
  const signingPlugins = useSigningPlugins({ apiHost, token });
20814
20843
  return (jsxRuntime.jsxs("div", { className: "flex flex-row justify-end items-center gap-4", id: "acceptance", style: {
20815
20844
  color: secondaryColor,
20816
- }, children: [isAccepted && formattedQuote.acceptedAt ? (jsxRuntime.jsx(Text$v, { children: `Quote was accepted by ${formattedQuote.acceptedByName} on ${common.formatDate(formattedQuote.acceptedAt)}` })) : null, (!isMobile || !isAccepted) && (jsxRuntime.jsxs("div", { className: isMobile ? 'flex w-full justify-end gap-2' : 'flex items-center justify-end gap-2', children: [paymentHold ? (jsxRuntime.jsx(PaymentHoldDisplay, { paymentHold: paymentHold, currency: formattedQuote.currency, amount: formattedQuote.amount })) : null, !isMobile && !hideDownloadButton ? (jsxRuntime.jsx(antd.Button, { icon: jsxRuntime.jsx(icons.DownloadOutlined, {}), onClick: () => downloadFile(apiHost + '/api/pdf/quote', token), children: "Download" })) : null, shouldDoPaymentHold && !paymentHoldCompleted ? (jsxRuntime.jsx(antd.Button, { disabled: isExpired, onClick: () => setPaymentHoldModalVisible(true), type: "primary", children: "Pay now" })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: !isAccepted ? (jsxRuntime.jsx(antd.Button, { disabled: isExpired || isAccepting, onClick: handleClickAccept, type: "primary", children: isExpired
20845
+ }, children: [isAccepted && formattedQuote.acceptedAt ? (jsxRuntime.jsx(Text$v, { children: `Quote was accepted by ${formattedQuote.acceptedByName} on ${common.formatDate(formattedQuote.acceptedAt)}` })) : null, (!isMobile || !isAccepted) && (jsxRuntime.jsxs("div", { className: isMobile ? 'flex w-full justify-end gap-2' : 'flex items-center justify-end gap-2', children: [paymentHold ? (jsxRuntime.jsx(PaymentHoldDisplay, { paymentHold: paymentHold, currency: formattedQuote.currency, amount: formattedQuote.amount })) : null, !isMobile && !hideDownloadButton ? (jsxRuntime.jsx(antd.Button, { icon: jsxRuntime.jsx(icons.DownloadOutlined, {}), onClick: () => downloadFile(apiHost + '/api/pdf/quote', token), children: "Download" })) : null, shouldDoPaymentHold && !paymentHoldCompleted ? (jsxRuntime.jsx(antd.Button, { disabled: isExpired, onClick: () => setPaymentHoldModalVisible(true), type: "primary", children: "Pay and sign" })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: !isAccepted ? (jsxRuntime.jsx(antd.Button, { disabled: isExpired || isAccepting, onClick: handleClickAccept, type: "primary", children: isExpired
20817
20846
  ? 'Quote is expired'
20818
20847
  : (signingPlugins === null || signingPlugins === void 0 ? void 0 : signingPlugins.length)
20819
20848
  ? 'Start signing'
@@ -22175,12 +22204,8 @@ const CheckoutBarInput = ({ disabled, priceListCharge, quantity, onQuantityChang
22175
22204
  }, min: priceListCharge === null || priceListCharge === void 0 ? void 0 : priceListCharge.quantityMin, max: priceListCharge === null || priceListCharge === void 0 ? void 0 : priceListCharge.quantityMax, style: { minWidth: '120px' }, type: "number", value: quantity, required: true }) })] }));
22176
22205
  };
22177
22206
  const QuantityLabel = ({ activeCharge }) => {
22178
- var _a;
22179
- const featureName = (_a = activeCharge.feature) === null || _a === void 0 ? void 0 : _a.name;
22180
- const pluralizedFeatureName = (common.StringUtils.isStringPluralized(featureName) || !featureName
22181
- ? featureName
22182
- : common.StringUtils.pluralizeEntityName(featureName)) || '';
22183
- return (jsxRuntime.jsx(Text$n, { className: "bunny-text-slate-500 bunny-font-medium bunny-text-nowrap", style: { fontSize: '11px' }, children: pluralizedFeatureName.toUpperCase() }));
22207
+ const chargeName = activeCharge.name;
22208
+ return (jsxRuntime.jsx(Text$n, { className: "bunny-text-slate-500 bunny-font-medium bunny-text-nowrap", style: { fontSize: '11px' }, children: chargeName.toUpperCase() }));
22184
22209
  };
22185
22210
 
22186
22211
  const periodMonthsConverter = (period) => {
@@ -22709,11 +22734,8 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22709
22734
  token,
22710
22735
  });
22711
22736
  const { mutate: recalculateTaxesMutation } = reactQuery.useMutation({
22712
- mutationFn: () => {
22713
- if (!(quote === null || quote === void 0 ? void 0 : quote.id)) {
22714
- throw new Error('Quote ID is required');
22715
- }
22716
- return quoteRecalculateTaxes({ token, apiHost, quoteId: quote.id });
22737
+ mutationFn: (quoteId) => {
22738
+ return quoteRecalculateTaxes({ token, apiHost, quoteId });
22717
22739
  },
22718
22740
  onError: (error) => {
22719
22741
  if (!error[0].message.includes('Ensure that you have a taxation plugin')) {
@@ -22762,8 +22784,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22762
22784
  }),
22763
22785
  });
22764
22786
  setInitialQuote(data.quote);
22765
- console.log('handleRecalculateTaxes in quoteAccountSignupMutate');
22766
- handleRecalculateTaxes();
22787
+ handleRecalculateTaxes(data.quote.id);
22767
22788
  },
22768
22789
  onError: (error) => {
22769
22790
  const errorMessage = error.response.errors[0].message;
@@ -22801,7 +22822,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22801
22822
  queryKey: ['quote', quote === null || quote === void 0 ? void 0 : quote.id],
22802
22823
  });
22803
22824
  showSuccessNotification('Coupon applied');
22804
- handleRecalculateTaxes();
22825
+ handleRecalculateTaxes(quote === null || quote === void 0 ? void 0 : quote.id);
22805
22826
  setCouponEditorCouponCode('');
22806
22827
  },
22807
22828
  onCouponRemoved: () => {
@@ -22809,7 +22830,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22809
22830
  queryKey: ['quote', quote === null || quote === void 0 ? void 0 : quote.id],
22810
22831
  });
22811
22832
  showSuccessNotification('Coupon removed');
22812
- handleRecalculateTaxes();
22833
+ handleRecalculateTaxes(quote === null || quote === void 0 ? void 0 : quote.id);
22813
22834
  },
22814
22835
  });
22815
22836
  // Handle default coupon application
@@ -22843,18 +22864,21 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22843
22864
  accountId,
22844
22865
  });
22845
22866
  }
22846
- function handleRecalculateTaxes() {
22847
- console.log('handleRecalculateTaxes', hasTaxPlugin);
22867
+ function handleRecalculateTaxes(quoteId) {
22868
+ if (!quoteId) {
22869
+ showErrorNotification$1('No quote ID found to recalculate taxes');
22870
+ return;
22871
+ }
22848
22872
  if (!hasTaxPlugin) {
22849
- console.log('no tax plugin, returning');
22873
+ showErrorNotification$1('No tax plugin found to recalculate taxes');
22850
22874
  return;
22851
22875
  }
22852
- recalculateTaxesMutation();
22876
+ recalculateTaxesMutation(quoteId);
22853
22877
  }
22854
22878
  if (purchaseSucceeded) {
22855
22879
  return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: (quote === null || quote === void 0 ? void 0 : quote.currencyId) !== undefined ? (jsxRuntime.jsx("div", { className: "bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 bunny-flex bunny-items-start bunny-pt-[25vh]", children: jsxRuntime.jsx(PaymentSuccessDisplay, { amountPaid: (quote === null || quote === void 0 ? void 0 : quote.amountDue) || 0, className: "bunny-w-full", companyName: companyName, returnUrl: returnUrl, currencyId: quote === null || quote === void 0 ? void 0 : quote.currencyId }) })) : (jsxRuntime.jsx("div", { children: "No currency ID found from Quote" })) }));
22856
22880
  }
22857
- return (jsxRuntime.jsxs("div", { className: `bunny-flex ${isMobile ? 'bunny-flex-col' : 'bunny-flex-row'} bunny-h-screen bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 ${shadow} ${className}`, style: style, children: [jsxRuntime.jsx("div", { className: `bunny-flex bunny-items-center bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2' : 'bunny-w-1/2 bunny-h-full'} bunny-justify-center`, children: jsxRuntime.jsxs("div", { className: "bunny-flex bunny-flex-col bunny-items-start bunny-justify-between bunny-w-3/5 bunny-h-full bunny-gap-4 bunny-my-24", children: [jsxRuntime.jsx("div", { children: topNavImageUrl.length > 0 && (jsxRuntime.jsx(antd.Image, { width: 24, src: topNavImageUrl, alt: "Logo", preview: false })) }), data ? (jsxRuntime.jsx(CheckoutSummary, { quote: data, className: "bunny-h-full bunny-w-full", onAddCoupon: addCoupon, onRemoveCoupon: removeCoupon, isRemovingCoupon: isRemovingCoupon, priceListData: priceListData, isAddingCoupon: isAddingCoupon, couponCode: couponEditorCouponCode, setCouponCode: setCouponEditorCouponCode, activeCouponsExist: activeCouponsExist })) : (jsxRuntime.jsx("div", { className: "bunny-h-full", children: isLoadingPriceList ? (jsxRuntime.jsx(antd.Skeleton, { active: true })) : (jsxRuntime.jsx(PriceListDisplay, { priceListData: priceListData })) })), jsxRuntime.jsx(Footer, {})] }) }), jsxRuntime.jsx("div", { className: `bunny-flex bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2 bunny-overflow-auto' : 'bunny-w-1/2 bunny-h-full'} bunny-items-center`, style: {
22881
+ return (jsxRuntime.jsxs("div", { className: `bunny-flex ${isMobile ? 'bunny-flex-col' : 'bunny-flex-row'} bunny-h-screen bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 ${shadow} ${className}`, style: style, children: [jsxRuntime.jsx("div", { className: `bunny-flex bunny-items-center bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2' : 'bunny-w-1/2 bunny-h-full'} bunny-justify-center`, children: jsxRuntime.jsxs("div", { className: "bunny-flex bunny-flex-col bunny-items-start bunny-justify-between bunny-w-3/5 bunny-h-full bunny-gap-4 bunny-my-24", children: [jsxRuntime.jsx("div", { children: topNavImageUrl.length > 0 && (jsxRuntime.jsx("img", { alt: "Logo", src: topNavImageUrl, style: { width: 'auto', height: '28px' } })) }), data ? (jsxRuntime.jsx(CheckoutSummary, { quote: data, className: "bunny-h-full bunny-w-full", onAddCoupon: addCoupon, onRemoveCoupon: removeCoupon, isRemovingCoupon: isRemovingCoupon, priceListData: priceListData, isAddingCoupon: isAddingCoupon, couponCode: couponEditorCouponCode, setCouponCode: setCouponEditorCouponCode, activeCouponsExist: activeCouponsExist })) : (jsxRuntime.jsx("div", { className: "bunny-h-full", children: isLoadingPriceList ? (jsxRuntime.jsx(antd.Skeleton, { active: true })) : (jsxRuntime.jsx(PriceListDisplay, { priceListData: priceListData })) })), jsxRuntime.jsx(Footer, {})] }) }), jsxRuntime.jsx("div", { className: `bunny-flex bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2 bunny-overflow-auto' : 'bunny-w-1/2 bunny-h-full'} bunny-items-center`, style: {
22858
22882
  boxShadow: '-5px 0 20px 0 rgba(0, 0, 0, 0.05)',
22859
22883
  }, children: isLoadingPriceList ? (jsxRuntime.jsx(antd.Skeleton, { active: true, className: `bunny-flex bunny-flex-col bunny-w-3/5 bunny-mt-24` })) : (jsxRuntime.jsx("div", { className: `bunny-flex bunny-flex-col bunny-w-3/5 bunny-mt-24`, children: jsxRuntime.jsx(PaymentForms, { quote: quote, handlePaymentSuccess: handlePaymentSuccess, handleSubmit: handleSubmit, proceedingToPayment: isSigningUp || isLoadingQuote, accountId: accountId, overrideToken: portalSessionToken, customCheckoutFunction: accountSignupFunction, defaultValues: defaultValues }) })) })] }));
22860
22884
  }
@@ -24623,7 +24647,7 @@ function doesPriceListHaveFlatFeeCharges(maskedPriceList) {
24623
24647
  function priceDescriptionString({ unitName, showPriceAsMonthly, periodMonths, priceListHasFlatFeeCharges, }) {
24624
24648
  const periodMonthsConverted = periodMonthsConverter(periodMonths);
24625
24649
  const periodLabel = periodMonthsConverted ? common.PERIOD_LABELS[periodMonthsConverted] : null;
24626
- return `per ${unitName && !priceListHasFlatFeeCharges ? `${unitName.toLowerCase()} / ` : ''}${showPriceAsMonthly ? 'month' : periodLabel}`;
24650
+ return `Per ${unitName && !priceListHasFlatFeeCharges ? `${unitName.toLowerCase()} / ` : ''}${showPriceAsMonthly ? 'month' : periodLabel}`;
24627
24651
  }
24628
24652
 
24629
24653
  var localizedFormat$2 = {exports: {}};
@@ -1,6 +1,6 @@
1
1
  import { FormattedInvoice, Invoice, Quote } from '@bunnyapp/common';
2
2
  import { PaymentType } from '../types/PaymentType';
3
- declare const useAmountDue: () => string | undefined;
3
+ declare const useFormattedAmountDue: () => string | undefined;
4
4
  declare const usePaymentType: () => PaymentType | undefined;
5
5
  declare const useIsPaying: () => boolean | undefined;
6
6
  declare const useHandlePaymentFormSubmit: () => (demoPayCardDetails?: {
@@ -9,8 +9,9 @@ declare const useHandlePaymentFormSubmit: () => (demoPayCardDetails?: {
9
9
  cvc: string;
10
10
  }) => Promise<void>;
11
11
  declare const useIsSaving: () => boolean | undefined;
12
+ declare const useIsPaid: () => boolean | undefined;
12
13
  declare const useAccountId: () => string | undefined;
13
- export { useAccountId, useAmountDue, useHandlePaymentFormSubmit, useIsPaying, useIsSaving, usePaymentType, };
14
+ export { useAccountId, useFormattedAmountDue, useHandlePaymentFormSubmit, useIsPaying, useIsSaving, useIsPaid, usePaymentType, };
14
15
  export declare function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldOptions, onSavePaymentMethod, }: {
15
16
  children: React.ReactNode;
16
17
  accountId?: string;
@@ -18,6 +18,7 @@ declare const useHandlePayment: ({ quote, invoice, onPaymentSuccess, onPaymentHo
18
18
  savePaymentMethod: boolean | undefined;
19
19
  }, Error, void, unknown>;
20
20
  isPaying: boolean;
21
+ isPaid: boolean;
21
22
  paymentType: PaymentType;
22
23
  formattedAmountDue: string | undefined;
23
24
  };
@@ -0,0 +1 @@
1
+ export declare function usePaymentButtonText(): string;
@@ -2,5 +2,143 @@ declare const getFormattedQuote: ({ token, apiHost, id, }: {
2
2
  apiHost: string;
3
3
  token?: string;
4
4
  id?: string;
5
- }) => Promise<any>;
5
+ }) => Promise<{
6
+ quote: {
7
+ documentTemplateId: string | null;
8
+ documents: {
9
+ id: string;
10
+ filename: string;
11
+ size: string;
12
+ date: unknown;
13
+ url: string;
14
+ }[] | null;
15
+ firstInvoice: {
16
+ id: string | null;
17
+ state: "FAILED" | "DRAFT" | "NOT_DUE" | "DUE" | "UNPAID" | "PAID" | "VOIDED" | "READY" | "PREPARING" | "PROCESSING_PAYMENT" | "MERGED" | "QUEUED" | "CONSOLIDATED";
18
+ } | null;
19
+ payableId: string | null;
20
+ id: string | null;
21
+ payToAccept: boolean | null;
22
+ currentPaymentHold: {
23
+ createdAt: unknown;
24
+ expiresAt: unknown;
25
+ id: string | null;
26
+ updatedAt: unknown;
27
+ paymentMethod: {
28
+ accountId: string | null;
29
+ createdAt: unknown;
30
+ expirationDate: unknown;
31
+ failureCode: string | null;
32
+ id: string;
33
+ isDefault: boolean | null;
34
+ lastSuccess: unknown;
35
+ paymentType: "ach" | "card" | "other" | "wire" | null;
36
+ pluginId: string | null;
37
+ state: "UNKNOWN" | "SUCCESS" | "FAILED" | null;
38
+ updatedAt: unknown;
39
+ metadata: {
40
+ description: string | null;
41
+ expiration: unknown;
42
+ icon: string | null;
43
+ identifier: string | null;
44
+ issuer: string | null;
45
+ kind: string | null;
46
+ } | null;
47
+ } | null;
48
+ } | null;
49
+ };
50
+ payableId: string | null;
51
+ acceptedAt: unknown;
52
+ acceptedByName: string | null;
53
+ amount: number;
54
+ amountDue: number;
55
+ amountsByPeriod: {
56
+ id: string | null;
57
+ name: string | null;
58
+ amount: number | null;
59
+ }[];
60
+ billingCity: string | null;
61
+ billingCountry: string | null;
62
+ billingState: string | null;
63
+ billingStreet: string | null;
64
+ billingZip: string | null;
65
+ contactName: string | null;
66
+ currency: string | null;
67
+ customerBillingCity: string | null;
68
+ customerBillingCountry: string | null;
69
+ customerBillingState: string | null;
70
+ customerBillingStreet: string | null;
71
+ customerBillingZip: string | null;
72
+ customerName: string | null;
73
+ discount: number;
74
+ discountValue: number;
75
+ duration: string | null;
76
+ endDate: unknown;
77
+ expiresAt: unknown;
78
+ html: string | null;
79
+ formattedLines: {
80
+ amount: number;
81
+ amountsByPeriod: {
82
+ quantity: number | null;
83
+ id: string | null;
84
+ name: string | null;
85
+ startDate: unknown;
86
+ endDate: unknown;
87
+ amount: number | null;
88
+ amountsByTier: {
89
+ id: string | null;
90
+ tier: {
91
+ starts: number | null;
92
+ ends: number | null;
93
+ price: number | null;
94
+ } | null;
95
+ quantity: number | null;
96
+ amount: number | null;
97
+ }[] | null;
98
+ prorationRate: number | null;
99
+ }[];
100
+ billingPeriodEnd: string | null;
101
+ billingPeriodStart: string | null;
102
+ chargeType: string;
103
+ discount: number | null;
104
+ frequency: string;
105
+ isRamp: boolean;
106
+ periods: number;
107
+ planName: string;
108
+ position: number;
109
+ price: number | null;
110
+ priceDecimals: number | null;
111
+ priceListChargeId: string | null;
112
+ priceListChargeName: string | null;
113
+ priceListId: string;
114
+ priceListName: string | null;
115
+ priceTiers: {
116
+ price: number | null;
117
+ starts: number;
118
+ }[] | null;
119
+ pricingModel: string;
120
+ productName: string;
121
+ prorationRate: number | null;
122
+ quantity: number | null;
123
+ showProductNameOnLineItem: boolean;
124
+ taxCode: string | null;
125
+ trialEndDate: unknown;
126
+ trialStartDate: unknown;
127
+ unitOfMeasure: string;
128
+ vatCode: string | null;
129
+ }[];
130
+ netPaymentDays: number | null;
131
+ notes: string | null;
132
+ number: string | null;
133
+ poNumberRequired: boolean | null;
134
+ salesContactEmail: string | null;
135
+ sharedAt: unknown;
136
+ startDate: unknown;
137
+ state: "DRAFT" | "SHARED" | "VIEWED" | "ACCEPTED" | "IN_APPROVAL" | "APPROVED" | "REJECTED" | "UNDONE";
138
+ subtotal: number;
139
+ taxAmount: number;
140
+ taxNumberLabel: string | null;
141
+ taxNumberRequired: boolean | null;
142
+ vendorName: string | null;
143
+ }>;
6
144
  export default getFormattedQuote;
package/dist/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
2
  import { isValidElement, createContext, useContext, useMemo, useState, useEffect, useRef, createElement, useCallback, Fragment as Fragment$1 } from 'react';
3
3
  import { DownloadOutlined, CreditCardOutlined, EllipsisOutlined, BankOutlined, SearchOutlined, CloseOutlined, CheckCircleFilled, InfoCircleOutlined } from '@ant-design/icons';
4
- import { useErrorNotification, DEFAULT_CONFIG, createClientDevHeaders as createClientDevHeaders$1, X_BUNNY_COMPONENTS_VERSION_HEADER_NAME, QueryKeyFactory as QueryKeyFactory$1, useIsMobile as useIsMobile$1, DEFAULT_BRAND_COLOR, isColorTooDark, DEFAULT_TOP_NAV_IMAGE_URL, DEFAULT_ACCENT_COLOR, INPUT_BORDER_COLOR, SLATE_50, SLATE_400, SLATE_200, DEFAULT_SECONDARY_COLOR, request as request$1, invokePlugin, useAllErrorFormats as useAllErrorFormats$1, formatCurrency as formatCurrency$1, GRAY_500, GRAY_200, useSuccessNotification, PAYABLE_INVOICE_STATES, BreakpointNumbers as BreakpointNumbers$1, useGraphQLmutation, useInfoNotification, formatDate, TransactionState, SLATE_600, WHITE, TransactionKind, QuoteChangeKind, SLATE_500, PRIMARY_COLOR, Lists, getAccount, StringUtils, ChargeType, PERIOD_LABELS, MODAL_MAX_HEIGHT, DataInterval, SLATE_100 } from '@bunnyapp/common';
5
- import { ConfigProvider, Spin, Typography, Button, theme as theme$1, Tag, Dropdown, Input, Modal, Checkbox, Skeleton, Collapse, Form, Tooltip, Drawer, Card as Card$1, Select, Divider, Image, Popconfirm, Table, Radio, Space, Switch } from 'antd';
4
+ import { useErrorNotification, DEFAULT_CONFIG, createClientDevHeaders as createClientDevHeaders$1, X_BUNNY_COMPONENTS_VERSION_HEADER_NAME, QueryKeyFactory as QueryKeyFactory$1, useIsMobile as useIsMobile$1, DEFAULT_BRAND_COLOR, isColorTooDark, DEFAULT_TOP_NAV_IMAGE_URL, DEFAULT_ACCENT_COLOR, INPUT_BORDER_COLOR, SLATE_50, SLATE_400, SLATE_200, DEFAULT_SECONDARY_COLOR, request as request$1, invokePlugin, useAllErrorFormats as useAllErrorFormats$1, formatCurrency as formatCurrency$1, GRAY_500, GRAY_200, useSuccessNotification, PAYABLE_INVOICE_STATES, BreakpointNumbers as BreakpointNumbers$1, useGraphQLmutation, useInfoNotification, formatDate, TransactionState, SLATE_600, WHITE, TransactionKind, QuoteChangeKind, SLATE_500, PRIMARY_COLOR, Lists, getAccount, ChargeType, PERIOD_LABELS, MODAL_MAX_HEIGHT, DataInterval, SLATE_100 } from '@bunnyapp/common';
5
+ import { ConfigProvider, Spin, Typography, Button, theme as theme$1, Tag, Dropdown, Input, Modal, Checkbox, Skeleton, Collapse, Form, Tooltip, Drawer, Card as Card$1, Select, Divider, Popconfirm, Table, Radio, Space, Switch } from 'antd';
6
6
  import { Markup } from 'interweave';
7
7
  import request from 'graphql-request';
8
8
  import { QueryClient, QueryClientProvider, useQuery, useQueryClient, useMutation, keepPreviousData } from '@tanstack/react-query';
@@ -141,7 +141,7 @@ const useAllErrorFormats = () => {
141
141
  };
142
142
 
143
143
  // This will be replaced at build time by rollup-plugin-replace
144
- const PACKAGE_VERSION = '1.6.0-beta.19';
144
+ const PACKAGE_VERSION = '1.6.0-beta.20';
145
145
  const createRequestHeaders = (token) => {
146
146
  const headers = createClientDevHeaders({ token });
147
147
  // Add the components version header
@@ -927,7 +927,7 @@ function readFragment(...r) {
927
927
 
928
928
  var t = initGraphQLTada();
929
929
 
930
- const query$6 = t(`
930
+ const query$7 = t(`
931
931
  query entityBranding {
932
932
  entityBranding {
933
933
  accentColor
@@ -937,7 +937,7 @@ const query$6 = t(`
937
937
  }
938
938
  `);
939
939
  const getBranding = async ({ token, apiHost }) => {
940
- return await execute(query$6, { apiHost, token }, {});
940
+ return await execute(query$7, { apiHost, token }, {});
941
941
  };
942
942
 
943
943
  const BunnyContext = createContext({});
@@ -1144,7 +1144,7 @@ const InvoiceQuoteView = ({ children, formattedInvoice, html, backButtonName, on
1144
1144
  }, children: [targetUrl ? (jsx(DocumentTemplatePreview, { targetUrl: targetUrl })) : (jsx(Markup, { content: html })), children] }))] }));
1145
1145
  };
1146
1146
 
1147
- const MUTATION$a = `
1147
+ const MUTATION$9 = `
1148
1148
  query FormattedInvoice($id: ID) {
1149
1149
  formattedInvoice(id: $id) {
1150
1150
  amount
@@ -1212,7 +1212,7 @@ query FormattedInvoice($id: ID) {
1212
1212
  const getFormattedInvoice = async ({ id, token, apiHost, }) => {
1213
1213
  const vars = { id };
1214
1214
  const response = await gqlRequest({
1215
- query: MUTATION$a,
1215
+ query: MUTATION$9,
1216
1216
  token,
1217
1217
  vars,
1218
1218
  apiHost,
@@ -18730,7 +18730,7 @@ const QueryKeyFactory = {
18730
18730
  paymentPluginsKey: (token) => ['paymentPlugins', ...(token ? [token] : [])],
18731
18731
  };
18732
18732
 
18733
- const query$5 = t(`
18733
+ const query$6 = t(`
18734
18734
  query PaymentMethods($accountId: ID) {
18735
18735
  paymentMethods(accountId: $accountId) {
18736
18736
  nodes {
@@ -18759,7 +18759,7 @@ const query$5 = t(`
18759
18759
  `, [PaymentForm_PaymentMethodsFragment]);
18760
18760
  const getPaymentMethods = async ({ apiHost, token, accountId, }) => {
18761
18761
  var _a, _b, _c;
18762
- const response = await execute(query$5, { apiHost, token }, { accountId });
18762
+ const response = await execute(query$6, { apiHost, token }, { accountId });
18763
18763
  // Filter out null values that are technically possible due to api schema
18764
18764
  return (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.paymentMethods) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter(paymentMethod => paymentMethod !== null)) !== null && _c !== void 0 ? _c : [];
18765
18765
  };
@@ -19005,7 +19005,7 @@ function useSave$1({ onSaveSuccess, onSaveError, accountId, }) {
19005
19005
  return { save, isSaving };
19006
19006
  }
19007
19007
 
19008
- const MUTATION$9 = `
19008
+ const MUTATION$8 = `
19009
19009
  mutation checkout(
19010
19010
  $invoiceId: ID,
19011
19011
  $quoteId: ID,
@@ -19054,7 +19054,7 @@ const checkout = async ({ quoteId, invoiceId, paymentMethodId, paymentMethodData
19054
19054
  };
19055
19055
  }
19056
19056
  const response = await gqlRequest({
19057
- query: MUTATION$9,
19057
+ query: MUTATION$8,
19058
19058
  token,
19059
19059
  vars: mutationVars,
19060
19060
  apiHost: apiHost,
@@ -19342,14 +19342,20 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19342
19342
  const { defaultPaymentMethod } = usePaymentMethod({ accountId });
19343
19343
  const { apiHost } = useContext(BunnyContext);
19344
19344
  const token = useToken();
19345
+ // State
19346
+ const [isPaid, setIsPaid] = useState(false);
19345
19347
  const formattedAmountDue = () => {
19346
19348
  const amountDue = quote ? getQuoteAmountDue(quote) : invoice === null || invoice === void 0 ? void 0 : invoice.amountDue;
19347
19349
  const currencyId = (quote === null || quote === void 0 ? void 0 : quote.currencyId) || (invoice === null || invoice === void 0 ? void 0 : invoice.currencyId);
19348
19350
  return amountDue && currencyId ? formatCurrency$1(amountDue, currencyId).toString() : undefined;
19349
19351
  };
19352
+ // Validation checks
19353
+ if (quote !== undefined && invoice !== undefined) {
19354
+ throw new Error('Either quote or invoice must be provided, not both');
19355
+ }
19350
19356
  // Payment hooks
19351
19357
  const { pay: payDemoPay, isPaying: isPayingDemoPay } = usePay$1({
19352
- onPaymentSuccess,
19358
+ onPaymentSuccess: handlePaymentSuccess,
19353
19359
  onPaymentError: error => {
19354
19360
  handleAllErrorFormats$2(error);
19355
19361
  },
@@ -19358,7 +19364,7 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19358
19364
  plugin,
19359
19365
  });
19360
19366
  const { pay: payStripe, isPaying: isPayingStripe } = usePay({
19361
- onPaymentSuccess,
19367
+ onPaymentSuccess: handlePaymentSuccess,
19362
19368
  onPaymentError: error => {
19363
19369
  handleAllErrorFormats$2(error);
19364
19370
  },
@@ -19430,6 +19436,10 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19430
19436
  break;
19431
19437
  }
19432
19438
  };
19439
+ function handlePaymentSuccess(response) {
19440
+ setIsPaid(true);
19441
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
19442
+ }
19433
19443
  const handlePayment = async (overridePaymentMethodId) => {
19434
19444
  var _a, _b;
19435
19445
  if (!plugin)
@@ -19452,6 +19462,7 @@ const useHandlePayment = ({ quote, invoice, onPaymentSuccess, onPaymentHoldSucce
19452
19462
  isPayingStripe ||
19453
19463
  isApprovingHoldDemoPay ||
19454
19464
  isApprovingHoldStripe,
19465
+ isPaid,
19455
19466
  paymentType: paymentHoldOptions.payToAccept ? PaymentType.APPROVE_HOLD : PaymentType.PAY,
19456
19467
  formattedAmountDue: formattedAmountDue(),
19457
19468
  };
@@ -19539,11 +19550,13 @@ const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(u
19539
19550
  const handleAllErrorFormats$1 = useAllErrorFormats$1();
19540
19551
  const showErrorNotification$5 = useErrorNotification();
19541
19552
  // Contexts
19542
- const [AmountDueProvider, useAmountDue] = createValueContext();
19553
+ const [FormattedAmountDueProvider, useFormattedAmountDue] = createValueContext();
19543
19554
  const [PaymentTypeProvider, usePaymentType] = createValueContext();
19544
19555
  const [IsPayingProvider, useIsPaying] = createValueContext();
19545
19556
  const [HandlePaymentFormSubmitProvider, useHandlePaymentFormSubmit] = createValueContext();
19546
19557
  const [IsSavingProvider, useIsSaving] = createValueContext();
19558
+ // Used for PaymentMethodFooter to disable pay button if payment is already successful
19559
+ const [IsPaidProvider, useIsPaid] = createValueContext();
19547
19560
  // Used for Signup component. Signup uses an apiClient token which api can't infer an account from, so accountId must be used
19548
19561
  const [AccountIdProvider, useAccountId] = createValueContext();
19549
19562
  function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldOptions, onSavePaymentMethod, }) {
@@ -19554,7 +19567,7 @@ function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess
19554
19567
  // Hooks
19555
19568
  const queryClient = useQueryClient();
19556
19569
  const token = useToken();
19557
- const { handlePayment, handleApproveHold, handleCheckoutNoPayment, isPaying, paymentType, formattedAmountDue, } = useHandlePayment({
19570
+ const { handlePayment, handleApproveHold, handleCheckoutNoPayment, isPaying, isPaid, paymentType, formattedAmountDue, } = useHandlePayment({
19558
19571
  quote,
19559
19572
  invoice,
19560
19573
  onPaymentSuccess: (response) => {
@@ -19660,11 +19673,12 @@ function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess
19660
19673
  }
19661
19674
  };
19662
19675
  const providers = [
19663
- [AmountDueProvider, { value: formattedAmountDue }],
19676
+ [FormattedAmountDueProvider, { value: formattedAmountDue }],
19664
19677
  [PaymentTypeProvider, { value: paymentType }],
19665
19678
  [IsPayingProvider, { value: isPaying }],
19666
19679
  [HandlePaymentFormSubmitProvider, { value: handlePaymentFormSubmit }],
19667
19680
  [IsSavingProvider, { value: isSavingDemoPay || isSavingStripe }],
19681
+ [IsPaidProvider, { value: isPaid }],
19668
19682
  [AccountIdProvider, { value: accountId }],
19669
19683
  ];
19670
19684
  return (jsx(Fragment, { children: providers.reduceRight((acc, [Provider, props]) => createElement(Provider, props, acc), children) }));
@@ -19675,33 +19689,49 @@ function StripeWrapper({ children, currencyId, accountId, }) {
19675
19689
  return (jsx(Elements, { options: options, stripe: stripe, children: children }));
19676
19690
  }
19677
19691
 
19692
+ function usePaymentButtonText() {
19693
+ const formattedAmountDue = useFormattedAmountDue();
19694
+ const paymentType = usePaymentType();
19695
+ const isPaid = useIsPaid();
19696
+ const isSaving = useIsSaving();
19697
+ const isPaying = useIsPaying();
19698
+ const isProcessing = isSaving || isPaying;
19699
+ // If payment is already successful, show the amount paid
19700
+ if (isPaid) {
19701
+ return 'Paid ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
19702
+ }
19703
+ const paymentTypeText = isProcessing
19704
+ ? paymentType === PaymentType.APPROVE_HOLD
19705
+ ? 'Approving hold for'
19706
+ : 'Paying'
19707
+ : paymentType === PaymentType.APPROVE_HOLD
19708
+ ? 'Approve hold for'
19709
+ : 'Pay';
19710
+ return paymentTypeText + ' ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
19711
+ }
19712
+
19678
19713
  const CheckoutFooter = () => {
19714
+ const isPaid = useIsPaid();
19679
19715
  const isMobile = useIsMobile();
19680
- const paymentType = usePaymentType();
19681
19716
  const isPaying = useIsPaying();
19682
- const formattedAmountDue = useAmountDue();
19717
+ const paymentButtonText = usePaymentButtonText();
19683
19718
  const onPaymentFormSubmit = useHandlePaymentFormSubmit();
19684
- const buttonText = paymentType === PaymentType.APPROVE_HOLD ? 'Approve hold for' : 'Pay';
19685
- const isPayingButtonText = paymentType === PaymentType.APPROVE_HOLD ? 'Approving hold for' : 'Paying';
19686
- return (jsx(Button, { className: "bunny-w-full", disabled: isPaying, onClick: () => onPaymentFormSubmit(), size: isMobile ? 'large' : 'middle', type: "primary", children: `${isPaying ? isPayingButtonText : buttonText} ${formattedAmountDue}` }));
19719
+ return (jsx(Button, { className: "bunny-w-full", disabled: isPaying || isPaid, onClick: () => onPaymentFormSubmit(), size: isMobile ? 'large' : 'middle', type: "primary", children: paymentButtonText }));
19687
19720
  };
19688
19721
 
19689
19722
  function PaymentMethodFooter({ onSubmit, noPadding, }) {
19690
19723
  const isPaying = useIsPaying();
19691
19724
  const isSaving = useIsSaving();
19692
- const buttonText = useSubmitButtonText();
19725
+ const formattedAmountDue = useFormattedAmountDue();
19726
+ const paymentButtonText = usePaymentButtonText();
19693
19727
  const isMobile = useIsMobile$1();
19694
19728
  const [, setShowPaymentMethodForm] = useShowPaymentDetails();
19729
+ // If amount due is undefined, show the save button
19730
+ const payableAvailable = formattedAmountDue !== undefined;
19731
+ const saveButtonText = isSaving ? 'Saving' : 'Save';
19732
+ const buttonText = payableAvailable ? paymentButtonText : saveButtonText;
19695
19733
  return (jsxs("div", { className: `bunny-flex bunny-justify-end bunny-gap-2 ${noPadding ? '' : 'pt-6'}`, children: [jsx(Button, { className: "bunny-w-full", size: isMobile ? 'large' : 'middle', onClick: () => setShowPaymentMethodForm(false), children: "Cancel" }), jsx(Button, { className: "bunny-w-full", disabled: isSaving || isPaying, loading: isSaving || isPaying, onClick: onSubmit, size: isMobile ? 'large' : 'middle', type: "primary", children: buttonText })] }));
19696
19734
  }
19697
- function useSubmitButtonText() {
19698
- const amountDue = useAmountDue();
19699
- const paymentType = usePaymentType();
19700
- const payableAvailable = amountDue !== undefined;
19701
- const paymentTypeText = paymentType === PaymentType.APPROVE_HOLD ? 'approve hold for' : 'pay';
19702
- const buttonTextPrefix = payableAvailable ? paymentTypeText : 'save';
19703
- return lodashExports.capitalize(buttonTextPrefix) + ' ' + (amountDue !== null && amountDue !== void 0 ? amountDue : '');
19704
- }
19705
19735
 
19706
19736
  const DemoPayCardCvc = ({ autoFocus, onChange, placeholder, value, }) => {
19707
19737
  const onKeyPress = (event) => {
@@ -19902,7 +19932,7 @@ const useAutoSetDefaultPaymentMethod = ({ handleSetDefault, setDefaultPaymentMet
19902
19932
  ]);
19903
19933
  };
19904
19934
 
19905
- const query$4 = t(`
19935
+ const query$5 = t(`
19906
19936
  query GetCurrentUserData {
19907
19937
  company {
19908
19938
  name
@@ -19925,7 +19955,7 @@ const query$4 = t(`
19925
19955
  `);
19926
19956
  const getCurrentUserData = async ({ token, apiHost }) => {
19927
19957
  var _a, _b, _c, _d, _e, _f, _g, _h;
19928
- const response = await execute(query$4, { apiHost, token }, {});
19958
+ const response = await execute(query$5, { apiHost, token }, {});
19929
19959
  return {
19930
19960
  authObjectName: (_a = response === null || response === void 0 ? void 0 : response.currentUser) === null || _a === void 0 ? void 0 : _a.authObjectName,
19931
19961
  account: (_b = response === null || response === void 0 ? void 0 : response.currentUser) === null || _b === void 0 ? void 0 : _b.account,
@@ -20084,7 +20114,7 @@ function PaymentFormContent({ onRemovePaymentMethod, onSetDefaultPaymentMethod,
20084
20114
  const [showPaymentMethodForm, setShowPaymentMethodForm] = useShowPaymentDetails();
20085
20115
  const paymentType = usePaymentType();
20086
20116
  const isPaying = useIsPaying();
20087
- const amountDue = useAmountDue();
20117
+ const formattedAmountDue = useFormattedAmountDue();
20088
20118
  const selectedPlugin = useSelectedPlugin();
20089
20119
  const onPaymentFormSubmit = useHandlePaymentFormSubmit();
20090
20120
  const accountId = useAccountId();
@@ -20131,7 +20161,7 @@ function PaymentFormContent({ onRemovePaymentMethod, onSetDefaultPaymentMethod,
20131
20161
  label: !showPaymentMethodForm ? (jsx("div", { className: "bunny-pt-2", children: jsx(Button, { onClick: handleClickAddPaymentMethod, type: "default", className: "bunny-w-full", id: "addPaymentMethod", children: "Add payment method" }) })) : null,
20132
20162
  children: (jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2 bunny-mt-2", children: [jsx(PaymentMethodSelector, {}), selectedPlugin && (jsx("div", { className: "bunny-flex bunny-flex-col", children: jsx(PaymentMethodDetails, {}) }))] })),
20133
20163
  },
20134
- ] }), amountDue !== undefined && !showPaymentMethodForm && (jsx("div", { className: "bunny-px-4", children: jsx(CheckoutFooter, {}) }))] })) : (jsx(Button, { style: { margin: '0 16px' }, loading: isPaying, onClick: () => onPaymentFormSubmit(), type: "primary", children: isPaying ? 'Processing...' : 'Complete Order' })) }));
20164
+ ] }), formattedAmountDue !== undefined && !showPaymentMethodForm && (jsx("div", { className: "bunny-px-4", children: jsx(CheckoutFooter, {}) }))] })) : (jsx(Button, { style: { margin: '0 16px' }, loading: isPaying, onClick: () => onPaymentFormSubmit(), type: "primary", children: isPaying ? 'Processing...' : 'Complete Order' })) }));
20135
20165
  }
20136
20166
 
20137
20167
  function Invoice({ id, invoiceQuoteViewComponent, backButtonName, onBackButtonClick, onInvoiceDownloadError, onPaymentSuccess, shadow = 'shadow-md', className, hideDownloadButton = false, hidePaymentForm = false, onInvoiceLoaded, }) {
@@ -20166,7 +20196,7 @@ function ActualInvoice({ hidePaymentForm }) {
20166
20196
  const isInvoicePayable = PAYABLE_INVOICE_STATES.includes((formattedInvoice === null || formattedInvoice === void 0 ? void 0 : formattedInvoice.state) || '');
20167
20197
  // Local state
20168
20198
  const isMobile = useIsMobile$1(isInvoicePayable ? BreakpointNumbers$1.lg : undefined);
20169
- const onSuccess = () => {
20199
+ const handlePaymentSuccess = () => {
20170
20200
  queryClient.invalidateQueries({
20171
20201
  queryKey: QueryKeyFactory$1.default.transactionsKey({ token }),
20172
20202
  });
@@ -20186,15 +20216,21 @@ function ActualInvoice({ hidePaymentForm }) {
20186
20216
  }, [formattedInvoice]);
20187
20217
  if (!formattedInvoice)
20188
20218
  return jsx(Fragment, {});
20189
- return (jsx("div", { className: "bunny-invoice-container", children: jsxs("div", { className: `bunny-flex bunny-gap-6 ${isMobile ? 'bunny-flex-col bunny-w-full' : ''} ${className}`, children: [formattedInvoice.isLegacy ? (jsx("div", { className: "bunny-flex bunny-justify-center bunny-w-full", children: jsx(LegacyDocument, { documentUuid: formattedInvoice.uuid, documentType: "invoice" }) })) : (invoiceQuoteViewComponent || (jsx(InvoiceQuoteView, { html: formattedInvoice.html, formattedInvoice: formattedInvoice, backButtonName: backButtonName, onBackButtonClick: onBackButtonClick }))), isInvoicePayable && !hidePaymentForm && (jsx("div", { className: `bunny-w-full ${hideDownloadButton || formattedInvoice.isLegacy ? '' : 'pt-12'}`, children: jsx(PaymentForm, { onPaymentSuccess: onSuccess, invoice: formattedInvoice }) }))] }) }));
20219
+ return (jsx("div", { className: "bunny-invoice-container", children: jsxs("div", { className: `bunny-flex bunny-gap-6 ${isMobile ? 'bunny-flex-col bunny-w-full' : ''} ${className}`, children: [formattedInvoice.isLegacy ? (jsx("div", { className: "bunny-flex bunny-justify-center bunny-w-full", children: jsx(LegacyDocument, { documentUuid: formattedInvoice.uuid, documentType: "invoice" }) })) : (invoiceQuoteViewComponent || (jsx(InvoiceQuoteView, { html: formattedInvoice.html, formattedInvoice: formattedInvoice, backButtonName: backButtonName, onBackButtonClick: onBackButtonClick }))), isInvoicePayable && !hidePaymentForm && (jsx("div", { className: `bunny-w-full ${hideDownloadButton || formattedInvoice.isLegacy ? '' : 'pt-12'}`, children: jsx(PaymentForm, { onPaymentSuccess: handlePaymentSuccess, invoice: formattedInvoice }) }))] }) }));
20190
20220
  }
20191
20221
 
20192
- const MUTATION$8 = () => `
20193
- query formattedQuote ($id: ID) {
20194
- formattedQuote (id: $id) {
20222
+ const query$4 = t(`
20223
+ query formattedQuote($id: ID) {
20224
+ formattedQuote(id: $id) {
20195
20225
  quote {
20196
20226
  documentTemplateId
20197
- documents { id filename size date url }
20227
+ documents {
20228
+ id
20229
+ filename
20230
+ size
20231
+ date
20232
+ url
20233
+ }
20198
20234
  firstInvoice {
20199
20235
  id
20200
20236
  state
@@ -20324,17 +20360,10 @@ const MUTATION$8 = () => `
20324
20360
  taxNumberRequired
20325
20361
  vendorName
20326
20362
  }
20327
- }`;
20363
+ }
20364
+ `);
20328
20365
  const getFormattedQuote = async ({ token, apiHost, id, }) => {
20329
- const response = await gqlRequest({
20330
- query: MUTATION$8(),
20331
- token,
20332
- apiHost,
20333
- vars: { id },
20334
- });
20335
- if (response.errors && response.errors.length > 0) {
20336
- throw new Error(response.errors[0].message);
20337
- }
20366
+ const response = await execute(query$4, { apiHost, token }, { id });
20338
20367
  return response === null || response === void 0 ? void 0 : response.formattedQuote;
20339
20368
  };
20340
20369
 
@@ -20669,7 +20698,7 @@ const PaymentHoldModal = ({ visible, setVisible, quote, }) => {
20669
20698
  const token = useToken();
20670
20699
  return (jsxs(StyledModal$2, { centered: true, onCancel: () => {
20671
20700
  setVisible(false);
20672
- }, footer: null, open: visible, width: 600, children: [jsxs("div", { className: "bunny-mt-5 bunny-pb-4 bunny-mx-4", children: [jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay now" }), jsxs(Text$w, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', formatCurrency$1(quote.amount, quote.currency), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsx("div", { className: "bunny-mb-3", children: jsx(PaymentForm, { quote: {
20701
+ }, footer: null, open: visible, width: 600, children: [jsxs("div", { className: "bunny-mt-5 bunny-pb-4 bunny-mx-4", children: [jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay and sign" }), jsxs(Text$w, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', formatCurrency$1(quote.amount, quote.currency), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsx("div", { className: "bunny-mb-3", children: jsx(PaymentForm, { quote: {
20673
20702
  amount: quote.amount,
20674
20703
  currencyId: quote.currency,
20675
20704
  id: quote.quote.id,
@@ -20764,7 +20793,7 @@ function ActualQuote({ onQuoteAccepted }) {
20764
20793
  },
20765
20794
  placeholderData: keepPreviousData,
20766
20795
  });
20767
- const formattedQuote = data;
20796
+ const formattedQuote = data; // TODO: fix to use the correct type
20768
20797
  // Hooks
20769
20798
  const { acceptBoxVisible, isAccepting, sendAccept, setAcceptBoxVisible, setIsAccepting, startAcceptance, pandadocPollingModalVisible, setPandadocPollingModalVisible, isSendAcceptPending, } = useSendAcceptQuote({
20770
20799
  token,
@@ -20811,7 +20840,7 @@ function QuoteButtons({ isAccepted, formattedQuote, isMobile, hideDownloadButton
20811
20840
  const signingPlugins = useSigningPlugins({ apiHost, token });
20812
20841
  return (jsxs("div", { className: "flex flex-row justify-end items-center gap-4", id: "acceptance", style: {
20813
20842
  color: secondaryColor,
20814
- }, children: [isAccepted && formattedQuote.acceptedAt ? (jsx(Text$v, { children: `Quote was accepted by ${formattedQuote.acceptedByName} on ${formatDate(formattedQuote.acceptedAt)}` })) : null, (!isMobile || !isAccepted) && (jsxs("div", { className: isMobile ? 'flex w-full justify-end gap-2' : 'flex items-center justify-end gap-2', children: [paymentHold ? (jsx(PaymentHoldDisplay, { paymentHold: paymentHold, currency: formattedQuote.currency, amount: formattedQuote.amount })) : null, !isMobile && !hideDownloadButton ? (jsx(Button, { icon: jsx(DownloadOutlined, {}), onClick: () => downloadFile(apiHost + '/api/pdf/quote', token), children: "Download" })) : null, shouldDoPaymentHold && !paymentHoldCompleted ? (jsx(Button, { disabled: isExpired, onClick: () => setPaymentHoldModalVisible(true), type: "primary", children: "Pay now" })) : (jsx(Fragment, { children: !isAccepted ? (jsx(Button, { disabled: isExpired || isAccepting, onClick: handleClickAccept, type: "primary", children: isExpired
20843
+ }, children: [isAccepted && formattedQuote.acceptedAt ? (jsx(Text$v, { children: `Quote was accepted by ${formattedQuote.acceptedByName} on ${formatDate(formattedQuote.acceptedAt)}` })) : null, (!isMobile || !isAccepted) && (jsxs("div", { className: isMobile ? 'flex w-full justify-end gap-2' : 'flex items-center justify-end gap-2', children: [paymentHold ? (jsx(PaymentHoldDisplay, { paymentHold: paymentHold, currency: formattedQuote.currency, amount: formattedQuote.amount })) : null, !isMobile && !hideDownloadButton ? (jsx(Button, { icon: jsx(DownloadOutlined, {}), onClick: () => downloadFile(apiHost + '/api/pdf/quote', token), children: "Download" })) : null, shouldDoPaymentHold && !paymentHoldCompleted ? (jsx(Button, { disabled: isExpired, onClick: () => setPaymentHoldModalVisible(true), type: "primary", children: "Pay and sign" })) : (jsx(Fragment, { children: !isAccepted ? (jsx(Button, { disabled: isExpired || isAccepting, onClick: handleClickAccept, type: "primary", children: isExpired
20815
20844
  ? 'Quote is expired'
20816
20845
  : (signingPlugins === null || signingPlugins === void 0 ? void 0 : signingPlugins.length)
20817
20846
  ? 'Start signing'
@@ -22173,12 +22202,8 @@ const CheckoutBarInput = ({ disabled, priceListCharge, quantity, onQuantityChang
22173
22202
  }, min: priceListCharge === null || priceListCharge === void 0 ? void 0 : priceListCharge.quantityMin, max: priceListCharge === null || priceListCharge === void 0 ? void 0 : priceListCharge.quantityMax, style: { minWidth: '120px' }, type: "number", value: quantity, required: true }) })] }));
22174
22203
  };
22175
22204
  const QuantityLabel = ({ activeCharge }) => {
22176
- var _a;
22177
- const featureName = (_a = activeCharge.feature) === null || _a === void 0 ? void 0 : _a.name;
22178
- const pluralizedFeatureName = (StringUtils.isStringPluralized(featureName) || !featureName
22179
- ? featureName
22180
- : StringUtils.pluralizeEntityName(featureName)) || '';
22181
- return (jsx(Text$n, { className: "bunny-text-slate-500 bunny-font-medium bunny-text-nowrap", style: { fontSize: '11px' }, children: pluralizedFeatureName.toUpperCase() }));
22205
+ const chargeName = activeCharge.name;
22206
+ return (jsx(Text$n, { className: "bunny-text-slate-500 bunny-font-medium bunny-text-nowrap", style: { fontSize: '11px' }, children: chargeName.toUpperCase() }));
22182
22207
  };
22183
22208
 
22184
22209
  const periodMonthsConverter = (period) => {
@@ -22707,11 +22732,8 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22707
22732
  token,
22708
22733
  });
22709
22734
  const { mutate: recalculateTaxesMutation } = useMutation({
22710
- mutationFn: () => {
22711
- if (!(quote === null || quote === void 0 ? void 0 : quote.id)) {
22712
- throw new Error('Quote ID is required');
22713
- }
22714
- return quoteRecalculateTaxes({ token, apiHost, quoteId: quote.id });
22735
+ mutationFn: (quoteId) => {
22736
+ return quoteRecalculateTaxes({ token, apiHost, quoteId });
22715
22737
  },
22716
22738
  onError: (error) => {
22717
22739
  if (!error[0].message.includes('Ensure that you have a taxation plugin')) {
@@ -22760,8 +22782,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22760
22782
  }),
22761
22783
  });
22762
22784
  setInitialQuote(data.quote);
22763
- console.log('handleRecalculateTaxes in quoteAccountSignupMutate');
22764
- handleRecalculateTaxes();
22785
+ handleRecalculateTaxes(data.quote.id);
22765
22786
  },
22766
22787
  onError: (error) => {
22767
22788
  const errorMessage = error.response.errors[0].message;
@@ -22799,7 +22820,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22799
22820
  queryKey: ['quote', quote === null || quote === void 0 ? void 0 : quote.id],
22800
22821
  });
22801
22822
  showSuccessNotification('Coupon applied');
22802
- handleRecalculateTaxes();
22823
+ handleRecalculateTaxes(quote === null || quote === void 0 ? void 0 : quote.id);
22803
22824
  setCouponEditorCouponCode('');
22804
22825
  },
22805
22826
  onCouponRemoved: () => {
@@ -22807,7 +22828,7 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22807
22828
  queryKey: ['quote', quote === null || quote === void 0 ? void 0 : quote.id],
22808
22829
  });
22809
22830
  showSuccessNotification('Coupon removed');
22810
- handleRecalculateTaxes();
22831
+ handleRecalculateTaxes(quote === null || quote === void 0 ? void 0 : quote.id);
22811
22832
  },
22812
22833
  });
22813
22834
  // Handle default coupon application
@@ -22841,18 +22862,21 @@ function Signup({ companyName, priceListCode, returnUrl, couponCode, className,
22841
22862
  accountId,
22842
22863
  });
22843
22864
  }
22844
- function handleRecalculateTaxes() {
22845
- console.log('handleRecalculateTaxes', hasTaxPlugin);
22865
+ function handleRecalculateTaxes(quoteId) {
22866
+ if (!quoteId) {
22867
+ showErrorNotification$1('No quote ID found to recalculate taxes');
22868
+ return;
22869
+ }
22846
22870
  if (!hasTaxPlugin) {
22847
- console.log('no tax plugin, returning');
22871
+ showErrorNotification$1('No tax plugin found to recalculate taxes');
22848
22872
  return;
22849
22873
  }
22850
- recalculateTaxesMutation();
22874
+ recalculateTaxesMutation(quoteId);
22851
22875
  }
22852
22876
  if (purchaseSucceeded) {
22853
22877
  return (jsx(Fragment, { children: (quote === null || quote === void 0 ? void 0 : quote.currencyId) !== undefined ? (jsx("div", { className: "bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 bunny-flex bunny-items-start bunny-pt-[25vh]", children: jsx(PaymentSuccessDisplay, { amountPaid: (quote === null || quote === void 0 ? void 0 : quote.amountDue) || 0, className: "bunny-w-full", companyName: companyName, returnUrl: returnUrl, currencyId: quote === null || quote === void 0 ? void 0 : quote.currencyId }) })) : (jsx("div", { children: "No currency ID found from Quote" })) }));
22854
22878
  }
22855
- return (jsxs("div", { className: `bunny-flex ${isMobile ? 'bunny-flex-col' : 'bunny-flex-row'} bunny-h-screen bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 ${shadow} ${className}`, style: style, children: [jsx("div", { className: `bunny-flex bunny-items-center bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2' : 'bunny-w-1/2 bunny-h-full'} bunny-justify-center`, children: jsxs("div", { className: "bunny-flex bunny-flex-col bunny-items-start bunny-justify-between bunny-w-3/5 bunny-h-full bunny-gap-4 bunny-my-24", children: [jsx("div", { children: topNavImageUrl.length > 0 && (jsx(Image, { width: 24, src: topNavImageUrl, alt: "Logo", preview: false })) }), data ? (jsx(CheckoutSummary, { quote: data, className: "bunny-h-full bunny-w-full", onAddCoupon: addCoupon, onRemoveCoupon: removeCoupon, isRemovingCoupon: isRemovingCoupon, priceListData: priceListData, isAddingCoupon: isAddingCoupon, couponCode: couponEditorCouponCode, setCouponCode: setCouponEditorCouponCode, activeCouponsExist: activeCouponsExist })) : (jsx("div", { className: "bunny-h-full", children: isLoadingPriceList ? (jsx(Skeleton, { active: true })) : (jsx(PriceListDisplay, { priceListData: priceListData })) })), jsx(Footer, {})] }) }), jsx("div", { className: `bunny-flex bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2 bunny-overflow-auto' : 'bunny-w-1/2 bunny-h-full'} bunny-items-center`, style: {
22879
+ return (jsxs("div", { className: `bunny-flex ${isMobile ? 'bunny-flex-col' : 'bunny-flex-row'} bunny-h-screen bunny-w-screen bunny-absolute bunny-top-0 bunny-left-0 ${shadow} ${className}`, style: style, children: [jsx("div", { className: `bunny-flex bunny-items-center bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2' : 'bunny-w-1/2 bunny-h-full'} bunny-justify-center`, children: jsxs("div", { className: "bunny-flex bunny-flex-col bunny-items-start bunny-justify-between bunny-w-3/5 bunny-h-full bunny-gap-4 bunny-my-24", children: [jsx("div", { children: topNavImageUrl.length > 0 && (jsx("img", { alt: "Logo", src: topNavImageUrl, style: { width: 'auto', height: '28px' } })) }), data ? (jsx(CheckoutSummary, { quote: data, className: "bunny-h-full bunny-w-full", onAddCoupon: addCoupon, onRemoveCoupon: removeCoupon, isRemovingCoupon: isRemovingCoupon, priceListData: priceListData, isAddingCoupon: isAddingCoupon, couponCode: couponEditorCouponCode, setCouponCode: setCouponEditorCouponCode, activeCouponsExist: activeCouponsExist })) : (jsx("div", { className: "bunny-h-full", children: isLoadingPriceList ? (jsx(Skeleton, { active: true })) : (jsx(PriceListDisplay, { priceListData: priceListData })) })), jsx(Footer, {})] }) }), jsx("div", { className: `bunny-flex bunny-flex-col ${isMobile ? 'bunny-w-full bunny-h-1/2 bunny-overflow-auto' : 'bunny-w-1/2 bunny-h-full'} bunny-items-center`, style: {
22856
22880
  boxShadow: '-5px 0 20px 0 rgba(0, 0, 0, 0.05)',
22857
22881
  }, children: isLoadingPriceList ? (jsx(Skeleton, { active: true, className: `bunny-flex bunny-flex-col bunny-w-3/5 bunny-mt-24` })) : (jsx("div", { className: `bunny-flex bunny-flex-col bunny-w-3/5 bunny-mt-24`, children: jsx(PaymentForms, { quote: quote, handlePaymentSuccess: handlePaymentSuccess, handleSubmit: handleSubmit, proceedingToPayment: isSigningUp || isLoadingQuote, accountId: accountId, overrideToken: portalSessionToken, customCheckoutFunction: accountSignupFunction, defaultValues: defaultValues }) })) })] }));
22858
22882
  }
@@ -24621,7 +24645,7 @@ function doesPriceListHaveFlatFeeCharges(maskedPriceList) {
24621
24645
  function priceDescriptionString({ unitName, showPriceAsMonthly, periodMonths, priceListHasFlatFeeCharges, }) {
24622
24646
  const periodMonthsConverted = periodMonthsConverter(periodMonths);
24623
24647
  const periodLabel = periodMonthsConverted ? PERIOD_LABELS[periodMonthsConverted] : null;
24624
- return `per ${unitName && !priceListHasFlatFeeCharges ? `${unitName.toLowerCase()} / ` : ''}${showPriceAsMonthly ? 'month' : periodLabel}`;
24648
+ return `Per ${unitName && !priceListHasFlatFeeCharges ? `${unitName.toLowerCase()} / ` : ''}${showPriceAsMonthly ? 'month' : periodLabel}`;
24625
24649
  }
24626
24650
 
24627
24651
  var localizedFormat$2 = {exports: {}};
@@ -1,6 +1,6 @@
1
1
  import { FormattedInvoice, Invoice, Quote } from '@bunnyapp/common';
2
2
  import { PaymentType } from '../types/PaymentType';
3
- declare const useAmountDue: () => string | undefined;
3
+ declare const useFormattedAmountDue: () => string | undefined;
4
4
  declare const usePaymentType: () => PaymentType | undefined;
5
5
  declare const useIsPaying: () => boolean | undefined;
6
6
  declare const useHandlePaymentFormSubmit: () => (demoPayCardDetails?: {
@@ -9,8 +9,9 @@ declare const useHandlePaymentFormSubmit: () => (demoPayCardDetails?: {
9
9
  cvc: string;
10
10
  }) => Promise<void>;
11
11
  declare const useIsSaving: () => boolean | undefined;
12
+ declare const useIsPaid: () => boolean | undefined;
12
13
  declare const useAccountId: () => string | undefined;
13
- export { useAccountId, useAmountDue, useHandlePaymentFormSubmit, useIsPaying, useIsSaving, usePaymentType, };
14
+ export { useAccountId, useFormattedAmountDue, useHandlePaymentFormSubmit, useIsPaying, useIsSaving, useIsPaid, usePaymentType, };
14
15
  export declare function PaymentProvider({ children, accountId, quote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldOptions, onSavePaymentMethod, }: {
15
16
  children: React.ReactNode;
16
17
  accountId?: string;
@@ -18,6 +18,7 @@ declare const useHandlePayment: ({ quote, invoice, onPaymentSuccess, onPaymentHo
18
18
  savePaymentMethod: boolean | undefined;
19
19
  }, Error, void, unknown>;
20
20
  isPaying: boolean;
21
+ isPaid: boolean;
21
22
  paymentType: PaymentType;
22
23
  formattedAmountDue: string | undefined;
23
24
  };
@@ -0,0 +1 @@
1
+ export declare function usePaymentButtonText(): string;
@@ -2,5 +2,143 @@ declare const getFormattedQuote: ({ token, apiHost, id, }: {
2
2
  apiHost: string;
3
3
  token?: string;
4
4
  id?: string;
5
- }) => Promise<any>;
5
+ }) => Promise<{
6
+ quote: {
7
+ documentTemplateId: string | null;
8
+ documents: {
9
+ id: string;
10
+ filename: string;
11
+ size: string;
12
+ date: unknown;
13
+ url: string;
14
+ }[] | null;
15
+ firstInvoice: {
16
+ id: string | null;
17
+ state: "FAILED" | "DRAFT" | "NOT_DUE" | "DUE" | "UNPAID" | "PAID" | "VOIDED" | "READY" | "PREPARING" | "PROCESSING_PAYMENT" | "MERGED" | "QUEUED" | "CONSOLIDATED";
18
+ } | null;
19
+ payableId: string | null;
20
+ id: string | null;
21
+ payToAccept: boolean | null;
22
+ currentPaymentHold: {
23
+ createdAt: unknown;
24
+ expiresAt: unknown;
25
+ id: string | null;
26
+ updatedAt: unknown;
27
+ paymentMethod: {
28
+ accountId: string | null;
29
+ createdAt: unknown;
30
+ expirationDate: unknown;
31
+ failureCode: string | null;
32
+ id: string;
33
+ isDefault: boolean | null;
34
+ lastSuccess: unknown;
35
+ paymentType: "ach" | "card" | "other" | "wire" | null;
36
+ pluginId: string | null;
37
+ state: "UNKNOWN" | "SUCCESS" | "FAILED" | null;
38
+ updatedAt: unknown;
39
+ metadata: {
40
+ description: string | null;
41
+ expiration: unknown;
42
+ icon: string | null;
43
+ identifier: string | null;
44
+ issuer: string | null;
45
+ kind: string | null;
46
+ } | null;
47
+ } | null;
48
+ } | null;
49
+ };
50
+ payableId: string | null;
51
+ acceptedAt: unknown;
52
+ acceptedByName: string | null;
53
+ amount: number;
54
+ amountDue: number;
55
+ amountsByPeriod: {
56
+ id: string | null;
57
+ name: string | null;
58
+ amount: number | null;
59
+ }[];
60
+ billingCity: string | null;
61
+ billingCountry: string | null;
62
+ billingState: string | null;
63
+ billingStreet: string | null;
64
+ billingZip: string | null;
65
+ contactName: string | null;
66
+ currency: string | null;
67
+ customerBillingCity: string | null;
68
+ customerBillingCountry: string | null;
69
+ customerBillingState: string | null;
70
+ customerBillingStreet: string | null;
71
+ customerBillingZip: string | null;
72
+ customerName: string | null;
73
+ discount: number;
74
+ discountValue: number;
75
+ duration: string | null;
76
+ endDate: unknown;
77
+ expiresAt: unknown;
78
+ html: string | null;
79
+ formattedLines: {
80
+ amount: number;
81
+ amountsByPeriod: {
82
+ quantity: number | null;
83
+ id: string | null;
84
+ name: string | null;
85
+ startDate: unknown;
86
+ endDate: unknown;
87
+ amount: number | null;
88
+ amountsByTier: {
89
+ id: string | null;
90
+ tier: {
91
+ starts: number | null;
92
+ ends: number | null;
93
+ price: number | null;
94
+ } | null;
95
+ quantity: number | null;
96
+ amount: number | null;
97
+ }[] | null;
98
+ prorationRate: number | null;
99
+ }[];
100
+ billingPeriodEnd: string | null;
101
+ billingPeriodStart: string | null;
102
+ chargeType: string;
103
+ discount: number | null;
104
+ frequency: string;
105
+ isRamp: boolean;
106
+ periods: number;
107
+ planName: string;
108
+ position: number;
109
+ price: number | null;
110
+ priceDecimals: number | null;
111
+ priceListChargeId: string | null;
112
+ priceListChargeName: string | null;
113
+ priceListId: string;
114
+ priceListName: string | null;
115
+ priceTiers: {
116
+ price: number | null;
117
+ starts: number;
118
+ }[] | null;
119
+ pricingModel: string;
120
+ productName: string;
121
+ prorationRate: number | null;
122
+ quantity: number | null;
123
+ showProductNameOnLineItem: boolean;
124
+ taxCode: string | null;
125
+ trialEndDate: unknown;
126
+ trialStartDate: unknown;
127
+ unitOfMeasure: string;
128
+ vatCode: string | null;
129
+ }[];
130
+ netPaymentDays: number | null;
131
+ notes: string | null;
132
+ number: string | null;
133
+ poNumberRequired: boolean | null;
134
+ salesContactEmail: string | null;
135
+ sharedAt: unknown;
136
+ startDate: unknown;
137
+ state: "DRAFT" | "SHARED" | "VIEWED" | "ACCEPTED" | "IN_APPROVAL" | "APPROVED" | "REJECTED" | "UNDONE";
138
+ subtotal: number;
139
+ taxAmount: number;
140
+ taxNumberLabel: string | null;
141
+ taxNumberRequired: boolean | null;
142
+ vendorName: string | null;
143
+ }>;
6
144
  export default getFormattedQuote;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnyapp/components",
3
- "version": "1.6.0-beta.20",
3
+ "version": "1.6.0-beta.21",
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",