@patientos/website-kit 0.2.8 → 0.2.10
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/{chunk-2CZFJOKK.js → chunk-55W3KKJD.js} +5 -2
- package/dist/{chunk-HTVKKLWI.js → chunk-FW2X2SUZ.js} +3 -3
- package/dist/{chunk-IOC6QLB7.js → chunk-LW2LLAL4.js} +26 -8
- package/dist/{chunk-ZNH6TSYP.js → chunk-Q5DIBKWX.js} +9 -2
- package/dist/{chunk-4GKSSM5N.js → chunk-SYXA5WP7.js} +165 -97
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/islands-impl/cart-button.js +2 -2
- package/dist/islands-impl/cart.js +2 -2
- package/dist/islands-impl.js +4 -4
- package/dist/islands-registry.js +5 -5
- package/dist/website-kit.css +141 -13
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
UnsupportedCartLineList,
|
|
6
6
|
quoteSourceQuantities,
|
|
7
7
|
useCartQuote
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LW2LLAL4.js";
|
|
9
9
|
import {
|
|
10
10
|
createStoreClient
|
|
11
11
|
} from "./chunk-7BUDUYXN.js";
|
|
@@ -24,7 +24,8 @@ function CartClient({ storeApiOrigin }) {
|
|
|
24
24
|
);
|
|
25
25
|
const [cart, setCart] = React.useState(() => readCart());
|
|
26
26
|
const [mutationUnavailable, setMutationUnavailable] = React.useState(false);
|
|
27
|
-
const { quote, quoteSourceCart, failed } = useCartQuote(client, cart);
|
|
27
|
+
const { quote, quoteSourceCart, failed, pending } = useCartQuote(client, cart);
|
|
28
|
+
const liveQuantities = React.useMemo(() => quoteSourceQuantities(cart), [cart]);
|
|
28
29
|
async function runMutation(mutation) {
|
|
29
30
|
const result = await mutation;
|
|
30
31
|
setMutationUnavailable(result.status === "cart_storage_unavailable");
|
|
@@ -61,6 +62,8 @@ function CartClient({ storeApiOrigin }) {
|
|
|
61
62
|
{
|
|
62
63
|
lines: quote.lines,
|
|
63
64
|
sourceQuantities: quoteSourceQuantities(quoteSourceCart),
|
|
65
|
+
liveQuantities,
|
|
66
|
+
pending,
|
|
64
67
|
onMutate: (m) => void runMutation(m)
|
|
65
68
|
}
|
|
66
69
|
),
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
CertificateFunnelClient,
|
|
4
4
|
PortalAccountClient,
|
|
5
5
|
PortalPanelClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SYXA5WP7.js";
|
|
7
7
|
import {
|
|
8
8
|
mountIslandsWith
|
|
9
9
|
} from "./chunk-THMT43MV.js";
|
|
10
10
|
import {
|
|
11
11
|
CartClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-55W3KKJD.js";
|
|
13
13
|
import {
|
|
14
14
|
CartButtonClient
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Q5DIBKWX.js";
|
|
16
16
|
import {
|
|
17
17
|
CheckoutClient
|
|
18
18
|
} from "./chunk-S3ZQQOQT.js";
|
|
@@ -50,9 +50,11 @@ function UnsupportedCartLineList({
|
|
|
50
50
|
function CartLineList({
|
|
51
51
|
lines,
|
|
52
52
|
sourceQuantities,
|
|
53
|
-
onMutate
|
|
53
|
+
onMutate,
|
|
54
|
+
liveQuantities,
|
|
55
|
+
pending
|
|
54
56
|
}) {
|
|
55
|
-
return /* @__PURE__ */ jsx("ul", { className: "sk-cart__lines", children: lines.map((line) => /* @__PURE__ */ jsxs("li", { className: "sk-cart__line", children: [
|
|
57
|
+
return /* @__PURE__ */ jsx("ul", { className: "sk-cart__lines", "aria-busy": pending || void 0, children: lines.map((line) => /* @__PURE__ */ jsxs("li", { className: "sk-cart__line", children: [
|
|
56
58
|
/* @__PURE__ */ jsx("a", { className: "sk-cart__line-title", href: `/store/${line.productHandle}`, children: line.title }),
|
|
57
59
|
/* @__PURE__ */ jsxs("span", { className: "sk-cart__line-unit", children: [
|
|
58
60
|
formatMoney(line.unitPrice),
|
|
@@ -75,7 +77,7 @@ function CartLineList({
|
|
|
75
77
|
children: "\u2212"
|
|
76
78
|
}
|
|
77
79
|
),
|
|
78
|
-
/* @__PURE__ */ jsx("span", { "aria-live": "polite", children: line.quantity }),
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "sk-cart__qty-value", "aria-live": "polite", children: (pending ? liveQuantities?.get(line.variantId) : void 0) ?? line.quantity }),
|
|
79
81
|
/* @__PURE__ */ jsx(
|
|
80
82
|
"button",
|
|
81
83
|
{
|
|
@@ -125,22 +127,38 @@ function useCartQuote(client, cart, enabled = true) {
|
|
|
125
127
|
const [state, setState] = React.useState({
|
|
126
128
|
quote: null,
|
|
127
129
|
quoteSourceCart: null,
|
|
128
|
-
failed: false
|
|
130
|
+
failed: false,
|
|
131
|
+
pending: false
|
|
129
132
|
});
|
|
130
133
|
React.useEffect(() => {
|
|
131
134
|
if (!enabled || cart.lines.length === 0) {
|
|
132
|
-
setState({
|
|
135
|
+
setState({
|
|
136
|
+
quote: null,
|
|
137
|
+
quoteSourceCart: null,
|
|
138
|
+
failed: false,
|
|
139
|
+
pending: false
|
|
140
|
+
});
|
|
133
141
|
return;
|
|
134
142
|
}
|
|
135
143
|
let alive = true;
|
|
136
|
-
setState({
|
|
144
|
+
setState((previous) => ({ ...previous, failed: false, pending: true }));
|
|
137
145
|
void client.post("/api/store/quote", { lines: cartToWireLines(cart) }).then((result) => {
|
|
138
146
|
if (!alive) return;
|
|
139
147
|
if (!result.ok) {
|
|
140
|
-
setState({
|
|
148
|
+
setState({
|
|
149
|
+
quote: null,
|
|
150
|
+
quoteSourceCart: null,
|
|
151
|
+
failed: true,
|
|
152
|
+
pending: false
|
|
153
|
+
});
|
|
141
154
|
return;
|
|
142
155
|
}
|
|
143
|
-
setState({
|
|
156
|
+
setState({
|
|
157
|
+
quote: result.data,
|
|
158
|
+
quoteSourceCart: cart,
|
|
159
|
+
failed: false,
|
|
160
|
+
pending: false
|
|
161
|
+
});
|
|
144
162
|
});
|
|
145
163
|
return () => {
|
|
146
164
|
alive = false;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
UnsupportedCartLineList,
|
|
6
6
|
quoteSourceQuantities,
|
|
7
7
|
useCartQuote
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LW2LLAL4.js";
|
|
9
9
|
import {
|
|
10
10
|
createStoreClient
|
|
11
11
|
} from "./chunk-7BUDUYXN.js";
|
|
@@ -87,7 +87,12 @@ function CartDrawer({
|
|
|
87
87
|
window.addEventListener("keydown", onKeyDown);
|
|
88
88
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
89
89
|
}, [open, onClose]);
|
|
90
|
-
const { quote, quoteSourceCart, failed } = useCartQuote(
|
|
90
|
+
const { quote, quoteSourceCart, failed, pending } = useCartQuote(
|
|
91
|
+
client,
|
|
92
|
+
cart,
|
|
93
|
+
open
|
|
94
|
+
);
|
|
95
|
+
const liveQuantities = React.useMemo(() => quoteSourceQuantities(cart), [cart]);
|
|
91
96
|
async function runMutation(mutation) {
|
|
92
97
|
const result = await mutation;
|
|
93
98
|
setMutationUnavailable(result.status === "cart_storage_unavailable");
|
|
@@ -144,6 +149,8 @@ function CartDrawer({
|
|
|
144
149
|
{
|
|
145
150
|
lines: quote.lines,
|
|
146
151
|
sourceQuantities: quoteSourceQuantities(quoteSourceCart),
|
|
152
|
+
liveQuantities,
|
|
153
|
+
pending,
|
|
147
154
|
onMutate: (m) => void runMutation(m)
|
|
148
155
|
}
|
|
149
156
|
),
|
|
@@ -11062,6 +11062,23 @@ function PortalConsultPanel({
|
|
|
11062
11062
|
] });
|
|
11063
11063
|
}
|
|
11064
11064
|
|
|
11065
|
+
// src/portal-account-cache.ts
|
|
11066
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
11067
|
+
function rememberPortalAccount(surface, state) {
|
|
11068
|
+
if (state.kind === "signed-in") {
|
|
11069
|
+
snapshots.set(surface, state);
|
|
11070
|
+
return;
|
|
11071
|
+
}
|
|
11072
|
+
if (state.kind === "loading" || state.kind === "link-sent") return;
|
|
11073
|
+
clearPortalAccountCache();
|
|
11074
|
+
}
|
|
11075
|
+
function recallPortalAccount(surface) {
|
|
11076
|
+
return snapshots.get(surface) ?? null;
|
|
11077
|
+
}
|
|
11078
|
+
function clearPortalAccountCache() {
|
|
11079
|
+
snapshots.clear();
|
|
11080
|
+
}
|
|
11081
|
+
|
|
11065
11082
|
// src/portal-account.client.tsx
|
|
11066
11083
|
import { Fragment as Fragment11, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
11067
11084
|
var APPOINTMENTS_URL = "/portal/api/appointments";
|
|
@@ -11220,21 +11237,36 @@ function PortalAccountSurfaceClient({
|
|
|
11220
11237
|
const portalClient = usePortalClient();
|
|
11221
11238
|
const fetchImpl = React21.useMemo(() => portalFetchForClient(portalClient), [portalClient]);
|
|
11222
11239
|
const [state, setState] = React21.useState(
|
|
11223
|
-
//
|
|
11240
|
+
// STALE-WHILE-REVALIDATE (PAT-949). Three ways to start, in falling order of how
|
|
11241
|
+
// much we already know:
|
|
11242
|
+
// 1. the caller drove it (tests, a future SSR seed);
|
|
11243
|
+
// 2. this document already resolved this surface — render that body NOW and
|
|
11244
|
+
// revalidate behind it, so returning to a surface is not a redraw. In-memory
|
|
11245
|
+
// only; see `./portal-account-cache` for the PHI rules;
|
|
11246
|
+
// 3. genuinely cold — a skeleton, shaped like the rows about to land.
|
|
11247
|
+
// `readPortalSignedInHint` stays in the initialiser (not an effect) on purpose: the
|
|
11224
11248
|
// whole point is that the FIRST paint already knows, so there is no placeholder
|
|
11225
11249
|
// swap. It reads one boolean out of sessionStorage — no PHI, no network.
|
|
11226
|
-
() => initialState ?? { kind: "loading", hinted: readPortalSignedInHint() }
|
|
11250
|
+
() => initialState ?? recallPortalAccount(surface) ?? { kind: "loading", hinted: readPortalSignedInHint() }
|
|
11227
11251
|
);
|
|
11228
11252
|
const [signingOut, setSigningOut] = React21.useState(false);
|
|
11229
|
-
const
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11253
|
+
const [revalidating, setRevalidating] = React21.useState(false);
|
|
11254
|
+
const commit = React21.useCallback(
|
|
11255
|
+
(next) => {
|
|
11256
|
+
writePortalSignedInHint(next.kind === "signed-in" || next.kind === "not-available");
|
|
11257
|
+
rememberPortalAccount(surface, next);
|
|
11258
|
+
setState(next);
|
|
11259
|
+
},
|
|
11260
|
+
[surface]
|
|
11261
|
+
);
|
|
11233
11262
|
React21.useEffect(() => {
|
|
11234
11263
|
if (initialState) return;
|
|
11235
11264
|
let live = true;
|
|
11265
|
+
setRevalidating(true);
|
|
11236
11266
|
void loadPortalAccount(surface, fetchImpl).then((next) => {
|
|
11237
|
-
if (live)
|
|
11267
|
+
if (!live) return;
|
|
11268
|
+
setRevalidating(false);
|
|
11269
|
+
commit(next);
|
|
11238
11270
|
});
|
|
11239
11271
|
return () => {
|
|
11240
11272
|
live = false;
|
|
@@ -11246,6 +11278,7 @@ function PortalAccountSurfaceClient({
|
|
|
11246
11278
|
async function onSignOut() {
|
|
11247
11279
|
if (signingOut) return;
|
|
11248
11280
|
setSigningOut(true);
|
|
11281
|
+
clearPortalAccountCache();
|
|
11249
11282
|
await signOutOfPortal(fetchImpl);
|
|
11250
11283
|
const next = await loadPortalAccount(surface, fetchImpl);
|
|
11251
11284
|
setSigningOut(false);
|
|
@@ -11253,91 +11286,119 @@ function PortalAccountSurfaceClient({
|
|
|
11253
11286
|
}
|
|
11254
11287
|
function replaceData(data) {
|
|
11255
11288
|
const refetched = refetchedGivenName(data);
|
|
11256
|
-
setState(
|
|
11257
|
-
(prev
|
|
11258
|
-
|
|
11289
|
+
setState((prev) => {
|
|
11290
|
+
if (prev.kind !== "signed-in") return prev;
|
|
11291
|
+
const next = {
|
|
11292
|
+
...prev,
|
|
11293
|
+
data,
|
|
11294
|
+
givenName: refetched === void 0 ? prev.givenName : refetched
|
|
11295
|
+
};
|
|
11296
|
+
rememberPortalAccount(surface, next);
|
|
11297
|
+
return next;
|
|
11298
|
+
});
|
|
11259
11299
|
}
|
|
11260
|
-
return /* @__PURE__ */ jsxs23(
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11300
|
+
return /* @__PURE__ */ jsxs23(
|
|
11301
|
+
"section",
|
|
11302
|
+
{
|
|
11303
|
+
className: "sk-portal-account__body",
|
|
11304
|
+
"aria-busy": state.kind === "loading" || revalidating,
|
|
11305
|
+
children: [
|
|
11306
|
+
/* @__PURE__ */ jsx26("h2", { className: "sk-portal-account__heading", children: heading2 }),
|
|
11307
|
+
state.kind === "loading" && !state.hinted ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__placeholder", role: "status", children: "Checking your sign-in\u2026" }) : null,
|
|
11308
|
+
state.kind === "loading" && state.hinted ? /* @__PURE__ */ jsx26(PortalAccountSkeleton, { surface }) : null,
|
|
11309
|
+
state.kind === "link-out" ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__placeholder", children: /* @__PURE__ */ jsx26("a", { className: "sk-portal-link", href: base2, children: "Sign in to your patient portal" }) }) : null,
|
|
11310
|
+
state.kind === "signed-out" ? /* @__PURE__ */ jsx26(
|
|
11311
|
+
PortalSignInRamp,
|
|
11312
|
+
{
|
|
11313
|
+
idSuffix: `account-${surface}`,
|
|
11314
|
+
submit: (email) => portalClient.requestMagicLink(email).then((ok) => ({ ok })),
|
|
11315
|
+
onResult: ({ ok, email }) => setState(ok ? { kind: "link-sent", email } : { kind: "link-out" })
|
|
11316
|
+
}
|
|
11317
|
+
) : null,
|
|
11318
|
+
state.kind === "link-sent" ? /* @__PURE__ */ jsx26(PortalLinkSentNotice, { email: state.email }) : null,
|
|
11319
|
+
state.kind === "not-available" || state.kind === "signed-in" ? /* @__PURE__ */ jsxs23("div", { className: "sk-portal-account__header", children: [
|
|
11320
|
+
/* @__PURE__ */ jsxs23("span", { className: "sk-portal-account__who", children: [
|
|
11321
|
+
"Signed in as ",
|
|
11322
|
+
state.givenName ?? "you"
|
|
11323
|
+
] }),
|
|
11324
|
+
/* @__PURE__ */ jsx26(
|
|
11325
|
+
"button",
|
|
11326
|
+
{
|
|
11327
|
+
type: "button",
|
|
11328
|
+
className: "sk-portal-button sk-portal-button--ghost",
|
|
11329
|
+
onClick: () => void onSignOut(),
|
|
11330
|
+
disabled: signingOut,
|
|
11331
|
+
children: signingOut ? "Signing out\u2026" : "Sign out"
|
|
11332
|
+
}
|
|
11333
|
+
)
|
|
11334
|
+
] }) : null,
|
|
11335
|
+
state.kind === "not-available" ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__note", role: "note", children: "This isn't available online at this clinic." }) : null,
|
|
11336
|
+
state.kind === "signed-in" && surface === "appointments" ? /* @__PURE__ */ jsx26(
|
|
11337
|
+
PortalAccountAppointments,
|
|
11338
|
+
{
|
|
11339
|
+
data: state.data,
|
|
11340
|
+
base: base2,
|
|
11341
|
+
bookHref: book,
|
|
11342
|
+
fetchImpl,
|
|
11343
|
+
onReloaded: replaceData
|
|
11344
|
+
}
|
|
11345
|
+
) : null,
|
|
11346
|
+
state.kind === "signed-in" && surface === "addresses" ? /* @__PURE__ */ jsx26(
|
|
11347
|
+
PortalAccountAddresses,
|
|
11348
|
+
{
|
|
11349
|
+
data: state.data,
|
|
11350
|
+
client: portalClient,
|
|
11351
|
+
fetchImpl,
|
|
11352
|
+
onReloaded: replaceData
|
|
11353
|
+
}
|
|
11354
|
+
) : null,
|
|
11355
|
+
state.kind === "signed-in" && surface === "documents" ? /* @__PURE__ */ jsx26(
|
|
11356
|
+
PortalAccountDocuments,
|
|
11357
|
+
{
|
|
11358
|
+
data: state.data,
|
|
11359
|
+
fetchImpl,
|
|
11360
|
+
openUrl
|
|
11361
|
+
}
|
|
11362
|
+
) : null,
|
|
11363
|
+
state.kind === "signed-in" && surface === "orders" ? /* @__PURE__ */ jsx26(
|
|
11364
|
+
PortalAccountOrders,
|
|
11365
|
+
{
|
|
11366
|
+
data: state.data,
|
|
11367
|
+
fetchImpl,
|
|
11368
|
+
openUrl
|
|
11369
|
+
}
|
|
11370
|
+
) : null,
|
|
11371
|
+
state.kind === "signed-in" && surface === "profile" ? /* @__PURE__ */ jsx26(
|
|
11372
|
+
PortalAccountProfile,
|
|
11373
|
+
{
|
|
11374
|
+
data: state.data,
|
|
11375
|
+
fetchImpl,
|
|
11376
|
+
onSaved: replaceData
|
|
11377
|
+
}
|
|
11378
|
+
) : null
|
|
11379
|
+
]
|
|
11380
|
+
}
|
|
11381
|
+
);
|
|
11335
11382
|
}
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11383
|
+
var SKELETON_ROWS = 3;
|
|
11384
|
+
function PortalAccountSkeleton({
|
|
11385
|
+
surface
|
|
11386
|
+
}) {
|
|
11387
|
+
const grouped = surface === "appointments";
|
|
11388
|
+
return /* @__PURE__ */ jsxs23(Fragment11, { children: [
|
|
11389
|
+
/* @__PURE__ */ jsx26("p", { className: "sk-portal-account__placeholder sk-portal-account__placeholder--sr", role: "status", children: "Loading your account\u2026" }),
|
|
11390
|
+
/* @__PURE__ */ jsxs23("div", { className: "sk-portal-account__skeleton", "aria-hidden": "true", children: [
|
|
11391
|
+
/* @__PURE__ */ jsxs23("div", { className: "sk-portal-account__header sk-portal-account__header--skeleton", children: [
|
|
11392
|
+
/* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--who" }),
|
|
11393
|
+
/* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--button" })
|
|
11394
|
+
] }),
|
|
11395
|
+
grouped ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__group-title sk-portal-account__group-title--skeleton", children: /* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--group" }) }) : null,
|
|
11396
|
+
/* @__PURE__ */ jsx26("ul", { className: "sk-portal-account__list", children: Array.from({ length: SKELETON_ROWS }, (_, index) => /* @__PURE__ */ jsxs23("li", { className: "sk-portal-account__item sk-portal-account__item--skeleton", children: [
|
|
11397
|
+
/* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--title" }),
|
|
11398
|
+
/* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--meta" }),
|
|
11399
|
+
grouped ? /* @__PURE__ */ jsx26("span", { className: "sk-portal-account__skeleton-line sk-portal-account__skeleton-line--meta" }) : null
|
|
11400
|
+
] }, index)) })
|
|
11401
|
+
] })
|
|
11341
11402
|
] });
|
|
11342
11403
|
}
|
|
11343
11404
|
function StatusBadge({ status }) {
|
|
@@ -11598,8 +11659,11 @@ function PortalAccountOrders({
|
|
|
11598
11659
|
initialUi
|
|
11599
11660
|
}) {
|
|
11600
11661
|
const [expandedId, setExpandedId] = React21.useState(initialUi?.expandedId ?? null);
|
|
11601
|
-
const [
|
|
11602
|
-
|
|
11662
|
+
const [details, setDetails] = React21.useState(
|
|
11663
|
+
() => initialUi?.detail && initialUi.expandedId ? { [initialUi.expandedId]: initialUi.detail } : {}
|
|
11664
|
+
);
|
|
11665
|
+
const [loadingId, setLoadingId] = React21.useState(null);
|
|
11666
|
+
const [failedIds, setFailedIds] = React21.useState([]);
|
|
11603
11667
|
const [error2, setError] = React21.useState(initialUi?.error ?? null);
|
|
11604
11668
|
async function toggle(id) {
|
|
11605
11669
|
if (expandedId === id) {
|
|
@@ -11607,12 +11671,13 @@ function PortalAccountOrders({
|
|
|
11607
11671
|
return;
|
|
11608
11672
|
}
|
|
11609
11673
|
setExpandedId(id);
|
|
11610
|
-
setDetail(null);
|
|
11611
11674
|
setError(null);
|
|
11612
|
-
|
|
11675
|
+
if (details[id]) return;
|
|
11676
|
+
setLoadingId(id);
|
|
11613
11677
|
const next = await loadPortalOrderDetail(id, fetchImpl);
|
|
11614
|
-
|
|
11615
|
-
|
|
11678
|
+
setLoadingId((current) => current === id ? null : current);
|
|
11679
|
+
if (next) setDetails((prev) => ({ ...prev, [id]: next }));
|
|
11680
|
+
else setFailedIds((prev) => prev.includes(id) ? prev : [...prev, id]);
|
|
11616
11681
|
}
|
|
11617
11682
|
async function receipt(id) {
|
|
11618
11683
|
setError(null);
|
|
@@ -11626,6 +11691,9 @@ function PortalAccountOrders({
|
|
|
11626
11691
|
/* @__PURE__ */ jsx26(ErrorNote, { message: error2 }),
|
|
11627
11692
|
/* @__PURE__ */ jsx26("ul", { className: "sk-portal-account__list", children: data.items.map((order) => {
|
|
11628
11693
|
const open = expandedId === order.id;
|
|
11694
|
+
const detail = details[order.id] ?? null;
|
|
11695
|
+
const loading = loadingId === order.id && !detail;
|
|
11696
|
+
const failed = !detail && failedIds.includes(order.id);
|
|
11629
11697
|
return /* @__PURE__ */ jsxs23("li", { className: "sk-portal-account__item", children: [
|
|
11630
11698
|
/* @__PURE__ */ jsxs23(
|
|
11631
11699
|
"button",
|
|
@@ -11645,7 +11713,7 @@ function PortalAccountOrders({
|
|
|
11645
11713
|
]
|
|
11646
11714
|
}
|
|
11647
11715
|
),
|
|
11648
|
-
open ? /* @__PURE__ */ jsx26("div", { className: "sk-portal-account__detail", children: loading ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__placeholder", role: "status", children: "Loading this order\u2026" }) : !detail ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__note", role: "note", children: "We couldn't load this order just now." }) : /* @__PURE__ */ jsxs23(Fragment11, { children: [
|
|
11716
|
+
open ? /* @__PURE__ */ jsx26("div", { className: "sk-portal-account__detail", "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__placeholder", role: "status", children: "Loading this order\u2026" }) : failed || !detail ? /* @__PURE__ */ jsx26("p", { className: "sk-portal-account__note", role: "note", children: "We couldn't load this order just now." }) : /* @__PURE__ */ jsxs23(Fragment11, { children: [
|
|
11649
11717
|
/* @__PURE__ */ jsx26("ul", { className: "sk-portal-account__lines", children: (detail.lines ?? []).map((line) => /* @__PURE__ */ jsxs23("li", { className: "sk-portal-account__line", children: [
|
|
11650
11718
|
/* @__PURE__ */ jsxs23("span", { children: [
|
|
11651
11719
|
line.title ?? "\u2014",
|
package/dist/index.d.ts
CHANGED
|
@@ -217,6 +217,6 @@ interface PortalSession {
|
|
|
217
217
|
*/
|
|
218
218
|
declare function usePortalSession(): PortalSession;
|
|
219
219
|
|
|
220
|
-
declare const WEBSITE_KIT_VERSION = "0.2.
|
|
220
|
+
declare const WEBSITE_KIT_VERSION = "0.2.10";
|
|
221
221
|
|
|
222
222
|
export { Button, type ButtonProps, type ButtonVariant, Container, type ContainerProps, FAQ, type FAQProps, type FaqItem, Hero, type HeroProps, Hours, type HoursProps, type HoursRow, MapEmbed, type MapEmbedProps, PortalClient, PortalPrefill, PortalProvider, type PortalProviderProps, type PortalSession, type PortalSessionStatus, PublicClaim, Row, type RowProps, Section, type SectionProps, type Service, ServiceGrid, type ServiceGridProps, Stack, type StackProps, TeamGrid, type TeamGridProps, type TeamMember, ThemeProvider, type ThemeProviderProps, WEBSITE_KIT_VERSION, WebsiteShell, type WebsiteShellProps, type WebsiteTheme, WhoAmI, defaultTheme, resolveTheme, themeToCssVars, usePortalClient, usePortalSession };
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
ISLANDS,
|
|
13
13
|
mountIslands
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FW2X2SUZ.js";
|
|
15
15
|
import {
|
|
16
16
|
PortalAccount,
|
|
17
17
|
PortalPanel,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
getThemeTokens,
|
|
24
24
|
usePortalClient,
|
|
25
25
|
usePortalSession
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-SYXA5WP7.js";
|
|
27
27
|
import "./chunk-3T7UWKAH.js";
|
|
28
28
|
import {
|
|
29
29
|
evaluateShowWhen,
|
|
@@ -36,9 +36,9 @@ import {
|
|
|
36
36
|
mountIslandsWith,
|
|
37
37
|
readIslandProps
|
|
38
38
|
} from "./chunk-THMT43MV.js";
|
|
39
|
-
import "./chunk-
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
39
|
+
import "./chunk-55W3KKJD.js";
|
|
40
|
+
import "./chunk-Q5DIBKWX.js";
|
|
41
|
+
import "./chunk-LW2LLAL4.js";
|
|
42
42
|
import "./chunk-S3ZQQOQT.js";
|
|
43
43
|
import {
|
|
44
44
|
BPOINT_SCRIPT_ORIGIN,
|
|
@@ -511,7 +511,7 @@ function Checkout({
|
|
|
511
511
|
}
|
|
512
512
|
|
|
513
513
|
// src/index.ts
|
|
514
|
-
var WEBSITE_KIT_VERSION = "0.2.
|
|
514
|
+
var WEBSITE_KIT_VERSION = "0.2.10";
|
|
515
515
|
export {
|
|
516
516
|
BPOINT_SCRIPT_ORIGIN,
|
|
517
517
|
BPOINT_SCRIPT_URL,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CartButtonClient,
|
|
3
3
|
CartDrawer
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-Q5DIBKWX.js";
|
|
5
|
+
import "../chunk-LW2LLAL4.js";
|
|
6
6
|
import "../chunk-7BUDUYXN.js";
|
|
7
7
|
import "../chunk-4NF7SSIX.js";
|
|
8
8
|
import "../chunk-FZ4WKWIE.js";
|
package/dist/islands-impl.js
CHANGED
|
@@ -3,17 +3,17 @@ import {
|
|
|
3
3
|
CertificateFunnelClient,
|
|
4
4
|
PortalAccountClient,
|
|
5
5
|
PortalPanelClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SYXA5WP7.js";
|
|
7
7
|
import "./chunk-3T7UWKAH.js";
|
|
8
8
|
import "./chunk-QDO3SJWR.js";
|
|
9
9
|
import "./chunk-THMT43MV.js";
|
|
10
10
|
import {
|
|
11
11
|
CartClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-55W3KKJD.js";
|
|
13
13
|
import {
|
|
14
14
|
CartButtonClient
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-Q5DIBKWX.js";
|
|
16
|
+
import "./chunk-LW2LLAL4.js";
|
|
17
17
|
import {
|
|
18
18
|
CheckoutClient
|
|
19
19
|
} from "./chunk-S3ZQQOQT.js";
|
package/dist/islands-registry.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ISLANDS,
|
|
3
3
|
mountIslands
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-FW2X2SUZ.js";
|
|
5
|
+
import "./chunk-SYXA5WP7.js";
|
|
6
6
|
import "./chunk-3T7UWKAH.js";
|
|
7
7
|
import "./chunk-QDO3SJWR.js";
|
|
8
8
|
import "./chunk-THMT43MV.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-55W3KKJD.js";
|
|
10
|
+
import "./chunk-Q5DIBKWX.js";
|
|
11
|
+
import "./chunk-LW2LLAL4.js";
|
|
12
12
|
import "./chunk-S3ZQQOQT.js";
|
|
13
13
|
import "./chunk-ZOF22TJA.js";
|
|
14
14
|
import "./chunk-UTAMB2SK.js";
|
package/dist/website-kit.css
CHANGED
|
@@ -2414,16 +2414,78 @@ select.sk-identity__input[data-empty] {
|
|
|
2414
2414
|
* to earn its keep, and it buys nothing here — this is on screen for a few hundred ms.
|
|
2415
2415
|
* This is NOT the no-JS fallback below; that one is real content and must stay real. */
|
|
2416
2416
|
.sk-portal-account__skeleton {
|
|
2417
|
-
display:
|
|
2418
|
-
|
|
2419
|
-
gap: var(--sk-space, 0.5rem);
|
|
2420
|
-
margin-bottom: calc(var(--sk-space, 0.5rem) * 2);
|
|
2417
|
+
display: block;
|
|
2418
|
+
margin-bottom: 0;
|
|
2421
2419
|
}
|
|
2422
2420
|
|
|
2423
|
-
|
|
2424
|
-
|
|
2421
|
+
/* PAT-949 — the skeleton borrows `__header` and `__item` for its boxes (see the
|
|
2422
|
+
* component), so the only thing defined here is the INK inside them and the two
|
|
2423
|
+
* differences a placeholder needs: it cannot be clicked, and its header carries no
|
|
2424
|
+
* text to give it height, so it gets the height back explicitly. */
|
|
2425
|
+
.sk-portal-account__header--skeleton {
|
|
2426
|
+
min-height: 2.25rem;
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
.sk-portal-account__item--skeleton {
|
|
2430
|
+
pointer-events: none;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
.sk-portal-account__skeleton-line {
|
|
2434
|
+
display: block;
|
|
2435
|
+
height: 0.875rem;
|
|
2425
2436
|
border-radius: var(--sk-radius, 0.625rem);
|
|
2426
|
-
background: color-mix(in srgb, var(--sk-neutral-foreground, #1b2733)
|
|
2437
|
+
background: color-mix(in srgb, var(--sk-neutral-foreground, #1b2733) 12%, transparent);
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
/* Widths chosen to sit under the real strings they stand in for: "Signed in as Jane",
|
|
2441
|
+
* a "Sign out" ghost button, an item title and its date · clinician · amount line. */
|
|
2442
|
+
.sk-portal-account__skeleton-line--who {
|
|
2443
|
+
width: 10rem;
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
.sk-portal-account__skeleton-line--button {
|
|
2447
|
+
width: 5.5rem;
|
|
2448
|
+
height: 2rem;
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
.sk-portal-account__skeleton-line--title {
|
|
2452
|
+
width: 60%;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
.sk-portal-account__skeleton-line--meta {
|
|
2456
|
+
width: 40%;
|
|
2457
|
+
height: 0.75rem;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/* Stands in for a group heading ("UPCOMING"), so the list does not slide up under one
|
|
2461
|
+
* when the real body lands. */
|
|
2462
|
+
.sk-portal-account__group-title--skeleton {
|
|
2463
|
+
display: block;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
.sk-portal-account__skeleton-line--group {
|
|
2467
|
+
width: 6rem;
|
|
2468
|
+
height: 0.75rem;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2472
|
+
.sk-portal-account__skeleton-line {
|
|
2473
|
+
animation: sk-skeleton-pulse 1.4s ease-in-out infinite;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
/* The screen-reader-only status line the skeleton announces itself with — the bars
|
|
2478
|
+
* themselves are `aria-hidden`. Same recipe as the store block's (PAT-944). */
|
|
2479
|
+
.sk-portal-account__placeholder--sr {
|
|
2480
|
+
position: absolute;
|
|
2481
|
+
width: 1px;
|
|
2482
|
+
height: 1px;
|
|
2483
|
+
padding: 0;
|
|
2484
|
+
margin: -1px;
|
|
2485
|
+
overflow: hidden;
|
|
2486
|
+
clip-path: inset(50%);
|
|
2487
|
+
white-space: nowrap;
|
|
2488
|
+
border: 0;
|
|
2427
2489
|
}
|
|
2428
2490
|
|
|
2429
2491
|
/* The build-time no-JS fallback: REAL visible content, never a hidden skeleton. */
|
|
@@ -2887,23 +2949,75 @@ select.sk-identity__input[data-empty] {
|
|
|
2887
2949
|
border-radius: var(--sk-radius, 0.625rem);
|
|
2888
2950
|
font-size: 0.9375rem;
|
|
2889
2951
|
}
|
|
2952
|
+
.sk-cart__lines[aria-busy='true'] {
|
|
2953
|
+
/* A re-quote is in flight over prices that are still correct enough to read. Dim the
|
|
2954
|
+
money a touch rather than tearing the list down — the teardown is what read as the
|
|
2955
|
+
cart reloading (PAT-949). Steppers stay live: the cart write already succeeded. */
|
|
2956
|
+
opacity: 0.7;
|
|
2957
|
+
transition: opacity 120ms ease-out;
|
|
2958
|
+
}
|
|
2959
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2960
|
+
.sk-cart__lines[aria-busy='true'] {
|
|
2961
|
+
transition: none;
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2890
2964
|
.sk-cart__lines {
|
|
2891
2965
|
list-style: none;
|
|
2892
2966
|
margin: 0;
|
|
2893
2967
|
padding: 0;
|
|
2968
|
+
/* The drawer is ~380px inside a wide viewport, so a VIEWPORT media query can never
|
|
2969
|
+
tell us the row is cramped (PAT-948). Size the row off its own container instead. */
|
|
2970
|
+
container-type: inline-size;
|
|
2971
|
+
container-name: sk-cart-lines;
|
|
2894
2972
|
}
|
|
2895
2973
|
.sk-cart__line {
|
|
2896
2974
|
display: grid;
|
|
2897
|
-
|
|
2975
|
+
/* Five children, so name five tracks. They used to run in a FOUR-column grid, which
|
|
2976
|
+
wrapped Remove onto its own row where it stretched the full 1fr title column and
|
|
2977
|
+
centred its own label — the reason it floated mid-row (PAT-948). */
|
|
2978
|
+
grid-template-columns: minmax(0, 1fr) auto auto auto auto;
|
|
2979
|
+
grid-template-areas: 'title unit qty total remove';
|
|
2898
2980
|
align-items: center;
|
|
2899
2981
|
gap: calc(var(--sk-space, 0.5rem) * 2);
|
|
2900
2982
|
padding: calc(var(--sk-space, 0.5rem) * 2) 0;
|
|
2901
2983
|
border-bottom: 1px solid color-mix(in srgb, var(--sk-muted, #5b6b7a) 30%, transparent);
|
|
2902
2984
|
}
|
|
2985
|
+
.sk-cart__line-title {
|
|
2986
|
+
grid-area: title;
|
|
2987
|
+
}
|
|
2988
|
+
.sk-cart__line-unit {
|
|
2989
|
+
grid-area: unit;
|
|
2990
|
+
}
|
|
2991
|
+
.sk-cart__qty {
|
|
2992
|
+
grid-area: qty;
|
|
2993
|
+
}
|
|
2994
|
+
.sk-cart__line-total {
|
|
2995
|
+
grid-area: total;
|
|
2996
|
+
}
|
|
2997
|
+
.sk-cart__remove {
|
|
2998
|
+
grid-area: remove;
|
|
2999
|
+
}
|
|
3000
|
+
/* Narrow CONTAINER (the drawer, and a phone): two columns, three rows. */
|
|
3001
|
+
@container sk-cart-lines (max-width: 34rem) {
|
|
3002
|
+
.sk-cart__line {
|
|
3003
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
3004
|
+
grid-template-areas:
|
|
3005
|
+
'title title'
|
|
3006
|
+
'unit total'
|
|
3007
|
+
'qty remove';
|
|
3008
|
+
row-gap: calc(var(--sk-space, 0.5rem) * 1);
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
2903
3011
|
.sk-cart__line-title {
|
|
2904
3012
|
color: inherit;
|
|
2905
3013
|
font-weight: 600;
|
|
2906
3014
|
text-decoration: none;
|
|
3015
|
+
/* One line, clipped — a product name is not worth reflowing the row it sits in.
|
|
3016
|
+
min-width:0 is what lets the 1fr track actually shrink inside a grid. */
|
|
3017
|
+
min-width: 0;
|
|
3018
|
+
overflow: hidden;
|
|
3019
|
+
text-overflow: ellipsis;
|
|
3020
|
+
white-space: nowrap;
|
|
2907
3021
|
}
|
|
2908
3022
|
.sk-cart__line-unit {
|
|
2909
3023
|
font-size: 0.875rem;
|
|
@@ -2913,6 +3027,14 @@ select.sk-identity__input[data-empty] {
|
|
|
2913
3027
|
display: inline-flex;
|
|
2914
3028
|
align-items: center;
|
|
2915
3029
|
gap: calc(var(--sk-space, 0.5rem) * 1);
|
|
3030
|
+
justify-self: start;
|
|
3031
|
+
}
|
|
3032
|
+
/* The quantity reserves room for two digits and uses tabular figures, so stepping
|
|
3033
|
+
1 -> 2 -> 10 never shifts the − and + buttons under the shopper's cursor. */
|
|
3034
|
+
.sk-cart__qty-value {
|
|
3035
|
+
min-width: 2ch;
|
|
3036
|
+
text-align: center;
|
|
3037
|
+
font-variant-numeric: tabular-nums;
|
|
2916
3038
|
}
|
|
2917
3039
|
.sk-cart__qty button {
|
|
2918
3040
|
min-width: 2rem;
|
|
@@ -2928,6 +3050,10 @@ select.sk-identity__input[data-empty] {
|
|
|
2928
3050
|
.sk-cart__line-total {
|
|
2929
3051
|
font-variant-numeric: tabular-nums;
|
|
2930
3052
|
font-weight: 600;
|
|
3053
|
+
/* Reserve the widest realistic total so $70.00 and $599.00 hold the same column
|
|
3054
|
+
and the stepper beside them does not slide between lines. */
|
|
3055
|
+
min-width: 6ch;
|
|
3056
|
+
text-align: right;
|
|
2931
3057
|
}
|
|
2932
3058
|
.sk-cart__remove {
|
|
2933
3059
|
border: 0;
|
|
@@ -2938,6 +3064,11 @@ select.sk-identity__input[data-empty] {
|
|
|
2938
3064
|
opacity: 0.75;
|
|
2939
3065
|
cursor: pointer;
|
|
2940
3066
|
text-decoration: underline;
|
|
3067
|
+
/* A grid item stretches by default, so this button used to fill its track and
|
|
3068
|
+
centre its own label. Shrink it to its text and pin it to the row's end. */
|
|
3069
|
+
justify-self: end;
|
|
3070
|
+
padding: 0;
|
|
3071
|
+
width: max-content;
|
|
2941
3072
|
}
|
|
2942
3073
|
.sk-cart__totals {
|
|
2943
3074
|
margin: calc(var(--sk-space, 0.5rem) * 3) 0 0;
|
|
@@ -2993,11 +3124,8 @@ select.sk-identity__input[data-empty] {
|
|
|
2993
3124
|
font-weight: 600;
|
|
2994
3125
|
text-decoration: none;
|
|
2995
3126
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
grid-template-columns: 1fr auto;
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3127
|
+
/* The old viewport `max-width: 600px` rule lived here. It could not see the drawer,
|
|
3128
|
+
which is ~380px inside a wide window; the @container rule above replaces it. */
|
|
3001
3129
|
|
|
3002
3130
|
|
|
3003
3131
|
/* ── checkout (PAT-766) ──────────────────────────────────────────────────── */
|