@owlmeans/web-payment 0.1.18-rc.11 → 0.1.18-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/web-payment/SKILL.md +70 -20
  4. package/build/@/components/ui/select.d.ts +8 -0
  5. package/build/@/components/ui/select.d.ts.map +1 -0
  6. package/build/@/components/ui/select.js +9 -0
  7. package/build/@/components/ui/select.js.map +1 -0
  8. package/build/dialog.d.ts +1 -1
  9. package/build/dialog.d.ts.map +1 -1
  10. package/build/dialog.js +5 -2
  11. package/build/dialog.js.map +1 -1
  12. package/build/estimate-cache.d.ts +14 -0
  13. package/build/estimate-cache.d.ts.map +1 -0
  14. package/build/estimate-cache.js +39 -0
  15. package/build/estimate-cache.js.map +1 -0
  16. package/build/estimate-summary.d.ts +56 -0
  17. package/build/estimate-summary.d.ts.map +1 -0
  18. package/build/estimate-summary.js +91 -0
  19. package/build/estimate-summary.js.map +1 -0
  20. package/build/estimate.d.ts +28 -0
  21. package/build/estimate.d.ts.map +1 -0
  22. package/build/estimate.js +67 -0
  23. package/build/estimate.js.map +1 -0
  24. package/build/i18n/be.json +1 -1
  25. package/build/i18n/de.json +1 -1
  26. package/build/i18n/en.json +1 -1
  27. package/build/i18n/es.json +1 -1
  28. package/build/i18n/pl.json +1 -1
  29. package/build/i18n/ru.json +1 -1
  30. package/build/i18n/uk.json +1 -1
  31. package/build/index.d.ts +2 -0
  32. package/build/index.d.ts.map +1 -1
  33. package/build/index.js +2 -0
  34. package/build/index.js.map +1 -1
  35. package/build/types.d.ts +23 -1
  36. package/build/types.d.ts.map +1 -1
  37. package/package.json +4 -2
  38. package/src/@/components/ui/select.tsx +62 -0
  39. package/src/dialog.tsx +5 -2
  40. package/src/estimate-cache.ts +40 -0
  41. package/src/estimate-summary.tsx +162 -0
  42. package/src/estimate.ts +93 -0
  43. package/src/i18n/be.json +1 -1
  44. package/src/i18n/de.json +1 -1
  45. package/src/i18n/en.json +1 -1
  46. package/src/i18n/es.json +1 -1
  47. package/src/i18n/pl.json +1 -1
  48. package/src/i18n/ru.json +1 -1
  49. package/src/i18n/uk.json +1 -1
  50. package/src/index.ts +2 -0
  51. package/src/types.ts +27 -1
  52. package/tests/context.ts +2 -1
  53. package/tests/dialog.spec.ts +49 -0
  54. package/tests/estimate-cache.spec.ts +46 -0
  55. package/tests/harness/entitlement.tsx +43 -4
  56. package/tests/harness/estimate-fixtures.ts +48 -0
  57. package/tests/harness/mount.tsx +3 -0
  58. package/tests/pieces.spec.ts +22 -0
package/README.md CHANGED
@@ -65,7 +65,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
65
65
  your project's skill store (`.agents/skills/`):
66
66
 
67
67
  ```sh
68
- npx @owlmeans/agent-skills@^0.1.18-rc.28
68
+ npx @owlmeans/agent-skills@^0.1.18-rc.29
69
69
  ```
70
70
 
71
71
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/web-payment",
4
- "version": "0.1.18-rc.11",
5
- "generatedAt": "2026-09-16T17:32:31.942Z",
4
+ "version": "0.1.18-rc.12",
5
+ "generatedAt": "2026-09-18T19:03:53.990Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -7,7 +7,7 @@ user-invocable: false
7
7
 
8
8
  # @owlmeans/web-payment
9
9
 
10
- **Install:** `bun add @owlmeans/web-payment@^0.1.18-rc.11`
10
+ **Install:** `bun add @owlmeans/web-payment@^0.1.18-rc.12`
11
11
 
12
12
  Public MIT web package paired with `@owlmeans/server-payment`. It incorporates checkout, balance,
13
13
  entitlement and shallow-auth helpers over the underlying `@owlmeans/client-payment` service, plus
@@ -82,25 +82,73 @@ and action text come from the application). Every one accepts `className`.
82
82
 
83
83
  ## AmountCheckoutDialog
84
84
 
85
- The dialog takes `{ open, onOpenChange, policy, pending, onConfirm }`. It keeps integer minor units,
86
- accepts locale decimal separators and at most two decimals, shows configured presets plus custom
87
- input, and reports below/above-bound errors without clamping. Confirmation is disabled when invalid
88
- or pending.
85
+ The dialog takes `{ open, onOpenChange, policy, pending, onConfirm, estimate? }`. It keeps integer
86
+ minor units, accepts locale decimal separators and at most two decimals, shows configured presets
87
+ plus custom input, and reports below/above-bound errors without clamping. Confirmation is disabled
88
+ when invalid or pending.
89
89
 
90
- It shows net credit value, processing adjustment and pre-tax subtotal from `chargeAmountMinor`, plus
91
- the note that Stripe calculates applicable tax. The policy maximum bounds net credit, not the
92
- adjusted or tax-inclusive debit.
90
+ It shows net credit value, processing adjustment and pre-tax subtotal from `chargeAmountMinor`.
91
+ With no `estimate`, the plain note that Stripe calculates applicable tax; with one, `PriceEstimateSummary`
92
+ renders instead (see below) against the SAME `chargeMinor`, so the country picker and its numbers
93
+ track whatever the buyer has typed. The policy maximum bounds net credit, not the adjusted or
94
+ tax-inclusive debit.
95
+
96
+ ## Price estimate: a live tax/currency read for any amount
97
+
98
+ ```typescript
99
+ // self-contained (one estimate, its own picker) — the credit dialog:
100
+ const estimate = usePriceEstimate(ctx.entrypoint(account.priceEstimate), { enabled: dialogOpen })
101
+ <AmountCheckoutDialog … estimate={estimate} />
102
+ <PriceEstimateSummary control={estimate} subtotalMinor={planPriceMinor} currency="usd" />
103
+
104
+ // shared (one picker, several estimates) — a plan comparison table:
105
+ const [country, setCountry] = useState('')
106
+ const pro = usePriceEstimate(entry, { enabled, country, onCountryChange: setCountry }, { body: { planSku: 'pro' } })
107
+ const team = usePriceEstimate(entry, { enabled, country, onCountryChange: setCountry }, { body: { planSku: 'team' } })
108
+ <CountrySelect value={country} onChange={setCountry} /> {/* rendered ONCE */}
109
+ <PriceEstimateAmount control={pro} subtotalMinor={proPriceMinor} currency="usd" /> {/* per offer */}
110
+ <PriceEstimateAmount control={team} subtotalMinor={teamPriceMinor} currency="usd" />
111
+ ```
112
+
113
+ - **`usePriceEstimate(entry, { enabled, ttlMs?, country?, onCountryChange? }, ...request)`** calls a
114
+ `PriceEstimateBody` protocol (`@owlmeans/payment`'s `PriceEstimate` response) only while `enabled`,
115
+ and again whenever the country changes — a closed dialog costs nothing. A module-level client
116
+ cache (keyed by the entry's alias, the request body and the country; default TTL 5 min) shares an
117
+ answer across every mounted instance and never caches a rejected fetch. The caller's `request`
118
+ omits `body.country` — the hook owns it — the same way `usePortal`'s omits `flow`.
119
+ - **Uncontrolled** (no `opts.country`): the hook owns the country itself, and the first answer
120
+ whose `source` is `'customer'` preselects it, once.
121
+ - **Controlled** (`opts.country` given): the caller owns the country — several `usePriceEstimate`
122
+ calls can share ONE value (a plan comparison table, one picker, one estimate per offer); the
123
+ hook never preselects on its own, and the returned control's `onCountryChange` calls
124
+ `opts.onCountryChange` instead of an internal setter.
125
+ - **`CountrySelect({ value, onChange, label?, id?, className? })`** is the picker alone —
126
+ `Intl.DisplayNames` + `Intl.Collator` over `@owlmeans/payment`'s `COUNTRY_CODES`, so it lists the
127
+ buyer's language's own country names. Render it once and drive every estimate that shares it.
128
+ - **`PriceEstimateAmount({ control, subtotalMinor, currency, suffix?, className? })`** is the
129
+ numbers alone, no picker: via `estimateOf` re-derived against `subtotalMinor`, a rate row for a
130
+ `taxed` status, an estimated total, and one sentence for every status that leaves no number to
131
+ trust (reverse charge, no tax, "at checkout", "choose a country"). `data-price-estimate` /
132
+ `data-status` are stable test hooks.
133
+ - When the estimate carries `local` (Adaptive Pricing found the country's currency), the tax and
134
+ total show ONLY in that currency, each marked `≈` — never both currencies at once, which would
135
+ read as two different prices for the same line. A short note explains the amount is converted
136
+ at Stripe's current rate and confirmed at checkout. With no `local`, the amounts show in
137
+ `currency` (the integration currency) with no `≈` and no note.
138
+ - **`PriceEstimateSummary`** is `CountrySelect` bound to the control's own `country`/
139
+ `onCountryChange` followed by `PriceEstimateAmount` — the one-estimate composition. Building a
140
+ shared picker composes the two pieces separately instead, one `PriceEstimateAmount` per estimate.
93
141
 
94
142
  ## The `@/components/ui/*` contract
95
143
 
96
144
  Components import the consuming app's shadcn primitives through the app alias:
97
- `@/components/ui/{button,dialog,input,label}` for the dialog and
145
+ `@/components/ui/{button,dialog,input,label,select}` for the dialog and estimate summary and
98
146
  `@/components/ui/{card,progress,button}` for the pieces (`Card`, `CardHeader`, `CardTitle`,
99
147
  `CardDescription`, `CardContent`, `CardFooter`, `Progress`). The consumer therefore provides those
100
- files and the peer `@radix-ui/react-progress` beside the dialog's Radix peers. The package carries
101
- standalone copies under `src/@` only so its own build and tests resolve the same alias; they are
102
- never exported. Styling uses semantic theme tokens only (`primary`, `muted-foreground`,
103
- `destructive`, `border`, `card`).
148
+ files and the peers `@radix-ui/react-progress` / `@radix-ui/react-select` beside the dialog's Radix
149
+ ones. The package carries standalone copies under `src/@` only so its own build and tests resolve
150
+ the same alias; they are never exported. Styling uses semantic theme tokens only (`primary`,
151
+ `muted-foreground`, `destructive`, `border`, `card`).
104
152
 
105
153
  A Tailwind v4 consumer scanning package source adds:
106
154
 
@@ -111,15 +159,17 @@ A Tailwind v4 consumer scanning package source adds:
111
159
  ## Strings
112
160
 
113
161
  All visible strings live in the `web-payment` library resource (`lib` namespace) in all seven
114
- languages — `en pl ru be uk es de` — under two branches: `amount-checkout` and `entitlement`
115
- (`plan`, `status`, `limit`, `promo`, `capability`). A new key lands in every language at once, with
162
+ languages — `en pl ru be uk es de` — under three branches: `amount-checkout`, `entitlement`
163
+ (`plan`, `status`, `limit`, `promo`, `capability`) and `estimate` (`country`, `tax-type`, `rate`,
164
+ `total`, `converted-note`, `status`, `loading`, `failed`). A new key lands in every language at once, with
116
165
  the same `{{placeholders}}`; the parity spec walks the resources recursively. An application
117
166
  overrides a string with `addI18nApp(lng, 'web-payment', data, { ns: LIB_NAMESPACE })`.
118
167
 
119
168
  ## Tests
120
169
 
121
- Category D plus unit specs: `bun test ./tests` runs the selector and hook specs (hooks rendered with
122
- `react-dom/server`, a fake `window` for navigation) and drives chromium through the Vite harness,
123
- which picks a surface by `?case=` (none = the dialog, `pieces`, `hook`). The harness pre-bundles
124
- every runtime dependency in `optimizeDeps.include`; one discovered mid-navigation loads a second
125
- React and the page throws "Invalid hook call".
170
+ Category D plus unit specs: `bun test ./tests` runs the selector, hook and cache specs (hooks
171
+ rendered with `react-dom/server`, a fake `window` for navigation) and drives chromium through the
172
+ Vite harness, which picks a surface by `?case=` (none = the dialog, `pieces`, `hook`) and, for the
173
+ dialog, an `?estimate=<name>` canned `PriceEstimateControl` fixture (`tests/harness/estimate-fixtures.ts`
174
+ — no server, no Stripe). The harness pre-bundles every runtime dependency in `optimizeDeps.include`;
175
+ one discovered mid-navigation loads a second React and the page throws "Invalid hook call".
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import * as SelectPrimitive from '@radix-ui/react-select';
3
+ export declare const Select: (props: React.ComponentProps<typeof SelectPrimitive.Root>) => React.JSX.Element;
4
+ export declare const SelectValue: (props: React.ComponentProps<typeof SelectPrimitive.Value>) => React.JSX.Element;
5
+ export declare const SelectTrigger: ({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger>) => React.JSX.Element;
6
+ export declare const SelectContent: ({ className, children, position, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>) => React.JSX.Element;
7
+ export declare const SelectItem: ({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Item>) => React.JSX.Element;
8
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../../src/@/components/ui/select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAGzD,eAAO,MAAM,MAAM,UAAW,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,sBAAwC,CAAA;AACvH,eAAO,MAAM,WAAW,UAAW,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,sBAAyC,CAAA;AAE9H,eAAO,MAAM,aAAa,sCAAuC,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,sBAepH,CAAA;AAED,eAAO,MAAM,aAAa,gDAA4D,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,sBAsBzI,CAAA;AAED,eAAO,MAAM,UAAU,sCAAuC,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,sBAa9G,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as SelectPrimitive from '@radix-ui/react-select';
3
+ import { cn } from '@/lib/utils';
4
+ export const Select = (props) => _jsx(SelectPrimitive.Root, { ...props });
5
+ export const SelectValue = (props) => _jsx(SelectPrimitive.Value, { ...props });
6
+ export const SelectTrigger = ({ className, children, ...props }) => (_jsxs(SelectPrimitive.Trigger, { className: cn('border-input data-[placeholder]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50', 'flex h-9 w-full items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs', 'outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx("span", { "aria-hidden": "true", className: "text-muted-foreground shrink-0", children: "\u25BE" }) })] }));
7
+ export const SelectContent = ({ className, children, position = 'popper', ...props }) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { position: position, className: cn('bg-popover text-popover-foreground relative z-50 max-h-96 min-w-[8rem] overflow-y-auto rounded-md border shadow-md', position === 'popper' && 'data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1', className), ...props, children: [_jsx(SelectPrimitive.ScrollUpButton, { className: "flex items-center justify-center py-1", children: _jsx("span", { "aria-hidden": "true", children: "\u25B4" }) }), _jsx(SelectPrimitive.Viewport, { className: cn('p-1', position === 'popper' && 'w-full min-w-[var(--radix-select-trigger-width)]'), children: children }), _jsx(SelectPrimitive.ScrollDownButton, { className: "flex items-center justify-center py-1", children: _jsx("span", { "aria-hidden": "true", children: "\u25BE" }) })] }) }));
8
+ export const SelectItem = ({ className, children, ...props }) => (_jsxs(SelectPrimitive.Item, { className: cn('focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50', className), ...props, children: [_jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { "aria-hidden": "true", children: "\u2713" }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
9
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../../../src/@/components/ui/select.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAEhC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAwD,EAAE,EAAE,CAAC,KAAC,eAAe,CAAC,IAAI,OAAK,KAAK,GAAI,CAAA;AACvH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAyD,EAAE,EAAE,CAAC,KAAC,eAAe,CAAC,KAAK,OAAK,KAAK,GAAI,CAAA;AAE9H,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAwD,EAAE,EAAE,CAAC,CACxH,MAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4GAA4G,EAC5G,iHAAiH,EACjH,qHAAqH,EACrH,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,KAAC,eAAe,CAAC,IAAI,IAAC,OAAO,kBAC3B,8BAAkB,MAAM,EAAC,SAAS,EAAC,gCAAgC,uBAAS,GACvD,IACC,CAC3B,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAwD,EAAE,EAAE,CAAC,CAC7I,KAAC,eAAe,CAAC,MAAM,cACrB,MAAC,eAAe,CAAC,OAAO,IACtB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,EAAE,CACX,oHAAoH,EACpH,QAAQ,KAAK,QAAQ,IAAI,iEAAiE,EAC1F,SAAS,CACV,KACG,KAAK,aAET,KAAC,eAAe,CAAC,cAAc,IAAC,SAAS,EAAC,uCAAuC,YAC/E,8BAAkB,MAAM,uBAAS,GACF,EACjC,KAAC,eAAe,CAAC,QAAQ,IAAC,SAAS,EAAE,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,QAAQ,IAAI,kDAAkD,CAAC,YACxH,QAAQ,GACgB,EAC3B,KAAC,eAAe,CAAC,gBAAgB,IAAC,SAAS,EAAC,uCAAuC,YACjF,8BAAkB,MAAM,uBAAS,GACA,IACX,GACH,CAC1B,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAqD,EAAE,EAAE,CAAC,CAClH,MAAC,eAAe,CAAC,IAAI,IACnB,SAAS,EAAE,EAAE,CACX,mOAAmO,EACnO,SAAS,CACV,KACG,KAAK,aAET,eAAM,SAAS,EAAC,4DAA4D,YAC1E,KAAC,eAAe,CAAC,aAAa,mBAAa,MAAM,uBAAkC,GAC9E,EACP,KAAC,eAAe,CAAC,QAAQ,cAAE,QAAQ,GAA4B,IAC1C,CACxB,CAAA"}
package/build/dialog.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import type { AmountCheckoutDialogProps } from './types.js';
2
- export declare const AmountCheckoutDialog: ({ open, onOpenChange, policy, pending, onConfirm, }: AmountCheckoutDialogProps) => import("react").JSX.Element;
2
+ export declare const AmountCheckoutDialog: ({ open, onOpenChange, policy, pending, onConfirm, estimate, }: AmountCheckoutDialogProps) => import("react").JSX.Element;
3
3
  //# sourceMappingURL=dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../src/dialog.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAM3D,eAAO,MAAM,oBAAoB,wDAE9B,yBAAyB,gCAiE3B,CAAA"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../src/dialog.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAM3D,eAAO,MAAM,oBAAoB,kEAE9B,yBAAyB,gCAmE3B,CAAA"}
package/build/dialog.js CHANGED
@@ -7,10 +7,11 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
7
7
  import { Input } from '@/components/ui/input';
8
8
  import { Label } from '@/components/ui/label';
9
9
  import { inputAmount, parseAmountMinor } from './amount.js';
10
+ import { PriceEstimateSummary } from './estimate-summary.js';
10
11
  const money = (minor, currency, locale) => new Intl.NumberFormat(locale, {
11
12
  style: 'currency', currency: currency.toUpperCase(), minimumFractionDigits: 2,
12
13
  }).format(minor / 100);
13
- export const AmountCheckoutDialog = ({ open, onOpenChange, policy, pending = false, onConfirm, }) => {
14
+ export const AmountCheckoutDialog = ({ open, onOpenChange, policy, pending = false, onConfirm, estimate, }) => {
14
15
  const t = useI18nLib('web-payment', 'amount-checkout');
15
16
  const [locale] = useLanguage();
16
17
  const [value, setValue] = useState(() => inputAmount(policy.defaultMinor, locale));
@@ -38,6 +39,8 @@ export const AmountCheckoutDialog = ({ open, onOpenChange, policy, pending = fal
38
39
  return _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { closeLabel: t('close'), children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t('title') }), _jsx(DialogDescription, { children: t('description') })] }), _jsxs("div", { className: "grid gap-5", children: [_jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", "aria-label": t('presets'), children: policy.presetsMinor.map(preset => _jsx(Button, { type: "button", variant: preset === amountMinor ? 'default' : 'outline', disabled: pending, onClick: () => setValue(inputAmount(preset, locale)), children: money(preset, policy.currency, locale) }, preset)) }), _jsxs("div", { className: "grid gap-2", children: [_jsx(Label, { htmlFor: "payment-amount", children: t('custom') }), _jsx(Input, { id: "payment-amount", inputMode: "decimal", autoComplete: "off", value: value, disabled: pending, "aria-invalid": error != null, "aria-describedby": "payment-amount-help", onChange: event => setValue(event.target.value) }), _jsx("p", { id: "payment-amount-help", className: error == null ? 'text-muted-foreground text-xs' : 'text-destructive text-xs', children: error ?? t('bounds', {
39
40
  minimum: money(policy.minimumMinor, policy.currency, locale),
40
41
  maximum: money(policy.maximumMinor, policy.currency, locale),
41
- }) })] }), _jsxs("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-2 rounded-lg border bg-muted/30 p-4 text-sm", children: [_jsx("dt", { className: "text-muted-foreground", children: t('credits') }), _jsx("dd", { children: valid && amountMinor != null ? money(amountMinor, policy.currency, locale) : '—' }), _jsx("dt", { className: "text-muted-foreground", children: t('adjustment') }), _jsx("dd", { children: valid ? money(adjustmentMinor, policy.currency, locale) : '—' }), _jsx("dt", { className: "font-medium", children: t('subtotal') }), _jsx("dd", { className: "font-medium", children: valid ? money(chargeMinor, policy.currency, locale) : '—' })] }), _jsx("p", { className: "text-muted-foreground text-xs", children: t('tax-note') })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: pending, onClick: () => onOpenChange(false), children: t('cancel') }), _jsx(Button, { type: "button", disabled: !valid || pending, onClick: () => void submit(), children: pending ? t('pending') : t('continue') })] })] }) });
42
+ }) })] }), _jsxs("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-2 rounded-lg border bg-muted/30 p-4 text-sm", children: [_jsx("dt", { className: "text-muted-foreground", children: t('credits') }), _jsx("dd", { children: valid && amountMinor != null ? money(amountMinor, policy.currency, locale) : '—' }), _jsx("dt", { className: "text-muted-foreground", children: t('adjustment') }), _jsx("dd", { children: valid ? money(adjustmentMinor, policy.currency, locale) : '—' }), _jsx("dt", { className: "font-medium", children: t('subtotal') }), _jsx("dd", { className: "font-medium", children: valid ? money(chargeMinor, policy.currency, locale) : '—' })] }), estimate != null
43
+ ? valid && _jsx(PriceEstimateSummary, { control: estimate, subtotalMinor: chargeMinor, currency: policy.currency })
44
+ : _jsx("p", { className: "text-muted-foreground text-xs", children: t('tax-note') })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: pending, onClick: () => onOpenChange(false), children: t('cancel') }), _jsx(Button, { type: "button", disabled: !valid || pending, onClick: () => void submit(), children: pending ? t('pending') : t('continue') })] })] }) });
42
45
  };
43
46
  //# sourceMappingURL=dialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","sourceRoot":"","sources":["../src/dialog.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dialog.js","sourceRoot":"","sources":["../src/dialog.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A small keyed TTL cache with in-flight de-duplication — the client-side twin of
3
+ * `@owlmeans/server-payment`'s `EstimateCache`, so opening the credit dialog twice in a row (or two
4
+ * mounts asking for the same plan/country) never issues a second request while the first is still
5
+ * on the wire, and a settled answer is reused for its TTL. A rejected fetch is never cached: the
6
+ * next call simply tries again.
7
+ */
8
+ export declare class EstimateCache<T> {
9
+ private readonly store;
10
+ private readonly inflight;
11
+ get(key: string): T | undefined;
12
+ load(key: string, ttlMs: number, factory: () => Promise<T>): Promise<T>;
13
+ }
14
+ //# sourceMappingURL=estimate-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate-cache.d.ts","sourceRoot":"","sources":["../src/estimate-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,qBAAa,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqD;IAC3E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IAEzD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAM9B;IAEK,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAmB5E;CACF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A small keyed TTL cache with in-flight de-duplication — the client-side twin of
3
+ * `@owlmeans/server-payment`'s `EstimateCache`, so opening the credit dialog twice in a row (or two
4
+ * mounts asking for the same plan/country) never issues a second request while the first is still
5
+ * on the wire, and a settled answer is reused for its TTL. A rejected fetch is never cached: the
6
+ * next call simply tries again.
7
+ */
8
+ export class EstimateCache {
9
+ store = new Map();
10
+ inflight = new Map();
11
+ get(key) {
12
+ const hit = this.store.get(key);
13
+ if (hit == null || hit.expiresAt <= Date.now()) {
14
+ return undefined;
15
+ }
16
+ return hit.value;
17
+ }
18
+ async load(key, ttlMs, factory) {
19
+ const hit = this.get(key);
20
+ if (hit !== undefined) {
21
+ return hit;
22
+ }
23
+ const pending = this.inflight.get(key);
24
+ if (pending != null) {
25
+ return pending;
26
+ }
27
+ const promise = factory().then(value => {
28
+ this.store.set(key, { value, expiresAt: Date.now() + ttlMs });
29
+ this.inflight.delete(key);
30
+ return value;
31
+ }).catch(error => {
32
+ this.inflight.delete(key);
33
+ throw error;
34
+ });
35
+ this.inflight.set(key, promise);
36
+ return promise;
37
+ }
38
+ }
39
+ //# sourceMappingURL=estimate-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate-cache.js","sourceRoot":"","sources":["../src/estimate-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IACP,KAAK,GAAG,IAAI,GAAG,EAA2C,CAAA;IAC1D,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEzD,GAAG,CAAC,GAAW;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC/C,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyB;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACzB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACpB,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACrC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;YAC7D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACzB,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;YACf,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACzB,MAAM,KAAK,CAAA;QACb,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC/B,OAAO,OAAO,CAAA;IAChB,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ import type { PriceEstimateControl } from './types.js';
2
+ export interface CountrySelectProps {
3
+ /** ISO 3166-1 alpha-2, or `''` for the placeholder. */
4
+ value: string;
5
+ onChange: (country: string) => void;
6
+ /** Defaults to the `estimate.country` string (also the field's own label). */
7
+ label?: string;
8
+ id?: string;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * The billing-country picker alone — the piece `PriceEstimateSummary` composes for a single,
13
+ * self-contained estimate, and what a shared picker (several estimates, one country — a plan
14
+ * comparison table) renders once and drives every `usePriceEstimate({ country, onCountryChange })`
15
+ * with. Country names are the viewer's own language, alphabetically ordered.
16
+ */
17
+ export declare const CountrySelect: ({ value, onChange, label, id, className }: CountrySelectProps) => import("react").JSX.Element;
18
+ export interface PriceEstimateAmountProps {
19
+ control: Pick<PriceEstimateControl, 'estimate' | 'loading' | 'failed'>;
20
+ /** The amount to show tax and a total for — may differ from the estimate's own reference amount. */
21
+ subtotalMinor: number;
22
+ currency: string;
23
+ /** Appended after the total row, e.g. `"/ month"`. */
24
+ suffix?: string;
25
+ className?: string;
26
+ }
27
+ /**
28
+ * What Stripe Tax says at the estimate's country, with NO country picker of its own: the rate(s),
29
+ * an estimated total, and one sentence for whichever status leaves no number to trust (reverse
30
+ * charge, no tax, "at checkout", "choose a country"). Several of these may share one
31
+ * `CountrySelect` (a plan comparison table).
32
+ *
33
+ * A country whose currency differs from `currency` (Adaptive Pricing found a rate) shows the tax
34
+ * and the total in THAT currency instead, marked `≈` throughout — never both currencies at once,
35
+ * which would read as two different prices for the same line. The rate percentage itself is
36
+ * currency-agnostic and always shows as Stripe stated it.
37
+ *
38
+ * `data-price-estimate` / `data-status` are stable test hooks.
39
+ */
40
+ export declare const PriceEstimateAmount: ({ control, subtotalMinor, currency, suffix, className }: PriceEstimateAmountProps) => import("react").JSX.Element;
41
+ export interface PriceEstimateSummaryProps {
42
+ control: PriceEstimateControl;
43
+ /** The amount the summary shows tax and a total for — may differ from the estimate's own reference amount. */
44
+ subtotalMinor: number;
45
+ currency: string;
46
+ /** Appended after the total row, e.g. `"/ month"`. */
47
+ suffix?: string;
48
+ className?: string;
49
+ }
50
+ /**
51
+ * A self-contained estimate: `CountrySelect` bound to the control's own `country`/`onCountryChange`,
52
+ * plus `PriceEstimateAmount` beneath it. What a single estimate (the credit dialog) wants; several
53
+ * estimates sharing one picker (a plan comparison table) compose the two pieces separately instead.
54
+ */
55
+ export declare const PriceEstimateSummary: ({ control, subtotalMinor, currency, suffix, className }: PriceEstimateSummaryProps) => import("react").JSX.Element;
56
+ //# sourceMappingURL=estimate-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate-summary.d.ts","sourceRoot":"","sources":["../src/estimate-summary.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAwBtD,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,8CAA0E,kBAAkB,gCAmBrH,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAA;IACtE,oGAAoG;IACpG,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,4DAA6D,wBAAwB,gCA+CpH,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,oBAAoB,CAAA;IAC7B,8GAA8G;IAC9G,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,4DAA6D,yBAAyB,gCAOtH,CAAA"}
@@ -0,0 +1,91 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment, useMemo } from 'react';
3
+ import { useI18nLib, useLanguage } from '@owlmeans/client-i18n';
4
+ import { COUNTRY_CODES, TaxEstimateStatus, estimateOf } from '@owlmeans/payment';
5
+ import { cn } from '@/lib/utils';
6
+ import { Label } from '@/components/ui/label';
7
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8
+ const money = (minor, currency, locale) => new Intl.NumberFormat(locale, {
9
+ style: 'currency', currency: currency.toUpperCase(), minimumFractionDigits: 2,
10
+ }).format(minor / 100);
11
+ const localMoney = (amount, currency, locale) => {
12
+ try {
13
+ return new Intl.NumberFormat(locale, { style: 'currency', currency: currency.toUpperCase() }).format(amount);
14
+ }
15
+ catch {
16
+ return `${amount.toFixed(2)} ${currency.toUpperCase()}`;
17
+ }
18
+ };
19
+ /** The country names a `<Select>` offers, in the viewer's own language and alphabetical order. */
20
+ const useCountryOptions = (locale) => useMemo(() => {
21
+ let names = null;
22
+ try {
23
+ names = new Intl.DisplayNames([locale], { type: 'region' });
24
+ }
25
+ catch {
26
+ names = null;
27
+ }
28
+ const collator = new Intl.Collator(locale);
29
+ return COUNTRY_CODES
30
+ .map(code => ({ code, name: names?.of(code) ?? code }))
31
+ .sort((a, b) => collator.compare(a.name, b.name));
32
+ }, [locale]);
33
+ /**
34
+ * The billing-country picker alone — the piece `PriceEstimateSummary` composes for a single,
35
+ * self-contained estimate, and what a shared picker (several estimates, one country — a plan
36
+ * comparison table) renders once and drives every `usePriceEstimate({ country, onCountryChange })`
37
+ * with. Country names are the viewer's own language, alphabetically ordered.
38
+ */
39
+ export const CountrySelect = ({ value, onChange, label, id = 'price-estimate-country', className }) => {
40
+ const t = useI18nLib('web-payment', 'estimate');
41
+ const [locale] = useLanguage();
42
+ const countries = useCountryOptions(locale);
43
+ const text = label ?? t('country');
44
+ return _jsxs("div", { className: cn('grid gap-2', className), children: [_jsx(Label, { htmlFor: id, children: text }), _jsxs(Select, { value: value, onValueChange: onChange, children: [_jsx(SelectTrigger, { id: id, "aria-label": text, children: _jsx(SelectValue, { placeholder: t('country-placeholder') }) }), _jsx(SelectContent, { children: countries.map(({ code, name }) => _jsx(SelectItem, { value: code, children: name }, code)) })] })] });
45
+ };
46
+ /**
47
+ * What Stripe Tax says at the estimate's country, with NO country picker of its own: the rate(s),
48
+ * an estimated total, and one sentence for whichever status leaves no number to trust (reverse
49
+ * charge, no tax, "at checkout", "choose a country"). Several of these may share one
50
+ * `CountrySelect` (a plan comparison table).
51
+ *
52
+ * A country whose currency differs from `currency` (Adaptive Pricing found a rate) shows the tax
53
+ * and the total in THAT currency instead, marked `≈` throughout — never both currencies at once,
54
+ * which would read as two different prices for the same line. The rate percentage itself is
55
+ * currency-agnostic and always shows as Stripe stated it.
56
+ *
57
+ * `data-price-estimate` / `data-status` are stable test hooks.
58
+ */
59
+ export const PriceEstimateAmount = ({ control, subtotalMinor, currency, suffix, className }) => {
60
+ const t = useI18nLib('web-payment', 'estimate');
61
+ const [locale] = useLanguage();
62
+ const { estimate, loading, failed } = control;
63
+ const derived = estimate != null ? estimateOf(subtotalMinor, estimate) : null;
64
+ const status = estimate?.tax.status;
65
+ const numeric = status === TaxEstimateStatus.Taxed || status === TaxEstimateStatus.ReverseCharge
66
+ || status === TaxEstimateStatus.None;
67
+ const rateLabel = estimate != null && estimate.tax.rates.length > 0
68
+ ? estimate.tax.rates
69
+ .map(rate => t('rate', { type: t(`tax-type.${rate.type}`), percentage: rate.percentage }))
70
+ .join(' + ')
71
+ : null;
72
+ const local = derived?.local;
73
+ const taxText = local != null
74
+ ? (derived?.taxMinor != null ? localMoney(local.taxAmount, local.currency, locale) : null)
75
+ : (derived?.taxMinor != null ? money(derived.taxMinor, currency, locale) : null);
76
+ const totalText = local != null
77
+ ? (derived?.totalMinor != null ? localMoney(local.totalAmount, local.currency, locale) : null)
78
+ : (derived?.totalMinor != null ? money(derived.totalMinor, currency, locale) : null);
79
+ const approx = local != null ? '≈ ' : '';
80
+ return _jsxs("div", { "data-price-estimate": "", "data-status": status ?? 'pending', className: cn('grid gap-1.5', className), children: [estimate == null && loading && _jsx("p", { className: "text-muted-foreground text-xs", children: t('loading') }), estimate == null && !loading && failed && _jsx("p", { className: "text-destructive text-xs", children: t('failed') }), estimate != null && _jsxs(_Fragment, { children: [numeric && taxText != null && totalText != null && _jsxs("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-1 text-sm", "data-estimate-rows": "", children: [status === TaxEstimateStatus.Taxed && rateLabel != null && _jsxs(Fragment, { children: [_jsx("dt", { className: "text-muted-foreground", "data-estimate-rate": "", children: rateLabel }), _jsxs("dd", { "data-estimate-tax": "", children: [approx, taxText] })] }), _jsx("dt", { className: "font-medium", children: t('total') }), _jsxs("dd", { className: "font-medium", "data-estimate-total": "", children: [approx, totalText, suffix != null ? ` ${suffix}` : ''] })] }), numeric && local != null && _jsx("p", { className: "text-muted-foreground text-xs", "data-estimate-converted": "", children: t('converted-note') }), _jsx("p", { className: "text-muted-foreground text-xs", "data-estimate-status": "", children: t(`status.${status}`) })] })] });
81
+ };
82
+ /**
83
+ * A self-contained estimate: `CountrySelect` bound to the control's own `country`/`onCountryChange`,
84
+ * plus `PriceEstimateAmount` beneath it. What a single estimate (the credit dialog) wants; several
85
+ * estimates sharing one picker (a plan comparison table) compose the two pieces separately instead.
86
+ */
87
+ export const PriceEstimateSummary = ({ control, subtotalMinor, currency, suffix, className }) => {
88
+ const { country, onCountryChange } = control;
89
+ return _jsxs("div", { className: cn('grid gap-3', className), children: [_jsx(CountrySelect, { value: country, onChange: onCountryChange }), _jsx(PriceEstimateAmount, { control: control, subtotalMinor: subtotalMinor, currency: currency, suffix: suffix })] });
90
+ };
91
+ //# sourceMappingURL=estimate-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate-summary.js","sourceRoot":"","sources":["../src/estimate-summary.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { RegisteredEntrypoint } from '@owlmeans/entrypoint';
2
+ import type { PriceEstimate } from '@owlmeans/payment';
3
+ import type { PriceEstimateArguments, PriceEstimateControl, PriceEstimateRequest } from './types.js';
4
+ export interface UsePriceEstimateOptions {
5
+ /** Fetch (and refetch on a country change) only while `true` — a closed dialog costs nothing. */
6
+ enabled: boolean;
7
+ ttlMs?: number;
8
+ /**
9
+ * Controls the country from outside (several estimates sharing one picker — a plan comparison
10
+ * table). Given: the hook never owns `useState` for it, never preselects from `source:
11
+ * 'customer'` (the owner does, once, for whichever estimate answers first), and the returned
12
+ * control's `onCountryChange` calls this back instead of an internal setter. Absent (the
13
+ * default): the hook is self-contained, exactly as a standalone dialog needs.
14
+ */
15
+ country?: string;
16
+ onCountryChange?: (country: string) => void;
17
+ }
18
+ /**
19
+ * A live `PriceEstimate` for one protocol/request, with a country the caller may change.
20
+ *
21
+ * Fetches once `enabled` and again on every `onCountryChange`, from a client-side cache keyed by
22
+ * the entry's alias, the request body and the country — a TTL match answers instantly, an identical
23
+ * in-flight request is shared. Uncontrolled (no `opts.country`): the first answer whose `source` is
24
+ * `'customer'` preselects its `country`, once, so a returning buyer sees their own country without
25
+ * picking it. Controlled (`opts.country` given): the country and its preselection are the caller's.
26
+ */
27
+ export declare const usePriceEstimate: <Request extends PriceEstimateRequest>(entry: RegisteredEntrypoint<Request, PriceEstimate>, opts: UsePriceEstimateOptions, ...request: PriceEstimateArguments<Request>) => PriceEstimateControl;
28
+ //# sourceMappingURL=estimate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.d.ts","sourceRoot":"","sources":["../src/estimate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAOpG,MAAM,WAAW,uBAAuB;IACtC,iGAAiG;IACjG,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,SAAS,oBAAoB,SAC5D,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,QAAQ,uBAAuB,cACtE,sBAAsB,CAAC,OAAO,CAAC,KAC1C,oBAsDF,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { EstimateCache } from './estimate-cache.js';
3
+ const DEFAULT_TTL_MS = 5 * 60_000;
4
+ /** Shared across every mounted dialog/estimate card in the tab — the server already caches per TTL too. */
5
+ const cache = new EstimateCache();
6
+ /**
7
+ * A live `PriceEstimate` for one protocol/request, with a country the caller may change.
8
+ *
9
+ * Fetches once `enabled` and again on every `onCountryChange`, from a client-side cache keyed by
10
+ * the entry's alias, the request body and the country — a TTL match answers instantly, an identical
11
+ * in-flight request is shared. Uncontrolled (no `opts.country`): the first answer whose `source` is
12
+ * `'customer'` preselects its `country`, once, so a returning buyer sees their own country without
13
+ * picking it. Controlled (`opts.country` given): the country and its preselection are the caller's.
14
+ */
15
+ export const usePriceEstimate = (entry, opts, ...request) => {
16
+ const entryRef = useRef(entry);
17
+ entryRef.current = entry;
18
+ const base = (request[0] ?? {});
19
+ const bodyKey = JSON.stringify(base.body ?? {});
20
+ const controlled = opts.country !== undefined;
21
+ const [internalCountry, setInternalCountry] = useState('');
22
+ const country = controlled ? opts.country : internalCountry;
23
+ const [estimate, setEstimate] = useState(null);
24
+ const [loading, setLoading] = useState(false);
25
+ const [failed, setFailed] = useState(false);
26
+ const preselectedRef = useRef(false);
27
+ useEffect(() => {
28
+ if (!opts.enabled) {
29
+ return;
30
+ }
31
+ let active = true;
32
+ const key = `${entryRef.current.alias}:${bodyKey}:${country}`;
33
+ const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
34
+ setLoading(true);
35
+ setFailed(false);
36
+ cache.load(key, ttlMs, async () => await entryRef.current.call({
37
+ ...base, body: { ...(base.body ?? {}), ...(country !== '' ? { country } : {}) },
38
+ })).then(result => {
39
+ if (!active)
40
+ return;
41
+ setEstimate(result);
42
+ setLoading(false);
43
+ if (!controlled && !preselectedRef.current && country === '' && result.source === 'customer' && result.country != null) {
44
+ preselectedRef.current = true;
45
+ setInternalCountry(result.country);
46
+ }
47
+ }).catch(() => {
48
+ if (active) {
49
+ setFailed(true);
50
+ setLoading(false);
51
+ }
52
+ });
53
+ return () => { active = false; };
54
+ // eslint-disable-next-line react-hooks/exhaustive-deps
55
+ }, [opts.enabled, opts.ttlMs, bodyKey, country]);
56
+ const onCountryChange = useCallback((next) => {
57
+ if (controlled) {
58
+ opts.onCountryChange?.(next);
59
+ return;
60
+ }
61
+ preselectedRef.current = true;
62
+ setInternalCountry(next);
63
+ // eslint-disable-next-line react-hooks/exhaustive-deps
64
+ }, [controlled, opts.onCountryChange]);
65
+ return { estimate, country, loading, failed, onCountryChange };
66
+ };
67
+ //# sourceMappingURL=estimate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../src/estimate.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- { "amount-checkout": { "title": "Папоўніць крэдыты", "description": "Выберыце чысты кошт крэдытаў для арганізацыі.", "presets": "Гатовыя сумы", "custom": "Свая сума", "invalid": "Увядзіце карэктную суму, не больш за два знакі пасля коскі.", "below": "Мінімальная сума — {{amount}}.", "above": "Максімальная сума — {{amount}}.", "bounds": "Выберыце суму ад {{minimum}} да {{maximum}}.", "credits": "Кошт крэдытаў", "adjustment": "Карэкціроўка апрацоўкі", "subtotal": "Сума да падатку", "tax-note": "Stripe разлічыць падатак падчас аплаты.", "cancel": "Скасаваць", "continue": "Перайсці ў Stripe", "pending": "Адкрываем аплату…", "close": "Закрыць" }, "entitlement": { "plan": { "current": "Бягучы план" }, "status": { "trial": "Пробны перыяд", "trial-on": "Пробны перыяд да {{date}}", "past-due": "Плацёж пратэрмінаваны", "cancel-scheduled": "Адмена запланавана", "cancel-scheduled-on": "Адмяняецца {{date}}", "paused": "На паўзе", "paused-on": "На паўзе з {{date}}", "suspended": "Прыпынены", "free": "Бясплатны план", "renews-on": "Падаўжаецца {{date}}", "active": "Актыўны", "created": "Чакае актывацыі", "canceled": "Адменены", "expired": "Тэрмін скончыўся", "ended": "Завершаны", "blocked": "Заблакаваны" }, "limit": { "usage": "{{used}} з {{limit}}", "usage-unit": "{{used}} з {{limit}} {{unit}}", "not-included": "Не ўваходзіць у план", "exhausted": "Ліміт вычарпаны", "resets-on": "Абновіцца {{date}}" }, "promo": { "free-until": "Бясплатна да {{date}}", "grandfathered": "Захавана для вашага плана", "ended": "Акцыя завершана" }, "capability": { "included": "Уваходзіць у план", "not-included": "Не ўваходзіць у план" } } }
1
+ { "amount-checkout": { "title": "Папоўніць крэдыты", "description": "Выберыце чысты кошт крэдытаў для арганізацыі.", "presets": "Гатовыя сумы", "custom": "Свая сума", "invalid": "Увядзіце карэктную суму, не больш за два знакі пасля коскі.", "below": "Мінімальная сума — {{amount}}.", "above": "Максімальная сума — {{amount}}.", "bounds": "Выберыце суму ад {{minimum}} да {{maximum}}.", "credits": "Кошт крэдытаў", "adjustment": "Карэкціроўка апрацоўкі", "subtotal": "Сума да падатку", "tax-note": "Stripe разлічыць падатак падчас аплаты.", "cancel": "Скасаваць", "continue": "Перайсці ў Stripe", "pending": "Адкрываем аплату…", "close": "Закрыць" }, "entitlement": { "plan": { "current": "Бягучы план" }, "status": { "trial": "Пробны перыяд", "trial-on": "Пробны перыяд да {{date}}", "past-due": "Плацёж пратэрмінаваны", "cancel-scheduled": "Адмена запланавана", "cancel-scheduled-on": "Адмяняецца {{date}}", "paused": "На паўзе", "paused-on": "На паўзе з {{date}}", "suspended": "Прыпынены", "free": "Бясплатны план", "renews-on": "Падаўжаецца {{date}}", "active": "Актыўны", "created": "Чакае актывацыі", "canceled": "Адменены", "expired": "Тэрмін скончыўся", "ended": "Завершаны", "blocked": "Заблакаваны" }, "limit": { "usage": "{{used}} з {{limit}}", "usage-unit": "{{used}} з {{limit}} {{unit}}", "not-included": "Не ўваходзіць у план", "exhausted": "Ліміт вычарпаны", "resets-on": "Абновіцца {{date}}" }, "promo": { "free-until": "Бясплатна да {{date}}", "grandfathered": "Захавана для вашага плана", "ended": "Акцыя завершана" }, "capability": { "included": "Уваходзіць у план", "not-included": "Не ўваходзіць у план" } }, "estimate": { "country": "Краіна плацельшчыка", "country-placeholder": "Выберыце краіну", "tax-type": { "vat": "ПДВ", "gst": "GST", "sales-tax": "Падатак з продажаў", "tax": "Падатак" }, "rate": "{{type}} ({{percentage}}%)", "total": "Прыблізная сума", "converted-note": "Пералічана па бягучым курсе Stripe; дакладная сума і магчымая камісія за канвертацыю будуць пацверджаны падчас аплаты.", "status": { "taxed": "Падатак ужо ўключаны ў прыблізную суму ніжэй.", "reverse-charge": "Адваротнае налічэнне — пры ўказаным нумары ПДВ вы ўлічваеце падатак самастойна, таму мы нічога не дадаём.", "none": "Для гэтай краіны падатак не прымяняецца.", "at-checkout": "Дакладны падатак будзе разлічаны падчас аплаты.", "location-required": "Выберыце краіну плацельшчыка, каб убачыць разлік." }, "loading": "Правяраем падатак…", "failed": "Зараз не ўдалося праверыць падатак." } }