@businessdash/sdk 0.9.60 → 0.9.80
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/README.md +991 -295
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +622 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +888 -1
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +107 -0
- package/dist/consent-core/index.d.ts +103 -0
- package/dist/consent-core/index.d.ts.map +1 -0
- package/dist/consent-core/index.js +157 -0
- package/dist/consent-core/index.js.map +1 -0
- package/dist/contracts.d.ts +3526 -767
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +571 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1355 -24
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4516 -1311
- package/package.json +128 -11
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +1097 -0
- package/src/consent-core/index.ts +213 -0
- package/src/contracts.ts +768 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- package/src/vue/index.ts +3 -0
package/src/react.tsx
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every export in this file is browser-only. The components hold state and
|
|
5
|
+
* subscribe to stores; the four programmatic helpers (`signIn`, `signUp`,
|
|
6
|
+
* `signOut`, `requestPasswordReset`) each bail out on
|
|
7
|
+
* `typeof window === "undefined"` because they navigate. Nothing here is meant
|
|
8
|
+
* to execute during a server render.
|
|
9
|
+
*
|
|
10
|
+
* WHY THE DIRECTIVE IS HERE
|
|
11
|
+
*
|
|
12
|
+
* Without it, a React Server Components framework treats this module as server
|
|
13
|
+
* code by default, and importing `<BiabForm>` into a page throws "you're
|
|
14
|
+
* importing a component that needs useState". The only way out is for every
|
|
15
|
+
* consumer to hand-write a `"use client"` wrapper per component — which is
|
|
16
|
+
* exactly what the Next App Router starter had to do, sixteen times, for
|
|
17
|
+
* components the SDK could have marked itself.
|
|
18
|
+
*
|
|
19
|
+
* `react-analytics.tsx` already carried this. `react.tsx` — the entry point
|
|
20
|
+
* that actually holds the components — did not.
|
|
21
|
+
*
|
|
22
|
+
* Bundlers that don't implement the directive treat it as a no-op string
|
|
23
|
+
* expression, so this is inert everywhere except RSC.
|
|
24
|
+
*/
|
|
25
|
+
|
|
1
26
|
import {
|
|
2
27
|
type CSSProperties,
|
|
3
28
|
createContext,
|
|
@@ -11,7 +36,22 @@ import {
|
|
|
11
36
|
useState,
|
|
12
37
|
useSyncExternalStore,
|
|
13
38
|
} from "react";
|
|
14
|
-
|
|
39
|
+
import {
|
|
40
|
+
createFollowersController,
|
|
41
|
+
createUserController,
|
|
42
|
+
} from "./account-core/index.js";
|
|
43
|
+
import {
|
|
44
|
+
buildAuthHref,
|
|
45
|
+
clearLocalFollow,
|
|
46
|
+
DEFAULT_AUTH_BASE_PATH,
|
|
47
|
+
followerStoreKey,
|
|
48
|
+
type LocalFollow,
|
|
49
|
+
readLocalFollow,
|
|
50
|
+
writeLocalFollow,
|
|
51
|
+
} from "./auth-links.js";
|
|
52
|
+
import { createBlogController } from "./blog-core/index.js";
|
|
53
|
+
import { createChatController } from "./chat-core/index.js";
|
|
54
|
+
import { createCheckoutController } from "./checkout-core/index.js";
|
|
15
55
|
import { BiabDevClient } from "./client.js";
|
|
16
56
|
import type {
|
|
17
57
|
BlogSessionInput,
|
|
@@ -37,6 +77,14 @@ import type {
|
|
|
37
77
|
StorefrontSessionInput,
|
|
38
78
|
StorefrontSessionResponse,
|
|
39
79
|
} from "./contracts.js";
|
|
80
|
+
import {
|
|
81
|
+
type BiabEmbedEvent,
|
|
82
|
+
DEFAULT_EMBED_SANDBOX,
|
|
83
|
+
DEFAULT_REFRESH_LEAD_MS,
|
|
84
|
+
type EmbedSessionState,
|
|
85
|
+
refreshDelayMs,
|
|
86
|
+
subscribeToEmbedMessages,
|
|
87
|
+
} from "./embed-protocol.js";
|
|
40
88
|
import {
|
|
41
89
|
type AutocompleteConfig,
|
|
42
90
|
type BiabFormsClient,
|
|
@@ -69,17 +117,25 @@ import {
|
|
|
69
117
|
unitsForCategory,
|
|
70
118
|
} from "./forms-core/units.js";
|
|
71
119
|
import { US_STATES } from "./forms-core/us-states.js";
|
|
120
|
+
import { createMarketingController } from "./marketing-core/index.js";
|
|
121
|
+
import { createPortalController } from "./portal-core/index.js";
|
|
72
122
|
import type { FormFileValue, FormSchema, FormSubmitResult } from "./sdk.js";
|
|
73
123
|
import { createBiabClient } from "./sdk.js";
|
|
74
|
-
import {
|
|
124
|
+
import {
|
|
125
|
+
resolveSocialLinks,
|
|
126
|
+
SOCIAL_FALLBACK_GLYPH,
|
|
127
|
+
SOCIAL_LIST_STYLE,
|
|
128
|
+
} from "./social-core/index.js";
|
|
129
|
+
import { createStoreController } from "./store-core/index.js";
|
|
130
|
+
import {
|
|
131
|
+
type CreateVerifyControllerOptions,
|
|
132
|
+
createVerifyController,
|
|
133
|
+
} from "./verify-core/index.js";
|
|
75
134
|
|
|
76
135
|
// ─── Shared session state machine ─────────────────────────────────────────
|
|
77
136
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| { status: "loading" }
|
|
81
|
-
| { status: "ready"; session: T }
|
|
82
|
-
| { status: "error"; error: Error };
|
|
137
|
+
/** Alias kept so the many call sites below read unchanged. */
|
|
138
|
+
type SessionState<T> = EmbedSessionState<T>;
|
|
83
139
|
|
|
84
140
|
function useEmbedSession<T extends { url: string; expiresAt: string }>(
|
|
85
141
|
loader: (client: BiabDevClient) => Promise<T>,
|
|
@@ -89,7 +145,7 @@ function useEmbedSession<T extends { url: string; expiresAt: string }>(
|
|
|
89
145
|
refreshLeadMs?: number | undefined;
|
|
90
146
|
},
|
|
91
147
|
) {
|
|
92
|
-
const { apiKey, baseUrl, refreshLeadMs =
|
|
148
|
+
const { apiKey, baseUrl, refreshLeadMs = DEFAULT_REFRESH_LEAD_MS } = options;
|
|
93
149
|
const client = useMemo(
|
|
94
150
|
() => new BiabDevClient({ apiKey, baseUrl }),
|
|
95
151
|
[apiKey, baseUrl],
|
|
@@ -119,9 +175,15 @@ function useEmbedSession<T extends { url: string; expiresAt: string }>(
|
|
|
119
175
|
|
|
120
176
|
useEffect(() => {
|
|
121
177
|
if (state.status !== "ready") return;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
178
|
+
// The schedule itself lives in `embed-protocol.ts` so both bindings
|
|
179
|
+
// re-mint at the same moment — an embed that expires in Solid but not in
|
|
180
|
+
// React would be a bug nobody thinks to look for.
|
|
181
|
+
const delay = refreshDelayMs(
|
|
182
|
+
state.session.expiresAt,
|
|
183
|
+
refreshLeadMs,
|
|
184
|
+
Date.now(),
|
|
185
|
+
);
|
|
186
|
+
if (delay === null) return;
|
|
125
187
|
const timer = setTimeout(() => {
|
|
126
188
|
void refresh();
|
|
127
189
|
}, delay);
|
|
@@ -133,50 +195,23 @@ function useEmbedSession<T extends { url: string; expiresAt: string }>(
|
|
|
133
195
|
|
|
134
196
|
// ─── postMessage protocol ─────────────────────────────────────────────────
|
|
135
197
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
productId: string;
|
|
143
|
-
variantId: string;
|
|
144
|
-
combinationKey?: string;
|
|
145
|
-
quantity: number;
|
|
146
|
-
}>;
|
|
147
|
-
}
|
|
148
|
-
| {
|
|
149
|
-
type: "biab:checkout:success";
|
|
150
|
-
sessionId: string;
|
|
151
|
-
orderId?: string | null;
|
|
152
|
-
}
|
|
153
|
-
| { type: "biab:checkout:cancel"; sessionId: string }
|
|
154
|
-
| { type: "biab:checkout:error"; message: string }
|
|
155
|
-
| { type: "biab:chatbot:user-message"; content: string }
|
|
156
|
-
| { type: "biab:chatbot:assistant-message"; content: string; model?: string }
|
|
157
|
-
| { type: "biab:chatbot:error"; message: string };
|
|
198
|
+
/**
|
|
199
|
+
* Re-exported from `./embed-protocol.js`, where the type and the origin check
|
|
200
|
+
* that guards it live together. Kept exported here so existing
|
|
201
|
+
* `@businessdash/sdk/react` imports keep working.
|
|
202
|
+
*/
|
|
203
|
+
export type { BiabEmbedEvent } from "./embed-protocol.js";
|
|
158
204
|
|
|
159
205
|
function useEmbedMessages(
|
|
160
206
|
iframeRef: React.RefObject<HTMLIFrameElement | null>,
|
|
161
207
|
handler: (event: BiabEmbedEvent) => void,
|
|
162
208
|
) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
e.source !== null &&
|
|
170
|
-
e.source !== iframeRef.current.contentWindow
|
|
171
|
-
)
|
|
172
|
-
return;
|
|
173
|
-
const { kind, ...rest } = e.data as { kind: string } & BiabEmbedEvent;
|
|
174
|
-
void kind;
|
|
175
|
-
handler(rest as BiabEmbedEvent);
|
|
176
|
-
};
|
|
177
|
-
window.addEventListener("message", onMessage);
|
|
178
|
-
return () => window.removeEventListener("message", onMessage);
|
|
179
|
-
}, [handler, iframeRef]);
|
|
209
|
+
// The listener, and the origin check inside it, live in
|
|
210
|
+
// `embed-protocol.ts` so the Solid binding enforces exactly the same rule.
|
|
211
|
+
useEffect(
|
|
212
|
+
() => subscribeToEmbedMessages(() => iframeRef.current, handler),
|
|
213
|
+
[handler, iframeRef],
|
|
214
|
+
);
|
|
180
215
|
}
|
|
181
216
|
|
|
182
217
|
// ─── Dashboard (existing) ─────────────────────────────────────────────────
|
|
@@ -286,7 +321,7 @@ export function Dashboard({
|
|
|
286
321
|
return (
|
|
287
322
|
<iframe
|
|
288
323
|
className={className}
|
|
289
|
-
sandbox=
|
|
324
|
+
sandbox={DEFAULT_EMBED_SANDBOX}
|
|
290
325
|
src={state.session.url}
|
|
291
326
|
style={{ ...defaultIframeStyle, ...style }}
|
|
292
327
|
title={title}
|
|
@@ -353,9 +388,9 @@ function EmbedIframe<T extends { url: string }>({
|
|
|
353
388
|
);
|
|
354
389
|
}
|
|
355
390
|
|
|
356
|
-
const sandbox =
|
|
357
|
-
|
|
358
|
-
|
|
391
|
+
const sandbox = extraSandbox
|
|
392
|
+
? `${DEFAULT_EMBED_SANDBOX} ${extraSandbox}`
|
|
393
|
+
: DEFAULT_EMBED_SANDBOX;
|
|
359
394
|
|
|
360
395
|
return (
|
|
361
396
|
<iframe
|
|
@@ -779,127 +814,100 @@ export function Chatbot({
|
|
|
779
814
|
*/
|
|
780
815
|
export function useChatbot(options: { apiKey: string; baseUrl: string }) {
|
|
781
816
|
const { apiKey, baseUrl } = options;
|
|
782
|
-
const
|
|
783
|
-
() =>
|
|
817
|
+
const controller = useMemo(
|
|
818
|
+
() =>
|
|
819
|
+
createChatController({
|
|
820
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
821
|
+
}),
|
|
784
822
|
[apiKey, baseUrl],
|
|
785
823
|
);
|
|
786
|
-
|
|
787
|
-
const [isPending, setIsPending] = useState(false);
|
|
788
|
-
const [error, setError] = useState<Error | null>(null);
|
|
789
|
-
const [latestUiActions, setLatestUiActions] = useState<ChatbotUiAction[]>([]);
|
|
790
|
-
const [availability, setAvailability] = useState<ChatbotAvailability | null>(
|
|
791
|
-
null,
|
|
792
|
-
);
|
|
824
|
+
useEffect(() => () => controller.destroy(), [controller]);
|
|
793
825
|
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (!trimmed) return null;
|
|
798
|
-
setError(null);
|
|
799
|
-
setIsPending(true);
|
|
800
|
-
const next: ChatbotMessage[] = [
|
|
801
|
-
...messages,
|
|
802
|
-
{ role: "user", content: trimmed },
|
|
803
|
-
];
|
|
804
|
-
setMessages(next);
|
|
805
|
-
try {
|
|
806
|
-
const input: ChatbotChatInput = { messages: next };
|
|
807
|
-
const reply = await client.chatbot.chat(input);
|
|
808
|
-
setMessages((prev) => [
|
|
809
|
-
...prev,
|
|
810
|
-
{ role: "assistant", content: reply.text },
|
|
811
|
-
]);
|
|
812
|
-
setLatestUiActions(reply.uiActions ?? []);
|
|
813
|
-
if (reply.availability) setAvailability(reply.availability);
|
|
814
|
-
return reply;
|
|
815
|
-
} catch (err) {
|
|
816
|
-
const e = err instanceof Error ? err : new Error("Chat failed");
|
|
817
|
-
setError(e);
|
|
818
|
-
return null;
|
|
819
|
-
} finally {
|
|
820
|
-
setIsPending(false);
|
|
821
|
-
}
|
|
822
|
-
},
|
|
823
|
-
[client, messages],
|
|
826
|
+
const subscribe = useCallback(
|
|
827
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
828
|
+
[controller],
|
|
824
829
|
);
|
|
830
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
831
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
825
832
|
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}, []);
|
|
831
|
-
|
|
832
|
-
const getConfig = useCallback(
|
|
833
|
-
async (): Promise<ChatbotConfig> => client.chatbot.getConfig(),
|
|
834
|
-
[client],
|
|
833
|
+
const uploadFile = useCallback(
|
|
834
|
+
(args: { sessionId: string; file: File; uploaderEmail?: string | null }) =>
|
|
835
|
+
new BiabDevClient({ apiKey, baseUrl }).chatbot.uploadFile(args),
|
|
836
|
+
[apiKey, baseUrl],
|
|
835
837
|
);
|
|
836
838
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
839
|
+
// The returned shape is unchanged from before this hook was moved onto
|
|
840
|
+
// `chat-core`, so existing consumers keep working — only the implementation
|
|
841
|
+
// moved, and it now backs every other framework's chat binding too.
|
|
842
|
+
return {
|
|
843
|
+
messages: snap.messages,
|
|
844
|
+
send: controller.send,
|
|
845
|
+
reset: controller.reset,
|
|
846
|
+
isPending: snap.isPending,
|
|
847
|
+
error: snap.error,
|
|
848
|
+
getConfig: controller.loadConfig,
|
|
849
|
+
latestUiActions: snap.uiActions,
|
|
850
|
+
availability: snap.availability,
|
|
851
|
+
refreshAvailability: controller.refreshAvailability,
|
|
852
|
+
getForm: controller.getForm,
|
|
853
|
+
submitForm: controller.submitForm,
|
|
854
|
+
requestHuman: controller.requestHuman,
|
|
855
|
+
uploadFile,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
847
858
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
859
|
+
/**
|
|
860
|
+
* Headless storefront + cart for React — the native alternative to
|
|
861
|
+
* `<Storefront>`, which is a hosted page in an iframe. Everything lives in
|
|
862
|
+
* `createStoreController`; this only bridges it into React.
|
|
863
|
+
*
|
|
864
|
+
* ```tsx
|
|
865
|
+
* const store = useStorefront({ apiKey, baseUrl });
|
|
866
|
+
* return store.products.map((p) => <MyCard key={p.id} product={p} />);
|
|
867
|
+
* ```
|
|
868
|
+
*/
|
|
869
|
+
export function useStorefront(options: {
|
|
870
|
+
apiKey: string;
|
|
871
|
+
baseUrl: string;
|
|
872
|
+
pageSize?: number;
|
|
873
|
+
categoryId?: string | null;
|
|
874
|
+
}) {
|
|
875
|
+
const { apiKey, baseUrl, pageSize, categoryId } = options;
|
|
876
|
+
const controller = useMemo(
|
|
877
|
+
() =>
|
|
878
|
+
createStoreController({
|
|
879
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
880
|
+
...(pageSize ? { pageSize } : {}),
|
|
881
|
+
...(categoryId ? { categoryId } : {}),
|
|
882
|
+
}),
|
|
883
|
+
[apiKey, baseUrl, pageSize, categoryId],
|
|
858
884
|
);
|
|
859
885
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
): Promise<ChatbotFormSubmitResponse | null> => {
|
|
865
|
-
try {
|
|
866
|
-
const priorChat = messages.map((m, i) => ({
|
|
867
|
-
role: m.role,
|
|
868
|
-
content: m.content,
|
|
869
|
-
createdAt: i,
|
|
870
|
-
}));
|
|
871
|
-
// Chatbot submit carries the transcript → chatbot path.
|
|
872
|
-
return await client.chatbot.submitChatForm(slug, {
|
|
873
|
-
...input,
|
|
874
|
-
priorChat: input.priorChat ?? priorChat,
|
|
875
|
-
});
|
|
876
|
-
} catch (err) {
|
|
877
|
-
setError(err instanceof Error ? err : new Error("Submit failed"));
|
|
878
|
-
return null;
|
|
879
|
-
}
|
|
880
|
-
},
|
|
881
|
-
[client, messages],
|
|
882
|
-
);
|
|
886
|
+
useEffect(() => {
|
|
887
|
+
void controller.load();
|
|
888
|
+
return () => controller.destroy();
|
|
889
|
+
}, [controller]);
|
|
883
890
|
|
|
884
|
-
const
|
|
885
|
-
(
|
|
886
|
-
|
|
887
|
-
[client],
|
|
891
|
+
const subscribe = useCallback(
|
|
892
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
893
|
+
[controller],
|
|
888
894
|
);
|
|
895
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
896
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
889
897
|
|
|
890
898
|
return {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
899
|
+
...snap,
|
|
900
|
+
loadMore: controller.loadMore,
|
|
901
|
+
filterByCategory: controller.filterByCategory,
|
|
902
|
+
openProduct: controller.openProduct,
|
|
903
|
+
closeProduct: controller.closeProduct,
|
|
904
|
+
addToCart: controller.addToCart,
|
|
905
|
+
updateQuantity: controller.updateQuantity,
|
|
906
|
+
removeItem: controller.removeItem,
|
|
907
|
+
applyCoupon: controller.applyCoupon,
|
|
908
|
+
removeCoupon: controller.removeCoupon,
|
|
909
|
+
clearCart: controller.clearCart,
|
|
910
|
+
refreshCart: controller.refreshCart,
|
|
903
911
|
};
|
|
904
912
|
}
|
|
905
913
|
|
|
@@ -1323,7 +1331,10 @@ export function ChatbotInline({
|
|
|
1323
1331
|
let cancelled = false;
|
|
1324
1332
|
void getConfig()
|
|
1325
1333
|
.then((cfg) => {
|
|
1326
|
-
|
|
1334
|
+
// `null` means the config couldn't be read — the core reports that
|
|
1335
|
+
// rather than throwing, because a chat that can't fetch its upload
|
|
1336
|
+
// settings should still let the visitor talk. Keep the defaults.
|
|
1337
|
+
if (cancelled || !cfg) return;
|
|
1327
1338
|
setFileUploadsEnabled(cfg.fileUploadsEnabled);
|
|
1328
1339
|
setEmailCaptureMode(cfg.emailCaptureMode);
|
|
1329
1340
|
})
|
|
@@ -2050,23 +2061,8 @@ type AuthLinkProps = {
|
|
|
2050
2061
|
children?: ReactNode;
|
|
2051
2062
|
};
|
|
2052
2063
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
function buildAuthHref(
|
|
2056
|
-
subroute: "sign-in" | "sign-up" | "sign-out",
|
|
2057
|
-
props: {
|
|
2058
|
-
returnTo?: string | undefined;
|
|
2059
|
-
loginHint?: string | undefined;
|
|
2060
|
-
basePath?: string | undefined;
|
|
2061
|
-
},
|
|
2062
|
-
): string {
|
|
2063
|
-
const base = (props.basePath ?? DEFAULT_AUTH_BASE_PATH).replace(/\/$/, "");
|
|
2064
|
-
const params = new URLSearchParams();
|
|
2065
|
-
if (props.returnTo) params.set("returnTo", props.returnTo);
|
|
2066
|
-
if (props.loginHint) params.set("loginHint", props.loginHint);
|
|
2067
|
-
const qs = params.toString();
|
|
2068
|
-
return `${base}/${subroute}${qs ? `?${qs}` : ""}`;
|
|
2069
|
-
}
|
|
2064
|
+
// `buildAuthHref` and `DEFAULT_AUTH_BASE_PATH` come from `./auth-links.js` so
|
|
2065
|
+
// every binding builds the same handler URLs from the same query contract.
|
|
2070
2066
|
|
|
2071
2067
|
export function SignIn({ children, ...rest }: AuthLinkProps) {
|
|
2072
2068
|
return (
|
|
@@ -2233,37 +2229,29 @@ type UseUserState =
|
|
|
2233
2229
|
* the cookie + handler path are the source of truth.
|
|
2234
2230
|
*/
|
|
2235
2231
|
export function useUser(options?: { basePath?: string }): UseUserState {
|
|
2236
|
-
const basePath =
|
|
2237
|
-
|
|
2238
|
-
|
|
2232
|
+
const basePath = options?.basePath;
|
|
2233
|
+
const controller = useMemo(
|
|
2234
|
+
() => createUserController(basePath ? { basePath } : {}),
|
|
2235
|
+
[basePath],
|
|
2239
2236
|
);
|
|
2240
|
-
const [state, setState] = useState<UseUserState>({
|
|
2241
|
-
status: "loading",
|
|
2242
|
-
user: null,
|
|
2243
|
-
});
|
|
2244
2237
|
|
|
2245
2238
|
useEffect(() => {
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
.then((body: TenantAuthMeResponse) => {
|
|
2250
|
-
if (cancelled) return;
|
|
2251
|
-
if (body) {
|
|
2252
|
-
setState({ status: "signed-in", user: body });
|
|
2253
|
-
} else {
|
|
2254
|
-
setState({ status: "signed-out", user: null });
|
|
2255
|
-
}
|
|
2256
|
-
})
|
|
2257
|
-
.catch(() => {
|
|
2258
|
-
if (cancelled) return;
|
|
2259
|
-
setState({ status: "signed-out", user: null });
|
|
2260
|
-
});
|
|
2261
|
-
return () => {
|
|
2262
|
-
cancelled = true;
|
|
2263
|
-
};
|
|
2264
|
-
}, [basePath]);
|
|
2239
|
+
controller.start();
|
|
2240
|
+
return () => controller.destroy();
|
|
2241
|
+
}, [controller]);
|
|
2265
2242
|
|
|
2266
|
-
|
|
2243
|
+
const subscribe = useCallback(
|
|
2244
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
2245
|
+
[controller],
|
|
2246
|
+
);
|
|
2247
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
2248
|
+
// The shape is unchanged for callers; the fetch moved to `account-core` so
|
|
2249
|
+
// every framework reads the session the same way.
|
|
2250
|
+
return useSyncExternalStore(
|
|
2251
|
+
subscribe,
|
|
2252
|
+
getSnapshot,
|
|
2253
|
+
getSnapshot,
|
|
2254
|
+
) as UseUserState;
|
|
2267
2255
|
}
|
|
2268
2256
|
|
|
2269
2257
|
// ─── Followers / subscribe ───────────────────────────────────────────────────
|
|
@@ -2278,39 +2266,9 @@ export type UseFollowersOptions = {
|
|
|
2278
2266
|
baseUrl?: string;
|
|
2279
2267
|
};
|
|
2280
2268
|
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
function readLocalFollow(key: string): LocalFollow | null {
|
|
2286
|
-
if (typeof window === "undefined") return null;
|
|
2287
|
-
try {
|
|
2288
|
-
const raw = window.localStorage.getItem(key);
|
|
2289
|
-
if (!raw) return null;
|
|
2290
|
-
const parsed = JSON.parse(raw) as Partial<LocalFollow>;
|
|
2291
|
-
return typeof parsed?.email === "string"
|
|
2292
|
-
? { email: parsed.email, at: Number(parsed.at) || 0 }
|
|
2293
|
-
: null;
|
|
2294
|
-
} catch {
|
|
2295
|
-
return null;
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
function writeLocalFollow(key: string, value: LocalFollow): void {
|
|
2299
|
-
if (typeof window === "undefined") return;
|
|
2300
|
-
try {
|
|
2301
|
-
window.localStorage.setItem(key, JSON.stringify(value));
|
|
2302
|
-
} catch {
|
|
2303
|
-
// storage disabled / quota — the local hint is best-effort.
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
function clearLocalFollow(key: string): void {
|
|
2307
|
-
if (typeof window === "undefined") return;
|
|
2308
|
-
try {
|
|
2309
|
-
window.localStorage.removeItem(key);
|
|
2310
|
-
} catch {
|
|
2311
|
-
// ignore
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2269
|
+
// `LocalFollow`, `followerStoreKey`, `readLocalFollow`, `writeLocalFollow` and
|
|
2270
|
+
// `clearLocalFollow` come from `./auth-links.js` — one storage format, so a
|
|
2271
|
+
// visitor who subscribed through one binding is recognised by another.
|
|
2314
2272
|
|
|
2315
2273
|
/**
|
|
2316
2274
|
* Visitor-facing followers/subscribe client, bound to a publishable token so it
|
|
@@ -2336,70 +2294,71 @@ function clearLocalFollow(key: string): void {
|
|
|
2336
2294
|
*/
|
|
2337
2295
|
export function useFollowers(options: UseFollowersOptions) {
|
|
2338
2296
|
const { token, siteId, baseUrl } = options;
|
|
2339
|
-
const
|
|
2297
|
+
const controller = useMemo(
|
|
2340
2298
|
() =>
|
|
2341
|
-
|
|
2342
|
-
|
|
2299
|
+
createFollowersController({
|
|
2300
|
+
client: createBiabClient({
|
|
2301
|
+
apiKey: token,
|
|
2302
|
+
siteId,
|
|
2303
|
+
baseUrl: baseUrl ?? DEFAULT_PACKAGE_BASE_URL,
|
|
2304
|
+
}),
|
|
2343
2305
|
siteId,
|
|
2344
|
-
baseUrl: baseUrl ?? DEFAULT_PACKAGE_BASE_URL,
|
|
2345
2306
|
}),
|
|
2346
2307
|
[token, siteId, baseUrl],
|
|
2347
2308
|
);
|
|
2348
|
-
const storeKey = `${FOLLOWER_STORE_PREFIX}${siteId}`;
|
|
2349
|
-
const [local, setLocal] = useState<LocalFollow | null>(() =>
|
|
2350
|
-
readLocalFollow(storeKey),
|
|
2351
|
-
);
|
|
2352
|
-
useEffect(() => {
|
|
2353
|
-
setLocal(readLocalFollow(storeKey));
|
|
2354
|
-
}, [storeKey]);
|
|
2355
|
-
|
|
2356
|
-
const join = useCallback(
|
|
2357
|
-
async (input: { email: string; name?: string; source?: string }) => {
|
|
2358
|
-
const res = await client.followers.join(input);
|
|
2359
|
-
const entry: LocalFollow = { email: input.email, at: Date.now() };
|
|
2360
|
-
writeLocalFollow(storeKey, entry);
|
|
2361
|
-
setLocal(entry);
|
|
2362
|
-
return res;
|
|
2363
|
-
},
|
|
2364
|
-
[client, storeKey],
|
|
2365
|
-
);
|
|
2366
2309
|
|
|
2367
|
-
|
|
2368
|
-
async (email: string) => {
|
|
2369
|
-
const res = await client.followers.leave({ email });
|
|
2370
|
-
clearLocalFollow(storeKey);
|
|
2371
|
-
setLocal(null);
|
|
2372
|
-
return res;
|
|
2373
|
-
},
|
|
2374
|
-
[client, storeKey],
|
|
2375
|
-
);
|
|
2310
|
+
useEffect(() => () => controller.destroy(), [controller]);
|
|
2376
2311
|
|
|
2377
|
-
const
|
|
2378
|
-
(
|
|
2379
|
-
[
|
|
2312
|
+
const subscribe = useCallback(
|
|
2313
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
2314
|
+
[controller],
|
|
2380
2315
|
);
|
|
2316
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
2317
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
2318
|
+
|
|
2319
|
+
// Same shape as before the move onto `account-core`, including the
|
|
2320
|
+
// local-storage hint, so existing consumers are untouched.
|
|
2321
|
+
return {
|
|
2322
|
+
join: controller.join,
|
|
2323
|
+
leave: controller.leave,
|
|
2324
|
+
me: controller.me,
|
|
2325
|
+
edit: controller.edit,
|
|
2326
|
+
subscribedLocally: snap.subscribedLocally,
|
|
2327
|
+
localEmail: snap.localEmail,
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2381
2330
|
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2331
|
+
/**
|
|
2332
|
+
* Headless checkout for React. `start()` mints a Stripe session and `redirect()`
|
|
2333
|
+
* navigates — split apart so a native app or an SSR render can use the first
|
|
2334
|
+
* without the second.
|
|
2335
|
+
*/
|
|
2336
|
+
export function useCheckout(options: { apiKey: string; baseUrl: string }) {
|
|
2337
|
+
const { apiKey, baseUrl } = options;
|
|
2338
|
+
const controller = useMemo(
|
|
2339
|
+
() =>
|
|
2340
|
+
createCheckoutController({
|
|
2341
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
2342
|
+
}),
|
|
2343
|
+
[apiKey, baseUrl],
|
|
2386
2344
|
);
|
|
2387
2345
|
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
edit,
|
|
2394
|
-
/** This browser already subscribed (instant, no network). Use to hide a
|
|
2395
|
-
* newsletter form for anonymous visitors. */
|
|
2396
|
-
subscribedLocally: local !== null,
|
|
2397
|
-
/** Email this browser subscribed with — pass to `me()` for an
|
|
2398
|
-
* authoritative re-check, or `null` if this browser hasn't subscribed. */
|
|
2399
|
-
localEmail: local?.email ?? null,
|
|
2400
|
-
}),
|
|
2401
|
-
[join, leave, me, edit, local],
|
|
2346
|
+
useEffect(() => () => controller.destroy(), [controller]);
|
|
2347
|
+
|
|
2348
|
+
const subscribe = useCallback(
|
|
2349
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
2350
|
+
[controller],
|
|
2402
2351
|
);
|
|
2352
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
2353
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
2354
|
+
|
|
2355
|
+
return {
|
|
2356
|
+
...snap,
|
|
2357
|
+
start: controller.start,
|
|
2358
|
+
redirect: controller.redirect,
|
|
2359
|
+
resolve: controller.resolve,
|
|
2360
|
+
reset: controller.reset,
|
|
2361
|
+
};
|
|
2403
2362
|
}
|
|
2404
2363
|
|
|
2405
2364
|
// ─── Marketing pages — list, lazy-load per page, preload ─────────────────────
|
|
@@ -5380,22 +5339,17 @@ export function BiabForm(props: BiabFormProps) {
|
|
|
5380
5339
|
}
|
|
5381
5340
|
|
|
5382
5341
|
function SocialGlyph({
|
|
5383
|
-
|
|
5384
|
-
color,
|
|
5342
|
+
iconUrl,
|
|
5385
5343
|
size,
|
|
5386
5344
|
className,
|
|
5387
5345
|
label,
|
|
5388
5346
|
}: {
|
|
5389
|
-
|
|
5390
|
-
color?: string | undefined;
|
|
5347
|
+
iconUrl: string | null;
|
|
5391
5348
|
size: number;
|
|
5392
5349
|
className?: string | undefined;
|
|
5393
5350
|
label: string;
|
|
5394
5351
|
}) {
|
|
5395
|
-
if (
|
|
5396
|
-
const src = color
|
|
5397
|
-
? `https://cdn.simpleicons.org/${iconSlug}/${encodeURIComponent(color)}`
|
|
5398
|
-
: `https://cdn.simpleicons.org/${iconSlug}`;
|
|
5352
|
+
if (iconUrl) {
|
|
5399
5353
|
return (
|
|
5400
5354
|
// biome-ignore lint: remote brand icon from the simple-icons CDN
|
|
5401
5355
|
<img
|
|
@@ -5403,7 +5357,7 @@ function SocialGlyph({
|
|
|
5403
5357
|
className={className}
|
|
5404
5358
|
height={size}
|
|
5405
5359
|
loading="lazy"
|
|
5406
|
-
src={
|
|
5360
|
+
src={iconUrl}
|
|
5407
5361
|
width={size}
|
|
5408
5362
|
/>
|
|
5409
5363
|
);
|
|
@@ -5415,13 +5369,13 @@ function SocialGlyph({
|
|
|
5415
5369
|
fill="none"
|
|
5416
5370
|
height={size}
|
|
5417
5371
|
stroke="currentColor"
|
|
5418
|
-
strokeWidth={
|
|
5419
|
-
viewBox=
|
|
5372
|
+
strokeWidth={SOCIAL_FALLBACK_GLYPH.strokeWidth}
|
|
5373
|
+
viewBox={SOCIAL_FALLBACK_GLYPH.viewBox}
|
|
5420
5374
|
width={size}
|
|
5421
5375
|
>
|
|
5422
5376
|
<title>{label}</title>
|
|
5423
|
-
<circle
|
|
5424
|
-
<path d=
|
|
5377
|
+
<circle {...SOCIAL_FALLBACK_GLYPH.circle} />
|
|
5378
|
+
<path d={SOCIAL_FALLBACK_GLYPH.path} />
|
|
5425
5379
|
</svg>
|
|
5426
5380
|
);
|
|
5427
5381
|
}
|
|
@@ -5455,21 +5409,12 @@ export function SocialLinks({
|
|
|
5455
5409
|
linkClassName,
|
|
5456
5410
|
iconClassName,
|
|
5457
5411
|
}: SocialLinksProps) {
|
|
5458
|
-
|
|
5412
|
+
// Rows, URLs and icon URLs come from `social-core`, shared with every other
|
|
5413
|
+
// binding — only the markup below is React's.
|
|
5414
|
+
const rows = resolveSocialLinks(socials, { color });
|
|
5459
5415
|
if (rows.length === 0) return null;
|
|
5460
5416
|
return (
|
|
5461
|
-
<ul
|
|
5462
|
-
className={className}
|
|
5463
|
-
style={{
|
|
5464
|
-
display: "flex",
|
|
5465
|
-
flexWrap: "wrap",
|
|
5466
|
-
alignItems: "center",
|
|
5467
|
-
gap: "0.75rem",
|
|
5468
|
-
listStyle: "none",
|
|
5469
|
-
margin: 0,
|
|
5470
|
-
padding: 0,
|
|
5471
|
-
}}
|
|
5472
|
-
>
|
|
5417
|
+
<ul className={className} style={SOCIAL_LIST_STYLE as CSSProperties}>
|
|
5473
5418
|
{rows.map((row) => (
|
|
5474
5419
|
<li key={row.key}>
|
|
5475
5420
|
<a
|
|
@@ -5482,8 +5427,7 @@ export function SocialLinks({
|
|
|
5482
5427
|
>
|
|
5483
5428
|
<SocialGlyph
|
|
5484
5429
|
className={iconClassName}
|
|
5485
|
-
|
|
5486
|
-
iconSlug={row.iconSlug}
|
|
5430
|
+
iconUrl={row.iconUrl}
|
|
5487
5431
|
label={row.label}
|
|
5488
5432
|
size={size}
|
|
5489
5433
|
/>
|
|
@@ -5493,3 +5437,174 @@ export function SocialLinks({
|
|
|
5493
5437
|
</ul>
|
|
5494
5438
|
);
|
|
5495
5439
|
}
|
|
5440
|
+
|
|
5441
|
+
/**
|
|
5442
|
+
* Headless blog for React — the native alternative to `<Blog>`, which is a
|
|
5443
|
+
* hosted page in an iframe. Posts, taxonomy, a single post, comments and likes
|
|
5444
|
+
* as data; the layout and the URLs are yours.
|
|
5445
|
+
*
|
|
5446
|
+
* ```tsx
|
|
5447
|
+
* const blog = useBlog({ apiKey, baseUrl });
|
|
5448
|
+
* return blog.posts.map((p) => <MyCard key={p.slug} post={p} />);
|
|
5449
|
+
* ```
|
|
5450
|
+
*/
|
|
5451
|
+
export function useBlog(options: {
|
|
5452
|
+
apiKey: string;
|
|
5453
|
+
baseUrl: string;
|
|
5454
|
+
pageSize?: number;
|
|
5455
|
+
categoryId?: string | null;
|
|
5456
|
+
}) {
|
|
5457
|
+
const { apiKey, baseUrl, pageSize, categoryId } = options;
|
|
5458
|
+
const controller = useMemo(
|
|
5459
|
+
() =>
|
|
5460
|
+
createBlogController({
|
|
5461
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
5462
|
+
...(pageSize ? { pageSize } : {}),
|
|
5463
|
+
...(categoryId ? { categoryId } : {}),
|
|
5464
|
+
}),
|
|
5465
|
+
[apiKey, baseUrl, pageSize, categoryId],
|
|
5466
|
+
);
|
|
5467
|
+
|
|
5468
|
+
useEffect(() => {
|
|
5469
|
+
void controller.load();
|
|
5470
|
+
return () => controller.destroy();
|
|
5471
|
+
}, [controller]);
|
|
5472
|
+
|
|
5473
|
+
const subscribe = useCallback(
|
|
5474
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
5475
|
+
[controller],
|
|
5476
|
+
);
|
|
5477
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
5478
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
5479
|
+
|
|
5480
|
+
return {
|
|
5481
|
+
...snap,
|
|
5482
|
+
loadMore: controller.loadMore,
|
|
5483
|
+
filterByCategory: controller.filterByCategory,
|
|
5484
|
+
openPost: controller.openPost,
|
|
5485
|
+
closePost: controller.closePost,
|
|
5486
|
+
loadComments: controller.loadComments,
|
|
5487
|
+
loadMoreComments: controller.loadMoreComments,
|
|
5488
|
+
postComment: controller.postComment,
|
|
5489
|
+
toggleLike: controller.toggleLike,
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5493
|
+
/**
|
|
5494
|
+
* Headless email / phone verification for React.
|
|
5495
|
+
*
|
|
5496
|
+
* Read `flow` before rendering: `otp` wants a code field, `link` wants a
|
|
5497
|
+
* "check your email" message and no field at all.
|
|
5498
|
+
*/
|
|
5499
|
+
export function useVerify(
|
|
5500
|
+
options: { apiKey: string; baseUrl: string } & Omit<
|
|
5501
|
+
CreateVerifyControllerOptions,
|
|
5502
|
+
"client"
|
|
5503
|
+
>,
|
|
5504
|
+
) {
|
|
5505
|
+
const { apiKey, baseUrl, kind, resendCooldownSeconds } = options;
|
|
5506
|
+
const controller = useMemo(
|
|
5507
|
+
() =>
|
|
5508
|
+
createVerifyController({
|
|
5509
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
5510
|
+
kind,
|
|
5511
|
+
...(resendCooldownSeconds ? { resendCooldownSeconds } : {}),
|
|
5512
|
+
}),
|
|
5513
|
+
[apiKey, baseUrl, kind, resendCooldownSeconds],
|
|
5514
|
+
);
|
|
5515
|
+
|
|
5516
|
+
useEffect(() => () => controller.destroy(), [controller]);
|
|
5517
|
+
|
|
5518
|
+
const subscribe = useCallback(
|
|
5519
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
5520
|
+
[controller],
|
|
5521
|
+
);
|
|
5522
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
5523
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
5524
|
+
|
|
5525
|
+
return {
|
|
5526
|
+
...snap,
|
|
5527
|
+
start: controller.start,
|
|
5528
|
+
resend: controller.resend,
|
|
5529
|
+
confirm: controller.confirm,
|
|
5530
|
+
canResend: controller.canResend,
|
|
5531
|
+
reset: controller.reset,
|
|
5532
|
+
};
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5535
|
+
/**
|
|
5536
|
+
* The signed-in customer's portal for React — work, quotes, contracts,
|
|
5537
|
+
* invoices, orders, downloads, shipping, reviews, referrals, profile and
|
|
5538
|
+
* notification settings. The native alternative to `<Dashboard>`, which is a
|
|
5539
|
+
* hosted page in an iframe.
|
|
5540
|
+
*
|
|
5541
|
+
* `sessionToken` is required: every portal route is scoped to the signed-in
|
|
5542
|
+
* customer, and without one the portal renders empty rather than signed-out.
|
|
5543
|
+
*
|
|
5544
|
+
* Two things the API imposes and this hook does not hide. There are no list
|
|
5545
|
+
* endpoints for invoices, quotes, contracts or shipments — `work` carries the
|
|
5546
|
+
* counts and you open each by id. And a submitted review is queued for staff
|
|
5547
|
+
* approval, so it will NOT appear in `reviews`; watch `reviewAwaitingModeration`
|
|
5548
|
+
* to tell the customer it is pending rather than live.
|
|
5549
|
+
*/
|
|
5550
|
+
export function usePortal(options: {
|
|
5551
|
+
apiKey: string;
|
|
5552
|
+
baseUrl: string;
|
|
5553
|
+
sessionToken: string;
|
|
5554
|
+
}) {
|
|
5555
|
+
const { apiKey, baseUrl, sessionToken } = options;
|
|
5556
|
+
const controller = useMemo(
|
|
5557
|
+
() =>
|
|
5558
|
+
createPortalController({
|
|
5559
|
+
client: new BiabDevClient({ apiKey, baseUrl }),
|
|
5560
|
+
sessionToken,
|
|
5561
|
+
}),
|
|
5562
|
+
[apiKey, baseUrl, sessionToken],
|
|
5563
|
+
);
|
|
5564
|
+
|
|
5565
|
+
useEffect(() => {
|
|
5566
|
+
void controller.load();
|
|
5567
|
+
return () => controller.destroy();
|
|
5568
|
+
}, [controller]);
|
|
5569
|
+
|
|
5570
|
+
const subscribe = useCallback(
|
|
5571
|
+
(onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
|
|
5572
|
+
[controller],
|
|
5573
|
+
);
|
|
5574
|
+
const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
|
|
5575
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
5576
|
+
|
|
5577
|
+
return {
|
|
5578
|
+
...snap,
|
|
5579
|
+
loadOrders: controller.loadOrders,
|
|
5580
|
+
loadInvoices: controller.loadInvoices,
|
|
5581
|
+
loadQuotes: controller.loadQuotes,
|
|
5582
|
+
loadContracts: controller.loadContracts,
|
|
5583
|
+
loadShipments: controller.loadShipments,
|
|
5584
|
+
loadMessages: controller.loadMessages,
|
|
5585
|
+
postMessage: controller.postMessage,
|
|
5586
|
+
markMessagesRead: controller.markMessagesRead,
|
|
5587
|
+
loadPayments: controller.loadPayments,
|
|
5588
|
+
loadReviews: controller.loadReviews,
|
|
5589
|
+
loadReferrals: controller.loadReferrals,
|
|
5590
|
+
loadNotificationPreferences: controller.loadNotificationPreferences,
|
|
5591
|
+
loadOtherOrgs: controller.loadOtherOrgs,
|
|
5592
|
+
openJob: controller.openJob,
|
|
5593
|
+
openOrder: controller.openOrder,
|
|
5594
|
+
openInvoice: controller.openInvoice,
|
|
5595
|
+
openQuote: controller.openQuote,
|
|
5596
|
+
openContract: controller.openContract,
|
|
5597
|
+
trackShipment: controller.trackShipment,
|
|
5598
|
+
closeDetail: controller.closeDetail,
|
|
5599
|
+
postJobComment: controller.postJobComment,
|
|
5600
|
+
acceptQuote: controller.acceptQuote,
|
|
5601
|
+
rejectQuote: controller.rejectQuote,
|
|
5602
|
+
signContract: controller.signContract,
|
|
5603
|
+
payInvoice: controller.payInvoice,
|
|
5604
|
+
updateProfile: controller.updateProfile,
|
|
5605
|
+
updateNotificationPreferences: controller.updateNotificationPreferences,
|
|
5606
|
+
submitReview: controller.submitReview,
|
|
5607
|
+
submitReturnRequest: controller.submitReturnRequest,
|
|
5608
|
+
contactCompany: controller.contactCompany,
|
|
5609
|
+
};
|
|
5610
|
+
}
|