@usethrottle/quotes 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { CurrencyCode } from '@usethrottle/money';
2
+ import * as money from '@usethrottle/money';
3
+ export { money };
4
+ export { CURRENCIES, CurrencyCode, MoneyValue, UnknownCurrencyError, currencySymbol, isKnownCurrency, minorUnitsOf, moneyValue, parseMoney, toDecimalString, toMinor } from '@usethrottle/money';
1
5
  import { ThrottleError, ThrottleErrorArgs } from '@usethrottle/errors';
2
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
7
  import { ReactNode } from 'react';
@@ -96,7 +100,7 @@ interface BuyerQuote {
96
100
  quoteNumber: string;
97
101
  status: BuyerQuoteStatus;
98
102
  buyerReference: string | null;
99
- currency: string;
103
+ currency: CurrencyCode;
100
104
  expiresAt: string | null;
101
105
  currentRevision: QuoteRevision | null;
102
106
  revisionHistory: QuoteRevision[];
@@ -133,6 +137,8 @@ interface RequestFormConfig {
133
137
  fields: {
134
138
  companyName?: boolean;
135
139
  phone?: boolean;
140
+ /** Show an optional shipping address section. */
141
+ shippingAddress?: boolean;
136
142
  buyerReference?: boolean;
137
143
  lineItems?: boolean;
138
144
  targetPrice?: boolean;
@@ -174,6 +180,19 @@ interface SubmitQuoteRequestInput {
174
180
  companyName?: string | null;
175
181
  phone?: string | null;
176
182
  buyerReference?: string | null;
183
+ /**
184
+ * Optional. All blank = no address; otherwise `addressLine1`, `city` and a
185
+ * 2-letter `countryCode` are required. Stored on the quote and prefilled
186
+ * into checkout when the buyer accepts.
187
+ */
188
+ shippingAddress?: {
189
+ addressLine1?: string | null;
190
+ addressLine2?: string | null;
191
+ city?: string | null;
192
+ stateProvince?: string | null;
193
+ postalCode?: string | null;
194
+ countryCode?: string | null;
195
+ } | null;
177
196
  /** Honeypot — leave empty. The component wires this for you. */
178
197
  website?: string | null;
179
198
  /** Client mount time in ms; the API enforces a minimum fill duration. */
@@ -268,7 +287,16 @@ declare class QuotesClient {
268
287
  }>;
269
288
  }
270
289
  declare function createQuotesClient(opts?: QuotesClientOptions): QuotesClient;
271
- /** Minor units → a display string, e.g. (104000, 'USD') → "$1,040.00". */
290
+ /**
291
+ * Minor units → a display string, e.g. (104000, 'USD') → "$1,040.00".
292
+ *
293
+ * Digits are en-US on every surface and runtime, so `locale` no longer changes
294
+ * them (it is kept so existing calls compile); the exponent is the currency's
295
+ * own (JPY has none, KWD has three).
296
+ *
297
+ * @deprecated Use `money.formatMoney` from this package or `formatMoney` from
298
+ * `@usethrottle/money`.
299
+ */
272
300
  declare function formatMoney(minorUnits: number, currency: string, locale?: string): string;
273
301
  /**
274
302
  * Scale a line's quoted tax/discount to an edited quantity.
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/client.ts
2
+ import { formatMoney as formatMinor } from "@usethrottle/money";
3
+
1
4
  // src/errors.ts
2
5
  import { ThrottleError } from "@usethrottle/errors";
3
6
  var ThrottleQuotesError = class extends ThrottleError {
@@ -151,7 +154,8 @@ function createQuotesClient(opts = {}) {
151
154
  return new QuotesClient(opts);
152
155
  }
153
156
  function formatMoney(minorUnits, currency, locale) {
154
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(minorUnits / 100);
157
+ void locale;
158
+ return formatMinor(minorUnits, currency);
155
159
  }
156
160
  function scaleLineCharges(item, quantity) {
157
161
  const base = item.quantity;
@@ -563,12 +567,12 @@ function cadenceNoun(cadence) {
563
567
  }
564
568
  return cadence.interval;
565
569
  }
566
- function recurringTermsLabel(terms, price, money) {
570
+ function recurringTermsLabel(terms, price, money2) {
567
571
  const quantity = price.quantity ?? 1;
568
572
  if (terms.schedule && terms.schedule.length > 0) {
569
- return `${money(scheduleUnitTotal(terms.schedule) * quantity)} total \xB7 ${terms.schedule.length} payments`;
573
+ return `${money2(scheduleUnitTotal(terms.schedule) * quantity)} total \xB7 ${terms.schedule.length} payments`;
570
574
  }
571
- const each = money(price.unitPrice * quantity);
575
+ const each = money2(price.unitPrice * quantity);
572
576
  const noun = cadenceNoun(terms);
573
577
  const trialDays = terms.trialDays ?? 0;
574
578
  if (trialDays > 0) return `Free for ${trialDays} day${trialDays === 1 ? "" : "s"}, then ${each} / ${noun}`;
@@ -576,6 +580,7 @@ function recurringTermsLabel(terms, price, money) {
576
580
  }
577
581
 
578
582
  // src/QuoteView.tsx
583
+ import { formatMoney as formatMoney2 } from "@usethrottle/money";
579
584
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
580
585
  var row = { display: "flex", justifyContent: "space-between", gap: 16, padding: "8px 0" };
581
586
  var muted = { color: "#666", fontSize: 13 };
@@ -657,10 +662,10 @@ function QuoteView({ token, options, onCheckout, loadingFallback, className }) {
657
662
  item.description ? /* @__PURE__ */ jsx("div", { style: muted, children: item.description }) : null,
658
663
  item.recurring ? /* @__PURE__ */ jsxs("div", { style: muted, children: [
659
664
  "Subscription \xB7 ",
660
- recurringTermsLabel(item.recurring, { unitPrice: item.unitPrice, quantity: state.quantity }, (m) => formatMoney(m, currency))
665
+ recurringTermsLabel(item.recurring, { unitPrice: item.unitPrice, quantity: state.quantity }, (m) => formatMoney2(m, currency))
661
666
  ] }) : null
662
667
  ] }),
663
- /* @__PURE__ */ jsx("td", { align: "right", children: formatMoney(item.unitPrice, currency) }),
668
+ /* @__PURE__ */ jsx("td", { align: "right", children: formatMoney2(item.unitPrice, currency) }),
664
669
  /* @__PURE__ */ jsx("td", { align: "right", children: item.qtyEditable && canAct ? /* @__PURE__ */ jsx(
665
670
  "input",
666
671
  {
@@ -673,21 +678,21 @@ function QuoteView({ token, options, onCheckout, loadingFallback, className }) {
673
678
  style: { width: 64 }
674
679
  }
675
680
  ) : state.quantity }),
676
- /* @__PURE__ */ jsx("td", { align: "right", children: formatMoney(included ? lineDueAtAcceptance(item, state.quantity) : 0, currency) })
681
+ /* @__PURE__ */ jsx("td", { align: "right", children: formatMoney2(included ? lineDueAtAcceptance(item, state.quantity) : 0, currency) })
677
682
  ] }, item.id);
678
683
  }) })
679
684
  ] }) : null,
680
685
  rev ? /* @__PURE__ */ jsxs("section", { children: [
681
686
  /* @__PURE__ */ jsxs("div", { style: row, children: [
682
687
  /* @__PURE__ */ jsx("strong", { children: recurringKept.length > 0 ? "Due at acceptance" : "Total" }),
683
- /* @__PURE__ */ jsx("strong", { children: formatMoney(q.total, currency) })
688
+ /* @__PURE__ */ jsx("strong", { children: formatMoney2(q.total, currency) })
684
689
  ] }),
685
690
  recurringKept.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
686
691
  /* @__PURE__ */ jsx("p", { style: muted, children: "Recurring after" }),
687
692
  recurringKept.map((i) => /* @__PURE__ */ jsxs("p", { style: muted, children: [
688
693
  i.name,
689
694
  ": ",
690
- recurringTermsLabel(i.recurring, { unitPrice: i.unitPrice, quantity: q.selections[i.id]?.quantity ?? i.quantity }, (m) => formatMoney(m, currency))
695
+ recurringTermsLabel(i.recurring, { unitPrice: i.unitPrice, quantity: q.selections[i.id]?.quantity ?? i.quantity }, (m) => formatMoney2(m, currency))
691
696
  ] }, i.id))
692
697
  ] }) : null,
693
698
  /* @__PURE__ */ jsx("p", { style: muted, children: termsLabel(rev.paymentTerms, rev.depositAmount, currency) }),
@@ -751,7 +756,7 @@ function QuoteView({ token, options, onCheckout, loadingFallback, className }) {
751
756
  "Revision ",
752
757
  r.revisionNumber,
753
758
  " \u2014 ",
754
- formatMoney(r.total, currency),
759
+ formatMoney2(r.total, currency),
755
760
  r.changeSummary ? ` \xB7 ${r.changeSummary}` : ""
756
761
  ] }, r.id)) })
757
762
  ] }) : null,
@@ -815,7 +820,7 @@ function statusLabel(status) {
815
820
  function termsLabel(terms, depositAmount, currency) {
816
821
  if (terms.mode === "net_terms") return `Payment terms: Net ${terms.netN}`;
817
822
  if (terms.mode === "deposit_balance") {
818
- const dep = depositAmount != null ? formatMoney(depositAmount, currency) : "a deposit";
823
+ const dep = depositAmount != null ? formatMoney2(depositAmount, currency) : "a deposit";
819
824
  return `${dep} due today, balance Net ${terms.balanceNetN}`;
820
825
  }
821
826
  return "Payment in full";
@@ -852,6 +857,12 @@ function QuoteRequestForm({
852
857
  companyName: "",
853
858
  phone: "",
854
859
  buyerReference: "",
860
+ addressLine1: "",
861
+ addressLine2: "",
862
+ city: "",
863
+ stateProvince: "",
864
+ postalCode: "",
865
+ countryCode: "",
855
866
  message: "",
856
867
  website: ""
857
868
  // honeypot
@@ -881,6 +892,16 @@ function QuoteRequestForm({
881
892
  companyName: fields.companyName ? values.companyName || null : null,
882
893
  phone: fields.phone ? values.phone || null : null,
883
894
  buyerReference: fields.buyerReference ? values.buyerReference || null : null,
895
+ ...fields.shippingAddress ? {
896
+ shippingAddress: {
897
+ addressLine1: values.addressLine1 || null,
898
+ addressLine2: values.addressLine2 || null,
899
+ city: values.city || null,
900
+ stateProvince: values.stateProvince || null,
901
+ postalCode: values.postalCode || null,
902
+ countryCode: values.countryCode || null
903
+ }
904
+ } : {},
884
905
  website: values.website || null,
885
906
  ...turnstileToken ? { turnstileToken } : {}
886
907
  }).then(() => onSubmitted?.()).catch(() => {
@@ -906,6 +927,33 @@ function QuoteRequestForm({
906
927
  "Phone",
907
928
  /* @__PURE__ */ jsx2("input", { value: values.phone, onChange: set("phone") })
908
929
  ] }) : null,
930
+ fields.shippingAddress ? /* @__PURE__ */ jsxs2("fieldset", { children: [
931
+ /* @__PURE__ */ jsx2("legend", { children: "Shipping address (optional)" }),
932
+ /* @__PURE__ */ jsxs2("label", { children: [
933
+ "Address line 1",
934
+ /* @__PURE__ */ jsx2("input", { value: values.addressLine1, onChange: set("addressLine1") })
935
+ ] }),
936
+ /* @__PURE__ */ jsxs2("label", { children: [
937
+ "Address line 2",
938
+ /* @__PURE__ */ jsx2("input", { value: values.addressLine2, onChange: set("addressLine2") })
939
+ ] }),
940
+ /* @__PURE__ */ jsxs2("label", { children: [
941
+ "City",
942
+ /* @__PURE__ */ jsx2("input", { value: values.city, onChange: set("city") })
943
+ ] }),
944
+ /* @__PURE__ */ jsxs2("label", { children: [
945
+ "State / province",
946
+ /* @__PURE__ */ jsx2("input", { value: values.stateProvince, onChange: set("stateProvince") })
947
+ ] }),
948
+ /* @__PURE__ */ jsxs2("label", { children: [
949
+ "Postal code",
950
+ /* @__PURE__ */ jsx2("input", { value: values.postalCode, onChange: set("postalCode") })
951
+ ] }),
952
+ /* @__PURE__ */ jsxs2("label", { children: [
953
+ "Country code (e.g. US)",
954
+ /* @__PURE__ */ jsx2("input", { value: values.countryCode, onChange: set("countryCode"), maxLength: 2 })
955
+ ] })
956
+ ] }) : null,
909
957
  fields.buyerReference ? /* @__PURE__ */ jsxs2("label", { children: [
910
958
  "Your reference #",
911
959
  /* @__PURE__ */ jsx2("input", { value: values.buyerReference, onChange: set("buyerReference") })
@@ -953,18 +1001,42 @@ function QuoteRequestForm({
953
1001
  }
954
1002
  );
955
1003
  }
1004
+
1005
+ // src/index.tsx
1006
+ import * as money from "@usethrottle/money";
1007
+ import {
1008
+ currencySymbol,
1009
+ minorUnitsOf,
1010
+ toDecimalString,
1011
+ toMinor,
1012
+ parseMoney,
1013
+ moneyValue,
1014
+ CURRENCIES,
1015
+ isKnownCurrency,
1016
+ UnknownCurrencyError
1017
+ } from "@usethrottle/money";
956
1018
  export {
1019
+ CURRENCIES,
957
1020
  QuoteRequestForm,
958
1021
  QuoteView,
959
1022
  QuotesClient,
960
1023
  ThrottleQuotesError,
1024
+ UnknownCurrencyError,
961
1025
  computeSelectionTotal,
962
1026
  createQuoteCart,
963
1027
  createQuotesClient,
1028
+ currencySymbol,
964
1029
  formatMoney,
965
1030
  isFreeTrialLine,
1031
+ isKnownCurrency,
966
1032
  lineDueAtAcceptance,
1033
+ minorUnitsOf,
1034
+ money,
1035
+ moneyValue,
1036
+ parseMoney,
967
1037
  scaleLineCharges,
1038
+ toDecimalString,
1039
+ toMinor,
968
1040
  useQuote,
969
1041
  useQuoteCart,
970
1042
  useQuoteRequestForm