@patientos/website-kit 0.2.6 → 0.2.8

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 (44) hide show
  1. package/README.md +54 -2
  2. package/dist/booking-block-D-UOAPx3.d.ts +74 -0
  3. package/dist/cart-block-B-ytc9us.d.ts +20 -0
  4. package/dist/cart-button-BCLFittB.d.ts +24 -0
  5. package/dist/cart-storage.d.ts +110 -0
  6. package/dist/cart-storage.js +39 -0
  7. package/dist/checkout-block-BeXwpqYe.d.ts +24 -0
  8. package/dist/chunk-2CZFJOKK.js +79 -0
  9. package/dist/{chunk-ZYX4TXBN.js → chunk-3T7UWKAH.js} +4 -310
  10. package/dist/{chunk-7ZOQ6UKG.js → chunk-4GKSSM5N.js} +578 -3163
  11. package/dist/chunk-4NF7SSIX.js +367 -0
  12. package/dist/chunk-7BUDUYXN.js +80 -0
  13. package/dist/chunk-FZ4WKWIE.js +310 -0
  14. package/dist/{chunk-ZZFBTLR4.js → chunk-HTVKKLWI.js} +20 -5
  15. package/dist/chunk-IOC6QLB7.js +159 -0
  16. package/dist/chunk-S3ZQQOQT.js +2057 -0
  17. package/dist/chunk-UTAMB2SK.js +214 -0
  18. package/dist/chunk-WQSJ46TP.js +19 -0
  19. package/dist/chunk-ZNH6TSYP.js +167 -0
  20. package/dist/index.d.ts +12 -32
  21. package/dist/index.js +88 -26
  22. package/dist/islands-impl/cart-button.d.ts +19 -0
  23. package/dist/islands-impl/cart-button.js +13 -0
  24. package/dist/islands-impl/cart.d.ts +31 -0
  25. package/dist/islands-impl/cart.js +11 -0
  26. package/dist/islands-impl/checkout.d.ts +6 -0
  27. package/dist/islands-impl/checkout.js +13 -0
  28. package/dist/islands-impl/store.d.ts +30 -0
  29. package/dist/islands-impl/store.js +16 -0
  30. package/dist/islands-impl.d.ts +14 -10
  31. package/dist/islands-impl.js +22 -7
  32. package/dist/islands-registry.js +13 -4
  33. package/dist/{portal-account-8uQc_Ncx.d.ts → portal-account-DRJyr3nz.d.ts} +1 -82
  34. package/dist/{portal-account.client-BBe_q9yC.d.ts → portal-account.client-Bke4NsgW.d.ts} +2 -1
  35. package/dist/portal-booking-client.d.ts +3 -2
  36. package/dist/portal-booking-client.js +2 -1
  37. package/dist/portal-client-my3q5GME.d.ts +83 -0
  38. package/dist/store-block-CFxnsfKU.d.ts +37 -0
  39. package/dist/store-catalog-p7TFOpfk.d.ts +78 -0
  40. package/dist/store-catalog.d.ts +3 -0
  41. package/dist/store-catalog.js +11 -0
  42. package/dist/website-kit.css +193 -0
  43. package/package.json +31 -1
  44. package/dist/checkout-block-BdpPIuQ5.d.ts +0 -136
package/README.md CHANGED
@@ -39,10 +39,15 @@ bearer; deployed sites continue to use host-only patient cookies.
39
39
 
40
40
  ## Store, cart and checkout
41
41
 
42
- Use the runtime implementations in an ordinary hydrated React site:
42
+ Use the runtime implementations in an ordinary hydrated React site. Import each surface
43
+ from its OWN subpath — the `islands-impl` barrel pulls the payment stack (BPOINT, the
44
+ address picker, Medicare) into every page that touches it:
43
45
 
44
46
  ```tsx
45
- import { StoreClient, CartClient, CheckoutClient } from '@patientos/website-kit/islands-impl'
47
+ import { StoreClient } from '@patientos/website-kit/islands-impl/store'
48
+ import { CartClient } from '@patientos/website-kit/islands-impl/cart'
49
+ import { CartButtonClient } from '@patientos/website-kit/islands-impl/cart-button'
50
+ import { CheckoutClient } from '@patientos/website-kit/islands-impl/checkout'
46
51
  import '@patientos/website-kit/website-kit.css'
47
52
 
48
53
  const storeApiOrigin = 'https://my.example-clinic.com'
@@ -59,6 +64,53 @@ const storeApiOrigin = 'https://my.example-clinic.com'
59
64
  Use `productHandle` for one exact product or `categoryHandle` for a category-filtered
60
65
  catalogue; an exact product takes precedence when both are supplied.
61
66
 
67
+ ### Server-rendering the shop
68
+
69
+ Fetch the catalogue in your own **per-request** route loader and pass it as
70
+ `initialCatalog`. The grid then renders on the server — real product markup for a crawler
71
+ and no loading swap — and the island still refreshes in the background, so prices stay
72
+ honest. Never bake a catalogue into a statically built artifact.
73
+
74
+ ```tsx
75
+ import { createStoreClient, fetchStoreCatalog } from '@patientos/website-kit'
76
+ import type { StoreCatalog } from '@patientos/website-kit'
77
+
78
+ // in the route loader (runs per request, on the server)
79
+ const catalog = await fetchStoreCatalog(createStoreClient({ apiOrigin: storeApiOrigin }))
80
+
81
+ // in the component
82
+ <StoreClient storeApiOrigin={storeApiOrigin} initialCatalog={catalog} hideCartLink />
83
+ ```
84
+
85
+ `fetchStoreCatalog` returns `null` on any failure, and `StoreClient` then falls back to
86
+ its own client fetch. Pass `hideCartLink` when your site has its own header cart.
87
+
88
+ ### The header cart
89
+
90
+ `<CartButtonClient/>` is a count badge that opens a cart drawer. It costs **no requests**:
91
+ the count comes from the cart already in the shopper's browser, and the drawer quotes
92
+ `POST /api/store/quote` only when opened. The badge is absent (never `0`) until the client
93
+ effect runs, so it adds nothing to server-rendered HTML and your header stays cacheable.
94
+
95
+ ```tsx
96
+ <CartButtonClient
97
+ storeApiOrigin={storeApiOrigin}
98
+ label="Cart"
99
+ cartHref="/cart"
100
+ checkoutHref="/checkout"
101
+ />
102
+ ```
103
+
104
+ To build your own affordance instead, import the cart module directly:
105
+
106
+ ```ts
107
+ import { cartItemCount, readCart, subscribeToCart } from '@patientos/website-kit/cart-storage'
108
+ ```
109
+
110
+ It is browser-only by design (`readCart()` answers empty on the server) and is shared by
111
+ every surface in one page, so a store block adding an item updates your badge with no
112
+ shared React tree.
113
+
62
114
  Omit `storeApiOrigin` when PatientOS serves the clinic website and store on one origin.
63
115
  For a separately deployed site it must be the clinic's verified patient/API origin, and
64
116
  the site Origin must be registered on that clinic's active web-form channel. The client
@@ -0,0 +1,74 @@
1
+ import * as React from 'react';
2
+
3
+ /** A service as the funnel offers it. */
4
+ interface FunnelServiceOption {
5
+ key: string;
6
+ label: string;
7
+ }
8
+ /** How the runtime half reaches the API — the snapshot's `publicApi`, serialised. */
9
+ interface FunnelPublicApi {
10
+ publishableKey: string;
11
+ apiBase: string;
12
+ turnstileSiteKey?: string;
13
+ portalUrl?: string;
14
+ portalOrigin?: string;
15
+ portalApiOrigin?: string;
16
+ }
17
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
18
+ type CertificateFunnelProps = {
19
+ /** Start on ONE service, skipping the picker. */
20
+ serviceKey?: string;
21
+ /** Heading above the funnel. */
22
+ heading?: string;
23
+ publicApi?: FunnelPublicApi;
24
+ services?: FunnelServiceOption[];
25
+ clinicName?: string;
26
+ clinicPhone?: string;
27
+ };
28
+ /** Marker-only props: what a clinic page actually writes. */
29
+ type CertificateFunnelMarkerProps = {
30
+ /**
31
+ * Offer only these services, in this order. Omitted ⇒ every certificate service the
32
+ * clinic has published.
33
+ */
34
+ serviceKeys?: string[];
35
+ /** Start on ONE service, skipping the picker. */
36
+ serviceKey?: string;
37
+ /** Per-service copy overriding the configured label, keyed by service key. */
38
+ serviceCopy?: Record<string, string>;
39
+ heading?: string;
40
+ className?: string;
41
+ };
42
+ declare function CertificateFunnel({ serviceKeys, serviceKey, serviceCopy, heading, className, }: CertificateFunnelMarkerProps): React.ReactElement;
43
+
44
+ /** An appointment type as the block offers it. */
45
+ interface BookingTypeOption {
46
+ id: string;
47
+ label: string;
48
+ durationMinutes: number;
49
+ modality: 'in_person' | 'telehealth';
50
+ }
51
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
52
+ type BookingBlockProps = {
53
+ /** Pre-select ONE appointment type, skipping the picker. */
54
+ appointmentTypeId?: string;
55
+ /** Pre-select a practitioner (availability is then filtered to them). */
56
+ practitionerId?: string;
57
+ heading?: string;
58
+ publicApi?: FunnelPublicApi;
59
+ appointmentTypes?: BookingTypeOption[];
60
+ clinicName?: string;
61
+ clinicPhone?: string;
62
+ };
63
+ /** Marker-only props: what a clinic page actually writes. */
64
+ type BookingBlockMarkerProps = {
65
+ appointmentTypeId?: string;
66
+ practitionerId?: string;
67
+ /** Offer only these appointment types, by id, in this order. */
68
+ appointmentTypeIds?: string[];
69
+ heading?: string;
70
+ className?: string;
71
+ };
72
+ declare function BookingBlock({ appointmentTypeId, practitionerId, appointmentTypeIds, heading, className, }: BookingBlockMarkerProps): React.ReactElement;
73
+
74
+ export { BookingBlock as B, CertificateFunnel as C, type FunnelPublicApi as F, type BookingBlockMarkerProps as a, type BookingBlockProps as b, type BookingTypeOption as c, type CertificateFunnelMarkerProps as d, type CertificateFunnelProps as e, type FunnelServiceOption as f };
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
4
+ type CartProps = {
5
+ /** PatientOS patient/API origin. Omitted keeps the original same-origin contract. */
6
+ storeApiOrigin?: string;
7
+ };
8
+ /** Marker-only props: runtime config plus presentation the page controls. */
9
+ type CartMarkerProps = CartProps & {
10
+ className?: string;
11
+ };
12
+ /**
13
+ * BUILD-TIME marker. Emits the mount point plus a static no-JS fallback.
14
+ *
15
+ * The fallback shows NO line items — the static artifact is served to every visitor and
16
+ * is edge-cacheable, so it must never carry a patient's cart.
17
+ */
18
+ declare function Cart({ storeApiOrigin, className }: CartMarkerProps): React.ReactElement;
19
+
20
+ export { Cart as C, type CartMarkerProps as a, type CartProps as b };
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+
3
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
4
+ type CartButtonProps = {
5
+ /** PatientOS patient/API origin. Omitted keeps the original same-origin contract. */
6
+ storeApiOrigin?: string;
7
+ /** Accessible label for the button. Defaults to "Cart". */
8
+ label?: string;
9
+ /** Where "View cart" and the no-JS fallback link point. Defaults to `/cart`. */
10
+ cartHref?: string;
11
+ /** Where the drawer's checkout action points. Defaults to `/checkout`. */
12
+ checkoutHref?: string;
13
+ };
14
+ /** Marker-only props: runtime config plus presentation the page controls. */
15
+ type CartButtonMarkerProps = CartButtonProps & {
16
+ className?: string;
17
+ };
18
+ /**
19
+ * BUILD-TIME marker. The no-JS fallback is a plain link to the cart page — never a
20
+ * count, and never a drawer that cannot open.
21
+ */
22
+ declare function CartButton({ storeApiOrigin, label, cartHref, checkoutHref, className, }: CartButtonMarkerProps): React.ReactElement;
23
+
24
+ export { CartButton as C, type CartButtonMarkerProps as a, type CartButtonProps as b };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The cart, in the shopper's own browser (PAT-766).
3
+ *
4
+ * ── Why localStorage and not a table ──────────────────────────────────────────
5
+ * A retail cart holds no PHI and no commitment: it is a list of things somebody
6
+ * is thinking about. Persisting it server-side would buy a cart table, a sweeper
7
+ * for abandoned rows, a merge story for "signed in on a second device", and an
8
+ * anonymous-cart identity — all to reproduce what the browser already does for
9
+ * free. Nothing here is trusted: the server re-derives every price, every stock
10
+ * fact and every title on each quote and again inside the checkout mint, so the
11
+ * worst a tampered cart can do is change WHAT is bought, never what it costs.
12
+ *
13
+ * ── The `kind` discriminator is a RESERVATION ─────────────────────────────────
14
+ * The product requirement one-origin exists to serve is ONE cart spanning
15
+ * medication fills and retail items (docs/one-origin-islands.md §1). v1 sells
16
+ * retail only — but the storage shape carries `kind` from day one, and every
17
+ * function here PRESERVES a `fill` line it does not understand rather than
18
+ * dropping it. That matters: the day fills become purchasable, a patient who added
19
+ * one to an older build must not find it silently gone. The server refuses to
20
+ * price a fill line (`not_purchasable`), so a preserved line is inert, visible,
21
+ * and honest.
22
+ *
23
+ * ── Total by construction ─────────────────────────────────────────────────────
24
+ * Every read is defensive. Absent storage (SSR, private mode, a browser with it
25
+ * disabled), malformed JSON, a wrong version, a non-array `lines`, a line missing
26
+ * its variant — each yields an EMPTY cart, never a throw. A shop that white-screens
27
+ * because a stale key had a stray brace is worse than a shop that has forgotten
28
+ * your cart.
29
+ */
30
+ /** The versioned storage key. A future shape change bumps this and starts clean. */
31
+ declare const CART_STORAGE_KEY = "patientos.cart.v1";
32
+ /** 'retail' is purchasable in v1; 'fill' is RESERVED and preserved but not sellable. */
33
+ type CartLineKind = "retail" | "fill";
34
+ type CartLine = {
35
+ lineId: string;
36
+ kind: CartLineKind;
37
+ variantId: string;
38
+ quantity: number;
39
+ };
40
+ type Cart = {
41
+ v: 1;
42
+ lines: CartLine[];
43
+ settledAttemptTokens?: string[];
44
+ };
45
+ type CartDraft = {
46
+ v: 1;
47
+ lines: readonly unknown[];
48
+ settledAttemptTokens?: string[];
49
+ };
50
+ type CartMutationResult = {
51
+ cart: Cart;
52
+ persisted: boolean;
53
+ status: "updated" | "cart_storage_unavailable" | "not_committed";
54
+ };
55
+ type AddToCartResult = {
56
+ cart: Cart;
57
+ status: "added" | "line_limit" | "quantity_limit" | "cart_storage_unavailable";
58
+ };
59
+ /**
60
+ * A FRESH empty cart, every call.
61
+ *
62
+ * Never hand out a shared instance: `addToCart` mutates the cart it reads, so a
63
+ * module-level singleton would accumulate lines across reads and survive a
64
+ * `clearCart` — a shop that remembers what you deleted.
65
+ */
66
+ declare function emptyCart(): Cart;
67
+ /** A frozen empty cart for comparisons. Never mutated, never returned from a read. */
68
+ declare const EMPTY_CART: Readonly<Cart>;
69
+ declare function normalizeCartLines(entries: readonly unknown[]): CartLine[];
70
+ declare function readCart(): Cart;
71
+ declare function mutateCart(mutation: (latest: Cart) => CartDraft, beforePersist?: (proposed: Cart, signal: AbortSignal) => Promise<boolean>): Promise<CartMutationResult>;
72
+ type CartLockResult<T> = {
73
+ status: "completed";
74
+ cart: Cart;
75
+ result: T;
76
+ } | {
77
+ status: "cart_storage_unavailable";
78
+ cart: Cart;
79
+ } | {
80
+ status: "not_completed";
81
+ cart: Cart;
82
+ };
83
+ declare function withCartLock<T>(operation: (latest: Cart, signal: AbortSignal) => Promise<T>): Promise<CartLockResult<T>>;
84
+ declare function writeCart(cart: CartDraft): Promise<CartMutationResult>;
85
+ declare function addToCart(variantId: string, quantity?: number): Promise<AddToCartResult>;
86
+ declare function setCartQuantity(kind: CartLineKind, variantId: string, quantity: number): Promise<CartMutationResult>;
87
+ declare function adjustCartQuantity(kind: CartLineKind, variantId: string, delta: number, maximumBaseline?: number): Promise<CartMutationResult>;
88
+ declare function removeFromCart(variantId: string, kind?: CartLineKind): Promise<CartMutationResult>;
89
+ declare function clearCart(): Promise<CartMutationResult>;
90
+ declare function subtractPurchasedCartLines(purchasedLines: readonly unknown[], attemptToken?: string): Promise<CartMutationResult>;
91
+ /** Total item count — the header badge. */
92
+ declare function cartItemCount(cart?: Cart): number;
93
+ /** The wire shape posted to `/api/store/quote` and `/checkout/init`. */
94
+ declare function cartToWireLines(cart: {
95
+ v?: 1;
96
+ lines: readonly {
97
+ kind: CartLineKind;
98
+ variantId: string;
99
+ quantity: number;
100
+ }[];
101
+ }): {
102
+ kind: CartLineKind;
103
+ variantId: string;
104
+ quantity: number;
105
+ }[];
106
+ type Listener = (cart: Cart) => void;
107
+ /** Subscribe to cart changes (this tab AND other tabs). Returns an unsubscribe. */
108
+ declare function subscribeToCart(fn: Listener): () => void;
109
+
110
+ export { type AddToCartResult, CART_STORAGE_KEY, type Cart, type CartLine, type CartLineKind, type CartLockResult, type CartMutationResult, EMPTY_CART, addToCart, adjustCartQuantity, cartItemCount, cartToWireLines, clearCart, emptyCart, mutateCart, normalizeCartLines, readCart, removeFromCart, setCartQuantity, subscribeToCart, subtractPurchasedCartLines, withCartLock, writeCart };
@@ -0,0 +1,39 @@
1
+ import {
2
+ CART_STORAGE_KEY,
3
+ EMPTY_CART,
4
+ addToCart,
5
+ adjustCartQuantity,
6
+ cartItemCount,
7
+ cartToWireLines,
8
+ clearCart,
9
+ emptyCart,
10
+ mutateCart,
11
+ normalizeCartLines,
12
+ readCart,
13
+ removeFromCart,
14
+ setCartQuantity,
15
+ subscribeToCart,
16
+ subtractPurchasedCartLines,
17
+ withCartLock,
18
+ writeCart
19
+ } from "./chunk-4NF7SSIX.js";
20
+ import "./chunk-MLKGABMK.js";
21
+ export {
22
+ CART_STORAGE_KEY,
23
+ EMPTY_CART,
24
+ addToCart,
25
+ adjustCartQuantity,
26
+ cartItemCount,
27
+ cartToWireLines,
28
+ clearCart,
29
+ emptyCart,
30
+ mutateCart,
31
+ normalizeCartLines,
32
+ readCart,
33
+ removeFromCart,
34
+ setCartQuantity,
35
+ subscribeToCart,
36
+ subtractPurchasedCartLines,
37
+ withCartLock,
38
+ writeCart
39
+ };
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+
3
+ /** JSON-safe island configuration. Crosses the build→runtime boundary — see `./islands`. */
4
+ type CheckoutProps = {
5
+ /** PatientOS patient/API origin. Omitted keeps the original same-origin contract. */
6
+ storeApiOrigin?: string;
7
+ /** Shopper-facing order history on the hosting site. */
8
+ ordersHref?: string;
9
+ /** Same-origin order confirmation route on the hosting clinic site. */
10
+ completionHref?: string;
11
+ };
12
+ /** Marker-only props: runtime config plus presentation the page controls. */
13
+ type CheckoutMarkerProps = CheckoutProps & {
14
+ className?: string;
15
+ };
16
+ /**
17
+ * BUILD-TIME marker. Emits the mount point plus a static no-JS fallback.
18
+ *
19
+ * The fallback carries no order, no amount and no payment field: this document is a
20
+ * static artifact served to every visitor.
21
+ */
22
+ declare function Checkout({ storeApiOrigin, ordersHref, completionHref, className, }: CheckoutMarkerProps): React.ReactElement;
23
+
24
+ export { Checkout as C, type CheckoutMarkerProps as a, type CheckoutProps as b };
@@ -0,0 +1,79 @@
1
+ import {
2
+ CartLineList,
3
+ CartProblemList,
4
+ CartTotals,
5
+ UnsupportedCartLineList,
6
+ quoteSourceQuantities,
7
+ useCartQuote
8
+ } from "./chunk-IOC6QLB7.js";
9
+ import {
10
+ createStoreClient
11
+ } from "./chunk-7BUDUYXN.js";
12
+ import {
13
+ readCart,
14
+ subscribeToCart
15
+ } from "./chunk-4NF7SSIX.js";
16
+
17
+ // src/cart-block.client.tsx
18
+ import * as React from "react";
19
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
20
+ function CartClient({ storeApiOrigin }) {
21
+ const client = React.useMemo(
22
+ () => createStoreClient({ apiOrigin: storeApiOrigin }),
23
+ [storeApiOrigin]
24
+ );
25
+ const [cart, setCart] = React.useState(() => readCart());
26
+ const [mutationUnavailable, setMutationUnavailable] = React.useState(false);
27
+ const { quote, quoteSourceCart, failed } = useCartQuote(client, cart);
28
+ async function runMutation(mutation) {
29
+ const result = await mutation;
30
+ setMutationUnavailable(result.status === "cart_storage_unavailable");
31
+ }
32
+ React.useEffect(() => subscribeToCart(setCart), []);
33
+ if (cart.lines.length === 0) {
34
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
35
+ /* @__PURE__ */ jsx("h2", { className: "sk-cart__heading", children: "Your cart" }),
36
+ /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "status", children: "Your cart is empty." }),
37
+ /* @__PURE__ */ jsx("a", { className: "sk-cart__continue", href: "/store", children: "Browse the shop" })
38
+ ] });
39
+ }
40
+ const quotedVariantIds = new Set(
41
+ quote?.lines.map((line) => line.variantId) ?? []
42
+ );
43
+ const unsupportedLines = cart.lines.filter(
44
+ (line) => line.kind !== "retail" || quote !== null && !quotedVariantIds.has(line.variantId)
45
+ );
46
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ /* @__PURE__ */ jsx("h2", { className: "sk-cart__heading", children: "Your cart" }),
48
+ quote ? /* @__PURE__ */ jsx(CartProblemList, { problems: quote.problems }) : null,
49
+ failed ? /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "alert", children: "We couldn't price your cart just now. Please try again in a moment." }) : null,
50
+ mutationUnavailable ? /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "alert", children: "This browser can\u2019t safely update the cart. Try a supported browser with site storage enabled." }) : null,
51
+ /* @__PURE__ */ jsx(
52
+ UnsupportedCartLineList,
53
+ {
54
+ lines: unsupportedLines,
55
+ onMutate: (m) => void runMutation(m)
56
+ }
57
+ ),
58
+ quote ? /* @__PURE__ */ jsxs(Fragment, { children: [
59
+ /* @__PURE__ */ jsx(
60
+ CartLineList,
61
+ {
62
+ lines: quote.lines,
63
+ sourceQuantities: quoteSourceQuantities(quoteSourceCart),
64
+ onMutate: (m) => void runMutation(m)
65
+ }
66
+ ),
67
+ /* @__PURE__ */ jsx(CartTotals, { quote }),
68
+ quote.requiresShipping ? /* @__PURE__ */ jsx("p", { className: "sk-cart__note", children: "Delivery details are confirmed at checkout." }) : null,
69
+ /* @__PURE__ */ jsxs("div", { className: "sk-cart__actions", children: [
70
+ /* @__PURE__ */ jsx("a", { className: "sk-cart__continue", href: "/store", children: "Keep shopping" }),
71
+ unsupportedLines.length === 0 && quote.lines.length > 0 ? /* @__PURE__ */ jsx("a", { className: "sk-cart__checkout", href: "/checkout", children: "Checkout" }) : /* @__PURE__ */ jsx("span", { className: "sk-cart__checkout", "aria-disabled": "true", children: "Remove unsupported items to checkout" })
72
+ ] })
73
+ ] }) : /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "status", children: "Pricing your cart\u2026" })
74
+ ] });
75
+ }
76
+
77
+ export {
78
+ CartClient
79
+ };