@fluid-app/rep-sdk 0.1.2 → 0.1.4

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 (73) hide show
  1. package/dist/ContactsScreen-33AJ5XUB.js +5 -0
  2. package/dist/{ContactsScreen-BYXF74BO.js.map → ContactsScreen-33AJ5XUB.js.map} +1 -1
  3. package/dist/ContactsScreen-ATASCZO2.cjs +18 -0
  4. package/dist/{ContactsScreen-XZOQJVFE.cjs.map → ContactsScreen-ATASCZO2.cjs.map} +1 -1
  5. package/dist/CustomersScreen-E4HXBKV7.js +5 -0
  6. package/dist/{CustomersScreen-VS6LGULO.js.map → CustomersScreen-E4HXBKV7.js.map} +1 -1
  7. package/dist/CustomersScreen-XMPMKSQA.cjs +18 -0
  8. package/dist/{CustomersScreen-53SXRDDK.cjs.map → CustomersScreen-XMPMKSQA.cjs.map} +1 -1
  9. package/dist/MessagingScreen-5TEWFUX4.js +4 -0
  10. package/dist/{MessagingScreen-O42JEJMW.js.map → MessagingScreen-5TEWFUX4.js.map} +1 -1
  11. package/dist/MessagingScreen-JC7I55NG.cjs +17 -0
  12. package/dist/{MessagingScreen-UCVLYWZB.cjs.map → MessagingScreen-JC7I55NG.cjs.map} +1 -1
  13. package/dist/OrdersScreen-IPPZLEYF.js +5 -0
  14. package/dist/{OrdersScreen-QQJFTTSS.js.map → OrdersScreen-IPPZLEYF.js.map} +1 -1
  15. package/dist/OrdersScreen-X7FYUROL.cjs +18 -0
  16. package/dist/{OrdersScreen-WNT2WDLI.cjs.map → OrdersScreen-X7FYUROL.cjs.map} +1 -1
  17. package/dist/ProductsScreen-4ZIUQNUU.cjs +18 -0
  18. package/dist/{ProductsScreen-CTIAKS3Z.cjs.map → ProductsScreen-4ZIUQNUU.cjs.map} +1 -1
  19. package/dist/ProductsScreen-YTSOZW7B.js +5 -0
  20. package/dist/{ProductsScreen-TRIT2FE3.js.map → ProductsScreen-YTSOZW7B.js.map} +1 -1
  21. package/dist/chunk-424PT5DM.js +21 -0
  22. package/dist/chunk-424PT5DM.js.map +1 -0
  23. package/dist/{chunk-QUVJ3R4T.cjs → chunk-5UBEM3AX.cjs} +4 -4
  24. package/dist/{chunk-QUVJ3R4T.cjs.map → chunk-5UBEM3AX.cjs.map} +1 -1
  25. package/dist/chunk-B6S6BEPL.cjs +16 -0
  26. package/dist/chunk-B6S6BEPL.cjs.map +1 -0
  27. package/dist/{chunk-WH7WZXT6.js → chunk-CMF2FYTD.js} +3 -3
  28. package/dist/{chunk-WH7WZXT6.js.map → chunk-CMF2FYTD.js.map} +1 -1
  29. package/dist/{chunk-MEOOAMH2.cjs → chunk-EOXYOOWS.cjs} +4 -4
  30. package/dist/{chunk-MEOOAMH2.cjs.map → chunk-EOXYOOWS.cjs.map} +1 -1
  31. package/dist/{chunk-YII3IXF4.cjs → chunk-FG2CI6HA.cjs} +4 -4
  32. package/dist/{chunk-YII3IXF4.cjs.map → chunk-FG2CI6HA.cjs.map} +1 -1
  33. package/dist/chunk-HDQ2JUQT.cjs +24 -0
  34. package/dist/chunk-HDQ2JUQT.cjs.map +1 -0
  35. package/dist/{chunk-MBUCXIUN.cjs → chunk-MHPK7YQ2.cjs} +4 -4
  36. package/dist/{chunk-MBUCXIUN.cjs.map → chunk-MHPK7YQ2.cjs.map} +1 -1
  37. package/dist/chunk-OE62VIZU.cjs +1955 -0
  38. package/dist/chunk-OE62VIZU.cjs.map +1 -0
  39. package/dist/{chunk-2AWTZV3T.js → chunk-QZMWG7EM.js} +3 -3
  40. package/dist/{chunk-2AWTZV3T.js.map → chunk-QZMWG7EM.js.map} +1 -1
  41. package/dist/chunk-R4SXB6EV.js +1881 -0
  42. package/dist/chunk-R4SXB6EV.js.map +1 -0
  43. package/dist/chunk-RS4OSTES.js +14 -0
  44. package/dist/chunk-RS4OSTES.js.map +1 -0
  45. package/dist/{chunk-PZIHCYDD.js → chunk-WFPYEYC7.js} +3 -3
  46. package/dist/{chunk-PZIHCYDD.js.map → chunk-WFPYEYC7.js.map} +1 -1
  47. package/dist/{chunk-CXRJSGO6.js → chunk-WMBD65GH.js} +3 -3
  48. package/dist/{chunk-CXRJSGO6.js.map → chunk-WMBD65GH.js.map} +1 -1
  49. package/dist/index.cjs +1726 -1194
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +2603 -0
  52. package/dist/index.d.ts +2603 -0
  53. package/dist/index.js +1395 -1021
  54. package/dist/index.js.map +1 -1
  55. package/package.json +26 -7
  56. package/dist/ContactsScreen-BYXF74BO.js +0 -4
  57. package/dist/ContactsScreen-XZOQJVFE.cjs +0 -17
  58. package/dist/CustomersScreen-53SXRDDK.cjs +0 -17
  59. package/dist/CustomersScreen-VS6LGULO.js +0 -4
  60. package/dist/MessagingScreen-O42JEJMW.js +0 -4
  61. package/dist/MessagingScreen-UCVLYWZB.cjs +0 -17
  62. package/dist/OrdersScreen-QQJFTTSS.js +0 -4
  63. package/dist/OrdersScreen-WNT2WDLI.cjs +0 -17
  64. package/dist/ProductsScreen-CTIAKS3Z.cjs +0 -17
  65. package/dist/ProductsScreen-TRIT2FE3.js +0 -4
  66. package/dist/chunk-DEQ3PBVX.cjs +0 -29
  67. package/dist/chunk-DEQ3PBVX.cjs.map +0 -1
  68. package/dist/chunk-JZRNKSKT.cjs +0 -19
  69. package/dist/chunk-JZRNKSKT.cjs.map +0 -1
  70. package/dist/chunk-LO2HDG6C.js +0 -26
  71. package/dist/chunk-LO2HDG6C.js.map +0 -1
  72. package/dist/chunk-PJWPO4BJ.js +0 -16
  73. package/dist/chunk-PJWPO4BJ.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,945 +1,39 @@
1
- export { MessagingScreen, messagingScreenPropertySchema } from './chunk-PJWPO4BJ.js';
2
- export { ContactsScreen, contactsScreenPropertySchema } from './chunk-WH7WZXT6.js';
3
- export { OrdersScreen, ordersScreenPropertySchema } from './chunk-PZIHCYDD.js';
4
- export { CustomersScreen, customersScreenPropertySchema } from './chunk-CXRJSGO6.js';
5
- export { ProductsScreen, productsScreenPropertySchema } from './chunk-2AWTZV3T.js';
6
- import { __reExport } from './chunk-LO2HDG6C.js';
7
- import { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
8
- import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
1
+ import { themes_exports, useFluidApi, transformManifestToRepAppData, useThemeContext, useFluidAuthContext, MessagingScreen } from './chunk-R4SXB6EV.js';
2
+ export { AUTH_CONSTANTS, ApiError, DEFAULT_AUTH_URL, DEFAULT_SDK_WIDGET_REGISTRY, FluidAuthProvider, FluidProvider, FluidThemeProvider, MessagingScreen, STORAGE_KEYS, URL_PARAMS, USER_TYPES, buildThemeDefinition, cleanTokenFromUrl, clearTokens, createDefaultAuthRedirect, createFluidClient, createFluidFileUploader, decodeToken, extractAllTokensFromUrl, extractCompanyTokenFromUrl, extractTokenFromUrl, getActiveThemeId, getStoredToken, getTokenExpiration, getTokenTimeRemaining, hasStoredToken, hasTokenInUrl, isApiError, isTokenExpired, isUserType, isValidToken, messagingScreenPropertySchema, normalizeComponentTree, storeToken, toNavigationItem, toScreenDefinition, transformManifestToRepAppData, transformThemes, useFluidApi, useFluidAuthContext, useFluidContext, useMessagingAuth, useMessagingConfig, useThemeContext, validateToken } from './chunk-R4SXB6EV.js';
3
+ import { ContactsScreen } from './chunk-CMF2FYTD.js';
4
+ export { ContactsScreen, contactsScreenPropertySchema } from './chunk-CMF2FYTD.js';
5
+ import { OrdersScreen } from './chunk-WFPYEYC7.js';
6
+ export { OrdersScreen, ordersScreenPropertySchema } from './chunk-WFPYEYC7.js';
7
+ import { CustomersScreen } from './chunk-WMBD65GH.js';
8
+ export { CustomersScreen, customersScreenPropertySchema } from './chunk-WMBD65GH.js';
9
+ import { ProductsScreen } from './chunk-QZMWG7EM.js';
10
+ export { ProductsScreen, productsScreenPropertySchema } from './chunk-QZMWG7EM.js';
11
+ import { CoreScreenPlaceholder } from './chunk-RS4OSTES.js';
12
+ import './chunk-424PT5DM.js';
13
+ import * as React2 from 'react';
14
+ import { createContext, memo, forwardRef, useContext, useRef, useEffect, useMemo, useState, useCallback } from 'react';
9
15
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
- import { decodeJwt, createRemoteJWKSet, jwtVerify } from 'jose';
16
+ import { useQuery } from '@tanstack/react-query';
11
17
  export { WIDGET_TYPE_NAMES, assertDefined, assertNever, isWidgetType, isWidgetTypeName, sectionLayoutConfig } from '@fluid-app/rep-core/types';
12
- import * as theme_star from '@fluid-app/rep-core/theme';
13
18
  export { AlertWidget, CalendarWidget, CarouselWidget, CatchUpWidget, ChartWidget, ContainerWidget, EmbedWidget, ImageWidget, LayoutWidget, ListWidget, MySiteWidget, NestedWidget, QuickShareWidget, RecentActivityWidget, SpacerWidget, TableWidget, TextWidget, ToDoWidget, VideoWidget, alertWidgetPropertySchema, calendarWidgetPropertySchema, carouselWidgetPropertySchema, catchUpWidgetPropertySchema, chartWidgetPropertySchema, containerWidgetPropertySchema, embedWidgetPropertySchema, imageWidgetPropertySchema, layoutWidgetPropertySchema, listWidgetPropertySchema, mySiteWidgetPropertySchema, nestedWidgetPropertySchema, quickShareWidgetPropertySchema, recentActivityWidgetPropertySchema, spacerWidgetPropertySchema, tableWidgetPropertySchema, textWidgetPropertySchema, toDoWidgetPropertySchema, videoWidgetPropertySchema, widgetPropertySchemas } from '@fluid-app/rep-widgets/widgets';
14
19
  export { createScreen, createWidgetFromShareable, createWidgetRegistry, groupChildrenByColumn } from '@fluid-app/rep-core/widget-utils';
15
20
  export { PROPERTY_FIELD_TYPES, gapValues, isPropertyFieldType } from '@fluid-app/rep-core/registries';
16
-
17
- // src/client/types.ts
18
- var HTTP_METHODS = {
19
- GET: "GET",
20
- POST: "POST",
21
- PUT: "PUT",
22
- PATCH: "PATCH",
23
- DELETE: "DELETE"
24
- };
25
-
26
- // src/auth/auth-redirect.ts
27
- var DEFAULT_AUTH_URL = "https://auth.fluid.app";
28
- var AUTH_REDIRECT_TOKEN_KEY = "jwt";
29
- var REDIRECT_TIMESTAMP_KEY = "__fluid_auth_redirect_ts";
30
- var REDIRECT_COOLDOWN_S = 10;
31
- function isRedirectLoop() {
32
- try {
33
- const ts = sessionStorage.getItem(REDIRECT_TIMESTAMP_KEY);
34
- if (!ts) return false;
35
- const elapsed = (Date.now() - Number(ts)) / 1e3;
36
- return elapsed < REDIRECT_COOLDOWN_S;
37
- } catch {
38
- return false;
39
- }
40
- }
41
- function markRedirect() {
42
- try {
43
- sessionStorage.setItem(REDIRECT_TIMESTAMP_KEY, String(Date.now()));
44
- } catch {
45
- }
46
- }
47
- function createDefaultAuthRedirect(authUrl) {
48
- return () => {
49
- if (isRedirectLoop()) {
50
- console.warn(
51
- "[FluidAuth] Auth redirect suppressed \u2014 possible redirect loop. Check that your auth server returns a token accepted by the API."
52
- );
53
- return;
54
- }
55
- markRedirect();
56
- const base = authUrl ?? DEFAULT_AUTH_URL;
57
- const currentUrl = encodeURIComponent(window.location.href);
58
- window.location.href = `${base}/?redirect_url=${currentUrl}`;
59
- };
60
- }
61
- function resolveAuthFailureHandler(onAuthFailure, authUrl) {
62
- return onAuthFailure ?? createDefaultAuthRedirect(authUrl);
63
- }
64
-
65
- // src/client/fluid-client.ts
66
- var ApiError = class _ApiError extends Error {
67
- status;
68
- data;
69
- constructor(message, status, data) {
70
- super(message);
71
- this.name = "ApiError";
72
- this.status = status;
73
- this.data = data;
74
- const errorWithCapture = Error;
75
- if (errorWithCapture.captureStackTrace) {
76
- errorWithCapture.captureStackTrace(this, _ApiError);
77
- }
78
- }
79
- toJSON() {
80
- return {
81
- name: this.name,
82
- message: this.message,
83
- status: this.status,
84
- data: this.data
85
- };
86
- }
87
- };
88
- function isApiError(error) {
89
- return error instanceof ApiError;
90
- }
91
- function isString(value) {
92
- return typeof value === "string";
93
- }
94
- function extractErrorMessage(data, fallback) {
95
- if ("message" in data && isString(data.message)) {
96
- return data.message;
97
- }
98
- if ("error_message" in data && isString(data.error_message)) {
99
- return data.error_message;
100
- }
101
- if ("error" in data && isString(data.error)) {
102
- return data.error;
103
- }
104
- return fallback;
105
- }
106
- function createFluidClient(config) {
107
- const { baseUrl, getAuthToken, onAuthError, defaultHeaders = {} } = config;
108
- const effectiveOnAuthError = onAuthError ?? createDefaultAuthRedirect();
109
- async function buildHeaders(customHeaders) {
110
- const headers = {
111
- "Content-Type": "application/json",
112
- ...defaultHeaders,
113
- ...customHeaders
114
- };
115
- if (getAuthToken) {
116
- const token = await getAuthToken();
117
- if (token) {
118
- headers.Authorization = `Bearer ${token}`;
119
- }
120
- }
121
- return headers;
122
- }
123
- function buildUrl(endpoint, params) {
124
- const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
125
- const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
126
- const url = new URL(normalizedBase + normalizedEndpoint);
127
- if (params) {
128
- for (const [key, value] of Object.entries(params)) {
129
- if (value === void 0 || value === null) {
130
- continue;
131
- }
132
- if (Array.isArray(value)) {
133
- for (const item of value) {
134
- url.searchParams.append(`${key}[]`, String(item));
135
- }
136
- } else if (typeof value === "object") {
137
- for (const [subKey, subValue] of Object.entries(
138
- value
139
- )) {
140
- if (subValue === void 0 || subValue === null) {
141
- continue;
142
- }
143
- if (Array.isArray(subValue)) {
144
- for (const item of subValue) {
145
- url.searchParams.append(`${key}[${subKey}][]`, String(item));
146
- }
147
- } else {
148
- url.searchParams.append(`${key}[${subKey}]`, String(subValue));
149
- }
150
- }
151
- } else {
152
- url.searchParams.append(key, String(value));
153
- }
154
- }
155
- }
156
- return url.toString();
157
- }
158
- const defaultRequestOptions = {
159
- method: HTTP_METHODS.GET
160
- };
161
- async function request(endpoint, options = {}) {
162
- const {
163
- method = defaultRequestOptions.method,
164
- headers: customHeaders,
165
- params,
166
- body,
167
- signal
168
- } = options;
169
- const url = buildUrl(
170
- endpoint,
171
- method === HTTP_METHODS.GET ? params : void 0
172
- );
173
- const headers = await buildHeaders(customHeaders);
174
- let response;
175
- try {
176
- const fetchOptions = {
177
- method,
178
- headers
179
- };
180
- if (signal !== void 0) {
181
- fetchOptions.signal = signal;
182
- }
183
- if (body && method !== HTTP_METHODS.GET) {
184
- fetchOptions.body = JSON.stringify(body);
185
- }
186
- response = await fetch(url, fetchOptions);
187
- } catch (networkError) {
188
- throw new ApiError(
189
- `Network error: ${networkError instanceof Error ? networkError.message : "Unknown network error"}`,
190
- 0,
191
- null
192
- );
193
- }
194
- if (response.status === 401) {
195
- effectiveOnAuthError();
196
- return null;
197
- }
198
- if (!response.ok) {
199
- try {
200
- const contentType = response.headers.get("content-type");
201
- if (contentType?.includes("application/json")) {
202
- const data = await response.json();
203
- const errorMessage = extractErrorMessage(
204
- data,
205
- `${method} request failed`
206
- );
207
- throw new ApiError(
208
- errorMessage,
209
- response.status,
210
- "errors" in data ? data.errors : data
211
- );
212
- } else {
213
- throw new ApiError(
214
- `${method} request failed with status ${response.status}`,
215
- response.status,
216
- null
217
- );
218
- }
219
- } catch (error) {
220
- if (isApiError(error)) {
221
- throw error;
222
- }
223
- throw new ApiError(
224
- `${method} request failed with status ${response.status}`,
225
- response.status,
226
- null
227
- );
228
- }
229
- }
230
- if (response.status === 204 || response.headers.get("content-length") === "0") {
231
- return null;
232
- }
233
- try {
234
- const data = await response.json();
235
- if (data === null || data === void 0) {
236
- throw new ApiError(
237
- "Unexpected null/undefined in JSON response",
238
- response.status,
239
- null
240
- );
241
- }
242
- return data;
243
- } catch (parseError) {
244
- if (isApiError(parseError)) {
245
- throw parseError;
246
- }
247
- throw new ApiError(
248
- "Failed to parse response as JSON",
249
- response.status,
250
- null
251
- );
252
- }
253
- }
254
- async function requestNullable(endpoint, options = {}) {
255
- return request(endpoint, options);
256
- }
257
- async function safeRequest(endpoint, options = {}) {
258
- try {
259
- const data = await request(endpoint, options);
260
- return { success: true, data };
261
- } catch (error) {
262
- if (isApiError(error)) {
263
- return { success: false, error };
264
- }
265
- return {
266
- success: false,
267
- error: new ApiError(
268
- error instanceof Error ? error.message : "Unknown error",
269
- 0,
270
- null
271
- )
272
- };
273
- }
274
- }
275
- function toParams(params) {
276
- return params;
277
- }
278
- const get = (endpoint, params, options) => {
279
- const baseOptions = {
280
- ...options,
281
- method: HTTP_METHODS.GET
282
- };
283
- const convertedParams = toParams(params);
284
- const requestOptions = convertedParams !== void 0 ? { ...baseOptions, params: convertedParams } : baseOptions;
285
- return request(endpoint, requestOptions);
286
- };
287
- const post = (endpoint, body, options) => request(endpoint, {
288
- ...options,
289
- method: HTTP_METHODS.POST,
290
- body
291
- });
292
- const put = (endpoint, body, options) => request(endpoint, {
293
- ...options,
294
- method: HTTP_METHODS.PUT,
295
- body
296
- });
297
- const patch = (endpoint, body, options) => request(endpoint, {
298
- ...options,
299
- method: HTTP_METHODS.PATCH,
300
- body
301
- });
302
- const del = (endpoint, options) => request(endpoint, {
303
- ...options,
304
- method: HTTP_METHODS.DELETE
305
- });
306
- return {
307
- // Low-level methods for custom endpoints
308
- request,
309
- requestNullable,
310
- safeRequest,
311
- get,
312
- post,
313
- put,
314
- patch,
315
- delete: del,
316
- // Products API - matches fluid-admin's /company/v1/products
317
- products: {
318
- list: (params) => get(
319
- "/company/v1/products",
320
- params
321
- ),
322
- get: (id) => get(`/company/v1/products/${id}`),
323
- search: (query, params) => get("/company/v1/products", {
324
- search_query: query,
325
- ...params
326
- })
327
- },
328
- // Orders API
329
- orders: {
330
- list: (params) => get("/orders", params),
331
- get: (id) => get(`/orders/${id}`),
332
- create: (data) => post("/orders", data)
333
- },
334
- // Reps API
335
- reps: {
336
- current: () => get("/reps/me"),
337
- updateProfile: (data) => patch("/reps/me", data)
338
- },
339
- // Profile API (themes, navigation, screens)
340
- profile: {
341
- get: () => get("/rep_app/manifest")
342
- },
343
- // Permissions API
344
- permissions: {
345
- get: () => get("/company/roles/my_permissions")
346
- },
347
- // Analytics API
348
- analytics: {
349
- dashboard: () => get("/analytics/dashboard"),
350
- sales: (params) => get("/analytics/sales", params)
351
- }
352
- };
353
- }
354
- var ThemeContext = createContext(null);
355
- function applyThemeVariables(theme, container) {
356
- const target = container ?? document.documentElement;
357
- const toRemove = [];
358
- for (let i = 0; i < target.style.length; i++) {
359
- const prop = target.style[i];
360
- if (prop.startsWith("--fluid-")) {
361
- toRemove.push(prop);
362
- }
363
- }
364
- for (const prop of toRemove) {
365
- target.style.removeProperty(prop);
366
- }
367
- for (const [key, value] of Object.entries(theme.config)) {
368
- target.style.setProperty(`--fluid-${key}`, value);
369
- }
370
- if (theme.mode) {
371
- target.dataset.fluidThemeMode = theme.mode;
372
- }
373
- }
374
- function FluidThemeProvider({
375
- children,
376
- initialTheme,
377
- container
378
- }) {
379
- const [currentTheme, setCurrentTheme] = useState(
380
- initialTheme ?? null
381
- );
382
- useEffect(() => {
383
- if (currentTheme) {
384
- applyThemeVariables(currentTheme, container ?? null);
385
- }
386
- }, [currentTheme, container]);
387
- const setTheme = useCallback((theme) => {
388
- setCurrentTheme(theme);
389
- }, []);
390
- const setThemeMode = useCallback((mode) => {
391
- setCurrentTheme((prev) => prev ? { ...prev, mode } : null);
392
- }, []);
393
- const value = useMemo(
394
- () => ({
395
- currentTheme,
396
- setTheme,
397
- setThemeMode
398
- }),
399
- [currentTheme, setTheme, setThemeMode]
400
- );
401
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
402
- }
403
- function useThemeContext() {
404
- const context = useContext(ThemeContext);
405
- if (!context) {
406
- throw new Error("useThemeContext must be used within a FluidThemeProvider");
407
- }
408
- return context;
409
- }
410
- var FluidContext = createContext(null);
411
- function FluidProvider({
412
- config,
413
- children,
414
- queryClient,
415
- initialTheme,
416
- themeContainer
417
- }) {
418
- const defaultQueryClient = useMemo(
419
- () => new QueryClient({
420
- defaultOptions: {
421
- queries: {
422
- staleTime: 1e3 * 60,
423
- // 1 minute
424
- retry: 1
425
- }
426
- }
427
- }),
428
- []
429
- );
430
- const configRef = useRef(config);
431
- configRef.current = config;
432
- const client = useMemo(
433
- () => createFluidClient({
434
- ...configRef.current,
435
- getAuthToken: () => configRef.current.getAuthToken?.() ?? null,
436
- onAuthError: () => configRef.current.onAuthError?.()
437
- }),
438
- // eslint-disable-next-line react-hooks/exhaustive-deps
439
- [config.baseUrl]
440
- );
441
- const contextValue = useMemo(
442
- () => ({ client, config: configRef.current }),
443
- [client]
444
- );
445
- const themeProviderProps = {
446
- ...initialTheme !== void 0 && { initialTheme },
447
- ...themeContainer !== void 0 && { container: themeContainer }
448
- };
449
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient ?? defaultQueryClient, children: /* @__PURE__ */ jsx(FluidContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(FluidThemeProvider, { ...themeProviderProps, children }) }) });
450
- }
451
- function useFluidContext() {
452
- const context = useContext(FluidContext);
453
- if (!context) {
454
- throw new Error("useFluidContext must be used within a FluidProvider");
455
- }
456
- return context;
457
- }
458
-
459
- // src/auth/constants.ts
460
- var AUTH_CONSTANTS = {
461
- /**
462
- * Grace period in milliseconds to account for clock skew
463
- * when checking token expiration. Tokens are considered valid
464
- * if they expire within this period.
465
- */
466
- TOKEN_GRACE_PERIOD_MS: 30 * 1e3,
467
- // 30 seconds
468
- /**
469
- * Default cookie max age in seconds (9 days).
470
- * This matches the typical JWT token lifetime from the Fluid API.
471
- */
472
- COOKIE_MAX_AGE: 9 * 24 * 60 * 60
473
- // 9 days = 777600 seconds
474
- };
475
- var STORAGE_KEYS = {
476
- /** localStorage key for user token */
477
- USER_TOKEN: "fluidUserToken",
478
- /** localStorage key for company token (legacy) */
479
- COMPANY_TOKEN: "fluidCompanyToken",
480
- /** Cookie name for auth token */
481
- AUTH_COOKIE: "auth_token"
482
- };
483
- var URL_PARAMS = {
484
- /** URL parameter name for user token */
485
- USER_TOKEN: "fluidUserToken",
486
- /** URL parameter name for company token (legacy) */
487
- COMPANY_TOKEN: "fluidCompanyToken"
488
- };
489
-
490
- // src/auth/browser-utils.ts
491
- function isBrowser() {
492
- return typeof window !== "undefined" && typeof document !== "undefined";
493
- }
494
-
495
- // src/auth/url-token.ts
496
- function extractTokenFromUrl(tokenKey = URL_PARAMS.USER_TOKEN) {
497
- if (!isBrowser()) {
498
- return null;
499
- }
500
- try {
501
- const searchParams = new URLSearchParams(window.location.search);
502
- return searchParams.get(tokenKey);
503
- } catch {
504
- return null;
505
- }
506
- }
507
- function extractCompanyTokenFromUrl(tokenKey = URL_PARAMS.COMPANY_TOKEN) {
508
- if (!isBrowser()) {
509
- return null;
510
- }
511
- try {
512
- const searchParams = new URLSearchParams(window.location.search);
513
- return searchParams.get(tokenKey);
514
- } catch {
515
- return null;
516
- }
517
- }
518
- function cleanTokenFromUrl(tokenKey = URL_PARAMS.USER_TOKEN) {
519
- if (!isBrowser()) {
520
- return;
521
- }
522
- try {
523
- const url = new URL(window.location.href);
524
- const hadToken = url.searchParams.has(tokenKey);
525
- const hadCompanyToken = url.searchParams.has(URL_PARAMS.COMPANY_TOKEN);
526
- url.searchParams.delete(tokenKey);
527
- url.searchParams.delete(URL_PARAMS.COMPANY_TOKEN);
528
- if (hadToken || hadCompanyToken) {
529
- window.history.replaceState(
530
- window.history.state,
531
- document.title,
532
- url.toString()
533
- );
534
- }
535
- } catch (error) {
536
- console.warn("[FluidAuth] Failed to clean token from URL:", error);
537
- }
538
- }
539
- function hasTokenInUrl(tokenKey = URL_PARAMS.USER_TOKEN) {
540
- if (!isBrowser()) {
541
- return false;
542
- }
543
- try {
544
- const searchParams = new URLSearchParams(window.location.search);
545
- return searchParams.has(tokenKey);
546
- } catch {
547
- return false;
548
- }
549
- }
550
- function extractAllTokensFromUrl(userTokenKey = URL_PARAMS.USER_TOKEN, companyTokenKey = URL_PARAMS.COMPANY_TOKEN) {
551
- if (!isBrowser()) {
552
- return { userToken: null, companyToken: null };
553
- }
554
- try {
555
- const searchParams = new URLSearchParams(window.location.search);
556
- return {
557
- userToken: searchParams.get(userTokenKey),
558
- companyToken: searchParams.get(companyTokenKey)
559
- };
560
- } catch {
561
- return { userToken: null, companyToken: null };
562
- }
563
- }
564
-
565
- // src/auth/token-storage.ts
566
- function parseCookies() {
567
- if (!isBrowser()) {
568
- return {};
569
- }
570
- const cookies = {};
571
- const cookieString = document.cookie;
572
- if (!cookieString) {
573
- return cookies;
574
- }
575
- cookieString.split(";").forEach((cookie) => {
576
- const [name, ...valueParts] = cookie.trim().split("=");
577
- if (name) {
578
- cookies[name] = decodeURIComponent(valueParts.join("="));
579
- }
580
- });
581
- return cookies;
582
- }
583
- function setCookie(name, value, options = {}) {
584
- if (!isBrowser()) {
585
- return;
586
- }
587
- const {
588
- maxAge = AUTH_CONSTANTS.COOKIE_MAX_AGE,
589
- path = "/",
590
- sameSite = "lax",
591
- secure = window.location.protocol === "https:"
592
- } = options;
593
- let cookieString = `${name}=${encodeURIComponent(value)}`;
594
- cookieString += `; path=${path}`;
595
- cookieString += `; max-age=${maxAge}`;
596
- cookieString += `; samesite=${sameSite}`;
597
- if (secure) {
598
- cookieString += "; secure";
599
- }
600
- document.cookie = cookieString;
601
- }
602
- function deleteCookie(name, path = "/") {
603
- if (!isBrowser()) {
604
- return;
605
- }
606
- document.cookie = `${name}=; path=${path}; max-age=0`;
607
- }
608
- function getStoredToken(config) {
609
- if (!isBrowser()) {
610
- return null;
611
- }
612
- const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
613
- const localStorageKey = STORAGE_KEYS.USER_TOKEN;
614
- const cookies = parseCookies();
615
- const cookieToken = cookies[cookieKey];
616
- if (cookieToken) {
617
- return cookieToken;
618
- }
619
- try {
620
- return localStorage.getItem(localStorageKey);
621
- } catch {
622
- return null;
623
- }
624
- }
625
- function storeToken(token, config) {
626
- if (!isBrowser()) {
627
- return;
628
- }
629
- const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
630
- const maxAge = config?.cookieMaxAge ?? AUTH_CONSTANTS.COOKIE_MAX_AGE;
631
- try {
632
- setCookie(cookieKey, token, {
633
- maxAge,
634
- path: "/",
635
- // Use 'none' with secure for cross-origin iframe scenarios
636
- sameSite: window.self !== window.top ? "none" : "lax",
637
- secure: window.location.protocol === "https:"
638
- });
639
- } catch (error) {
640
- console.warn("[FluidAuth] Failed to store token in cookie:", error);
641
- }
642
- try {
643
- localStorage.setItem(STORAGE_KEYS.USER_TOKEN, token);
644
- } catch (error) {
645
- console.warn("[FluidAuth] Failed to store token in localStorage:", error);
646
- }
647
- }
648
- function clearTokens(config) {
649
- if (!isBrowser()) {
650
- return;
651
- }
652
- const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
653
- try {
654
- deleteCookie(cookieKey);
655
- } catch {
656
- }
657
- try {
658
- localStorage.removeItem(STORAGE_KEYS.USER_TOKEN);
659
- localStorage.removeItem(STORAGE_KEYS.COMPANY_TOKEN);
660
- } catch {
661
- }
662
- }
663
- function hasStoredToken(config) {
664
- return getStoredToken(config) !== null;
665
- }
666
-
667
- // src/auth/types.ts
668
- var USER_TYPES = {
669
- admin: "admin",
670
- rep: "rep",
671
- root_admin: "root_admin",
672
- customer: "customer"
673
- };
674
- function isUserType(value) {
675
- return Object.values(USER_TYPES).includes(value);
676
- }
677
-
678
- // src/auth/token-utils.ts
679
- function extractPayloadFromJose(decoded) {
680
- const rawUserType = decoded.user_type;
681
- const rawOgUserType = decoded.og_user_type;
682
- return {
683
- id: typeof decoded.id === "number" ? decoded.id : void 0,
684
- email: typeof decoded.email === "string" ? decoded.email : void 0,
685
- full_name: typeof decoded.full_name === "string" ? decoded.full_name : void 0,
686
- user_type: typeof rawUserType === "string" && isUserType(rawUserType) ? rawUserType : "rep",
687
- og_user_type: typeof rawOgUserType === "string" && isUserType(rawOgUserType) ? rawOgUserType : void 0,
688
- company_id: typeof decoded.company_id === "number" ? decoded.company_id : void 0,
689
- exp: decoded.exp,
690
- auth_type: typeof decoded.auth_type === "string" ? decoded.auth_type : void 0
691
- };
692
- }
693
- function decodeToken(token) {
694
- try {
695
- const decoded = decodeJwt(token);
696
- return extractPayloadFromJose(decoded);
697
- } catch (error) {
698
- console.error("[FluidAuth] Failed to decode JWT token:", error);
699
- return null;
700
- }
701
- }
702
- function isTokenExpired(token, gracePeriodMs = AUTH_CONSTANTS.TOKEN_GRACE_PERIOD_MS) {
703
- try {
704
- const decoded = decodeJwt(token);
705
- if (!decoded.exp) {
706
- return false;
707
- }
708
- const expirationTime = decoded.exp * 1e3;
709
- const currentTime = Date.now();
710
- return currentTime > expirationTime + gracePeriodMs;
711
- } catch {
712
- return true;
713
- }
714
- }
715
- function validateToken(token, gracePeriodMs = AUTH_CONSTANTS.TOKEN_GRACE_PERIOD_MS) {
716
- if (!token || token.trim() === "") {
717
- return {
718
- isValid: false,
719
- error: "Token is empty or not provided"
720
- };
721
- }
722
- const payload = decodeToken(token);
723
- if (!payload) {
724
- return {
725
- isValid: false,
726
- error: "Token has invalid format"
727
- };
728
- }
729
- if (isTokenExpired(token, gracePeriodMs)) {
730
- return {
731
- isValid: false,
732
- payload,
733
- error: "Token has expired"
734
- };
735
- }
736
- return {
737
- isValid: true,
738
- payload
739
- };
740
- }
741
- function isValidToken(result) {
742
- return result.isValid === true;
743
- }
744
- function getTokenExpiration(token) {
745
- try {
746
- const decoded = decodeJwt(token);
747
- if (!decoded.exp) {
748
- return null;
749
- }
750
- return new Date(decoded.exp * 1e3);
751
- } catch {
752
- return null;
753
- }
754
- }
755
- function getTokenTimeRemaining(token) {
756
- try {
757
- const decoded = decodeJwt(token);
758
- if (!decoded.exp) {
759
- return Infinity;
760
- }
761
- const expirationTime = decoded.exp * 1e3;
762
- const remaining = expirationTime - Date.now();
763
- return Math.max(0, remaining);
764
- } catch {
765
- return 0;
766
- }
767
- }
768
- async function verifyToken(token, jwksUrl) {
769
- try {
770
- const JWKS = createRemoteJWKSet(new URL(jwksUrl));
771
- const { payload } = await jwtVerify(token, JWKS);
772
- const decoded = payload;
773
- return extractPayloadFromJose(decoded);
774
- } catch (error) {
775
- console.error("[FluidAuth] JWT signature verification failed:", error);
776
- return null;
777
- }
778
- }
779
-
780
- // src/auth/dev-utils.ts
781
- function isDevBypassActive(devBypass) {
782
- if (!devBypass) return false;
783
- try {
784
- return import.meta.env.DEV === true;
785
- } catch {
786
- return false;
787
- }
788
- }
789
- function createDevUser() {
790
- return {
791
- id: 99999,
792
- // Dev placeholder — avoids falsy 0
793
- email: "dev@localhost",
794
- full_name: "Dev User",
795
- user_type: USER_TYPES.rep,
796
- og_user_type: void 0,
797
- company_id: 99999,
798
- // Dev placeholder — avoids falsy 0
799
- exp: void 0,
800
- // Never expires
801
- auth_type: "dev_bypass"
802
- };
803
- }
804
- var FluidAuthContext = createContext(null);
805
- function FluidAuthProvider({
806
- children,
807
- config
808
- }) {
809
- const configRef = useRef(config);
810
- configRef.current = config;
811
- const [isLoading2, setIsLoading] = useState(true);
812
- const [token, setToken] = useState(null);
813
- const [user, setUser] = useState(null);
814
- const [error, setError] = useState(null);
815
- useEffect(() => {
816
- const initializeAuth = async () => {
817
- const handleAuthFailure = () => {
818
- const current = configRef.current;
819
- const handler = resolveAuthFailureHandler(current?.onAuthFailure, current?.authUrl);
820
- handler();
821
- };
822
- try {
823
- if (isDevBypassActive(config?.devBypass)) {
824
- const envToken = import.meta.env.VITE_DEV_TOKEN;
825
- if (envToken) {
826
- const validation = validateToken(envToken, config?.gracePeriodMs);
827
- if (validation.isValid && validation.payload) {
828
- storeToken(envToken, config);
829
- setToken(envToken);
830
- setUser(validation.payload);
831
- setError(null);
832
- return;
833
- }
834
- console.warn(
835
- "[FluidAuth] VITE_DEV_TOKEN is invalid or expired, falling back to mock user"
836
- );
837
- }
838
- console.warn(
839
- "[FluidAuth] Dev bypass active - using mock user. API calls will fail without a real token."
840
- );
841
- const devUser = createDevUser();
842
- setToken(null);
843
- setUser(devUser);
844
- setError(null);
845
- return;
846
- }
847
- const tokenKey = config?.tokenKey ?? "fluidUserToken";
848
- let candidateToken = extractTokenFromUrl(tokenKey);
849
- if (!candidateToken && tokenKey !== AUTH_REDIRECT_TOKEN_KEY) {
850
- candidateToken = extractTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
851
- }
852
- cleanTokenFromUrl(tokenKey);
853
- cleanTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
854
- if (!candidateToken) {
855
- candidateToken = getStoredToken(config);
856
- }
857
- if (candidateToken) {
858
- let payload = null;
859
- if (config?.jwksUrl) {
860
- payload = await verifyToken(candidateToken, config.jwksUrl);
861
- if (!payload) {
862
- clearTokens(config);
863
- setToken(null);
864
- setUser(null);
865
- setError(new Error("JWT signature verification failed"));
866
- handleAuthFailure();
867
- return;
868
- }
869
- if (isTokenExpired(candidateToken, config?.gracePeriodMs)) {
870
- clearTokens(config);
871
- setToken(null);
872
- setUser(null);
873
- setError(new Error("Token has expired"));
874
- handleAuthFailure();
875
- return;
876
- }
877
- } else {
878
- const validation = validateToken(
879
- candidateToken,
880
- config?.gracePeriodMs
881
- );
882
- if (validation.isValid && validation.payload) {
883
- payload = validation.payload;
884
- } else {
885
- clearTokens(config);
886
- setToken(null);
887
- setUser(null);
888
- setError(new Error(validation.error ?? "Invalid token"));
889
- handleAuthFailure();
890
- return;
891
- }
892
- }
893
- storeToken(candidateToken, config);
894
- setToken(candidateToken);
895
- setUser(payload);
896
- setError(null);
897
- } else {
898
- setToken(null);
899
- setUser(null);
900
- setError(new Error("No authentication token found"));
901
- handleAuthFailure();
902
- }
903
- } catch (err) {
904
- const error2 = err instanceof Error ? err : new Error("Authentication error");
905
- setError(error2);
906
- setToken(null);
907
- setUser(null);
908
- handleAuthFailure();
909
- } finally {
910
- setIsLoading(false);
911
- }
912
- };
913
- void initializeAuth();
914
- }, []);
915
- const clearAuth = useCallback(() => {
916
- clearTokens(configRef.current);
917
- setToken(null);
918
- setUser(null);
919
- setError(null);
920
- }, []);
921
- const contextValue = useMemo(
922
- () => ({
923
- isAuthenticated: user !== null,
924
- isLoading: isLoading2,
925
- user,
926
- token,
927
- clearAuth,
928
- error
929
- }),
930
- [token, isLoading2, user, clearAuth, error]
931
- );
932
- return /* @__PURE__ */ jsx(FluidAuthContext.Provider, { value: contextValue, children });
933
- }
934
- function useFluidAuthContext() {
935
- const context = useContext(FluidAuthContext);
936
- if (!context) {
937
- throw new Error(
938
- "useFluidAuthContext must be used within a FluidAuthProvider. Wrap your app with <FluidAuthProvider> to use authentication features."
939
- );
940
- }
941
- return context;
942
- }
21
+ import { AppShellLayout } from '@fluid-app/rep-core/shell/AppShellLayout';
22
+ import { useThemeMode, ThemeModeProvider } from '@fluid-app/rep-core/shell/ThemeModeContext';
23
+ export { ThemeModeProvider, getThemeModeAttribute, useThemeMode } from '@fluid-app/rep-core/shell/ThemeModeContext';
24
+ import { normalizeSlug, getSystemNavigationBySection, isSystemNavigationItem } from '@fluid-app/rep-core/navigation/system-navigation-items';
25
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
26
+ import { faEllipsis } from '@fortawesome/pro-regular-svg-icons/faEllipsis';
27
+ import { faXmark } from '@fortawesome/pro-regular-svg-icons/faXmark';
28
+ import { RepIcon } from '@fluid-app/rep-core/components/RepIcon';
29
+ import { SidebarMenu, SidebarGroupLabel, SidebarMenuItem, SidebarMenuButton, useSidebar } from '@fluid-app/rep-core/shell/sidebar';
30
+ import { resolveTheme, applyTheme, removeAllThemes } from '@fluid-app/rep-core/theme';
31
+ import { faBars } from '@fortawesome/pro-regular-svg-icons/faBars';
32
+ import { faSun } from '@fortawesome/pro-regular-svg-icons/faSun';
33
+ import { faMoon } from '@fortawesome/pro-regular-svg-icons/faMoon';
34
+ import { faTableCellsLarge } from '@fortawesome/pro-regular-svg-icons/faTableCellsLarge';
35
+ import { DataAwareWidget } from '@fluid-app/rep-core/data-sources/DataAwareWidget';
36
+ import { useRegistry } from '@fluid-app/rep-widgets/contexts';
943
37
 
944
38
  // src/types/page-template.ts
945
39
  var PAGE_CATEGORIES = {
@@ -1171,60 +265,610 @@ function PageTemplateProvider({
1171
265
  for (const template of templates) {
1172
266
  if (!PageTemplateRegistry.has(template.id)) {
1173
267
  try {
1174
- PageTemplateRegistry.register(template);
1175
- registered.push(template.id);
1176
- } catch (error) {
1177
- console.warn(
1178
- `Failed to register page template "${template.id}":`,
1179
- error
1180
- );
268
+ PageTemplateRegistry.register(template);
269
+ registered.push(template.id);
270
+ } catch (error) {
271
+ console.warn(
272
+ `Failed to register page template "${template.id}":`,
273
+ error
274
+ );
275
+ }
276
+ }
277
+ }
278
+ registeredIds.current = registered;
279
+ return () => {
280
+ for (const id of registeredIds.current) {
281
+ PageTemplateRegistry.unregister(id);
282
+ }
283
+ registeredIds.current = [];
284
+ };
285
+ }, [templateKey]);
286
+ const contextValue = useMemo(
287
+ () => ({
288
+ resolvePages: resolveNavigationPages,
289
+ listTemplates: () => PageTemplateRegistry.listAll(),
290
+ getTemplate: (id) => PageTemplateRegistry.get(id),
291
+ hasTemplate: (id) => PageTemplateRegistry.has(id)
292
+ }),
293
+ []
294
+ );
295
+ return /* @__PURE__ */ jsx(PageTemplateContext.Provider, { value: contextValue, children });
296
+ }
297
+ function usePageTemplates() {
298
+ const context = useContext(PageTemplateContext);
299
+ if (!context) {
300
+ throw new Error(
301
+ "usePageTemplates must be used within a PageTemplateProvider"
302
+ );
303
+ }
304
+ return context;
305
+ }
306
+ function useResolvedPages(navigation) {
307
+ const { resolvePages } = usePageTemplates();
308
+ return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);
309
+ }
310
+ var PROFILE_QUERY_KEY = ["fluid", "profile"];
311
+ function useFluidProfile() {
312
+ const api = useFluidApi();
313
+ return useQuery({
314
+ queryKey: PROFILE_QUERY_KEY,
315
+ queryFn: () => api.profile.get()
316
+ });
317
+ }
318
+ function systemSetTimeoutZero(callback) {
319
+ setTimeout(callback, 0);
320
+ }
321
+ function matchQuery(filters, query) {
322
+ const {
323
+ type = "all",
324
+ exact,
325
+ fetchStatus,
326
+ predicate,
327
+ queryKey,
328
+ stale
329
+ } = filters;
330
+ if (queryKey) {
331
+ if (exact) {
332
+ if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {
333
+ return false;
334
+ }
335
+ } else if (!partialMatchKey(query.queryKey, queryKey)) {
336
+ return false;
337
+ }
338
+ }
339
+ if (type !== "all") {
340
+ const isActive = query.isActive();
341
+ if (type === "active" && !isActive) {
342
+ return false;
343
+ }
344
+ if (type === "inactive" && isActive) {
345
+ return false;
346
+ }
347
+ }
348
+ if (typeof stale === "boolean" && query.isStale() !== stale) {
349
+ return false;
350
+ }
351
+ if (fetchStatus && fetchStatus !== query.state.fetchStatus) {
352
+ return false;
353
+ }
354
+ if (predicate && !predicate(query)) {
355
+ return false;
356
+ }
357
+ return true;
358
+ }
359
+ function hashQueryKeyByOptions(queryKey, options) {
360
+ const hashFn = options?.queryKeyHashFn || hashKey;
361
+ return hashFn(queryKey);
362
+ }
363
+ function hashKey(queryKey) {
364
+ return JSON.stringify(
365
+ queryKey,
366
+ (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => {
367
+ result[key] = val[key];
368
+ return result;
369
+ }, {}) : val
370
+ );
371
+ }
372
+ function partialMatchKey(a, b) {
373
+ if (a === b) {
374
+ return true;
375
+ }
376
+ if (typeof a !== typeof b) {
377
+ return false;
378
+ }
379
+ if (a && b && typeof a === "object" && typeof b === "object") {
380
+ return Object.keys(b).every((key) => partialMatchKey(a[key], b[key]));
381
+ }
382
+ return false;
383
+ }
384
+ function isPlainObject(o) {
385
+ if (!hasObjectPrototype(o)) {
386
+ return false;
387
+ }
388
+ const ctor = o.constructor;
389
+ if (ctor === void 0) {
390
+ return true;
391
+ }
392
+ const prot = ctor.prototype;
393
+ if (!hasObjectPrototype(prot)) {
394
+ return false;
395
+ }
396
+ if (!prot.hasOwnProperty("isPrototypeOf")) {
397
+ return false;
398
+ }
399
+ if (Object.getPrototypeOf(o) !== Object.prototype) {
400
+ return false;
401
+ }
402
+ return true;
403
+ }
404
+ function hasObjectPrototype(o) {
405
+ return Object.prototype.toString.call(o) === "[object Object]";
406
+ }
407
+
408
+ // ../../../node_modules/.pnpm/@tanstack+query-core@5.90.12/node_modules/@tanstack/query-core/build/modern/notifyManager.js
409
+ var defaultScheduler = systemSetTimeoutZero;
410
+ function createNotifyManager() {
411
+ let queue = [];
412
+ let transactions = 0;
413
+ let notifyFn = (callback) => {
414
+ callback();
415
+ };
416
+ let batchNotifyFn = (callback) => {
417
+ callback();
418
+ };
419
+ let scheduleFn = defaultScheduler;
420
+ const schedule = (callback) => {
421
+ if (transactions) {
422
+ queue.push(callback);
423
+ } else {
424
+ scheduleFn(() => {
425
+ notifyFn(callback);
426
+ });
427
+ }
428
+ };
429
+ const flush = () => {
430
+ const originalQueue = queue;
431
+ queue = [];
432
+ if (originalQueue.length) {
433
+ scheduleFn(() => {
434
+ batchNotifyFn(() => {
435
+ originalQueue.forEach((callback) => {
436
+ notifyFn(callback);
437
+ });
438
+ });
439
+ });
440
+ }
441
+ };
442
+ return {
443
+ batch: (callback) => {
444
+ let result;
445
+ transactions++;
446
+ try {
447
+ result = callback();
448
+ } finally {
449
+ transactions--;
450
+ if (!transactions) {
451
+ flush();
452
+ }
453
+ }
454
+ return result;
455
+ },
456
+ /**
457
+ * All calls to the wrapped function will be batched.
458
+ */
459
+ batchCalls: (callback) => {
460
+ return (...args) => {
461
+ schedule(() => {
462
+ callback(...args);
463
+ });
464
+ };
465
+ },
466
+ schedule,
467
+ /**
468
+ * Use this method to set a custom notify function.
469
+ * This can be used to for example wrap notifications with `React.act` while running tests.
470
+ */
471
+ setNotifyFunction: (fn) => {
472
+ notifyFn = fn;
473
+ },
474
+ /**
475
+ * Use this method to set a custom function to batch notifications together into a single tick.
476
+ * By default React Query will use the batch function provided by ReactDOM or React Native.
477
+ */
478
+ setBatchNotifyFunction: (fn) => {
479
+ batchNotifyFn = fn;
480
+ },
481
+ setScheduler: (fn) => {
482
+ scheduleFn = fn;
483
+ }
484
+ };
485
+ }
486
+ var notifyManager = createNotifyManager();
487
+
488
+ // ../../../node_modules/.pnpm/@tanstack+query-persist-client-core@5.91.11/node_modules/@tanstack/query-persist-client-core/build/modern/createPersister.js
489
+ var PERSISTER_KEY_PREFIX = "tanstack-query";
490
+ function experimental_createQueryPersister({
491
+ storage: storage2,
492
+ buster = "",
493
+ maxAge = 1e3 * 60 * 60 * 24,
494
+ serialize = JSON.stringify,
495
+ deserialize = JSON.parse,
496
+ prefix = PERSISTER_KEY_PREFIX,
497
+ refetchOnRestore = true,
498
+ filters
499
+ }) {
500
+ function isExpiredOrBusted(persistedQuery) {
501
+ if (persistedQuery.state.dataUpdatedAt) {
502
+ const queryAge = Date.now() - persistedQuery.state.dataUpdatedAt;
503
+ const expired = queryAge > maxAge;
504
+ const busted = persistedQuery.buster !== buster;
505
+ if (expired || busted) {
506
+ return true;
507
+ }
508
+ return false;
509
+ }
510
+ return true;
511
+ }
512
+ async function retrieveQuery(queryHash, afterRestoreMacroTask) {
513
+ if (storage2 != null) {
514
+ const storageKey = `${prefix}-${queryHash}`;
515
+ try {
516
+ const storedData = await storage2.getItem(storageKey);
517
+ if (storedData) {
518
+ const persistedQuery = await deserialize(storedData);
519
+ if (isExpiredOrBusted(persistedQuery)) {
520
+ await storage2.removeItem(storageKey);
521
+ } else {
522
+ if (afterRestoreMacroTask) {
523
+ notifyManager.schedule(
524
+ () => afterRestoreMacroTask(persistedQuery)
525
+ );
526
+ }
527
+ return persistedQuery.state.data;
528
+ }
529
+ }
530
+ } catch (err) {
531
+ if (process.env.NODE_ENV === "development") {
532
+ console.error(err);
533
+ console.warn(
534
+ "Encountered an error attempting to restore query cache from persisted location."
535
+ );
536
+ }
537
+ await storage2.removeItem(storageKey);
538
+ }
539
+ }
540
+ return;
541
+ }
542
+ async function persistQueryByKey(queryKey, queryClient) {
543
+ if (storage2 != null) {
544
+ const query = queryClient.getQueryCache().find({ queryKey });
545
+ if (query) {
546
+ await persistQuery(query);
547
+ } else {
548
+ if (process.env.NODE_ENV === "development") {
549
+ console.warn(
550
+ "Could not find query to be persisted. QueryKey:",
551
+ JSON.stringify(queryKey)
552
+ );
553
+ }
554
+ }
555
+ }
556
+ }
557
+ async function persistQuery(query) {
558
+ if (storage2 != null) {
559
+ const storageKey = `${prefix}-${query.queryHash}`;
560
+ storage2.setItem(
561
+ storageKey,
562
+ await serialize({
563
+ state: query.state,
564
+ queryKey: query.queryKey,
565
+ queryHash: query.queryHash,
566
+ buster
567
+ })
568
+ );
569
+ }
570
+ }
571
+ async function persisterFn(queryFn, ctx, query) {
572
+ const matchesFilter = filters ? matchQuery(filters, query) : true;
573
+ if (matchesFilter && query.state.data === void 0 && storage2 != null) {
574
+ const restoredData = await retrieveQuery(
575
+ query.queryHash,
576
+ (persistedQuery) => {
577
+ query.setState({
578
+ dataUpdatedAt: persistedQuery.state.dataUpdatedAt,
579
+ errorUpdatedAt: persistedQuery.state.errorUpdatedAt
580
+ });
581
+ if (refetchOnRestore === "always" || refetchOnRestore === true && query.isStale()) {
582
+ query.fetch();
583
+ }
584
+ }
585
+ );
586
+ if (restoredData !== void 0) {
587
+ return Promise.resolve(restoredData);
588
+ }
589
+ }
590
+ const queryFnResult = await queryFn(ctx);
591
+ if (matchesFilter && storage2 != null) {
592
+ notifyManager.schedule(() => {
593
+ persistQuery(query);
594
+ });
595
+ }
596
+ return Promise.resolve(queryFnResult);
597
+ }
598
+ async function persisterGc() {
599
+ if (storage2?.entries) {
600
+ const entries = await storage2.entries();
601
+ for (const [key, value] of entries) {
602
+ if (key.startsWith(prefix)) {
603
+ const persistedQuery = await deserialize(value);
604
+ if (isExpiredOrBusted(persistedQuery)) {
605
+ await storage2.removeItem(key);
606
+ }
607
+ }
608
+ }
609
+ } else if (process.env.NODE_ENV === "development") {
610
+ throw new Error(
611
+ "Provided storage does not implement `entries` method. Garbage collection is not possible without ability to iterate over storage items."
612
+ );
613
+ }
614
+ }
615
+ async function restoreQueries(queryClient, filters2 = {}) {
616
+ const { exact, queryKey } = filters2;
617
+ if (storage2?.entries) {
618
+ const entries = await storage2.entries();
619
+ for (const [key, value] of entries) {
620
+ if (key.startsWith(prefix)) {
621
+ const persistedQuery = await deserialize(value);
622
+ if (isExpiredOrBusted(persistedQuery)) {
623
+ await storage2.removeItem(key);
624
+ continue;
625
+ }
626
+ if (queryKey) {
627
+ if (exact) {
628
+ if (persistedQuery.queryHash !== hashKey(queryKey)) {
629
+ continue;
630
+ }
631
+ } else if (!partialMatchKey(persistedQuery.queryKey, queryKey)) {
632
+ continue;
633
+ }
634
+ }
635
+ queryClient.setQueryData(
636
+ persistedQuery.queryKey,
637
+ persistedQuery.state.data,
638
+ {
639
+ updatedAt: persistedQuery.state.dataUpdatedAt
640
+ }
641
+ );
642
+ }
643
+ }
644
+ } else if (process.env.NODE_ENV === "development") {
645
+ throw new Error(
646
+ "Provided storage does not implement `entries` method. Restoration of all stored entries is not possible without ability to iterate over storage items."
647
+ );
648
+ }
649
+ }
650
+ return {
651
+ persisterFn,
652
+ persistQuery,
653
+ persistQueryByKey,
654
+ retrieveQuery,
655
+ persisterGc,
656
+ restoreQueries
657
+ };
658
+ }
659
+
660
+ // ../../platform/query-persister/src/persister.ts
661
+ var DB_NAME = "fluid_tanstack_query_cache";
662
+ var STORE_NAME = "fluid_queries";
663
+ var VERSION = 1;
664
+ var dbPromise = null;
665
+ async function deleteDatabase() {
666
+ return new Promise((resolve, reject) => {
667
+ console.warn("[IDB] Deleting database due to error");
668
+ const req = indexedDB.deleteDatabase(DB_NAME);
669
+ req.onsuccess = () => {
670
+ console.log("[IDB] Database deleted successfully");
671
+ resolve();
672
+ };
673
+ req.onerror = () => {
674
+ console.error("[IDB] Failed to delete database:", req.error);
675
+ reject(req.error ?? new Error("deleteDatabase failed"));
676
+ };
677
+ req.onblocked = () => {
678
+ console.warn("[IDB] Delete blocked: close all tabs using this database");
679
+ };
680
+ });
681
+ }
682
+ function openDatabase() {
683
+ return new Promise((resolve, reject) => {
684
+ const req = indexedDB.open(DB_NAME, VERSION);
685
+ req.onupgradeneeded = () => {
686
+ const upgradeDb = req.result;
687
+ if (!upgradeDb.objectStoreNames.contains(STORE_NAME)) {
688
+ upgradeDb.createObjectStore(STORE_NAME);
689
+ }
690
+ };
691
+ req.onsuccess = () => {
692
+ const conn = req.result;
693
+ conn.onversionchange = () => {
694
+ console.trace("[IDB] version change \u2013 closing connection");
695
+ conn.close();
696
+ dbPromise = null;
697
+ };
698
+ resolve(conn);
699
+ };
700
+ req.onblocked = () => {
701
+ console.warn("[IDB] open blocked: another connection is holding the DB");
702
+ };
703
+ req.onerror = () => {
704
+ reject(
705
+ req.error instanceof Error ? req.error : new Error(`IndexedDB open failed: ${String(req.error)}`)
706
+ );
707
+ };
708
+ });
709
+ }
710
+ async function getDbWithRecovery() {
711
+ try {
712
+ return await openDatabase();
713
+ } catch (err) {
714
+ console.warn("[IDB] Initial open failed, attempting recovery:", err);
715
+ try {
716
+ await deleteDatabase();
717
+ console.log("[IDB] Retrying database open after deletion");
718
+ return await openDatabase();
719
+ } catch (retryErr) {
720
+ console.error("[IDB] Recovery failed:", retryErr);
721
+ throw retryErr;
722
+ }
723
+ }
724
+ }
725
+ function getDb() {
726
+ if (dbPromise) return dbPromise;
727
+ dbPromise = getDbWithRecovery().catch((err) => {
728
+ dbPromise = null;
729
+ throw err;
730
+ });
731
+ return dbPromise;
732
+ }
733
+ var storage = {
734
+ async getItem(key) {
735
+ try {
736
+ const db = await getDb();
737
+ return new Promise((res) => {
738
+ try {
739
+ const r = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key);
740
+ r.onsuccess = () => res(r.result);
741
+ r.onerror = () => {
742
+ console.trace("[IDB] getItem error:", r.error);
743
+ res(void 0);
744
+ };
745
+ } catch (txErr) {
746
+ console.trace("[IDB] getItem transaction error:", txErr);
747
+ res(void 0);
1181
748
  }
749
+ });
750
+ } catch (err) {
751
+ console.trace("[IDB] getItem getDb error:", err);
752
+ try {
753
+ const db = await getDb();
754
+ return new Promise((res) => {
755
+ try {
756
+ const r = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key);
757
+ r.onsuccess = () => res(r.result);
758
+ r.onerror = () => {
759
+ console.trace("[IDB] getItem retry error:", r.error);
760
+ res(void 0);
761
+ };
762
+ } catch (txErr) {
763
+ console.trace("[IDB] getItem retry transaction error:", txErr);
764
+ res(void 0);
765
+ }
766
+ });
767
+ } catch (recoveryErr) {
768
+ console.trace("[IDB] getItem recovery failed:", recoveryErr);
1182
769
  }
770
+ return void 0;
1183
771
  }
1184
- registeredIds.current = registered;
1185
- return () => {
1186
- for (const id of registeredIds.current) {
1187
- PageTemplateRegistry.unregister(id);
772
+ },
773
+ async setItem(key, value) {
774
+ const cloneableValue = JSON.parse(JSON.stringify(value));
775
+ try {
776
+ const db = await getDb();
777
+ if (!db) return;
778
+ await new Promise((resolve) => {
779
+ try {
780
+ const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneableValue, key);
781
+ req.onsuccess = () => resolve();
782
+ req.onerror = () => {
783
+ console.trace("[IDB] setItem error:", req.error);
784
+ resolve();
785
+ };
786
+ } catch (txErr) {
787
+ console.trace("[IDB] setItem transaction error:", txErr);
788
+ resolve();
789
+ }
790
+ });
791
+ } catch (err) {
792
+ console.trace("[IDB] setItem getDb error:", err);
793
+ try {
794
+ const db = await getDb();
795
+ await new Promise((resolve) => {
796
+ try {
797
+ const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneableValue, key);
798
+ req.onsuccess = () => resolve();
799
+ req.onerror = () => {
800
+ console.trace("[IDB] setItem retry error:", req.error);
801
+ resolve();
802
+ };
803
+ } catch (txErr) {
804
+ console.trace("[IDB] setItem retry transaction error:", txErr);
805
+ resolve();
806
+ }
807
+ });
808
+ } catch (recoveryErr) {
809
+ console.trace("[IDB] setItem recovery failed:", recoveryErr);
1188
810
  }
1189
- registeredIds.current = [];
1190
- };
1191
- }, [templateKey]);
1192
- const contextValue = useMemo(
1193
- () => ({
1194
- resolvePages: resolveNavigationPages,
1195
- listTemplates: () => PageTemplateRegistry.listAll(),
1196
- getTemplate: (id) => PageTemplateRegistry.get(id),
1197
- hasTemplate: (id) => PageTemplateRegistry.has(id)
1198
- }),
1199
- []
1200
- );
1201
- return /* @__PURE__ */ jsx(PageTemplateContext.Provider, { value: contextValue, children });
1202
- }
1203
- function usePageTemplates() {
1204
- const context = useContext(PageTemplateContext);
1205
- if (!context) {
1206
- throw new Error(
1207
- "usePageTemplates must be used within a PageTemplateProvider"
1208
- );
811
+ }
812
+ },
813
+ async removeItem(key) {
814
+ try {
815
+ const db = await getDb();
816
+ if (!db) return;
817
+ await new Promise((resolve) => {
818
+ try {
819
+ const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key);
820
+ req.onsuccess = () => resolve();
821
+ req.onerror = () => {
822
+ console.trace("[IDB] removeItem error:", req.error);
823
+ resolve();
824
+ };
825
+ } catch (txErr) {
826
+ console.trace("[IDB] removeItem transaction error:", txErr);
827
+ resolve();
828
+ }
829
+ });
830
+ } catch (err) {
831
+ console.trace("[IDB] removeItem getDb error:", err);
832
+ try {
833
+ const db = await getDb();
834
+ await new Promise((resolve) => {
835
+ try {
836
+ const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key);
837
+ req.onsuccess = () => resolve();
838
+ req.onerror = () => {
839
+ console.trace("[IDB] removeItem retry error:", req.error);
840
+ resolve();
841
+ };
842
+ } catch (txErr) {
843
+ console.trace("[IDB] removeItem retry transaction error:", txErr);
844
+ resolve();
845
+ }
846
+ });
847
+ } catch (recoveryErr) {
848
+ console.trace("[IDB] removeItem recovery failed:", recoveryErr);
849
+ }
850
+ }
1209
851
  }
1210
- return context;
1211
- }
1212
- function useResolvedPages(navigation) {
1213
- const { resolvePages } = usePageTemplates();
1214
- return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);
852
+ };
853
+ function createPersister() {
854
+ return experimental_createQueryPersister({
855
+ storage,
856
+ serialize: (persistedQuery) => persistedQuery,
857
+ deserialize: (cached) => cached
858
+ });
1215
859
  }
1216
860
 
1217
- // src/hooks/use-fluid-api.ts
1218
- function useFluidApi() {
1219
- const { client } = useFluidContext();
1220
- return client;
1221
- }
1222
- var PROFILE_QUERY_KEY = ["fluid", "profile"];
1223
- function useFluidProfile() {
861
+ // src/hooks/use-fluid-app.ts
862
+ var APP_DATA_QUERY_KEY = ["fluid", "app"];
863
+ var appDataPersister = typeof window !== "undefined" ? createPersister() : void 0;
864
+ function useFluidApp(options) {
1224
865
  const api = useFluidApi();
1225
866
  return useQuery({
1226
- queryKey: PROFILE_QUERY_KEY,
1227
- queryFn: () => api.profile.get()
867
+ queryKey: APP_DATA_QUERY_KEY,
868
+ queryFn: () => api.app.getRaw(),
869
+ select: transformManifestToRepAppData,
870
+ ...appDataPersister && { persister: appDataPersister.persisterFn },
871
+ ...options?.enabled !== void 0 && { enabled: options.enabled }
1228
872
  });
1229
873
  }
1230
874
  var PERMISSIONS_QUERY_KEY = ["fluid", "permissions"];
@@ -1261,12 +905,12 @@ function useFluidPermissions() {
1261
905
 
1262
906
  // src/hooks/use-fluid-theme.ts
1263
907
  function useFluidTheme() {
1264
- const { currentTheme, setTheme, setThemeMode } = useThemeContext();
908
+ const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();
1265
909
  return {
1266
910
  currentTheme,
1267
911
  setTheme,
1268
912
  setThemeMode,
1269
- mode: currentTheme?.mode
913
+ mode
1270
914
  };
1271
915
  }
1272
916
  var CURRENT_REP_QUERY_KEY = ["fluid", "currentRep"];
@@ -2010,17 +1654,13 @@ function RequireAuth({
2010
1654
  return /* @__PURE__ */ jsx(Fragment, { children });
2011
1655
  }
2012
1656
 
2013
- // src/themes/index.ts
2014
- var themes_exports = {};
2015
- __reExport(themes_exports, theme_star);
2016
-
2017
1657
  // src/screens/index.ts
2018
1658
  var screenPropertySchemas = {
2019
- MessagingScreen: () => import('./MessagingScreen-O42JEJMW.js').then((m) => m.messagingScreenPropertySchema),
2020
- ContactsScreen: () => import('./ContactsScreen-BYXF74BO.js').then((m) => m.contactsScreenPropertySchema),
2021
- OrdersScreen: () => import('./OrdersScreen-QQJFTTSS.js').then((m) => m.ordersScreenPropertySchema),
2022
- CustomersScreen: () => import('./CustomersScreen-VS6LGULO.js').then((m) => m.customersScreenPropertySchema),
2023
- ProductsScreen: () => import('./ProductsScreen-TRIT2FE3.js').then((m) => m.productsScreenPropertySchema)
1659
+ MessagingScreen: () => import('./MessagingScreen-5TEWFUX4.js').then((m) => m.messagingScreenPropertySchema),
1660
+ ContactsScreen: () => import('./ContactsScreen-33AJ5XUB.js').then((m) => m.contactsScreenPropertySchema),
1661
+ OrdersScreen: () => import('./OrdersScreen-IPPZLEYF.js').then((m) => m.ordersScreenPropertySchema),
1662
+ CustomersScreen: () => import('./CustomersScreen-E4HXBKV7.js').then((m) => m.customersScreenPropertySchema),
1663
+ ProductsScreen: () => import('./ProductsScreen-YTSOZW7B.js').then((m) => m.productsScreenPropertySchema)
2024
1664
  };
2025
1665
  var CORE_PAGE_IDS = {
2026
1666
  MESSAGING: "core-messaging",
@@ -2122,34 +1762,768 @@ function registerCorePageTemplates() {
2122
1762
  });
2123
1763
  }
2124
1764
  registerCorePageTemplates();
2125
- var export_BUILT_IN_THEMES = themes_exports.BUILT_IN_THEMES;
2126
- var export_CORE_COLOR_KEYS = themes_exports.CORE_COLOR_KEYS;
2127
- var export_DEFAULT_CORE_COLORS = themes_exports.DEFAULT_CORE_COLORS;
2128
- var export_catppuccinMocha = themes_exports.catppuccinMocha;
2129
- var export_clampChroma = themes_exports.clampChroma;
2130
- var export_defaultTheme = themes_exports.defaultTheme;
2131
- var export_detectThemeMode = themes_exports.detectThemeMode;
2132
- var export_dracula = themes_exports.dracula;
2133
- var export_everforest = themes_exports.everforest;
2134
- var export_generateColorSwatches = themes_exports.generateColorSwatches;
2135
- var export_generateDualTheme = themes_exports.generateDualTheme;
2136
- var export_generateSmartDualTheme = themes_exports.generateSmartDualTheme;
2137
- var export_generateTheme = themes_exports.generateTheme;
2138
- var export_generateThemeCssVars = themes_exports.generateThemeCssVars;
2139
- var export_gruvbox = themes_exports.gruvbox;
2140
- var export_mod = themes_exports.mod;
2141
- var export_monokai = themes_exports.monokai;
2142
- var export_nord = themes_exports.nord;
2143
- var export_oklchString = themes_exports.oklchString;
2144
- var export_parseOklch = themes_exports.parseOklch;
2145
- var export_rosePine = themes_exports.rosePine;
2146
- var export_rotateHue = themes_exports.rotateHue;
2147
- var export_rotateSoft = themes_exports.rotateSoft;
2148
- var export_solarized = themes_exports.solarized;
2149
- var export_toDarkMode = themes_exports.toDarkMode;
2150
- var export_toLightMode = themes_exports.toLightMode;
2151
- var export_tokyoNight = themes_exports.tokyoNight;
1765
+ function isInSection(item, currentSlug) {
1766
+ const normalized = normalizeSlug(currentSlug);
1767
+ if (normalizeSlug(item.slug) === normalized) return true;
1768
+ return item.children?.some((child) => normalizeSlug(child.slug) === normalized) ?? false;
1769
+ }
1770
+ function getNavTarget(item) {
1771
+ if (item.slug && (!item.children || item.children.length === 0)) {
1772
+ return normalizeSlug(item.slug);
1773
+ }
1774
+ if (item.children && item.children.length > 0) {
1775
+ const firstChild = item.children[0];
1776
+ if (firstChild?.slug) return normalizeSlug(firstChild.slug);
1777
+ }
1778
+ return null;
1779
+ }
1780
+ function SdkBottomNav({
1781
+ navItems,
1782
+ currentSlug,
1783
+ onNavigate,
1784
+ maxVisibleItems = 5
1785
+ }) {
1786
+ const { isMobile, useBottomNav } = useSidebar();
1787
+ const [moreOpen, setMoreOpen] = React2.useState(false);
1788
+ if (!isMobile || !useBottomNav) return null;
1789
+ const navigableItems = navItems.filter((item) => !item.parent_id).filter((item) => item.slug || item.children && item.children.length > 0);
1790
+ const hasOverflow = navigableItems.length > maxVisibleItems;
1791
+ const visibleItems = hasOverflow ? navigableItems.slice(0, maxVisibleItems - 1) : navigableItems;
1792
+ const overflowItems = hasOverflow ? navigableItems.slice(maxVisibleItems - 1) : [];
1793
+ const handleItemClick = (item) => {
1794
+ const target = getNavTarget(item);
1795
+ if (target) onNavigate(target);
1796
+ setMoreOpen(false);
1797
+ };
1798
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1799
+ /* @__PURE__ */ jsxs("nav", { className: "fixed right-0 bottom-0 left-0 z-40 flex h-16 items-end justify-around border-t border-border bg-sidebar pb-[env(safe-area-inset-bottom)]", children: [
1800
+ visibleItems.map((item) => {
1801
+ const isActive = isInSection(item, currentSlug);
1802
+ return /* @__PURE__ */ jsxs(
1803
+ "button",
1804
+ {
1805
+ type: "button",
1806
+ onClick: () => handleItemClick(item),
1807
+ className: `flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${isActive ? "text-primary" : "text-muted-foreground"}`,
1808
+ children: [
1809
+ item.icon ? /* @__PURE__ */ jsx(RepIcon, { name: item.icon, className: "size-5" }) : /* @__PURE__ */ jsx("span", { className: "size-5" }),
1810
+ /* @__PURE__ */ jsx("span", { className: "max-w-[72px] truncate", children: item.label })
1811
+ ]
1812
+ },
1813
+ item.id ?? item.slug ?? item.label
1814
+ );
1815
+ }),
1816
+ hasOverflow && /* @__PURE__ */ jsxs(
1817
+ "button",
1818
+ {
1819
+ type: "button",
1820
+ onClick: () => setMoreOpen(true),
1821
+ className: `flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${overflowItems.some((item) => isInSection(item, currentSlug)) ? "text-primary" : "text-muted-foreground"}`,
1822
+ children: [
1823
+ /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: faEllipsis, className: "size-5" }),
1824
+ /* @__PURE__ */ jsx("span", { children: "More" })
1825
+ ]
1826
+ }
1827
+ )
1828
+ ] }),
1829
+ moreOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex flex-col", children: [
1830
+ /* @__PURE__ */ jsx(
1831
+ "div",
1832
+ {
1833
+ className: "flex-1 bg-black/50",
1834
+ onClick: () => setMoreOpen(false),
1835
+ "aria-hidden": "true"
1836
+ }
1837
+ ),
1838
+ /* @__PURE__ */ jsxs("div", { className: "rounded-t-2xl bg-sidebar pb-[env(safe-area-inset-bottom)]", children: [
1839
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 pt-4 pb-2", children: [
1840
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "More" }),
1841
+ /* @__PURE__ */ jsx(
1842
+ "button",
1843
+ {
1844
+ type: "button",
1845
+ onClick: () => setMoreOpen(false),
1846
+ className: "flex items-center justify-center rounded-full p-1 text-muted-foreground hover:bg-sidebar-accent",
1847
+ "aria-label": "Close",
1848
+ children: /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: faXmark, className: "size-5" })
1849
+ }
1850
+ )
1851
+ ] }),
1852
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-4", children: overflowItems.map((item) => {
1853
+ const isActive = isInSection(item, currentSlug);
1854
+ return /* @__PURE__ */ jsxs(
1855
+ "button",
1856
+ {
1857
+ type: "button",
1858
+ onClick: () => handleItemClick(item),
1859
+ className: `flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${isActive ? "bg-sidebar-primary text-sidebar-primary-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent"}`,
1860
+ children: [
1861
+ item.icon && /* @__PURE__ */ jsx(RepIcon, { name: item.icon, className: "size-5" }),
1862
+ /* @__PURE__ */ jsx("span", { children: item.label })
1863
+ ]
1864
+ },
1865
+ item.id ?? item.slug ?? item.label
1866
+ );
1867
+ }) })
1868
+ ] })
1869
+ ] })
1870
+ ] });
1871
+ }
1872
+ var SPIN_STYLE_ID2 = "fluid-app-shell-loading-spin";
1873
+ function ensureSpinStyle2() {
1874
+ if (typeof document === "undefined") return;
1875
+ if (document.getElementById(SPIN_STYLE_ID2)) return;
1876
+ const style = document.createElement("style");
1877
+ style.id = SPIN_STYLE_ID2;
1878
+ style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;
1879
+ document.head.appendChild(style);
1880
+ }
1881
+ function AppShellLoading() {
1882
+ useEffect(() => {
1883
+ ensureSpinStyle2();
1884
+ }, []);
1885
+ return /* @__PURE__ */ jsxs(
1886
+ "div",
1887
+ {
1888
+ style: {
1889
+ display: "flex",
1890
+ flexDirection: "column",
1891
+ alignItems: "center",
1892
+ justifyContent: "center",
1893
+ minHeight: "100vh",
1894
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1895
+ backgroundColor: "#f9fafb"
1896
+ },
1897
+ children: [
1898
+ /* @__PURE__ */ jsx(
1899
+ "div",
1900
+ {
1901
+ style: {
1902
+ width: "40px",
1903
+ height: "40px",
1904
+ border: "3px solid #e5e7eb",
1905
+ borderTopColor: "#3b82f6",
1906
+ borderRadius: "50%",
1907
+ animation: "spin 1s linear infinite"
1908
+ }
1909
+ }
1910
+ ),
1911
+ /* @__PURE__ */ jsx(
1912
+ "p",
1913
+ {
1914
+ style: {
1915
+ marginTop: "1rem",
1916
+ color: "#6b7280",
1917
+ fontSize: "0.875rem"
1918
+ },
1919
+ children: "Loading..."
1920
+ }
1921
+ )
1922
+ ]
1923
+ }
1924
+ );
1925
+ }
1926
+ function collectNavSlugs(items) {
1927
+ const slugs = [];
1928
+ for (const item of items) {
1929
+ if (item.slug) slugs.push(normalizeSlug(item.slug));
1930
+ for (const child of item.children ?? []) {
1931
+ if (child.slug) slugs.push(normalizeSlug(child.slug));
1932
+ }
1933
+ }
1934
+ return [...slugs].sort((a, b) => b.split("/").length - a.split("/").length);
1935
+ }
1936
+ function matchSlugPrefix(fullSlug, navSlugs) {
1937
+ const normalized = normalizeSlug(fullSlug);
1938
+ if (!normalized) return void 0;
1939
+ for (const navSlug of navSlugs) {
1940
+ if (normalized === navSlug) {
1941
+ return { matchedSlug: navSlug, rest: "" };
1942
+ }
1943
+ if (normalized.startsWith(navSlug + "/")) {
1944
+ return {
1945
+ matchedSlug: navSlug,
1946
+ rest: normalized.slice(navSlug.length + 1)
1947
+ };
1948
+ }
1949
+ }
1950
+ return void 0;
1951
+ }
1952
+ function extractSlugFromPathname(pathname, basePath) {
1953
+ if (basePath === "/") {
1954
+ return pathname.replace(/^\//, "");
1955
+ }
1956
+ if (pathname.startsWith(basePath)) {
1957
+ return pathname.slice(basePath.length).replace(/^\//, "");
1958
+ }
1959
+ return pathname.replace(/^\//, "");
1960
+ }
1961
+ function isSlugInSection(item, currentSlug, navSlugs) {
1962
+ const match = matchSlugPrefix(currentSlug, navSlugs);
1963
+ const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);
1964
+ if (normalizeSlug(item.slug) === baseSlug) return true;
1965
+ return item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ?? false;
1966
+ }
1967
+ function SdkNavigation({
1968
+ navItems,
1969
+ currentSlug,
1970
+ onNavigate,
1971
+ navSlugs
1972
+ }) {
1973
+ return /* @__PURE__ */ jsx(SidebarMenu, { children: navItems.map((item, index) => {
1974
+ if (!item.slug && !item.label) return null;
1975
+ if (!item.slug) {
1976
+ return /* @__PURE__ */ jsx(SidebarGroupLabel, { children: item.label }, item.id ?? `section-${index}`);
1977
+ }
1978
+ const itemSlug = normalizeSlug(item.slug);
1979
+ const isActive = isSlugInSection(item, currentSlug, navSlugs);
1980
+ return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(
1981
+ SidebarMenuButton,
1982
+ {
1983
+ isActive,
1984
+ onClick: () => onNavigate(itemSlug),
1985
+ children: [
1986
+ item.icon && /* @__PURE__ */ jsx(RepIcon, { name: item.icon }),
1987
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label })
1988
+ ]
1989
+ }
1990
+ ) }, item.id ?? itemSlug);
1991
+ }) });
1992
+ }
1993
+ function QuickLinksDropdown({ onNavigate }) {
1994
+ const [open, setOpen] = useState(false);
1995
+ const panelRef = useRef(null);
1996
+ const buttonRef = useRef(null);
1997
+ const close = useCallback(() => setOpen(false), []);
1998
+ useEffect(() => {
1999
+ if (!open) return;
2000
+ const handleMouseDown = (e) => {
2001
+ if (panelRef.current && !panelRef.current.contains(e.target) && buttonRef.current && !buttonRef.current.contains(e.target)) {
2002
+ close();
2003
+ }
2004
+ };
2005
+ const handleKeyDown = (e) => {
2006
+ if (e.key === "Escape") close();
2007
+ };
2008
+ document.addEventListener("mousedown", handleMouseDown);
2009
+ document.addEventListener("keydown", handleKeyDown);
2010
+ return () => {
2011
+ document.removeEventListener("mousedown", handleMouseDown);
2012
+ document.removeEventListener("keydown", handleKeyDown);
2013
+ };
2014
+ }, [open, close]);
2015
+ const sections = getSystemNavigationBySection();
2016
+ const handleItemClick = (slug) => {
2017
+ onNavigate(slug);
2018
+ close();
2019
+ };
2020
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2021
+ /* @__PURE__ */ jsx(
2022
+ "button",
2023
+ {
2024
+ ref: buttonRef,
2025
+ type: "button",
2026
+ onClick: () => setOpen((prev) => !prev),
2027
+ className: "flex shrink-0 items-center justify-center rounded-md p-2 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
2028
+ "aria-label": "Quick links",
2029
+ "aria-expanded": open,
2030
+ children: /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: faTableCellsLarge, className: "h-4 w-4" })
2031
+ }
2032
+ ),
2033
+ open && /* @__PURE__ */ jsx(
2034
+ "div",
2035
+ {
2036
+ ref: panelRef,
2037
+ className: "absolute top-full right-0 z-50 mt-1 w-[700px] max-w-[90vw] overflow-hidden rounded-lg border border-border bg-background shadow-lg",
2038
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2039
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b bg-background p-4", children: [
2040
+ /* @__PURE__ */ jsx(
2041
+ FontAwesomeIcon,
2042
+ {
2043
+ icon: faTableCellsLarge,
2044
+ className: "h-5 w-5 text-muted-foreground"
2045
+ }
2046
+ ),
2047
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Shortcuts" })
2048
+ ] }),
2049
+ /* @__PURE__ */ jsx("div", { className: "space-y-6 bg-muted p-6", children: Object.entries(sections).map(([sectionName, items]) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
2050
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: sectionName }),
2051
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-2", children: items.map((item) => /* @__PURE__ */ jsxs(
2052
+ "button",
2053
+ {
2054
+ type: "button",
2055
+ onClick: () => handleItemClick(item.slug),
2056
+ className: "flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-sidebar-foreground transition-colors hover:bg-sidebar-primary hover:text-sidebar-primary-foreground",
2057
+ children: [
2058
+ item.icon && /* @__PURE__ */ jsx(
2059
+ RepIcon,
2060
+ {
2061
+ name: item.icon,
2062
+ className: "h-4 w-4 shrink-0 text-muted-foreground"
2063
+ }
2064
+ ),
2065
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label })
2066
+ ]
2067
+ },
2068
+ item.slug
2069
+ )) })
2070
+ ] }, sectionName)) })
2071
+ ] })
2072
+ }
2073
+ )
2074
+ ] });
2075
+ }
2076
+ function SdkHeader({
2077
+ tabs,
2078
+ mobileTabs,
2079
+ currentSlug,
2080
+ onNavigate,
2081
+ navSlugs
2082
+ }) {
2083
+ const sidebar = useSidebar();
2084
+ const themeMode = useThemeMode();
2085
+ const match = matchSlugPrefix(currentSlug, navSlugs);
2086
+ const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);
2087
+ const themeIcon = themeMode.mode === "dark" ? faMoon : faSun;
2088
+ const activeTabs = sidebar.isMobile && sidebar.useBottomNav && mobileTabs ? mobileTabs : tabs;
2089
+ return /* @__PURE__ */ jsxs("header", { className: "flex h-[52px] shrink-0 items-center gap-2 bg-sidebar px-6", children: [
2090
+ sidebar.isMobile && !sidebar.useBottomNav && /* @__PURE__ */ jsx(
2091
+ "button",
2092
+ {
2093
+ type: "button",
2094
+ onClick: sidebar.toggleSidebar,
2095
+ className: "mr-2 flex shrink-0 items-center justify-center rounded-md p-2 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
2096
+ "aria-label": "Toggle sidebar",
2097
+ children: /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: faBars, className: "size-5" })
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsx("nav", { className: "scrollbar-none flex flex-1 items-center gap-1 overflow-x-auto", children: activeTabs.map((tab) => {
2101
+ const tabSlug = normalizeSlug(tab.slug);
2102
+ if (!tabSlug) return null;
2103
+ const isActive = tabSlug === baseSlug;
2104
+ return /* @__PURE__ */ jsx(
2105
+ "button",
2106
+ {
2107
+ type: "button",
2108
+ onClick: () => onNavigate(tabSlug),
2109
+ className: `border-b-2 px-4 py-3 text-sm font-medium whitespace-nowrap transition-colors ${isActive ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:border-border hover:text-foreground"}`,
2110
+ children: tab.label
2111
+ },
2112
+ tab.id ?? tabSlug
2113
+ );
2114
+ }) }),
2115
+ /* @__PURE__ */ jsx(QuickLinksDropdown, { onNavigate }),
2116
+ /* @__PURE__ */ jsx(
2117
+ "button",
2118
+ {
2119
+ type: "button",
2120
+ onClick: themeMode.cycleMode,
2121
+ className: "ml-2 flex shrink-0 items-center justify-center rounded-md p-2 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
2122
+ "aria-label": `Switch theme (current: ${themeMode.mode})`,
2123
+ children: /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: themeIcon, className: "size-3" })
2124
+ }
2125
+ )
2126
+ ] });
2127
+ }
2128
+ function WidgetRenderer({ widget, index }) {
2129
+ const registry = useRegistry();
2130
+ const Component = registry[widget.type];
2131
+ if (!Component) {
2132
+ console.warn(
2133
+ `[BuilderScreenView] Widget type "${String(widget.type)}" not found in registry`
2134
+ );
2135
+ return null;
2136
+ }
2137
+ if (widget.dataSource) {
2138
+ return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-x-hidden", children: /* @__PURE__ */ jsx(DataAwareWidget, { widget, Component }) }, widget.id ?? index);
2139
+ }
2140
+ return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-x-hidden", children: /* @__PURE__ */ jsx(Component, { ...widget.props }) }, widget.id ?? index);
2141
+ }
2142
+ function BuilderScreenViewImpl({ screen, className }) {
2143
+ const widgets = screen.component_tree;
2144
+ if (!widgets || widgets.length === 0) {
2145
+ return /* @__PURE__ */ jsx("div", { className: className ?? "h-full w-full", children: /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx("p", { children: "This screen has no content yet." }) }) });
2146
+ }
2147
+ return /* @__PURE__ */ jsx("div", { className: className ?? "h-full w-full", children: widgets.map((widget, index) => {
2148
+ if (!widget) return null;
2149
+ return /* @__PURE__ */ jsx(
2150
+ WidgetRenderer,
2151
+ {
2152
+ widget,
2153
+ index
2154
+ },
2155
+ widget.id ?? index
2156
+ );
2157
+ }) });
2158
+ }
2159
+ var BuilderScreenView = memo(BuilderScreenViewImpl);
2160
+ var SYSTEM_SLUG_SCREEN_MAP = {
2161
+ messages: MessagingScreen,
2162
+ contacts: ContactsScreen,
2163
+ shop: OrdersScreen,
2164
+ customers: CustomersScreen,
2165
+ products: ProductsScreen
2166
+ };
2167
+ function PageRouter({
2168
+ currentSlug,
2169
+ currentNavItem,
2170
+ customPages,
2171
+ screens,
2172
+ baseSlug,
2173
+ restParams
2174
+ }) {
2175
+ const screenById = useMemo(() => {
2176
+ if (!screens || screens.length === 0) return void 0;
2177
+ return new Map(screens.map((s) => [s.id, s]));
2178
+ }, [screens]);
2179
+ const screenBySlug = useMemo(() => {
2180
+ if (!screens || screens.length === 0) return void 0;
2181
+ return new Map(screens.filter((s) => s.slug).map((s) => [s.slug, s]));
2182
+ }, [screens]);
2183
+ const builderScreen = useMemo(() => {
2184
+ if (currentNavItem?.screen_id && screenById) {
2185
+ const byId = screenById.get(currentNavItem.screen_id);
2186
+ if (byId) return byId;
2187
+ }
2188
+ if (screenBySlug) {
2189
+ return screenBySlug.get(currentSlug) ?? screenBySlug.get(baseSlug) ?? void 0;
2190
+ }
2191
+ return void 0;
2192
+ }, [
2193
+ currentNavItem?.screen_id,
2194
+ screenById,
2195
+ screenBySlug,
2196
+ currentSlug,
2197
+ baseSlug
2198
+ ]);
2199
+ const SystemScreen = SYSTEM_SLUG_SCREEN_MAP[baseSlug];
2200
+ if (SystemScreen) {
2201
+ return /* @__PURE__ */ jsx(SystemScreen, {});
2202
+ }
2203
+ if (isSystemNavigationItem(baseSlug)) {
2204
+ return /* @__PURE__ */ jsx(CoreScreenPlaceholder, { name: currentNavItem?.label ?? baseSlug });
2205
+ }
2206
+ const ExactCustomPage = customPages?.[currentSlug];
2207
+ if (ExactCustomPage) {
2208
+ return /* @__PURE__ */ jsx(ExactCustomPage, { slug: currentSlug, params: restParams });
2209
+ }
2210
+ if (baseSlug !== currentSlug) {
2211
+ const PrefixCustomPage = customPages?.[baseSlug];
2212
+ if (PrefixCustomPage) {
2213
+ return /* @__PURE__ */ jsx(PrefixCustomPage, { slug: currentSlug, params: restParams });
2214
+ }
2215
+ }
2216
+ if (builderScreen) {
2217
+ return /* @__PURE__ */ jsx(BuilderScreenView, { screen: builderScreen });
2218
+ }
2219
+ return /* @__PURE__ */ jsx(CoreScreenPlaceholder, { name: currentNavItem?.label ?? currentSlug });
2220
+ }
2221
+ var AppNavigationContext = createContext(
2222
+ null
2223
+ );
2224
+ function AppNavigationProvider({
2225
+ currentSlug,
2226
+ basePath,
2227
+ navigate,
2228
+ children
2229
+ }) {
2230
+ const buildHref = useMemo(() => {
2231
+ return (slug) => {
2232
+ if (basePath === "/") return `/${slug}`;
2233
+ return `${basePath}/${slug}`;
2234
+ };
2235
+ }, [basePath]);
2236
+ const value = useMemo(
2237
+ () => ({ currentSlug, basePath, navigate, buildHref }),
2238
+ [currentSlug, basePath, navigate, buildHref]
2239
+ );
2240
+ return /* @__PURE__ */ jsx(AppNavigationContext.Provider, { value, children });
2241
+ }
2242
+ function useAppNavigation() {
2243
+ const ctx = useContext(AppNavigationContext);
2244
+ if (!ctx) {
2245
+ throw new Error(
2246
+ "useAppNavigation must be used within an <AppShell> or <AppNavigationProvider>"
2247
+ );
2248
+ }
2249
+ return ctx;
2250
+ }
2251
+ var THEME_STORAGE_KEY = "rep-theme-mode";
2252
+ var DEFAULT_NAVIGATION = [
2253
+ { label: "We Commerce", children: [] },
2254
+ { slug: "shop", label: "Shop", icon: "store", children: [] },
2255
+ { slug: "account", label: "Account", icon: "user-gear", children: [] },
2256
+ { slug: "messages", label: "Messaging", icon: "comment", children: [] },
2257
+ { label: "Marketing Assets", children: [] },
2258
+ {
2259
+ slug: "share/products",
2260
+ label: "Products",
2261
+ icon: "boxes-stacked",
2262
+ children: []
2263
+ }
2264
+ ];
2265
+ function getInitialThemeMode() {
2266
+ if (typeof window === "undefined") return "light";
2267
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
2268
+ if (stored === "light" || stored === "dark") return stored;
2269
+ return "light";
2270
+ }
2271
+ function findFirstNavigableSlug(items) {
2272
+ for (const item of items) {
2273
+ if (item.slug) return normalizeSlug(item.slug);
2274
+ for (const child of item.children ?? []) {
2275
+ if (child.slug) return normalizeSlug(child.slug);
2276
+ }
2277
+ }
2278
+ return "";
2279
+ }
2280
+ function findCurrentSection(items, slug) {
2281
+ const normalized = normalizeSlug(slug);
2282
+ for (const item of items) {
2283
+ if (normalizeSlug(item.slug) === normalized) return item;
2284
+ if (item.children?.some((child) => normalizeSlug(child.slug) === normalized)) {
2285
+ return item;
2286
+ }
2287
+ }
2288
+ return void 0;
2289
+ }
2290
+ function findNavItem(items, slug) {
2291
+ const normalized = normalizeSlug(slug);
2292
+ for (const item of items) {
2293
+ if (normalizeSlug(item.slug) === normalized) return item;
2294
+ for (const child of item.children ?? []) {
2295
+ if (normalizeSlug(child.slug) === normalized) return child;
2296
+ }
2297
+ }
2298
+ return void 0;
2299
+ }
2300
+ function AppShell({
2301
+ appData: appDataProp,
2302
+ navigation: navigationProp,
2303
+ customPages,
2304
+ basePath = "/",
2305
+ currentSlug: controlledSlug,
2306
+ onNavigate: onNavigateProp,
2307
+ sidebarHeader,
2308
+ sidebarFooter,
2309
+ children
2310
+ }) {
2311
+ const normalizedBasePath = useMemo(() => {
2312
+ const stripped = basePath.replace(/^\/|\/$/g, "");
2313
+ return stripped ? `/${stripped}` : "/";
2314
+ }, [basePath]);
2315
+ const { data: fetchedAppData, isLoading: isLoading2 } = useFluidApp({
2316
+ enabled: !appDataProp
2317
+ });
2318
+ const appData = appDataProp ?? fetchedAppData;
2319
+ const activeTheme = useMemo(() => {
2320
+ if (!appData?.profile?.themes?.length) return void 0;
2321
+ const themes = appData.profile.themes;
2322
+ const activeId = appData.profile.activeThemeId;
2323
+ return activeId ? themes.find((t) => t.id === activeId) ?? themes[0] : themes[0];
2324
+ }, [appData?.profile?.themes, appData?.profile?.activeThemeId]);
2325
+ useEffect(() => {
2326
+ if (!activeTheme) return;
2327
+ const resolved = resolveTheme(activeTheme);
2328
+ applyTheme(resolved);
2329
+ return () => {
2330
+ removeAllThemes();
2331
+ };
2332
+ }, [activeTheme]);
2333
+ const navItems = useMemo(() => {
2334
+ if (navigationProp) return navigationProp;
2335
+ const profileNav = appData?.profile?.navigation?.navigation_items;
2336
+ if (profileNav && profileNav.length > 0) return profileNav;
2337
+ return DEFAULT_NAVIGATION;
2338
+ }, [navigationProp, appData?.profile?.navigation?.navigation_items]);
2339
+ const mobileNavItems = useMemo(() => {
2340
+ const mobileNav = appData?.profile?.mobile_navigation?.navigation_items;
2341
+ if (mobileNav && mobileNav.length > 0) return mobileNav;
2342
+ return navItems;
2343
+ }, [appData?.profile?.mobile_navigation?.navigation_items, navItems]);
2344
+ const screens = appData?.screens;
2345
+ const navSlugs = useMemo(() => collectNavSlugs(navItems), [navItems]);
2346
+ const [themeMode, setThemeMode] = useState(getInitialThemeMode);
2347
+ const handleThemeModeChange = useCallback((mode) => {
2348
+ setThemeMode(mode);
2349
+ if (typeof window !== "undefined") {
2350
+ localStorage.setItem(THEME_STORAGE_KEY, mode);
2351
+ }
2352
+ }, []);
2353
+ const [pathSlug, setPathSlug] = useState(() => {
2354
+ if (typeof window === "undefined") return "";
2355
+ return extractSlugFromPathname(
2356
+ window.location.pathname,
2357
+ normalizedBasePath
2358
+ );
2359
+ });
2360
+ useEffect(() => {
2361
+ if (controlledSlug !== void 0) return;
2362
+ if (typeof window === "undefined") return;
2363
+ const currentPath = extractSlugFromPathname(
2364
+ window.location.pathname,
2365
+ normalizedBasePath
2366
+ );
2367
+ if (!currentPath) {
2368
+ const firstSlug = findFirstNavigableSlug(navItems);
2369
+ if (firstSlug) {
2370
+ const targetPath = normalizedBasePath === "/" ? `/${firstSlug}` : `${normalizedBasePath}/${firstSlug}`;
2371
+ history.replaceState(null, "", targetPath);
2372
+ setPathSlug(firstSlug);
2373
+ }
2374
+ }
2375
+ }, [navItems, controlledSlug, normalizedBasePath]);
2376
+ useEffect(() => {
2377
+ if (controlledSlug !== void 0) return;
2378
+ const handlePopState = () => {
2379
+ const slug = extractSlugFromPathname(
2380
+ window.location.pathname,
2381
+ normalizedBasePath
2382
+ );
2383
+ setPathSlug(slug);
2384
+ };
2385
+ window.addEventListener("popstate", handlePopState);
2386
+ return () => window.removeEventListener("popstate", handlePopState);
2387
+ }, [controlledSlug, normalizedBasePath]);
2388
+ const activeSlug = controlledSlug ?? pathSlug;
2389
+ const handleNavigate = useCallback(
2390
+ (slug) => {
2391
+ if (onNavigateProp) {
2392
+ onNavigateProp(slug);
2393
+ return;
2394
+ }
2395
+ const targetPath = normalizedBasePath === "/" ? `/${slug}` : `${normalizedBasePath}/${slug}`;
2396
+ history.pushState(null, "", targetPath);
2397
+ setPathSlug(slug);
2398
+ },
2399
+ [onNavigateProp, normalizedBasePath]
2400
+ );
2401
+ const slugMatch = useMemo(
2402
+ () => matchSlugPrefix(activeSlug, navSlugs),
2403
+ [activeSlug, navSlugs]
2404
+ );
2405
+ const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);
2406
+ const restParams = slugMatch?.rest ?? "";
2407
+ const currentSection = findCurrentSection(navItems, baseSlug);
2408
+ const secondLevelTabs = currentSection?.children ?? [];
2409
+ const mobileCurrentSection = findCurrentSection(mobileNavItems, baseSlug);
2410
+ const mobileSecondLevelTabs = mobileCurrentSection?.children ?? [];
2411
+ const currentNavItem = findNavItem(navItems, baseSlug);
2412
+ const content = typeof children === "function" ? children({ currentSlug: activeSlug, currentNavItem }) : children ?? /* @__PURE__ */ jsx(
2413
+ PageRouter,
2414
+ {
2415
+ currentSlug: activeSlug,
2416
+ currentNavItem,
2417
+ customPages,
2418
+ screens,
2419
+ baseSlug,
2420
+ restParams
2421
+ }
2422
+ );
2423
+ if (isLoading2 && !appData) {
2424
+ return /* @__PURE__ */ jsx(AppShellLoading, {});
2425
+ }
2426
+ return /* @__PURE__ */ jsx(
2427
+ ThemeModeProvider,
2428
+ {
2429
+ mode: themeMode,
2430
+ onModeChange: handleThemeModeChange,
2431
+ autoModeEnabled: false,
2432
+ children: /* @__PURE__ */ jsx(
2433
+ AppNavigationProvider,
2434
+ {
2435
+ currentSlug: activeSlug,
2436
+ basePath: normalizedBasePath,
2437
+ navigate: handleNavigate,
2438
+ children: /* @__PURE__ */ jsx(
2439
+ "div",
2440
+ {
2441
+ "data-theme": activeTheme?.id,
2442
+ "data-theme-mode": themeMode === "auto" ? void 0 : themeMode,
2443
+ children: /* @__PURE__ */ jsx(
2444
+ AppShellLayout,
2445
+ {
2446
+ sidebarContent: /* @__PURE__ */ jsx(
2447
+ SdkNavigation,
2448
+ {
2449
+ navItems,
2450
+ currentSlug: activeSlug,
2451
+ onNavigate: handleNavigate,
2452
+ navSlugs
2453
+ }
2454
+ ),
2455
+ headerContent: /* @__PURE__ */ jsx(
2456
+ SdkHeader,
2457
+ {
2458
+ tabs: secondLevelTabs,
2459
+ mobileTabs: mobileSecondLevelTabs,
2460
+ currentSlug: activeSlug,
2461
+ onNavigate: handleNavigate,
2462
+ navSlugs
2463
+ }
2464
+ ),
2465
+ sidebarHeader,
2466
+ sidebarFooter,
2467
+ useBottomNav: true,
2468
+ afterContent: /* @__PURE__ */ jsx(
2469
+ SdkBottomNav,
2470
+ {
2471
+ navItems: mobileNavItems,
2472
+ currentSlug: activeSlug,
2473
+ onNavigate: handleNavigate
2474
+ }
2475
+ ),
2476
+ children: content
2477
+ }
2478
+ )
2479
+ }
2480
+ )
2481
+ }
2482
+ )
2483
+ }
2484
+ );
2485
+ }
2486
+ var AppLink = forwardRef(
2487
+ function AppLink2({ to, onClick, children, ...rest }, ref) {
2488
+ const { navigate, buildHref } = useAppNavigation();
2489
+ const handleClick = (e) => {
2490
+ onClick?.(e);
2491
+ if (e.defaultPrevented) return;
2492
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
2493
+ if (e.button !== 0) return;
2494
+ if (rest.target && rest.target !== "_self") return;
2495
+ e.preventDefault();
2496
+ navigate(to);
2497
+ };
2498
+ return /* @__PURE__ */ jsx("a", { ref, href: buildHref(to), onClick: handleClick, ...rest, children });
2499
+ }
2500
+ );
2501
+ var export_DEFAULT_COLORS = themes_exports.DEFAULT_COLORS;
2502
+ var export_DEFAULT_FONT_FAMILIES = themes_exports.DEFAULT_FONT_FAMILIES;
2503
+ var export_DEFAULT_FONT_SIZES = themes_exports.DEFAULT_FONT_SIZES;
2504
+ var export_DEFAULT_RADII = themes_exports.DEFAULT_RADII;
2505
+ var export_DEFAULT_SPACING = themes_exports.DEFAULT_SPACING;
2506
+ var export_DEFAULT_THEME_ID = themes_exports.DEFAULT_THEME_ID;
2507
+ var export_DEFAULT_THEME_NAME = themes_exports.DEFAULT_THEME_NAME;
2508
+ var export_FONT_FAMILY_KEYS = themes_exports.FONT_FAMILY_KEYS;
2509
+ var export_FONT_SIZE_KEYS = themes_exports.FONT_SIZE_KEYS;
2510
+ var export_RADIUS_KEYS = themes_exports.RADIUS_KEYS;
2511
+ var export_SEMANTIC_COLOR_NAMES = themes_exports.SEMANTIC_COLOR_NAMES;
2512
+ var export_SHADE_STEPS = themes_exports.SHADE_STEPS;
2513
+ var export_applyTheme = themes_exports.applyTheme;
2514
+ var export_deriveDarkVariant = themes_exports.deriveDarkVariant;
2515
+ var export_deserialiseTheme = themes_exports.deserialiseTheme;
2516
+ var export_generateShades = themes_exports.generateShades;
2517
+ var export_generateThemeCSS = themes_exports.generateThemeCSS;
2518
+ var export_getDefaultThemeDefinition = themes_exports.getDefaultThemeDefinition;
2519
+ var export_getForegroundColor = themes_exports.getForegroundColor;
2520
+ var export_mergeDarkOverrides = themes_exports.mergeDarkOverrides;
2521
+ var export_parseColor = themes_exports.parseColor;
2522
+ var export_removeAllThemes = themes_exports.removeAllThemes;
2523
+ var export_removeTheme = themes_exports.removeTheme;
2524
+ var export_resolveTheme = themes_exports.resolveTheme;
2525
+ var export_serialiseTheme = themes_exports.serialiseTheme;
2152
2526
 
2153
- export { ACTIVITY_SLUGS, AUTH_CONSTANTS, ApiError, AuthError, AuthLoading, export_BUILT_IN_THEMES as BUILT_IN_THEMES, export_CORE_COLOR_KEYS as CORE_COLOR_KEYS, CORE_PAGE_IDS, CURRENT_REP_QUERY_KEY, DEFAULT_AUTH_URL, export_DEFAULT_CORE_COLORS as DEFAULT_CORE_COLORS, FluidAuthProvider, FluidProvider, FluidThemeProvider, PAGE_CATEGORIES, PERMISSIONS_QUERY_KEY, PROFILE_QUERY_KEY, PageTemplateProvider, PageTemplateRegistry, RequireAuth, STORAGE_KEYS, URL_PARAMS, USER_TYPES, export_catppuccinMocha as catppuccinMocha, export_clampChroma as clampChroma, cleanTokenFromUrl, clearTokens, createDefaultAuthRedirect, createFluidClient, decodeToken, export_defaultTheme as defaultTheme, export_detectThemeMode as detectThemeMode, export_dracula as dracula, export_everforest as everforest, extractAllTokensFromUrl, extractCompanyTokenFromUrl, extractTokenFromUrl, export_generateColorSwatches as generateColorSwatches, export_generateDualTheme as generateDualTheme, export_generateSmartDualTheme as generateSmartDualTheme, export_generateTheme as generateTheme, export_generateThemeCssVars as generateThemeCssVars, getAvailablePageTemplates, getCorePageTemplates, getOptionalPageTemplates, getProperty, getStoredToken, getTokenExpiration, getTokenTimeRemaining, export_gruvbox as gruvbox, hasData, hasStoredToken, hasTokenInUrl, isActivitySlug, isApiError, isContactStatus, isErrorResult, isIdle, isLoading, isTokenExpired, isUserType, isValidToken, export_mod as mod, export_monokai as monokai, export_nord as nord, export_oklchString as oklchString, export_parseOklch as parseOklch, resolveNavigationPages, export_rosePine as rosePine, export_rotateHue as rotateHue, export_rotateSoft as rotateSoft, screenPropertySchemas, selectProperty, export_solarized as solarized, storeToken, export_toDarkMode as toDarkMode, export_toLightMode as toLightMode, export_tokyoNight as tokyoNight, useActivities, useCalendarEvents, useCatchUps, useContact, useContacts, useConversationMessages, useConversations, useCurrentRep, useFluidApi, useFluidAuth, useFluidAuthContext, useFluidContext, useFluidPermissions, useFluidProfile, useFluidTheme, useMySite, usePageTemplates, useResolvedPages, useThemeContext, useTodos, validateNavigationPages, validateToken };
2527
+ export { ACTIVITY_SLUGS, APP_DATA_QUERY_KEY, AppLink, AppNavigationProvider, AppShell, AuthError, AuthLoading, BuilderScreenView, CORE_PAGE_IDS, CURRENT_REP_QUERY_KEY, export_DEFAULT_COLORS as DEFAULT_COLORS, export_DEFAULT_FONT_FAMILIES as DEFAULT_FONT_FAMILIES, export_DEFAULT_FONT_SIZES as DEFAULT_FONT_SIZES, export_DEFAULT_RADII as DEFAULT_RADII, export_DEFAULT_SPACING as DEFAULT_SPACING, export_DEFAULT_THEME_ID as DEFAULT_THEME_ID, export_DEFAULT_THEME_NAME as DEFAULT_THEME_NAME, export_FONT_FAMILY_KEYS as FONT_FAMILY_KEYS, export_FONT_SIZE_KEYS as FONT_SIZE_KEYS, PAGE_CATEGORIES, PERMISSIONS_QUERY_KEY, PROFILE_QUERY_KEY, PageRouter, PageTemplateProvider, PageTemplateRegistry, QuickLinksDropdown, export_RADIUS_KEYS as RADIUS_KEYS, RequireAuth, export_SEMANTIC_COLOR_NAMES as SEMANTIC_COLOR_NAMES, export_SHADE_STEPS as SHADE_STEPS, SdkHeader, SdkNavigation, export_applyTheme as applyTheme, collectNavSlugs, export_deriveDarkVariant as deriveDarkVariant, export_deserialiseTheme as deserialiseTheme, extractSlugFromPathname, export_generateShades as generateShades, export_generateThemeCSS as generateThemeCSS, getAvailablePageTemplates, getCorePageTemplates, export_getDefaultThemeDefinition as getDefaultThemeDefinition, export_getForegroundColor as getForegroundColor, getOptionalPageTemplates, getProperty, hasData, isActivitySlug, isContactStatus, isErrorResult, isIdle, isLoading, isSlugInSection, matchSlugPrefix, export_mergeDarkOverrides as mergeDarkOverrides, export_parseColor as parseColor, export_removeAllThemes as removeAllThemes, export_removeTheme as removeTheme, resolveNavigationPages, export_resolveTheme as resolveTheme, screenPropertySchemas, selectProperty, export_serialiseTheme as serialiseTheme, useActivities, useAppNavigation, useCalendarEvents, useCatchUps, useContact, useContacts, useConversationMessages, useConversations, useCurrentRep, useFluidApp, useFluidAuth, useFluidPermissions, useFluidProfile, useFluidTheme, useMySite, usePageTemplates, useResolvedPages, useTodos, validateNavigationPages };
2154
2528
  //# sourceMappingURL=index.js.map
2155
2529
  //# sourceMappingURL=index.js.map