@kizenapps/engine 0.0.1-b1f3597

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 (74) hide show
  1. package/LICENSE.md +674 -0
  2. package/README.md +160 -0
  3. package/dist/ThirdPartyScript-DdrF7zh7.d.ts +459 -0
  4. package/dist/automation-C8hsM_Rc.d.ts +36 -0
  5. package/dist/chunk-2MW4AGSN.js +2415 -0
  6. package/dist/chunk-2MW4AGSN.js.map +1 -0
  7. package/dist/chunk-3LDUYA57.js +61 -0
  8. package/dist/chunk-3LDUYA57.js.map +1 -0
  9. package/dist/chunk-5WRI5ZAA.js +29 -0
  10. package/dist/chunk-5WRI5ZAA.js.map +1 -0
  11. package/dist/chunk-727AYMPR.js +120 -0
  12. package/dist/chunk-727AYMPR.js.map +1 -0
  13. package/dist/chunk-GGGXYD3K.js +40 -0
  14. package/dist/chunk-GGGXYD3K.js.map +1 -0
  15. package/dist/chunk-LGJYUPYZ.js +3 -0
  16. package/dist/chunk-LGJYUPYZ.js.map +1 -0
  17. package/dist/chunk-NXLAROEM.js +730 -0
  18. package/dist/chunk-NXLAROEM.js.map +1 -0
  19. package/dist/chunk-P7GQBCEH.js +172 -0
  20. package/dist/chunk-P7GQBCEH.js.map +1 -0
  21. package/dist/chunk-UOTNQL56.js +46 -0
  22. package/dist/chunk-UOTNQL56.js.map +1 -0
  23. package/dist/chunk-VYBEGZLN.js +319 -0
  24. package/dist/chunk-VYBEGZLN.js.map +1 -0
  25. package/dist/chunk-XKKZPZMB.js +121 -0
  26. package/dist/chunk-XKKZPZMB.js.map +1 -0
  27. package/dist/communication.d.ts +13 -0
  28. package/dist/communication.js +6 -0
  29. package/dist/communication.js.map +1 -0
  30. package/dist/contexts/base.d.ts +99 -0
  31. package/dist/contexts/base.js +7 -0
  32. package/dist/contexts/base.js.map +1 -0
  33. package/dist/contexts/floatingFrame.d.ts +18 -0
  34. package/dist/contexts/floatingFrame.js +8 -0
  35. package/dist/contexts/floatingFrame.js.map +1 -0
  36. package/dist/contexts/recordDetail.d.ts +32 -0
  37. package/dist/contexts/recordDetail.js +8 -0
  38. package/dist/contexts/recordDetail.js.map +1 -0
  39. package/dist/floatingFrames-BzaoL7cq.d.ts +8 -0
  40. package/dist/generic-RiEgdDAq.d.ts +19 -0
  41. package/dist/index.d.ts +19 -0
  42. package/dist/index.js +6 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/merge-translations.js +28 -0
  45. package/dist/react.d.ts +367 -0
  46. package/dist/react.js +2732 -0
  47. package/dist/react.js.map +1 -0
  48. package/dist/routeScript-kttY2YZh.d.ts +303 -0
  49. package/dist/translation.json +8 -0
  50. package/dist/types.d.ts +90 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/util.d.ts +40 -0
  54. package/dist/util.js +87 -0
  55. package/dist/util.js.map +1 -0
  56. package/dist/vite.d.ts +11 -0
  57. package/dist/vite.js +19 -0
  58. package/dist/vite.js.map +1 -0
  59. package/dist/workers/calendarSource.worker.d.ts +2 -0
  60. package/dist/workers/calendarSource.worker.js +62 -0
  61. package/dist/workers/calendarSource.worker.js.map +1 -0
  62. package/dist/workers/expression.worker.d.ts +2 -0
  63. package/dist/workers/expression.worker.js +35 -0
  64. package/dist/workers/expression.worker.js.map +1 -0
  65. package/dist/workers/floatingFrame.worker.d.ts +2 -0
  66. package/dist/workers/floatingFrame.worker.js +57 -0
  67. package/dist/workers/floatingFrame.worker.js.map +1 -0
  68. package/dist/workers/generic.worker.d.ts +2 -0
  69. package/dist/workers/generic.worker.js +57 -0
  70. package/dist/workers/generic.worker.js.map +1 -0
  71. package/dist/workers/recordDetail.worker.d.ts +2 -0
  72. package/dist/workers/recordDetail.worker.js +59 -0
  73. package/dist/workers/recordDetail.worker.js.map +1 -0
  74. package/package.json +93 -0
package/dist/react.js ADDED
@@ -0,0 +1,2732 @@
1
+ import { runScript, runExpression, getStableHash, isFlagEnabled } from './chunk-2MW4AGSN.js';
2
+ import './chunk-LGJYUPYZ.js';
3
+ import { generateExecutionKey } from './chunk-XKKZPZMB.js';
4
+ import { getAllNestedInputsFromConfig, getFieldFromAction } from './chunk-VYBEGZLN.js';
5
+ import { runFrameScriptEventName } from './chunk-GGGXYD3K.js';
6
+ import { QUERY_KEYS, ROUTE_CHANGE_INTERNAL_EVENT, IFRAME_PREFIX, ROUTE_CHANGE_IFRAME_EVENT } from './chunk-P7GQBCEH.js';
7
+ import './chunk-5WRI5ZAA.js';
8
+ import { useQueries, useQueryClient } from '@tanstack/react-query';
9
+ import { createContext, useContext, useState, useCallback, useMemo, useRef, useEffect } from 'react';
10
+ import { jsx } from 'react/jsx-runtime';
11
+ import { formatInTimeZone } from 'date-fns-tz';
12
+ import { startOfDay, endOfDay } from 'date-fns';
13
+ import { flushSync } from 'react-dom';
14
+ import DOMPurify from 'dompurify';
15
+
16
+ var TerminatorsContext = createContext(null);
17
+ var TerminatorsWrapper = ({ children }) => {
18
+ const terminators = useRef({});
19
+ const cleanupWorkers = useCallback(() => {
20
+ Object.keys(terminators.current).forEach((key) => {
21
+ terminators.current[key]?.forEach((fn) => {
22
+ fn();
23
+ });
24
+ terminators.current[key] = [];
25
+ });
26
+ }, []);
27
+ useEffect(() => {
28
+ return () => {
29
+ cleanupWorkers();
30
+ };
31
+ }, [cleanupWorkers]);
32
+ return /* @__PURE__ */ jsx(TerminatorsContext.Provider, { value: { terminators, cleanupWorkers }, children });
33
+ };
34
+ var useTerminators = () => {
35
+ const context = useContext(TerminatorsContext);
36
+ if (!context) {
37
+ throw new Error("useTerminators must be used within a TerminatorsWrapper");
38
+ }
39
+ return context;
40
+ };
41
+ var SessionDataContext = createContext(null);
42
+ var SessionDataWrapper = ({ children }) => {
43
+ const [sessionData, _setSessionData] = useState({});
44
+ const setSessionData = useCallback((pluginId, update) => {
45
+ _setSessionData((prev) => ({
46
+ ...prev,
47
+ [pluginId]: {
48
+ ...prev[pluginId],
49
+ ...update
50
+ }
51
+ }));
52
+ }, []);
53
+ return /* @__PURE__ */ jsx(SessionDataContext.Provider, { value: { sessionData, setSessionData }, children });
54
+ };
55
+ var useSessionData = () => {
56
+ const context = useContext(SessionDataContext);
57
+ if (!context) {
58
+ throw new Error("useSessionData must be used within a SessionDataWrapper");
59
+ }
60
+ return context;
61
+ };
62
+ var AppStateContext = createContext(null);
63
+ var AppStateWrapper = ({
64
+ children,
65
+ bootstrapPlugins = [],
66
+ userConfigs = [],
67
+ user,
68
+ teamMember,
69
+ business,
70
+ clientObject,
71
+ appPath
72
+ }) => {
73
+ const [hasFinishedBootstrapping, setHasFinishedBootstrapping] = useState(false);
74
+ const [routeScriptRunState, setRouteScriptRunState] = useState({});
75
+ const waitingOnRouteScript = useMemo(() => {
76
+ return Object.values(routeScriptRunState).some((state) => state.waitingOnRouteScript);
77
+ }, [routeScriptRunState]);
78
+ const installedPluginAPINamesToIds = useMemo(() => {
79
+ return bootstrapPlugins.reduce((acc, plugin) => {
80
+ return {
81
+ ...acc,
82
+ [plugin.api_name]: plugin.id
83
+ };
84
+ }, {});
85
+ }, [bootstrapPlugins]);
86
+ const userConfigsByApiName = useMemo(() => {
87
+ return userConfigs.reduce((acc, item) => {
88
+ return {
89
+ ...acc,
90
+ [item.api_name]: item.config
91
+ };
92
+ }, {});
93
+ }, [userConfigs]);
94
+ const onInitialBootstrap = useCallback(() => {
95
+ setHasFinishedBootstrapping(true);
96
+ }, []);
97
+ return /* @__PURE__ */ jsx(
98
+ AppStateContext.Provider,
99
+ {
100
+ value: {
101
+ installedPluginAPINamesToIds,
102
+ userConfigsByApiName,
103
+ user,
104
+ teamMember,
105
+ business,
106
+ clientObject,
107
+ appPath,
108
+ onInitialBootstrap,
109
+ setRouteScriptRunState
110
+ },
111
+ children: children({
112
+ waitingOnRouteScript,
113
+ hasFinishedBootstrapping
114
+ })
115
+ }
116
+ );
117
+ };
118
+ var useAppState = () => {
119
+ const context = useContext(AppStateContext);
120
+ if (!context) {
121
+ throw new Error("useAppState must be used within an AppStateWrapper");
122
+ }
123
+ return context;
124
+ };
125
+ var RunnerStateContext = createContext(null);
126
+ var RunnerStateWrapper = ({ children }) => {
127
+ const [runnerState, setRunnerState] = useState({});
128
+ const getRunnerStateUpdater = useCallback(
129
+ (key) => (getNewState) => {
130
+ setRunnerState((prev) => {
131
+ const result = {
132
+ ...prev,
133
+ [key]: getNewState(
134
+ prev[key] ?? { indicator: "none" /* NONE */, hidden: false, pending: false }
135
+ )
136
+ };
137
+ return result;
138
+ });
139
+ },
140
+ []
141
+ );
142
+ const getRunnerState = useCallback(
143
+ (key) => {
144
+ if (!key || !runnerState[key]) {
145
+ return {
146
+ indicator: "none" /* NONE */,
147
+ hidden: false,
148
+ pending: false
149
+ };
150
+ }
151
+ return {
152
+ ...runnerState[key]
153
+ };
154
+ },
155
+ [runnerState]
156
+ );
157
+ const showLoadingIndicator = useMemo(() => {
158
+ const keys = Object.keys(runnerState);
159
+ return keys.length > 0 && keys.some((name) => runnerState[name]?.indicator === "block" /* BLOCK */);
160
+ }, [runnerState]);
161
+ return /* @__PURE__ */ jsx(
162
+ RunnerStateContext.Provider,
163
+ {
164
+ value: {
165
+ getRunnerStateUpdater,
166
+ getRunnerState
167
+ },
168
+ children: children(showLoadingIndicator)
169
+ }
170
+ );
171
+ };
172
+ var useRunnerState = () => {
173
+ const context = useContext(RunnerStateContext);
174
+ if (!context) {
175
+ throw new Error("useRunnerState must be used within a RunnerStateWrapper");
176
+ }
177
+ return context;
178
+ };
179
+ var ModalsContext = createContext(null);
180
+ var ModalsWrapper = ({
181
+ children,
182
+ showing,
183
+ showPrompt,
184
+ show,
185
+ onConfirm,
186
+ onHide
187
+ }) => {
188
+ const [modalQueue, setModalQueue] = useState([]);
189
+ const [createRecordModalQueue, setCreateRecordModalQueue] = useState([]);
190
+ const [createRelatedRecordModalQueue, setCreateRelatedRecordModalQueue] = useState([]);
191
+ const configRef = useRef(void 0);
192
+ const cbRef = useRef(void 0);
193
+ const handleShowModal = useCallback((config, cb) => {
194
+ setModalQueue((prev) => [...prev, { config, cb }]);
195
+ }, []);
196
+ const handleShowCreateRecordModal = useCallback(
197
+ (objectId, cb) => {
198
+ setCreateRecordModalQueue((prev) => [...prev, { objectId, cb }]);
199
+ },
200
+ []
201
+ );
202
+ const handleShowCreateRelatedRecordModal = useCallback(
203
+ (objectId, relatedEntityId, cb) => {
204
+ setCreateRelatedRecordModalQueue((prev) => [...prev, { objectId, relatedEntityId, cb }]);
205
+ },
206
+ []
207
+ );
208
+ const handleCreateRecordComplete = useCallback(
209
+ (result) => {
210
+ const current = createRecordModalQueue[0];
211
+ current?.cb(result);
212
+ setCreateRecordModalQueue((prev) => prev.slice(1));
213
+ },
214
+ [createRecordModalQueue]
215
+ );
216
+ const handleCreateRelatedRecordComplete = useCallback(
217
+ (result) => {
218
+ const current = createRelatedRecordModalQueue[0];
219
+ current?.cb(result);
220
+ setCreateRelatedRecordModalQueue((prev) => prev.slice(1));
221
+ },
222
+ [createRelatedRecordModalQueue]
223
+ );
224
+ useEffect(() => {
225
+ if (modalQueue.length > 0 && !showing) {
226
+ configRef.current = modalQueue[0]?.config;
227
+ cbRef.current = modalQueue[0]?.cb;
228
+ showPrompt();
229
+ }
230
+ }, [modalQueue, showing, showPrompt]);
231
+ const completeModal = useCallback(() => {
232
+ configRef.current = void 0;
233
+ cbRef.current = void 0;
234
+ setModalQueue((prev) => prev.slice(1));
235
+ }, []);
236
+ const derivedModalState = useMemo(() => {
237
+ return {
238
+ props: {
239
+ show,
240
+ onConfirm: (values) => {
241
+ onConfirm();
242
+ if (cbRef.current) {
243
+ cbRef.current({ canceled: false, values });
244
+ }
245
+ completeModal();
246
+ },
247
+ onHide: (eventSource, ...args) => {
248
+ onHide(...args);
249
+ if (cbRef.current) {
250
+ cbRef.current({ canceled: true, values: {}, eventSource });
251
+ }
252
+ completeModal();
253
+ }
254
+ },
255
+ config: configRef.current ?? {}
256
+ };
257
+ }, [onConfirm, onHide, show, completeModal]);
258
+ return /* @__PURE__ */ jsx(
259
+ ModalsContext.Provider,
260
+ {
261
+ value: {
262
+ modalState: derivedModalState,
263
+ showModal: handleShowModal,
264
+ showCreateRecordModal: handleShowCreateRecordModal,
265
+ showCreateRelatedRecordModal: handleShowCreateRelatedRecordModal,
266
+ onCreateRecordComplete: handleCreateRecordComplete,
267
+ onCreateRelatedRecordComplete: handleCreateRelatedRecordComplete
268
+ },
269
+ children: children({
270
+ showPluginModal: derivedModalState.props.show,
271
+ pluginApiName: derivedModalState.config.pluginApiName ?? "",
272
+ handleShowModal,
273
+ handleShowCreateRecordModal,
274
+ handleShowCreateRelatedRecordModal,
275
+ derivedModalState,
276
+ handleCreateRecordComplete,
277
+ handleCreateRelatedRecordComplete,
278
+ showCreateRecordModal: createRecordModalQueue.length > 0,
279
+ createRecordModalObjectId: createRecordModalQueue[0]?.objectId ?? "",
280
+ showCreateRelatedRecordModal: createRelatedRecordModalQueue.length > 0,
281
+ createRelatedRecordModalObjectId: createRelatedRecordModalQueue[0]?.objectId ?? "",
282
+ createRelatedRecordModalRelatedEntityId: createRelatedRecordModalQueue[0]?.relatedEntityId ?? ""
283
+ })
284
+ }
285
+ );
286
+ };
287
+ var useModals = () => {
288
+ const context = useContext(ModalsContext);
289
+ if (!context) {
290
+ throw new Error("useModals must be used within a ModalsWrapper");
291
+ }
292
+ return context;
293
+ };
294
+ var MonitoringContext = createContext(null);
295
+ var MonitoringWrapper = ({
296
+ children,
297
+ monitoringExceptionHelper
298
+ }) => {
299
+ const sendException = useCallback(
300
+ (error, extra) => {
301
+ monitoringExceptionHelper(error, {
302
+ extra
303
+ });
304
+ },
305
+ [monitoringExceptionHelper]
306
+ );
307
+ return /* @__PURE__ */ jsx(
308
+ MonitoringContext.Provider,
309
+ {
310
+ value: {
311
+ sendException
312
+ },
313
+ children
314
+ }
315
+ );
316
+ };
317
+ var useMonitoring = () => {
318
+ const context = useContext(MonitoringContext);
319
+ if (!context) {
320
+ throw new Error("useMonitoring must be used within an MonitoringWrapper");
321
+ }
322
+ return context;
323
+ };
324
+ var HistoryContext = createContext(null);
325
+ var HistoryWrapper = ({ children, onNavigate }) => {
326
+ const push = useCallback(
327
+ (path) => {
328
+ onNavigate(path, { replace: false });
329
+ },
330
+ [onNavigate]
331
+ );
332
+ const replace = useCallback(
333
+ (path) => {
334
+ onNavigate(path, { replace: true });
335
+ },
336
+ [onNavigate]
337
+ );
338
+ return /* @__PURE__ */ jsx(
339
+ HistoryContext.Provider,
340
+ {
341
+ value: {
342
+ push,
343
+ replace
344
+ },
345
+ children
346
+ }
347
+ );
348
+ };
349
+ var useHistory = () => {
350
+ const context = useContext(HistoryContext);
351
+ if (!context) {
352
+ throw new Error("useHistory must be used within an HistoryWrapper");
353
+ }
354
+ return context;
355
+ };
356
+ var NetworkContext = createContext(null);
357
+ var NetworkWrapper = ({
358
+ children,
359
+ performRequest,
360
+ createFileId,
361
+ performFileUpload,
362
+ getPendingCacheCount,
363
+ invalidateCache
364
+ }) => {
365
+ return /* @__PURE__ */ jsx(
366
+ NetworkContext.Provider,
367
+ {
368
+ value: {
369
+ performRequest,
370
+ createFileId,
371
+ performFileUpload,
372
+ getPendingCacheCount,
373
+ invalidateCache
374
+ },
375
+ children
376
+ }
377
+ );
378
+ };
379
+ var useNetwork = () => {
380
+ const context = useContext(NetworkContext);
381
+ if (!context) {
382
+ throw new Error("useNetwork must be used within a NetworkWrapper");
383
+ }
384
+ return context;
385
+ };
386
+ var defaultT = (s) => s;
387
+ var TranslationContext = createContext({ t: defaultT });
388
+ var TranslationWrapper = (props) => {
389
+ const { children, t = defaultT } = props;
390
+ return /* @__PURE__ */ jsx(TranslationContext.Provider, { value: { t }, children });
391
+ };
392
+ var useTranslation = () => {
393
+ const context = useContext(TranslationContext);
394
+ return context;
395
+ };
396
+
397
+ // src/react/hooks/useCalendarSourceCustomScript.ts
398
+ var validateSchema = (result, schema) => {
399
+ if (!Array.isArray(result)) {
400
+ throw new Error("Return value must be an array");
401
+ }
402
+ schema.required.forEach((requiredField) => {
403
+ result.forEach((resultRow, index) => {
404
+ if (!(requiredField.key in resultRow)) {
405
+ throw new Error(
406
+ `Missing required field: ${requiredField.key} in result row ${String(index)}`
407
+ );
408
+ }
409
+ const actualType = typeof resultRow[requiredField.key];
410
+ if (actualType !== requiredField.type) {
411
+ throw new Error(
412
+ `Invalid type for field: ${requiredField.key} in result row ${String(index)}. Expected ${requiredField.type}, got ${actualType}`
413
+ );
414
+ }
415
+ });
416
+ });
417
+ schema.optional.forEach((optionalField) => {
418
+ result.forEach((resultRow, index) => {
419
+ if (optionalField.key in resultRow) {
420
+ const actualType = typeof resultRow[optionalField.key];
421
+ if (actualType !== optionalField.type) {
422
+ throw new Error(
423
+ `Invalid type for field: ${optionalField.key} in result row ${String(index)}. Expected ${optionalField.type}, got ${actualType}`
424
+ );
425
+ }
426
+ }
427
+ });
428
+ });
429
+ };
430
+ var useCalendarSourceCustomScript = ({
431
+ onError,
432
+ showToast,
433
+ clearToasts
434
+ }) => {
435
+ const { t } = useTranslation();
436
+ const [pending, setPending] = useState(false);
437
+ const [executionKey, setExeutionKey] = useState(void 0);
438
+ const { terminators } = useTerminators();
439
+ const { sessionData, setSessionData } = useSessionData();
440
+ const {
441
+ installedPluginAPINamesToIds,
442
+ userConfigsByApiName,
443
+ user,
444
+ teamMember,
445
+ business,
446
+ clientObject,
447
+ appPath
448
+ } = useAppState();
449
+ const { getRunnerState, getRunnerStateUpdater } = useRunnerState();
450
+ const { showModal, showCreateRecordModal } = useModals();
451
+ const { sendException } = useMonitoring();
452
+ const history = useHistory();
453
+ const { performRequest, createFileId, performFileUpload, getPendingCacheCount, invalidateCache } = useNetwork();
454
+ const handleError = useCallback(
455
+ (plugin) => (error) => {
456
+ onError?.(error);
457
+ const errorMessage = error?.message ?? "Unknown plugin error";
458
+ sendException(new Error(errorMessage), {
459
+ error,
460
+ workerName: "calendarSource",
461
+ pluginApiName: plugin?.plugin_api_name ?? ""
462
+ });
463
+ },
464
+ [onError, sendException]
465
+ );
466
+ const executeScript = useCallback(
467
+ async (scriptBody, plugin, args, schema) => {
468
+ let stringArgs = "";
469
+ const runnerStateKey = generateExecutionKey(plugin);
470
+ setExeutionKey(runnerStateKey);
471
+ try {
472
+ const pluginAppId = installedPluginAPINamesToIds[plugin.plugin_api_name];
473
+ stringArgs = JSON.stringify({
474
+ ...args,
475
+ pluginId: pluginAppId,
476
+ __kizen_user_config: userConfigsByApiName[plugin.plugin_api_name]
477
+ });
478
+ } catch {
479
+ handleError(plugin)({
480
+ message: t("Arguments passed to the script are invalid")
481
+ });
482
+ }
483
+ let authError = false;
484
+ const result = await runScript({
485
+ user,
486
+ teamMember,
487
+ business,
488
+ onError: handleError(plugin),
489
+ setLoadingState: setPending,
490
+ scriptBody,
491
+ clientObject,
492
+ onStateChange: (state) => {
493
+ getRunnerStateUpdater(runnerStateKey)((prev) => {
494
+ return {
495
+ ...prev,
496
+ ...state
497
+ };
498
+ });
499
+ },
500
+ workerName: "calendarSource",
501
+ args: stringArgs,
502
+ pushHistory: history.push,
503
+ onShowToast: showToast,
504
+ onClearToasts: clearToasts,
505
+ terminators,
506
+ plugin,
507
+ sessionData,
508
+ setSessionData,
509
+ onShowModal: showModal,
510
+ onShowCreateRecordModal: showCreateRecordModal,
511
+ onShowCreateRelatedRecordModal: () => {
512
+ },
513
+ onNetworkError: (error) => {
514
+ const networkError = error;
515
+ if (networkError?.status === 503) {
516
+ authError = true;
517
+ }
518
+ },
519
+ appPath,
520
+ onNetworkRequest: performRequest,
521
+ createFileId,
522
+ performFileUpload,
523
+ getPendingCacheCount,
524
+ invalidateCache
525
+ });
526
+ try {
527
+ if (schema) {
528
+ validateSchema(result, schema);
529
+ }
530
+ } catch (ex) {
531
+ onError?.(ex);
532
+ }
533
+ return { result, authError };
534
+ },
535
+ [
536
+ user,
537
+ teamMember,
538
+ installedPluginAPINamesToIds,
539
+ business,
540
+ clientObject,
541
+ history,
542
+ showToast,
543
+ clearToasts,
544
+ terminators,
545
+ sessionData,
546
+ setSessionData,
547
+ showModal,
548
+ showCreateRecordModal,
549
+ handleError,
550
+ getRunnerStateUpdater,
551
+ userConfigsByApiName,
552
+ onError,
553
+ performRequest,
554
+ createFileId,
555
+ appPath,
556
+ performFileUpload,
557
+ getPendingCacheCount,
558
+ invalidateCache,
559
+ t
560
+ ]
561
+ );
562
+ return [
563
+ executeScript,
564
+ {
565
+ ...getRunnerState(executionKey),
566
+ pending
567
+ }
568
+ ];
569
+ };
570
+ var ToastContext = createContext(null);
571
+ var ToastWrapper = ({ children, showToast, clearToasts }) => {
572
+ return /* @__PURE__ */ jsx(ToastContext.Provider, { value: { showToast, clearToasts }, children });
573
+ };
574
+ var useToast = () => {
575
+ const context = useContext(ToastContext);
576
+ if (!context) {
577
+ throw new Error("useToast must be used within a ToastWrapper");
578
+ }
579
+ return context;
580
+ };
581
+
582
+ // src/react/hooks/useCalendarSourceData.ts
583
+ var executeCalendarSourceScript = async (script, plugin, executeScript, args) => {
584
+ const schema = {
585
+ required: [
586
+ {
587
+ key: "id",
588
+ type: "string"
589
+ },
590
+ {
591
+ key: "name",
592
+ type: "string"
593
+ }
594
+ ],
595
+ optional: [
596
+ {
597
+ key: "description",
598
+ type: "string"
599
+ }
600
+ ]
601
+ };
602
+ const { result, authError } = await executeScript(script, plugin, args, schema);
603
+ if (Array.isArray(result)) {
604
+ return { result, authError };
605
+ } else {
606
+ console.warn(`Expected calendar source script to return an array, but got:`, result);
607
+ return { result: [], authError };
608
+ }
609
+ };
610
+ var executeCalendarEventScript = async (script, plugin, executeScript, args) => {
611
+ const schema = {
612
+ required: [
613
+ {
614
+ key: "id",
615
+ type: "string"
616
+ },
617
+ {
618
+ key: "calendar_id",
619
+ type: "string"
620
+ },
621
+ {
622
+ key: "title",
623
+ type: "string"
624
+ },
625
+ {
626
+ key: "start_time",
627
+ type: "number"
628
+ },
629
+ {
630
+ key: "end_time",
631
+ type: "number"
632
+ }
633
+ ],
634
+ optional: [
635
+ {
636
+ key: "description",
637
+ type: "string"
638
+ },
639
+ {
640
+ key: "url",
641
+ type: "string"
642
+ },
643
+ {
644
+ key: "activity_id",
645
+ type: "string"
646
+ },
647
+ {
648
+ key: "all_day",
649
+ type: "boolean"
650
+ },
651
+ {
652
+ key: "busy",
653
+ type: "boolean"
654
+ }
655
+ ]
656
+ };
657
+ const { result, authError } = await executeScript(script, plugin, args, schema);
658
+ if (Array.isArray(result)) {
659
+ return { result, authError };
660
+ } else {
661
+ console.warn(`Expected calendar event script to return an array, but got:`, result);
662
+ return { result: [], authError };
663
+ }
664
+ };
665
+ var useCalendarOptions = (enabled, calendarSources, plugins = {}) => {
666
+ const { showToast, clearToasts } = useToast();
667
+ const { t } = useTranslation();
668
+ const [executeScript] = useCalendarSourceCustomScript({
669
+ onError: (error) => {
670
+ showToast({
671
+ message: `${t("Error fetching calendar options.")} ${error?.message ?? ""}`,
672
+ variant: "failure"
673
+ });
674
+ },
675
+ showToast,
676
+ clearToasts
677
+ });
678
+ const calendarsQueries = useQueries({
679
+ queries: calendarSources.map((source) => {
680
+ return {
681
+ queryKey: QUERY_KEYS.CALENDAR_SOURCE_CALENDAR_LIST(source.plugin_api_name, source.api_name),
682
+ queryFn: async () => {
683
+ const config = plugins[source.plugin_api_name]?.business_config ?? {};
684
+ const { result, authError } = await executeCalendarSourceScript(
685
+ source.calendars_script,
686
+ source,
687
+ executeScript,
688
+ config
689
+ );
690
+ return {
691
+ source,
692
+ calendars: result,
693
+ authError
694
+ };
695
+ },
696
+ enabled: enabled && Boolean(source.calendars_script)
697
+ };
698
+ })
699
+ });
700
+ const isLoading = calendarsQueries.some((q) => q.isLoading);
701
+ const calendarOptionsByPluginApiName = useMemo(() => {
702
+ if (isLoading || !enabled) {
703
+ return {};
704
+ }
705
+ return calendarsQueries.reduce(
706
+ (acc, curr) => {
707
+ if (!curr.data?.source.plugin_api_name || typeof curr.data.source.plugin_api_name !== "string") {
708
+ return acc;
709
+ }
710
+ const pluginApiName = curr.data.source.plugin_api_name;
711
+ acc[pluginApiName] ??= [];
712
+ acc[pluginApiName].push(
713
+ ...curr.data.calendars.map((c) => ({
714
+ calendar: c,
715
+ source: curr.data.source
716
+ }))
717
+ );
718
+ return acc;
719
+ },
720
+ {}
721
+ );
722
+ }, [calendarsQueries, isLoading, enabled]);
723
+ const servicesWithErrors = useMemo(() => {
724
+ return calendarsQueries.filter((q) => q.data?.authError).map((q) => q.data?.source.plugin_api_name);
725
+ }, [calendarsQueries]);
726
+ return {
727
+ calendars: calendarOptionsByPluginApiName,
728
+ errorServices: servicesWithErrors,
729
+ isLoading
730
+ };
731
+ };
732
+ var formatString = "yyyy-MM-dd'T'HH:mm:ssXXX";
733
+ var createDate = (dateString) => {
734
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
735
+ throw new Error(`Invalid date string: ${dateString}. Expected format: YYYY-MM-DD`);
736
+ }
737
+ const [year, month, day] = dateString.split("-").map(Number);
738
+ return new Date(year, month - 1, day);
739
+ };
740
+ var getTzDateString = (date, timeZone) => {
741
+ return formatInTimeZone(date, timeZone, formatString);
742
+ };
743
+ var useCalendarEvents = (flatCalendars, params, plugins = {}) => {
744
+ const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
745
+ const rangeStartFilter = getTzDateString(startOfDay(createDate(params.rangeStart)), userTimezone);
746
+ const rangeEndFilter = getTzDateString(endOfDay(createDate(params.rangeEnd)), userTimezone);
747
+ const { showToast, clearToasts } = useToast();
748
+ const { t } = useTranslation();
749
+ const [executeScript] = useCalendarSourceCustomScript({
750
+ onError: (error) => {
751
+ showToast({
752
+ message: `${t("Error fetching calendar events.")} ${error?.message ?? ""}`,
753
+ variant: "failure"
754
+ });
755
+ },
756
+ showToast,
757
+ clearToasts
758
+ });
759
+ const eventsQueries = useQueries({
760
+ queries: flatCalendars.map(({ calendar, source }) => {
761
+ return {
762
+ queryKey: QUERY_KEYS.CALENDAR_SOURCE_EVENTS(
763
+ source.plugin_api_name,
764
+ source.api_name,
765
+ calendar.id,
766
+ params
767
+ ),
768
+ queryFn: async () => {
769
+ const config = plugins[source.plugin_api_name]?.business_config ?? {};
770
+ const { result, authError } = await executeCalendarEventScript(
771
+ source.events_script,
772
+ source,
773
+ executeScript,
774
+ {
775
+ ...config,
776
+ calendar: {
777
+ calendar_id: calendar.id,
778
+ range_start: rangeStartFilter,
779
+ range_end: rangeEndFilter
780
+ }
781
+ }
782
+ );
783
+ return {
784
+ calendarId: calendar.id,
785
+ events: result,
786
+ authError
787
+ };
788
+ }
789
+ };
790
+ })
791
+ });
792
+ const isEventsLoading = eventsQueries.some((q) => q.isLoading);
793
+ const sourcesByCalendarId = useMemo(() => {
794
+ return flatCalendars.reduce((acc, { calendar, source }) => {
795
+ acc[calendar.id] = { source, calendar };
796
+ return acc;
797
+ }, {});
798
+ }, [flatCalendars]);
799
+ const eventsByCalendarId = useMemo(() => {
800
+ if (isEventsLoading) {
801
+ return {};
802
+ }
803
+ return eventsQueries.reduce((acc, curr) => {
804
+ if (!curr.data?.calendarId) {
805
+ return acc;
806
+ }
807
+ acc[curr.data.calendarId] = curr.data.events;
808
+ return acc;
809
+ }, {});
810
+ }, [eventsQueries, isEventsLoading]);
811
+ return {
812
+ events: eventsByCalendarId,
813
+ sources: sourcesByCalendarId,
814
+ isLoading: isEventsLoading
815
+ };
816
+ };
817
+ var useFloatingFrameCustomScript = ({
818
+ onError,
819
+ scriptUIRef,
820
+ plugin,
821
+ onChangeMinimized
822
+ }) => {
823
+ const { terminators } = useTerminators();
824
+ const { sessionData, setSessionData } = useSessionData();
825
+ const {
826
+ installedPluginAPINamesToIds,
827
+ userConfigsByApiName,
828
+ user,
829
+ teamMember,
830
+ business,
831
+ clientObject,
832
+ appPath
833
+ } = useAppState();
834
+ const { showModal, showCreateRecordModal, showCreateRelatedRecordModal } = useModals();
835
+ const { sendException } = useMonitoring();
836
+ const { showToast, clearToasts } = useToast();
837
+ const history = useHistory();
838
+ const { performRequest, createFileId, performFileUpload, getPendingCacheCount, invalidateCache } = useNetwork();
839
+ const { t } = useTranslation();
840
+ const [pending, setPending] = useState(false);
841
+ const [floatingFrameRunnerState, setFloatingFrameRunnerState] = useState({
842
+ indicator: "none",
843
+ hidden: true,
844
+ triggerHidden: false,
845
+ hideHeader: false
846
+ });
847
+ const pendingOnceLock = useRef(false);
848
+ const [pendingLocked, setPendingLocked] = useState(false);
849
+ if (pending && !pendingOnceLock.current) {
850
+ pendingOnceLock.current = true;
851
+ }
852
+ if (!pending && pendingOnceLock.current && !pendingLocked) {
853
+ setPendingLocked(true);
854
+ }
855
+ const handleError = useCallback(
856
+ (error) => {
857
+ onError?.(error);
858
+ const errorMessage = error?.message ?? "Unknown plugin error";
859
+ sendException(new Error(errorMessage), {
860
+ error,
861
+ workerName: "floatingFrame",
862
+ pluginApiName: plugin.plugin_api_name
863
+ });
864
+ },
865
+ [onError, plugin, sendException]
866
+ );
867
+ const executeScript = useCallback(
868
+ (scriptBody, args) => {
869
+ let stringArgs = "";
870
+ try {
871
+ const pluginAppId = installedPluginAPINamesToIds[plugin.plugin_api_name];
872
+ stringArgs = JSON.stringify({
873
+ ...args,
874
+ pluginId: pluginAppId,
875
+ __kizen_user_config: userConfigsByApiName[plugin.plugin_api_name]
876
+ });
877
+ } catch {
878
+ handleError({ message: t("Arguments passed to the script are invalid") });
879
+ }
880
+ return runScript({
881
+ user,
882
+ teamMember,
883
+ business,
884
+ onError: handleError,
885
+ setLoadingState: (...args2) => {
886
+ flushSync(() => {
887
+ setPending(...args2);
888
+ });
889
+ },
890
+ scriptBody,
891
+ clientObject,
892
+ scriptUIRef,
893
+ onStateChange: (state) => {
894
+ const { minimized, ...rest } = state;
895
+ if (typeof minimized === "boolean") {
896
+ onChangeMinimized?.(minimized);
897
+ }
898
+ setFloatingFrameRunnerState((s) => {
899
+ return { ...s, ...rest };
900
+ });
901
+ },
902
+ workerName: "floatingFramePlugin",
903
+ args: stringArgs,
904
+ pushHistory: history.push,
905
+ plugin,
906
+ onShowToast: showToast,
907
+ onClearToasts: clearToasts,
908
+ terminators,
909
+ sessionData,
910
+ setSessionData,
911
+ onShowModal: showModal,
912
+ onShowCreateRecordModal: showCreateRecordModal,
913
+ onShowCreateRelatedRecordModal: showCreateRelatedRecordModal,
914
+ appPath,
915
+ onNetworkRequest: performRequest,
916
+ createFileId,
917
+ performFileUpload,
918
+ getPendingCacheCount,
919
+ invalidateCache
920
+ });
921
+ },
922
+ [
923
+ user,
924
+ teamMember,
925
+ business,
926
+ installedPluginAPINamesToIds,
927
+ clientObject,
928
+ scriptUIRef,
929
+ history,
930
+ plugin,
931
+ onChangeMinimized,
932
+ showToast,
933
+ clearToasts,
934
+ terminators,
935
+ sessionData,
936
+ setSessionData,
937
+ handleError,
938
+ setFloatingFrameRunnerState,
939
+ userConfigsByApiName,
940
+ showModal,
941
+ showCreateRecordModal,
942
+ showCreateRelatedRecordModal,
943
+ performRequest,
944
+ appPath,
945
+ createFileId,
946
+ performFileUpload,
947
+ getPendingCacheCount,
948
+ invalidateCache,
949
+ t
950
+ ]
951
+ );
952
+ return [
953
+ executeScript,
954
+ {
955
+ ...floatingFrameRunnerState,
956
+ pending: pendingLocked ? false : pending
957
+ }
958
+ ];
959
+ };
960
+ var useGenericPluginCustomScript = ({
961
+ onError,
962
+ scriptUIRef,
963
+ plugin
964
+ }) => {
965
+ const { terminators } = useTerminators();
966
+ const { sessionData, setSessionData } = useSessionData();
967
+ const {
968
+ installedPluginAPINamesToIds,
969
+ userConfigsByApiName,
970
+ user,
971
+ teamMember,
972
+ business,
973
+ clientObject,
974
+ appPath
975
+ } = useAppState();
976
+ const { getRunnerState, getRunnerStateUpdater } = useRunnerState();
977
+ const { showModal, showCreateRecordModal } = useModals();
978
+ const { sendException } = useMonitoring();
979
+ const { showToast, clearToasts } = useToast();
980
+ const history = useHistory();
981
+ const { performRequest, createFileId, performFileUpload, getPendingCacheCount, invalidateCache } = useNetwork();
982
+ const { t } = useTranslation();
983
+ const [pending, setPending] = useState(false);
984
+ const [executionKey, setExeutionKey] = useState(void 0);
985
+ const handleError = useCallback(
986
+ (error, executionPlugin) => {
987
+ onError?.(error);
988
+ const errorMessage = error?.message ?? "Unknown plugin error";
989
+ sendException(new Error(errorMessage), {
990
+ error,
991
+ workerName: "genericPlugin",
992
+ pluginApiName: plugin?.plugin_api_name ?? executionPlugin?.plugin_api_name ?? ""
993
+ });
994
+ },
995
+ [onError, plugin, sendException]
996
+ );
997
+ const executeScript = useCallback(
998
+ (scriptBody, args, executionPlugin) => {
999
+ let stringArgs = "";
1000
+ const runnerStateKey = generateExecutionKey(executionPlugin);
1001
+ setExeutionKey(runnerStateKey);
1002
+ try {
1003
+ const pluginAppId = installedPluginAPINamesToIds[plugin?.plugin_api_name ?? executionPlugin?.plugin_api_name ?? ""];
1004
+ stringArgs = JSON.stringify({
1005
+ ...args,
1006
+ pluginId: pluginAppId,
1007
+ __kizen_user_config: userConfigsByApiName[plugin?.plugin_api_name ?? executionPlugin?.plugin_api_name ?? ""]
1008
+ });
1009
+ } catch {
1010
+ handleError({ message: t("Arguments passed to the script are invalid") });
1011
+ }
1012
+ return runScript({
1013
+ user,
1014
+ teamMember,
1015
+ business,
1016
+ onError: handleError,
1017
+ setLoadingState: setPending,
1018
+ scriptBody,
1019
+ clientObject,
1020
+ scriptUIRef,
1021
+ onStateChange: (state) => {
1022
+ getRunnerStateUpdater(runnerStateKey)((prev) => {
1023
+ return {
1024
+ ...prev,
1025
+ ...state
1026
+ };
1027
+ });
1028
+ },
1029
+ workerName: "genericPlugin",
1030
+ args: stringArgs,
1031
+ pushHistory: history.push,
1032
+ onShowToast: showToast,
1033
+ onClearToasts: clearToasts,
1034
+ terminators,
1035
+ plugin,
1036
+ executionPlugin,
1037
+ sessionData,
1038
+ setSessionData,
1039
+ onShowModal: showModal,
1040
+ onShowCreateRecordModal: showCreateRecordModal,
1041
+ onShowCreateRelatedRecordModal: () => {
1042
+ },
1043
+ appPath,
1044
+ onNetworkRequest: performRequest,
1045
+ createFileId,
1046
+ performFileUpload,
1047
+ getPendingCacheCount,
1048
+ invalidateCache
1049
+ });
1050
+ },
1051
+ [
1052
+ user,
1053
+ teamMember,
1054
+ installedPluginAPINamesToIds,
1055
+ business,
1056
+ clientObject,
1057
+ scriptUIRef,
1058
+ history,
1059
+ showToast,
1060
+ clearToasts,
1061
+ terminators,
1062
+ plugin,
1063
+ sessionData,
1064
+ setSessionData,
1065
+ handleError,
1066
+ getRunnerStateUpdater,
1067
+ userConfigsByApiName,
1068
+ showModal,
1069
+ showCreateRecordModal,
1070
+ performRequest,
1071
+ appPath,
1072
+ createFileId,
1073
+ performFileUpload,
1074
+ getPendingCacheCount,
1075
+ invalidateCache,
1076
+ t
1077
+ ]
1078
+ );
1079
+ return [
1080
+ executeScript,
1081
+ {
1082
+ ...getRunnerState(executionKey),
1083
+ pending
1084
+ }
1085
+ ];
1086
+ };
1087
+ var useRecordDetailCustomScript = ({
1088
+ entityId,
1089
+ objectId,
1090
+ onError,
1091
+ scriptUIRef,
1092
+ onReleaseBlockingScript
1093
+ }) => {
1094
+ const { terminators } = useTerminators();
1095
+ const { sessionData, setSessionData } = useSessionData();
1096
+ const {
1097
+ installedPluginAPINamesToIds,
1098
+ userConfigsByApiName,
1099
+ user,
1100
+ teamMember,
1101
+ business,
1102
+ clientObject,
1103
+ appPath
1104
+ } = useAppState();
1105
+ const { getRunnerState, getRunnerStateUpdater } = useRunnerState();
1106
+ const { showModal, showCreateRecordModal, showCreateRelatedRecordModal } = useModals();
1107
+ const { sendException } = useMonitoring();
1108
+ const { showToast, clearToasts } = useToast();
1109
+ const history = useHistory();
1110
+ const { performRequest, createFileId, performFileUpload, getPendingCacheCount, invalidateCache } = useNetwork();
1111
+ const { t } = useTranslation();
1112
+ const [pending, setPending] = useState(false);
1113
+ const [executionKey, setExeutionKey] = useState(void 0);
1114
+ const handleError = useCallback(
1115
+ (error, executionPlugin) => {
1116
+ onError?.(error);
1117
+ const errorMessage = error?.message ?? "Unknown plugin error";
1118
+ sendException(new Error(errorMessage), {
1119
+ error,
1120
+ workerName: "recordDetail",
1121
+ pluginApiName: executionPlugin?.plugin_api_name ?? "",
1122
+ objectId,
1123
+ entityId
1124
+ });
1125
+ },
1126
+ [onError, objectId, entityId, sendException]
1127
+ );
1128
+ const executeScript = useCallback(
1129
+ (scriptBody, args, executionPlugin, overrideContext) => {
1130
+ let stringArgs = "";
1131
+ try {
1132
+ const pluginAppId = installedPluginAPINamesToIds[executionPlugin?.plugin_api_name ?? ""];
1133
+ stringArgs = JSON.stringify({
1134
+ ...args,
1135
+ pluginId: pluginAppId,
1136
+ __kizen_user_config: userConfigsByApiName[executionPlugin?.plugin_api_name ?? ""]
1137
+ });
1138
+ } catch {
1139
+ handleError({ message: t("Arguments passed to the script are invalid") }, executionPlugin);
1140
+ }
1141
+ const runnerStateKey = generateExecutionKey(executionPlugin);
1142
+ setExeutionKey(runnerStateKey);
1143
+ return runScript({
1144
+ user,
1145
+ teamMember,
1146
+ business,
1147
+ onReleaseBlockingScript,
1148
+ onError: (e) => {
1149
+ handleError(e, executionPlugin);
1150
+ },
1151
+ setLoadingState: setPending,
1152
+ scriptBody,
1153
+ clientObject,
1154
+ scriptUIRef,
1155
+ onStateChange: (state) => {
1156
+ getRunnerStateUpdater(runnerStateKey)((prev) => {
1157
+ return {
1158
+ ...prev,
1159
+ ...state
1160
+ };
1161
+ });
1162
+ },
1163
+ workerName: "recordDetail",
1164
+ args: stringArgs,
1165
+ context: overrideContext ?? {
1166
+ entityId,
1167
+ objectId
1168
+ },
1169
+ pushHistory: history.push,
1170
+ onShowToast: showToast,
1171
+ onClearToasts: clearToasts,
1172
+ terminators,
1173
+ executionPlugin,
1174
+ sessionData,
1175
+ setSessionData,
1176
+ onShowModal: showModal,
1177
+ onShowCreateRecordModal: showCreateRecordModal,
1178
+ onShowCreateRelatedRecordModal: showCreateRelatedRecordModal,
1179
+ appPath,
1180
+ onNetworkRequest: performRequest,
1181
+ createFileId,
1182
+ performFileUpload,
1183
+ getPendingCacheCount,
1184
+ invalidateCache
1185
+ });
1186
+ },
1187
+ [
1188
+ user,
1189
+ teamMember,
1190
+ installedPluginAPINamesToIds,
1191
+ business,
1192
+ entityId,
1193
+ objectId,
1194
+ clientObject,
1195
+ scriptUIRef,
1196
+ history,
1197
+ showToast,
1198
+ clearToasts,
1199
+ terminators,
1200
+ sessionData,
1201
+ setSessionData,
1202
+ showModal,
1203
+ showCreateRecordModal,
1204
+ showCreateRelatedRecordModal,
1205
+ onReleaseBlockingScript,
1206
+ handleError,
1207
+ getRunnerStateUpdater,
1208
+ userConfigsByApiName,
1209
+ performRequest,
1210
+ appPath,
1211
+ createFileId,
1212
+ performFileUpload,
1213
+ getPendingCacheCount,
1214
+ invalidateCache,
1215
+ t
1216
+ ]
1217
+ );
1218
+ return [
1219
+ executeScript,
1220
+ {
1221
+ ...getRunnerState(executionKey),
1222
+ pending
1223
+ }
1224
+ ];
1225
+ };
1226
+ var usePluginCustomHTML = (currentPage, args) => {
1227
+ const { showToast } = useToast();
1228
+ const { t } = useTranslation();
1229
+ const outputUIRef = useRef(null);
1230
+ const interactableScriptRef = useRef(null);
1231
+ const css = currentPage?.css;
1232
+ const html = useMemo(() => {
1233
+ const rawHTML = currentPage?.html;
1234
+ return rawHTML ? DOMPurify.sanitize(rawHTML) : null;
1235
+ }, [currentPage]);
1236
+ const interactableScripts = useMemo(() => {
1237
+ return currentPage?.event_scripts ?? {};
1238
+ }, [currentPage]);
1239
+ const [executeInline, { pending: inlinePending }] = useGenericPluginCustomScript({
1240
+ onError: (e) => {
1241
+ showToast({
1242
+ message: `${t("Script could not be executed:")} ${e?.message ?? "Unknown Error"}`,
1243
+ variant: "failure"
1244
+ });
1245
+ },
1246
+ scriptUIRef: outputUIRef,
1247
+ plugin: currentPage
1248
+ });
1249
+ const handleClick = useCallback(
1250
+ (e) => {
1251
+ e.preventDefault();
1252
+ e.stopPropagation();
1253
+ if (!inlinePending) {
1254
+ const target = e.target;
1255
+ const scriptName = target.getAttribute("data-script");
1256
+ if (scriptName && interactableScripts[scriptName]) {
1257
+ void executeInline(interactableScripts[scriptName], args);
1258
+ }
1259
+ }
1260
+ },
1261
+ [inlinePending, executeInline, interactableScripts, args]
1262
+ );
1263
+ useEffect(() => {
1264
+ const e = interactableScriptRef.current;
1265
+ if (e) {
1266
+ e.addEventListener("click", handleClick);
1267
+ }
1268
+ return () => {
1269
+ if (e) {
1270
+ e.removeEventListener("click", handleClick);
1271
+ }
1272
+ };
1273
+ }, [handleClick, currentPage]);
1274
+ return {
1275
+ scopedCss: `
1276
+ @scope {
1277
+ ${css ?? ""}
1278
+ }
1279
+ `,
1280
+ sanitizedHtml: html,
1281
+ outputUIRef,
1282
+ interactableScriptRef
1283
+ };
1284
+ };
1285
+ var useCalendarSourceReset = () => {
1286
+ const queryClient = useQueryClient();
1287
+ const resetFn = useCallback(
1288
+ (pluginApiName, sourceApiName) => {
1289
+ queryClient.removeQueries({
1290
+ queryKey: QUERY_KEYS.CALENDAR_SOURCE_CALENDAR_LIST(pluginApiName, sourceApiName),
1291
+ exact: true
1292
+ });
1293
+ },
1294
+ [queryClient]
1295
+ );
1296
+ return resetFn;
1297
+ };
1298
+ var useManualInteraction = (execute, currentPage, elementRef, pending, args = {}) => {
1299
+ const interactableScripts = useMemo(() => {
1300
+ return currentPage?.event_scripts ?? {};
1301
+ }, [currentPage]);
1302
+ const handleClick = useCallback(
1303
+ (e) => {
1304
+ e.preventDefault();
1305
+ e.stopPropagation();
1306
+ if (!pending) {
1307
+ const target = e.target;
1308
+ const scriptName = target.getAttribute("data-script");
1309
+ if (scriptName && interactableScripts[scriptName]) {
1310
+ void execute(interactableScripts[scriptName], { ...args });
1311
+ }
1312
+ }
1313
+ },
1314
+ [pending, execute, interactableScripts, args]
1315
+ );
1316
+ useEffect(() => {
1317
+ const e = elementRef?.current;
1318
+ if (e) {
1319
+ e.addEventListener("click", handleClick);
1320
+ }
1321
+ return () => {
1322
+ if (e) {
1323
+ e.removeEventListener("click", handleClick);
1324
+ }
1325
+ };
1326
+ }, [handleClick, currentPage, elementRef]);
1327
+ };
1328
+ var usePluginCallback = (search) => {
1329
+ useEffect(() => {
1330
+ const queryString = new URLSearchParams(search);
1331
+ const query = Object.fromEntries(queryString);
1332
+ window.parent.postMessage({ type: "kizen:plugin_callback", query }, "*");
1333
+ }, [search]);
1334
+ };
1335
+ var usePluginPage = (currentPage, search, isLoading) => {
1336
+ const iframeURL = currentPage?.iframe_url;
1337
+ const script = currentPage?.script;
1338
+ const callback = currentPage?.callback;
1339
+ const pluginArgs = currentPage?.args;
1340
+ const { showToast } = useToast();
1341
+ const { t } = useTranslation();
1342
+ const scriptUIRef = useRef(null);
1343
+ const [execute, { pending }] = useGenericPluginCustomScript({
1344
+ onError: (e) => {
1345
+ showToast({
1346
+ message: `${t("Plugin error:")} ${e?.message ?? ""}`,
1347
+ variant: "failure"
1348
+ });
1349
+ },
1350
+ scriptUIRef,
1351
+ plugin: currentPage
1352
+ });
1353
+ const args = useMemo(() => {
1354
+ const queryString = new URLSearchParams(search);
1355
+ const query = Object.fromEntries(queryString);
1356
+ return {
1357
+ ...query,
1358
+ ...pluginArgs
1359
+ };
1360
+ }, [search, pluginArgs]);
1361
+ const { outputUIRef, scopedCss, sanitizedHtml, interactableScriptRef } = usePluginCustomHTML(
1362
+ currentPage,
1363
+ args
1364
+ );
1365
+ useManualInteraction(execute, currentPage, scriptUIRef, pending);
1366
+ const hasRunScript = useRef(false);
1367
+ useEffect(() => {
1368
+ if (!iframeURL && !hasRunScript.current && script && !isLoading) {
1369
+ hasRunScript.current = true;
1370
+ void execute(script, args);
1371
+ }
1372
+ }, [iframeURL, execute, script, args, isLoading]);
1373
+ const handleMessage = useCallback(
1374
+ (e) => {
1375
+ const message = e.data;
1376
+ if (message?.type === "kizen:plugin_callback") {
1377
+ const args2 = message.query;
1378
+ if (callback) {
1379
+ void execute(callback, args2);
1380
+ }
1381
+ }
1382
+ },
1383
+ [execute, callback]
1384
+ );
1385
+ useEffect(() => {
1386
+ window.addEventListener("message", handleMessage);
1387
+ return () => {
1388
+ window.removeEventListener("message", handleMessage);
1389
+ };
1390
+ }, [handleMessage]);
1391
+ return {
1392
+ scriptUIRef,
1393
+ outputUIRef,
1394
+ scopedCss,
1395
+ sanitizedHtml,
1396
+ interactableScriptRef,
1397
+ iframeURL,
1398
+ pending
1399
+ };
1400
+ };
1401
+ var FloatingFrameContext = createContext(null);
1402
+ var FloatingFrameWrapper = ({ children }) => {
1403
+ const [floatingFrameOrder, setFloatingFrameOrder] = useState([]);
1404
+ const [hiddenByModal, setHiddenByModal] = useState(false);
1405
+ const touchFloatingFrame = useCallback((id, cb) => {
1406
+ setFloatingFrameOrder((prev) => {
1407
+ const res = prev.filter((i) => i !== id);
1408
+ const done = [id, ...res];
1409
+ cb?.(done);
1410
+ return done;
1411
+ });
1412
+ }, []);
1413
+ const floatingFrameOffset = useMemo(() => {
1414
+ return [...floatingFrameOrder].reverse().reduce((acc, id, index) => {
1415
+ return {
1416
+ ...acc,
1417
+ [id]: index
1418
+ };
1419
+ }, {});
1420
+ }, [floatingFrameOrder]);
1421
+ useEffect(() => {
1422
+ const observer = new MutationObserver(() => {
1423
+ const modal = document.querySelector('div[role="dialog"]');
1424
+ if (modal) {
1425
+ setHiddenByModal(true);
1426
+ } else {
1427
+ setHiddenByModal(false);
1428
+ }
1429
+ });
1430
+ observer.observe(document.body, {
1431
+ childList: true
1432
+ });
1433
+ return () => {
1434
+ observer.disconnect();
1435
+ };
1436
+ }, []);
1437
+ return /* @__PURE__ */ jsx(
1438
+ FloatingFrameContext.Provider,
1439
+ {
1440
+ value: {
1441
+ touchFloatingFrame,
1442
+ floatingFrameOffset
1443
+ },
1444
+ children: children(hiddenByModal)
1445
+ }
1446
+ );
1447
+ };
1448
+ var useFloatingFrameContext = () => {
1449
+ const context = useContext(FloatingFrameContext);
1450
+ if (!context) {
1451
+ throw new Error("useFloatingFrame must be used within a FloatingFrameWrapper");
1452
+ }
1453
+ return context;
1454
+ };
1455
+ var defaultIgnorePatterns = [
1456
+ "^/$",
1457
+ "^/login*",
1458
+ "/settings/mine$",
1459
+ "/settings/others$",
1460
+ "/create-new-business$",
1461
+ "^/choose-business*",
1462
+ "^/inactive-business$",
1463
+ "^/reset$",
1464
+ "/welcome$",
1465
+ "/embed/*",
1466
+ "/create-new-business$",
1467
+ "/businessbuilder"
1468
+ ];
1469
+ var defaultFrameIgnoreUrls = [
1470
+ "https://js.stripe.com"
1471
+ // Exlude irrelevant Stripe events
1472
+ ];
1473
+ var getQuadrant = (topDelta, leftDelta, bottomDelta, rightDelta) => {
1474
+ if (topDelta < bottomDelta) {
1475
+ if (leftDelta < rightDelta) {
1476
+ return "top-left";
1477
+ } else {
1478
+ return "top-right";
1479
+ }
1480
+ } else {
1481
+ if (leftDelta < rightDelta) {
1482
+ return "bottom-left";
1483
+ } else {
1484
+ return "bottom-right";
1485
+ }
1486
+ }
1487
+ };
1488
+ var useFloatingFrame = (params) => {
1489
+ const {
1490
+ currentWindow,
1491
+ pathname,
1492
+ id,
1493
+ frameHeaderSize,
1494
+ defaultPositionGap = 0,
1495
+ viewportHeight = window.innerHeight,
1496
+ hiddenByModal,
1497
+ updateEmployeeConfig: maybeUpdateEmployeeConfig
1498
+ } = params;
1499
+ const scriptUIRef = useRef(null);
1500
+ const hasRunScript = useRef(false);
1501
+ const { showToast } = useToast();
1502
+ const { t } = useTranslation();
1503
+ const { installedPluginAPINamesToIds, clientObject } = useAppState();
1504
+ const { touchFloatingFrame, floatingFrameOffset } = useFloatingFrameContext();
1505
+ const frameOffset = floatingFrameOffset[id] ?? -1;
1506
+ const hideOnMinimized = currentWindow.minimized_style === "none";
1507
+ const isCircle = currentWindow.minimized_style === "circle" || hideOnMinimized;
1508
+ const isBottomRightFixed = currentWindow.default_position === "bottom-right-fixed";
1509
+ const isBottomLeftFixed = currentWindow.default_position === "bottom-left-fixed";
1510
+ const isFixed = isBottomRightFixed || isBottomLeftFixed;
1511
+ const circleIcon = currentWindow.minimized_config?.icon ?? "action-drag-handle";
1512
+ const circleColor = currentWindow.minimized_config?.color ?? "blue";
1513
+ const additionalArgs = useMemo(() => {
1514
+ const splits = pathname.split("/");
1515
+ const first = splits[1];
1516
+ const second = splits[2];
1517
+ if (first === "client") {
1518
+ return {
1519
+ objectId: clientObject?.id,
1520
+ entityId: second
1521
+ };
1522
+ }
1523
+ if (first === "custom-objects") {
1524
+ return {
1525
+ objectId: second,
1526
+ entityId: splits[3]
1527
+ };
1528
+ }
1529
+ }, [clientObject, pathname]);
1530
+ const isHiddenByPattern = useMemo(() => {
1531
+ const matchers = currentWindow.match;
1532
+ const ignore = currentWindow.ignore;
1533
+ if (matchers?.length && !matchers.some((m) => new RegExp(m).test(pathname))) {
1534
+ return true;
1535
+ }
1536
+ if (defaultIgnorePatterns.concat(ignore ?? []).filter(Boolean).some((m) => {
1537
+ try {
1538
+ return new RegExp(m).test(pathname);
1539
+ } catch {
1540
+ return false;
1541
+ }
1542
+ })) {
1543
+ return true;
1544
+ }
1545
+ return false;
1546
+ }, [currentWindow, pathname]);
1547
+ const currentPluginId = currentWindow.plugin_api_name ? installedPluginAPINamesToIds[currentWindow.plugin_api_name] : "";
1548
+ const [minimized, _setMinimized] = useState(
1549
+ () => currentWindow.employee_config?.[id]?.minimized ?? false
1550
+ );
1551
+ const script = currentWindow.script;
1552
+ const type = currentWindow.type;
1553
+ const args = currentWindow.args;
1554
+ const messageHandlerScript = currentWindow.message_handler;
1555
+ const width = currentWindow.width ?? 0;
1556
+ const [height, setHeight] = useState(() => {
1557
+ const viewportHeight2 = window.innerHeight;
1558
+ return Math.min(currentWindow.height ?? 0, viewportHeight2 - frameHeaderSize);
1559
+ });
1560
+ const [windowPosition, setWindowPosition] = useState(null);
1561
+ const defaultPosition = currentWindow.default_position;
1562
+ const getDesiredPosition = useCallback(
1563
+ (assumeExpanded) => {
1564
+ const position = currentWindow.employee_config?.[id]?.position;
1565
+ if (!position?.quadrant) {
1566
+ const maxAllowedLeft = window.innerWidth - width;
1567
+ let maxAllowedTop = window.innerHeight - height - frameHeaderSize;
1568
+ if (minimized && !assumeExpanded) {
1569
+ maxAllowedTop = window.innerHeight - frameHeaderSize;
1570
+ }
1571
+ if (position) {
1572
+ try {
1573
+ const coords = position;
1574
+ if (coords.left > maxAllowedLeft) {
1575
+ coords.left = maxAllowedLeft;
1576
+ }
1577
+ if (coords.top > maxAllowedTop) {
1578
+ coords.top = maxAllowedTop;
1579
+ }
1580
+ if (coords.left < 0) {
1581
+ coords.left = defaultPositionGap;
1582
+ }
1583
+ if (coords.top < 0) {
1584
+ coords.top = defaultPositionGap;
1585
+ }
1586
+ return coords;
1587
+ } catch {
1588
+ }
1589
+ }
1590
+ let defaultLeft = maxAllowedLeft;
1591
+ if (defaultPosition === "bottom-left") {
1592
+ defaultLeft = defaultPositionGap;
1593
+ }
1594
+ return { left: defaultLeft, top: maxAllowedTop - defaultPositionGap };
1595
+ } else if (position.quadrant === "top-left") {
1596
+ const left = position.deltas?.left ?? 0;
1597
+ const top = position.deltas?.top ?? 0;
1598
+ return { ...position, left, top };
1599
+ } else if (position.quadrant === "top-right") {
1600
+ const left = window.innerWidth - width - (position.deltas?.right ?? 0);
1601
+ const top = position.deltas?.top ?? 0;
1602
+ return { ...position, left, top };
1603
+ } else if (position.quadrant === "bottom-left") {
1604
+ const left = position.deltas?.left ?? 0;
1605
+ const top = window.innerHeight - (minimized ? 0 : height) - (position.deltas?.bottom ?? 0) - frameHeaderSize;
1606
+ return { ...position, left, top };
1607
+ } else {
1608
+ const left = window.innerWidth - width - (position.deltas?.right ?? 0);
1609
+ const top = window.innerHeight - (minimized ? 0 : height) - (position.deltas?.bottom ?? 0) - frameHeaderSize;
1610
+ return { ...position, left, top };
1611
+ }
1612
+ },
1613
+ [
1614
+ currentWindow,
1615
+ id,
1616
+ defaultPosition,
1617
+ minimized,
1618
+ width,
1619
+ height,
1620
+ frameHeaderSize,
1621
+ defaultPositionGap
1622
+ ]
1623
+ );
1624
+ const getPosition = useCallback(
1625
+ (assumeExpanded) => {
1626
+ const position = getDesiredPosition(assumeExpanded);
1627
+ const isBottom = ["bottom-right", "bottom-left"].includes(position.quadrant ?? "");
1628
+ const calculatedHeight = isBottom && minimized ? 0 : height;
1629
+ if (position.left < 0) {
1630
+ position.left = 0;
1631
+ } else if (position.left > window.innerWidth - width) {
1632
+ position.left = window.innerWidth - width;
1633
+ }
1634
+ if (position.top < 0) {
1635
+ position.top = 0;
1636
+ } else if (position.top > window.innerHeight - calculatedHeight) {
1637
+ position.top = window.innerHeight - calculatedHeight;
1638
+ }
1639
+ return position;
1640
+ },
1641
+ [getDesiredPosition, width, height, minimized]
1642
+ );
1643
+ useEffect(() => {
1644
+ const currentHeight = currentWindow.height ?? 0;
1645
+ if (currentHeight > viewportHeight) {
1646
+ setHeight(viewportHeight - frameHeaderSize);
1647
+ const position = getDesiredPosition(true);
1648
+ position.top = 0;
1649
+ setWindowPosition(position);
1650
+ } else {
1651
+ setHeight(currentHeight);
1652
+ }
1653
+ }, [currentWindow.height, getDesiredPosition, viewportHeight, frameHeaderSize]);
1654
+ const [delayedModalHide, setDelayedModalHide] = useState(minimized);
1655
+ const [circleMinimized, setCircleMinimized] = useState(minimized && isCircle);
1656
+ const animationDelay = height * 5e-4 * 1e3;
1657
+ const setMinimized = (min) => {
1658
+ _setMinimized(min);
1659
+ if (min && isCircle) {
1660
+ setTimeout(() => {
1661
+ setCircleMinimized(min);
1662
+ }, animationDelay);
1663
+ } else if (isCircle) {
1664
+ setCircleMinimized(min);
1665
+ }
1666
+ if (hiddenByModal) {
1667
+ setTimeout(() => {
1668
+ setDelayedModalHide(min);
1669
+ }, animationDelay);
1670
+ } else {
1671
+ setDelayedModalHide(min);
1672
+ }
1673
+ if (windowPosition?.quadrant === "bottom-left" || windowPosition?.quadrant === "bottom-right") {
1674
+ if (min) {
1675
+ setWindowPosition((p) => {
1676
+ if (!p) {
1677
+ return null;
1678
+ }
1679
+ return {
1680
+ ...p,
1681
+ top: p.top + height
1682
+ };
1683
+ });
1684
+ } else {
1685
+ setWindowPosition((p) => {
1686
+ if (!p) {
1687
+ return null;
1688
+ }
1689
+ const windowHeight = window.innerHeight;
1690
+ const movePositionUp = p.top >= 0 && p.top + height <= windowHeight;
1691
+ return {
1692
+ ...p,
1693
+ top: movePositionUp ? p.top : p.top - height
1694
+ };
1695
+ });
1696
+ }
1697
+ }
1698
+ maybeUpdateEmployeeConfig?.(id, minimized, windowPosition)({ minimized: min });
1699
+ };
1700
+ const [
1701
+ execute,
1702
+ { pending, hidden: hiddenByScript, indicator, triggerHidden, hideHeader: hideHeaderByScript }
1703
+ ] = useFloatingFrameCustomScript({
1704
+ onError: (e) => {
1705
+ showToast({
1706
+ message: `${t("Plugin error:")} ${e?.message ?? ""}`,
1707
+ variant: "failure"
1708
+ });
1709
+ },
1710
+ scriptUIRef,
1711
+ plugin: currentWindow,
1712
+ onChangeMinimized: (minimized2) => {
1713
+ setMinimized(minimized2);
1714
+ }
1715
+ });
1716
+ const hideHeader = useMemo(() => hideHeaderByScript && isFixed, [hideHeaderByScript, isFixed]);
1717
+ const tooltipPosition = useMemo(() => {
1718
+ if (!windowPosition) return "top";
1719
+ const { left, top } = windowPosition;
1720
+ const windowWidth = window.innerWidth;
1721
+ const atRightEdge = windowWidth - (left + width) < 40;
1722
+ const atTopEdge = top < 40;
1723
+ if (atRightEdge) {
1724
+ return "left";
1725
+ }
1726
+ if (atTopEdge) {
1727
+ return "bottom";
1728
+ }
1729
+ return "top";
1730
+ }, [windowPosition, width]);
1731
+ const resolveCircleEl = useCallback(() => {
1732
+ const currentSide2 = isBottomLeftFixed ? "left" : "right";
1733
+ const elementId = `${currentWindow.plugin_api_name}-${currentWindow.api_name}-trigger-${currentSide2}`;
1734
+ const el = document.getElementById(elementId);
1735
+ return el;
1736
+ }, [isBottomLeftFixed, currentWindow]);
1737
+ const computeFixedPosition = useCallback(() => {
1738
+ if (!isFixed) return;
1739
+ const circleEl = resolveCircleEl();
1740
+ if (!circleEl) return;
1741
+ const rect = circleEl.getBoundingClientRect();
1742
+ const contentHeight = minimized ? 0 : height;
1743
+ const headerH = hideHeader ? 0 : frameHeaderSize;
1744
+ let top = rect.top - (contentHeight + headerH) - defaultPositionGap;
1745
+ let left = isBottomLeftFixed ? rect.left : rect.right - width;
1746
+ const fullFrameHeight = contentHeight + headerH;
1747
+ const maxLeft = Math.max(0, window.innerWidth - width);
1748
+ const maxTop = Math.max(0, window.innerHeight - fullFrameHeight);
1749
+ left = Math.min(Math.max(0, left), maxLeft);
1750
+ top = Math.min(Math.max(0, top), maxTop);
1751
+ setWindowPosition({
1752
+ left,
1753
+ top,
1754
+ quadrant: isBottomLeftFixed ? "bottom-left" : "bottom-right",
1755
+ deltas: {
1756
+ top,
1757
+ left,
1758
+ right: window.innerWidth - (left + width),
1759
+ bottom: window.innerHeight - (top + fullFrameHeight)
1760
+ }
1761
+ });
1762
+ }, [
1763
+ isFixed,
1764
+ isBottomLeftFixed,
1765
+ height,
1766
+ minimized,
1767
+ width,
1768
+ hideHeader,
1769
+ resolveCircleEl,
1770
+ frameHeaderSize,
1771
+ defaultPositionGap
1772
+ ]);
1773
+ useEffect(() => {
1774
+ if (isFixed) {
1775
+ computeFixedPosition();
1776
+ }
1777
+ }, [isFixed, height, minimized, computeFixedPosition]);
1778
+ useEffect(() => {
1779
+ if (!isFixed) return;
1780
+ computeFixedPosition();
1781
+ const bodyRO = new ResizeObserver(() => {
1782
+ computeFixedPosition();
1783
+ });
1784
+ bodyRO.observe(document.body);
1785
+ return () => {
1786
+ bodyRO.disconnect();
1787
+ };
1788
+ }, [isFixed, computeFixedPosition]);
1789
+ useEffect(() => {
1790
+ if (!hasRunScript.current && script && type === "script" && !isHiddenByPattern) {
1791
+ hasRunScript.current = true;
1792
+ execute(script, { ...args });
1793
+ }
1794
+ }, [execute, script, type, args, isHiddenByPattern]);
1795
+ useEffect(() => {
1796
+ const handleRunFrameScriptFromInteraction = (e) => {
1797
+ const event = e;
1798
+ if (event?.detail?.recipient?.frame === currentWindow.api_name) {
1799
+ const payload = event.detail;
1800
+ if (payload.recipient?.plugin !== currentWindow.plugin_api_name) {
1801
+ return;
1802
+ }
1803
+ if (payload.recipient.script && currentWindow.event_scripts?.[payload.recipient.script]) {
1804
+ const script2 = currentWindow.event_scripts[payload.recipient.script] ?? "";
1805
+ execute(script2, {
1806
+ ...payload.args,
1807
+ ...args,
1808
+ ...additionalArgs
1809
+ });
1810
+ }
1811
+ }
1812
+ };
1813
+ window.addEventListener(runFrameScriptEventName, handleRunFrameScriptFromInteraction);
1814
+ return () => {
1815
+ window.removeEventListener(runFrameScriptEventName, handleRunFrameScriptFromInteraction);
1816
+ };
1817
+ }, [currentWindow, execute, args, additionalArgs]);
1818
+ const [dragging, setDragging] = useState(false);
1819
+ const dragEnd = (_event, context) => {
1820
+ setDragging(false);
1821
+ touchFloatingFrame(id);
1822
+ const windowWidth = window.innerWidth;
1823
+ const windowHeight = window.innerHeight;
1824
+ if (context.node) {
1825
+ const rect = context.node.getBoundingClientRect();
1826
+ const left = rect.left;
1827
+ const top = rect.top;
1828
+ const bottom = top + rect.height;
1829
+ const right = left + rect.width;
1830
+ const topDelta = top;
1831
+ const bottomDelta = windowHeight - bottom;
1832
+ const leftDelta = left;
1833
+ const rightDelta = windowWidth - right;
1834
+ const quadrant = getQuadrant(topDelta, leftDelta, bottomDelta, rightDelta);
1835
+ const result = {
1836
+ left,
1837
+ right,
1838
+ top,
1839
+ quadrant,
1840
+ deltas: {
1841
+ top: topDelta,
1842
+ bottom: bottomDelta,
1843
+ left: leftDelta,
1844
+ right: rightDelta
1845
+ }
1846
+ };
1847
+ setWindowPosition(result);
1848
+ maybeUpdateEmployeeConfig?.(id, minimized, windowPosition)({ position: result });
1849
+ }
1850
+ };
1851
+ useEffect(() => {
1852
+ const handleUpdate = () => {
1853
+ if (isFixed) {
1854
+ computeFixedPosition();
1855
+ } else {
1856
+ setWindowPosition(getPosition(false));
1857
+ }
1858
+ };
1859
+ window.addEventListener("resize", handleUpdate);
1860
+ return () => {
1861
+ window.removeEventListener("resize", handleUpdate);
1862
+ };
1863
+ }, [isFixed, computeFixedPosition, getPosition]);
1864
+ useManualInteraction(execute, currentWindow, scriptUIRef, pending, args);
1865
+ useEffect(() => {
1866
+ const handleMessageEvent = (ev) => {
1867
+ if (ev.source !== window && messageHandlerScript && !defaultFrameIgnoreUrls.includes(ev.origin)) {
1868
+ execute(messageHandlerScript, {
1869
+ ...args,
1870
+ eventData: ev.data
1871
+ });
1872
+ }
1873
+ };
1874
+ window.addEventListener("message", handleMessageEvent);
1875
+ return () => {
1876
+ window.removeEventListener("message", handleMessageEvent);
1877
+ };
1878
+ }, [execute, args, messageHandlerScript]);
1879
+ const { outputUIRef, scopedCss, sanitizedHtml, interactableScriptRef } = usePluginCustomHTML(
1880
+ currentWindow,
1881
+ {}
1882
+ );
1883
+ const hidden = !delayedModalHide ? hiddenByScript || isHiddenByPattern : hiddenByScript || isHiddenByPattern || hiddenByModal;
1884
+ const parentClassName = `floating-window-parent--${id}`;
1885
+ const dragHandleClassName = `floating-drag-handle--${id}`;
1886
+ const contentClassName = `floating-window-content--${id}`;
1887
+ let currentSide = currentWindow.employee_config?.[id]?.position?.quadrant?.split("-")[1] === "right" ? "right" : "left";
1888
+ if (isBottomLeftFixed) {
1889
+ currentSide = "left";
1890
+ } else if (isBottomRightFixed) {
1891
+ currentSide = "right";
1892
+ }
1893
+ const customIconDataUrl = currentWindow.minimized_config?.customIcon;
1894
+ let CustomIcon = null;
1895
+ if (customIconDataUrl?.startsWith("data:image/")) {
1896
+ CustomIcon = ({ className = "" }) => /* @__PURE__ */ jsx(
1897
+ "img",
1898
+ {
1899
+ src: customIconDataUrl,
1900
+ alt: currentWindow.title || "Open Frame",
1901
+ className: `object-cover select-none pointer-events-none ${className}`
1902
+ }
1903
+ );
1904
+ }
1905
+ if (!windowPosition) {
1906
+ setWindowPosition(getPosition(false));
1907
+ }
1908
+ return {
1909
+ circleProps: {
1910
+ id,
1911
+ side: currentSide,
1912
+ circleIcon,
1913
+ CustomIcon,
1914
+ circleColor,
1915
+ onClick: () => {
1916
+ setMinimized(!minimized);
1917
+ },
1918
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1919
+ hidden: Boolean(hidden || isHiddenByPattern || hideOnMinimized || triggerHidden)
1920
+ },
1921
+ parentProps: {
1922
+ className: parentClassName,
1923
+ dragging,
1924
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1925
+ hidden: hidden || circleMinimized || isHiddenByPattern || hideOnMinimized
1926
+ },
1927
+ draggableProps: {
1928
+ onStop: dragEnd,
1929
+ onStart: () => {
1930
+ setDragging(true);
1931
+ },
1932
+ bounds: `.${parentClassName}`,
1933
+ handle: `.${dragHandleClassName}`,
1934
+ defaultPosition: {
1935
+ x: windowPosition?.left ?? 0,
1936
+ y: windowPosition?.top ?? 0
1937
+ },
1938
+ position: {
1939
+ x: windowPosition?.left ?? 0,
1940
+ y: windowPosition?.top ?? 0
1941
+ },
1942
+ disabled: isFixed
1943
+ },
1944
+ floatProps: {
1945
+ pending,
1946
+ width,
1947
+ dragging,
1948
+ height,
1949
+ isCircle: isCircle && minimized
1950
+ },
1951
+ contentProps: {
1952
+ className: contentClassName,
1953
+ dragging,
1954
+ solid: true
1955
+ },
1956
+ minimized,
1957
+ pending,
1958
+ indicator,
1959
+ hideHeader,
1960
+ isFixed,
1961
+ isCircle,
1962
+ outputUIRef,
1963
+ scopedCss,
1964
+ sanitizedHtml,
1965
+ interactableScriptRef,
1966
+ tooltipPosition,
1967
+ currentPluginId,
1968
+ setMinimized,
1969
+ dragHandleClassName,
1970
+ height,
1971
+ script,
1972
+ dragging,
1973
+ frameOffset,
1974
+ scriptUIRef
1975
+ };
1976
+ };
1977
+ var useLocationChange = () => {
1978
+ useEffect(() => {
1979
+ let previousEventHash = null;
1980
+ const listener = (e) => {
1981
+ if (!e.detail?.location) {
1982
+ return;
1983
+ }
1984
+ const locationHash = getStableHash(e.detail.location);
1985
+ if (locationHash === previousEventHash) {
1986
+ return;
1987
+ }
1988
+ previousEventHash = locationHash;
1989
+ const frames = document.querySelectorAll(
1990
+ `iframe[id^=${IFRAME_PREFIX}-]`
1991
+ );
1992
+ frames.forEach((frame) => {
1993
+ let targetOrigin = null;
1994
+ try {
1995
+ const url = new URL(frame.src, window.location.href);
1996
+ targetOrigin = url.origin;
1997
+ } catch (error) {
1998
+ const isDebug = isFlagEnabled("script-runner-logging");
1999
+ if (isDebug) {
2000
+ console.warn(
2001
+ "Failed to parse iframe src for location change message target origin:",
2002
+ error
2003
+ );
2004
+ }
2005
+ targetOrigin = null;
2006
+ }
2007
+ if (!targetOrigin) {
2008
+ return;
2009
+ }
2010
+ frame.contentWindow?.postMessage(
2011
+ {
2012
+ action: ROUTE_CHANGE_IFRAME_EVENT,
2013
+ location: e.detail?.location
2014
+ },
2015
+ targetOrigin
2016
+ );
2017
+ });
2018
+ };
2019
+ window.addEventListener(ROUTE_CHANGE_INTERNAL_EVENT, listener);
2020
+ return () => {
2021
+ window.removeEventListener(ROUTE_CHANGE_INTERNAL_EVENT, listener);
2022
+ };
2023
+ }, []);
2024
+ };
2025
+ var PluginEngineProvider = (props) => {
2026
+ const {
2027
+ children,
2028
+ bootstrapPlugins = [],
2029
+ userConfigs = [],
2030
+ user,
2031
+ teamMember,
2032
+ business,
2033
+ clientObject,
2034
+ onNavigate,
2035
+ modal,
2036
+ monitoringExceptionHelper,
2037
+ showToast,
2038
+ clearToasts,
2039
+ performRequest,
2040
+ appPath,
2041
+ hideFramesOnModal = true,
2042
+ createFileId,
2043
+ performFileUpload,
2044
+ t
2045
+ } = props;
2046
+ useLocationChange();
2047
+ return /* @__PURE__ */ jsx(
2048
+ AppStateWrapper,
2049
+ {
2050
+ bootstrapPlugins,
2051
+ userConfigs,
2052
+ user,
2053
+ teamMember,
2054
+ business,
2055
+ clientObject,
2056
+ appPath,
2057
+ children: ({ hasFinishedBootstrapping, waitingOnRouteScript }) => {
2058
+ return /* @__PURE__ */ jsx(TranslationWrapper, { t, children: /* @__PURE__ */ jsx(RunnerStateWrapper, { children: (showLoadingIndicator) => {
2059
+ return /* @__PURE__ */ jsx(HistoryWrapper, { onNavigate, children: /* @__PURE__ */ jsx(ModalsWrapper, { ...modal, children: (modals) => {
2060
+ return /* @__PURE__ */ jsx(MonitoringWrapper, { monitoringExceptionHelper, children: /* @__PURE__ */ jsx(SessionDataWrapper, { children: /* @__PURE__ */ jsx(TerminatorsWrapper, { children: /* @__PURE__ */ jsx(ToastWrapper, { showToast, clearToasts, children: /* @__PURE__ */ jsx(FloatingFrameWrapper, { children: (hiddenByModal) => {
2061
+ return /* @__PURE__ */ jsx(
2062
+ NetworkWrapper,
2063
+ {
2064
+ performRequest,
2065
+ createFileId,
2066
+ performFileUpload,
2067
+ children: children({
2068
+ ...modals,
2069
+ showLoadingIndicator,
2070
+ hasFinishedBootstrapping,
2071
+ waitingOnRouteScript,
2072
+ hiddenByModal: hideFramesOnModal ? hiddenByModal : false
2073
+ })
2074
+ }
2075
+ );
2076
+ } }) }) }) }) });
2077
+ } }) });
2078
+ } }) });
2079
+ }
2080
+ }
2081
+ );
2082
+ };
2083
+ var PluginBootstrapSuccess = () => {
2084
+ const { onInitialBootstrap } = useAppState();
2085
+ useEffect(() => {
2086
+ onInitialBootstrap();
2087
+ }, [onInitialBootstrap]);
2088
+ return null;
2089
+ };
2090
+ var getRouteMatcher = (objectId, clientObjectId) => {
2091
+ if (objectId === clientObjectId || objectId === "client") {
2092
+ return { isClient: true, exp: new RegExp(`^/client/([a-zA-Z0-9-]+)/.*$`) };
2093
+ }
2094
+ return {
2095
+ isClient: false,
2096
+ exp: new RegExp(`^/custom-objects/${objectId}/([a-zA-Z0-9-]+).*$`)
2097
+ };
2098
+ };
2099
+ var getPluginStateKey = (item) => {
2100
+ return `${item.plugin_api_name}-${item.api_name}`;
2101
+ };
2102
+ var RouteScriptRunner = (props) => {
2103
+ const { showToast } = useToast();
2104
+ const { t } = useTranslation();
2105
+ const { pathname, routeScripts } = props;
2106
+ const previousPathname = useRef("");
2107
+ const { clientObject, setRouteScriptRunState } = useAppState();
2108
+ const clientObjectId = clientObject?.id;
2109
+ const [executeScript] = useRecordDetailCustomScript({
2110
+ onError: (e) => {
2111
+ showToast({
2112
+ message: `${t("Action could not run:")} ${e?.message ?? ""}`,
2113
+ variant: "failure"
2114
+ });
2115
+ },
2116
+ // Only route script handlers need to be able to release a blocking script, since they're the only type that
2117
+ // can block the app rendering.
2118
+ onReleaseBlockingScript: (executionPlugin) => {
2119
+ if (executionPlugin) {
2120
+ const key = getPluginStateKey(executionPlugin);
2121
+ setRouteScriptRunState((state) => {
2122
+ return {
2123
+ ...state,
2124
+ [key]: {
2125
+ ...state[key],
2126
+ waitingOnRouteScript: false
2127
+ }
2128
+ };
2129
+ });
2130
+ }
2131
+ },
2132
+ entityId: "",
2133
+ objectId: ""
2134
+ });
2135
+ const getRunnerStateForScript = (checkPath, checkObjectId) => {
2136
+ const { exp, isClient } = getRouteMatcher(checkObjectId, clientObjectId ?? "");
2137
+ const match = exp.exec(checkPath);
2138
+ if (match?.[1]) {
2139
+ return {
2140
+ canRunScript: true,
2141
+ entityId: match[1],
2142
+ customObjectId: isClient ? clientObjectId ?? "" : checkObjectId
2143
+ };
2144
+ }
2145
+ return {
2146
+ canRunScript: false
2147
+ };
2148
+ };
2149
+ const handlePathChange = (previousPath = "", currentPath = "") => {
2150
+ const runQueue = routeScripts.map((script) => {
2151
+ return {
2152
+ ...getRunnerStateForScript(currentPath, script.custom_object.id),
2153
+ ...script
2154
+ };
2155
+ }).filter(
2156
+ (q) => {
2157
+ if (!q.canRunScript) {
2158
+ return false;
2159
+ }
2160
+ if (q.routes?.length) {
2161
+ return q.routes.some((pattern) => {
2162
+ return new RegExp(pattern).test(currentPath);
2163
+ });
2164
+ }
2165
+ return true;
2166
+ }
2167
+ );
2168
+ runQueue.forEach((item) => {
2169
+ const itemKey = getPluginStateKey(item);
2170
+ setRouteScriptRunState((state) => {
2171
+ return {
2172
+ ...state,
2173
+ [itemKey]: {
2174
+ ...state[itemKey],
2175
+ waitingOnRouteScript: item.blocking
2176
+ }
2177
+ };
2178
+ });
2179
+ void executeScript(
2180
+ item.script,
2181
+ {
2182
+ previousRoute: previousPath,
2183
+ currentRoute: currentPath
2184
+ },
2185
+ item,
2186
+ {
2187
+ entityId: item.entityId,
2188
+ objectId: item.customObjectId
2189
+ }
2190
+ ).then(() => {
2191
+ setRouteScriptRunState((state) => {
2192
+ return {
2193
+ ...state,
2194
+ [itemKey]: {
2195
+ ...state[itemKey],
2196
+ waitingOnRouteScript: false
2197
+ }
2198
+ };
2199
+ });
2200
+ });
2201
+ });
2202
+ };
2203
+ if (previousPathname.current !== pathname && clientObjectId) {
2204
+ const previousValue = previousPathname.current;
2205
+ previousPathname.current = pathname;
2206
+ handlePathChange(previousValue, pathname);
2207
+ }
2208
+ return null;
2209
+ };
2210
+ var SetupAssistantContext = createContext(null);
2211
+ var SetupAssistantProvider = SetupAssistantContext.Provider;
2212
+ var getUsableValue = (type, value) => {
2213
+ if (type === "custom_object") {
2214
+ return value?.id;
2215
+ }
2216
+ };
2217
+ var doesValueExist = (field, value) => {
2218
+ if (field.type === "custom_object") {
2219
+ if (field.allow_multiple) {
2220
+ const consideredValue = value?.value ?? [];
2221
+ return consideredValue.length > 0;
2222
+ }
2223
+ return Boolean(value?.value?.id);
2224
+ } else if (field.type === "field") {
2225
+ if (field.allow_multiple) {
2226
+ const consideredValue = value?.value ?? [];
2227
+ return consideredValue.length > 0;
2228
+ }
2229
+ return Boolean(value?.value?.value);
2230
+ } else if (field.type === "text") {
2231
+ const consideredValue = value?.value ?? "";
2232
+ return Boolean(consideredValue.trim());
2233
+ } else if (field.type === "number") {
2234
+ const parsed = Number(value?.value);
2235
+ return Boolean(value?.value) && !isNaN(parsed);
2236
+ } else if (field.type === "select") {
2237
+ if (field.allow_multiple) {
2238
+ const consideredValue = value?.value ?? [];
2239
+ return consideredValue.length > 0;
2240
+ }
2241
+ return Boolean(value?.value?.value);
2242
+ } else if (field.type === "boolean") {
2243
+ return Boolean(value?.value);
2244
+ }
2245
+ return false;
2246
+ };
2247
+ var getNestedObjectsFromConfig = (config) => {
2248
+ const objects = (config.fields ?? []).filter(
2249
+ (entry) => entry.type === "custom_object"
2250
+ );
2251
+ const containers = (config.fields ?? []).filter((entry) => entry.type === "container");
2252
+ const recursiveObjects = containers.flatMap((container) => {
2253
+ return getNestedObjectsFromConfig(container);
2254
+ });
2255
+ const actionObjects = config.actions?.filter((action) => action.hint_object_name) ?? [];
2256
+ return [
2257
+ ...objects,
2258
+ ...recursiveObjects,
2259
+ ...actionObjects.map((action) => {
2260
+ return {
2261
+ ...getFieldFromAction(action),
2262
+ match_hint: action.hint_object_name ?? ""
2263
+ };
2264
+ })
2265
+ ];
2266
+ };
2267
+ var getNestedFieldsFromConfig = (config) => {
2268
+ const objects = (config.fields ?? []).filter((entry) => entry.type === "field");
2269
+ const containers = (config.fields ?? []).filter((entry) => entry.type === "container");
2270
+ const recursiveFields = containers.flatMap(
2271
+ (container) => getNestedFieldsFromConfig(container)
2272
+ );
2273
+ return [...objects, ...recursiveFields];
2274
+ };
2275
+ var SetupAssistantController = ({
2276
+ children,
2277
+ config,
2278
+ value,
2279
+ onStateChange,
2280
+ disabledKeys,
2281
+ templateAssociationsByActionApiName,
2282
+ getObjectByAPIName,
2283
+ getCustomObjectDetails
2284
+ }) => {
2285
+ const [_rawState, _setState] = useState(value ?? {});
2286
+ const flattenedFields = useMemo(() => {
2287
+ return getAllNestedInputsFromConfig(config);
2288
+ }, [config]);
2289
+ const defaultState = useMemo(() => {
2290
+ const defaultValues = flattenedFields.reduce((acc, field) => {
2291
+ return {
2292
+ ...acc,
2293
+ [field.key]: {
2294
+ type: field.type,
2295
+ value: field.default
2296
+ }
2297
+ };
2298
+ }, {});
2299
+ return defaultValues;
2300
+ }, [flattenedFields]);
2301
+ const state = useMemo(() => {
2302
+ const actionTemplatePartialState = Object.entries(
2303
+ templateAssociationsByActionApiName ?? {}
2304
+ ).reduce((acc, [actionApiName, associations]) => {
2305
+ const actionFieldKey = `action__${actionApiName}`;
2306
+ const associatedObjects = associations.map((assoc) => {
2307
+ return {
2308
+ id: assoc.custom_object.id ?? "",
2309
+ objectName: assoc.custom_object.object_name ?? "Unknown Object"
2310
+ };
2311
+ });
2312
+ if (associatedObjects.length > 0 && associations.length > 0 && associations[0]) {
2313
+ acc[actionFieldKey] = {
2314
+ type: "custom_object",
2315
+ value: associatedObjects,
2316
+ config: getFieldFromAction(associations[0].browser_js_action_template)
2317
+ };
2318
+ }
2319
+ return acc;
2320
+ }, {});
2321
+ const stateValues = {
2322
+ ...actionTemplatePartialState,
2323
+ ..._rawState
2324
+ };
2325
+ disabledKeys?.forEach((key) => {
2326
+ if (stateValues[key]) {
2327
+ stateValues[key] = void 0;
2328
+ }
2329
+ });
2330
+ return stateValues;
2331
+ }, [_rawState, templateAssociationsByActionApiName, disabledKeys]);
2332
+ const hasRunInference = useRef(Boolean(value));
2333
+ const inferenceState = useRef({});
2334
+ const [inferencePending, setInferencePending] = useState(!value);
2335
+ const [waitingExpressions, setWaitingExpressions] = useState({});
2336
+ const [expressionResults, setExpressionResults] = useState({});
2337
+ const [expressionsIdle, setExpressionsIdle] = useState(false);
2338
+ const idleTimer = useRef(null);
2339
+ const [errors, setErrors] = useState({});
2340
+ const [expressionsStarted, setExpressionsStarted] = useState(false);
2341
+ const [initialExpressionsPending, setInitialExpressionsPending] = useState(true);
2342
+ const fieldResetterRef = useRef({});
2343
+ const registerFieldResetter = useCallback((key, resetFn) => {
2344
+ fieldResetterRef.current[key] = resetFn;
2345
+ }, []);
2346
+ const setState = useCallback(
2347
+ (stateUpdate) => {
2348
+ if (typeof stateUpdate === "function") {
2349
+ _setState((prev) => {
2350
+ const result = stateUpdate(prev);
2351
+ inferenceState.current = result;
2352
+ onStateChange?.(result);
2353
+ return result;
2354
+ });
2355
+ } else {
2356
+ inferenceState.current = stateUpdate;
2357
+ _setState(stateUpdate);
2358
+ onStateChange?.(stateUpdate);
2359
+ }
2360
+ setErrors({});
2361
+ },
2362
+ [onStateChange]
2363
+ );
2364
+ const interpolateValueFromState = useCallback(
2365
+ (accessor, consideredState) => {
2366
+ if (accessor.startsWith("{{") && accessor.endsWith("}}")) {
2367
+ const stateKey = accessor.slice(2, -2).trim();
2368
+ return getUsableValue(
2369
+ consideredState[stateKey]?.type,
2370
+ consideredState[stateKey]?.value
2371
+ );
2372
+ }
2373
+ return accessor;
2374
+ },
2375
+ []
2376
+ );
2377
+ const interpolateValue = useCallback(
2378
+ (accessor) => {
2379
+ return interpolateValueFromState(accessor, state);
2380
+ },
2381
+ [state, interpolateValueFromState]
2382
+ );
2383
+ const { clientObject } = useAppState();
2384
+ const getPotentialMatch = useCallback(
2385
+ async (api_name) => {
2386
+ if (api_name === "client_client" && clientObject) {
2387
+ return [
2388
+ {
2389
+ id: clientObject.id,
2390
+ object_name: clientObject.objectName
2391
+ }
2392
+ ];
2393
+ }
2394
+ const potentialMatch = await getObjectByAPIName(api_name);
2395
+ return potentialMatch;
2396
+ },
2397
+ [clientObject, getObjectByAPIName]
2398
+ );
2399
+ const handleInferObjects = useCallback(async () => {
2400
+ const objectsToInfer = getNestedObjectsFromConfig(config);
2401
+ for (const inferrableObject of objectsToInfer) {
2402
+ if (!inferrableObject.match_hint) {
2403
+ continue;
2404
+ }
2405
+ const potentialMatch = await getPotentialMatch(inferrableObject.match_hint);
2406
+ if (potentialMatch?.[0]) {
2407
+ const value2 = {
2408
+ id: potentialMatch[0].id,
2409
+ objectName: potentialMatch[0].object_name
2410
+ };
2411
+ setState((prev) => {
2412
+ const result = {
2413
+ ...prev,
2414
+ [inferrableObject.key]: {
2415
+ type: "custom_object",
2416
+ value: inferrableObject.allow_multiple ? [value2] : value2
2417
+ }
2418
+ };
2419
+ return result;
2420
+ });
2421
+ }
2422
+ }
2423
+ }, [config, setState, getPotentialMatch]);
2424
+ const getInferrableFields = useCallback(() => {
2425
+ return getNestedFieldsFromConfig(config);
2426
+ }, [config]);
2427
+ const handleInferFields = useCallback(
2428
+ async (objectIdFilter, forceObjectId) => {
2429
+ const fieldsToInfer = getInferrableFields();
2430
+ const objectResults = {};
2431
+ for (const inferrableField of fieldsToInfer) {
2432
+ if (!inferrableField.object_id) {
2433
+ continue;
2434
+ }
2435
+ const objectId = forceObjectId ?? interpolateValueFromState(inferrableField.object_id, inferenceState.current);
2436
+ if (objectIdFilter && !inferrableField.object_id.includes(objectIdFilter)) {
2437
+ continue;
2438
+ }
2439
+ if (!objectResults[objectId] && objectId) {
2440
+ try {
2441
+ objectResults[objectId] = await getCustomObjectDetails(objectId);
2442
+ } catch {
2443
+ }
2444
+ }
2445
+ const objectDetail = objectResults[objectId];
2446
+ if (objectDetail) {
2447
+ const fieldMatch = objectDetail.fields.find((f) => f.name === inferrableField.match_hint);
2448
+ if (fieldMatch) {
2449
+ setState((prev) => {
2450
+ const result = {
2451
+ ...prev,
2452
+ [inferrableField.key]: {
2453
+ type: "field",
2454
+ value: {
2455
+ value: fieldMatch.id,
2456
+ label: fieldMatch.displayName
2457
+ },
2458
+ associatedObject: {
2459
+ id: objectId,
2460
+ name: objectDetail.objectName
2461
+ }
2462
+ }
2463
+ };
2464
+ return result;
2465
+ });
2466
+ }
2467
+ }
2468
+ }
2469
+ },
2470
+ [interpolateValueFromState, getInferrableFields, setState, getCustomObjectDetails]
2471
+ );
2472
+ const handleReinferObjectByKey = useCallback(
2473
+ async (key) => {
2474
+ const objectsToInfer = getNestedObjectsFromConfig(config);
2475
+ const matchingObject = objectsToInfer.find((obj) => obj.key === key);
2476
+ if (matchingObject?.match_hint) {
2477
+ let matchedObjectId = "";
2478
+ const potentialMatch = await getPotentialMatch(matchingObject.match_hint);
2479
+ const match = potentialMatch?.[0];
2480
+ if (match) {
2481
+ matchedObjectId = match.id;
2482
+ setState((prev) => {
2483
+ const result = {
2484
+ ...prev,
2485
+ [key]: {
2486
+ type: "custom_object",
2487
+ value: {
2488
+ id: match.id,
2489
+ objectName: match.object_name
2490
+ }
2491
+ }
2492
+ };
2493
+ return result;
2494
+ });
2495
+ if (matchedObjectId) {
2496
+ void handleInferFields(void 0, matchedObjectId);
2497
+ }
2498
+ }
2499
+ }
2500
+ },
2501
+ [config, setState, handleInferFields, getPotentialMatch]
2502
+ );
2503
+ const handleConfigInference = useCallback(async () => {
2504
+ await handleInferObjects();
2505
+ await handleInferFields();
2506
+ setInferencePending(false);
2507
+ }, [handleInferObjects, handleInferFields]);
2508
+ const getNestedFields = useCallback(() => {
2509
+ return getNestedFieldsFromConfig(config);
2510
+ }, [config]);
2511
+ const reInferFieldsForObject = useCallback(
2512
+ (objectKey) => {
2513
+ return handleInferFields(objectKey);
2514
+ },
2515
+ [handleInferFields]
2516
+ );
2517
+ const resetIdleTimeout = useCallback(() => {
2518
+ if (idleTimer.current) {
2519
+ clearTimeout(idleTimer.current);
2520
+ idleTimer.current = null;
2521
+ }
2522
+ idleTimer.current = setTimeout(() => {
2523
+ setExpressionsIdle(true);
2524
+ }, 500);
2525
+ }, []);
2526
+ const evaluateExpression = useCallback(
2527
+ async (expression, key) => {
2528
+ resetIdleTimeout();
2529
+ setWaitingExpressions((prev) => ({ ...prev, [key]: true }));
2530
+ setExpressionsStarted(true);
2531
+ const result = await runExpression(expression, {
2532
+ ...defaultState,
2533
+ ...state
2534
+ });
2535
+ setExpressionResults((prev) => {
2536
+ if (prev[key] === false && typeof result === "boolean" && result) {
2537
+ if (!state[key]) {
2538
+ void handleReinferObjectByKey(key);
2539
+ }
2540
+ }
2541
+ return { ...prev, [key]: result };
2542
+ });
2543
+ setWaitingExpressions((prev) => ({ ...prev, [key]: false }));
2544
+ return result;
2545
+ },
2546
+ [state, resetIdleTimeout, handleReinferObjectByKey, defaultState]
2547
+ );
2548
+ if (!hasRunInference.current) {
2549
+ hasRunInference.current = true;
2550
+ void handleConfigInference();
2551
+ }
2552
+ const shouldHideField = useCallback(
2553
+ (key) => {
2554
+ if (expressionResults[key] === false) {
2555
+ return true;
2556
+ }
2557
+ return false;
2558
+ },
2559
+ [expressionResults]
2560
+ );
2561
+ const shouldDisableField = useCallback(
2562
+ (key) => {
2563
+ if (waitingExpressions[key] === true) {
2564
+ return true;
2565
+ }
2566
+ return false;
2567
+ },
2568
+ [waitingExpressions]
2569
+ );
2570
+ const getFieldErrorState = useCallback(
2571
+ (key) => {
2572
+ if (errors[key]) {
2573
+ return {
2574
+ message: errors[key],
2575
+ showMessage: true,
2576
+ error: true
2577
+ };
2578
+ }
2579
+ },
2580
+ [errors]
2581
+ );
2582
+ const whenClauses = useMemo(() => {
2583
+ const sectionClauses = (config.fields ?? []).filter((entry) => entry.type === "container").map((c) => c.when);
2584
+ const fieldClauses = flattenedFields.map((f) => f.when);
2585
+ return [...sectionClauses, ...fieldClauses].filter(Boolean);
2586
+ }, [config, flattenedFields]);
2587
+ const flattenedFieldsByKey = useMemo(() => {
2588
+ return flattenedFields.reduce((acc, field) => {
2589
+ acc[field.key] = field;
2590
+ return acc;
2591
+ }, {});
2592
+ }, [flattenedFields]);
2593
+ const getAdditionalDeps = useCallback(
2594
+ (dep, results, visited) => {
2595
+ results.add(dep);
2596
+ const depField = flattenedFieldsByKey[dep];
2597
+ if (depField?.dependencies?.length) {
2598
+ depField.dependencies.forEach((d) => {
2599
+ if (!visited.has(d)) {
2600
+ visited.add(d);
2601
+ getAdditionalDeps(d, results, visited);
2602
+ }
2603
+ });
2604
+ }
2605
+ },
2606
+ [flattenedFieldsByKey]
2607
+ );
2608
+ const flattenedFieldsWithDependencies = useMemo(() => {
2609
+ const fields = flattenedFields.map((field) => {
2610
+ const depsResultSet = /* @__PURE__ */ new Set();
2611
+ const deps = field.dependencies ?? [];
2612
+ const visited = /* @__PURE__ */ new Set();
2613
+ deps.forEach((dep) => {
2614
+ getAdditionalDeps(dep, depsResultSet, visited);
2615
+ });
2616
+ return {
2617
+ ...field,
2618
+ dependencies: Array.from(depsResultSet)
2619
+ };
2620
+ });
2621
+ return fields;
2622
+ }, [flattenedFields, getAdditionalDeps]);
2623
+ const afterFieldChange = useCallback(
2624
+ (key) => {
2625
+ const dependentFields = flattenedFieldsWithDependencies.filter((f) => {
2626
+ if (f.dependencies.length === 0) {
2627
+ return false;
2628
+ }
2629
+ return f.dependencies.includes(key) && f.key !== key;
2630
+ });
2631
+ dependentFields.forEach((field) => {
2632
+ fieldResetterRef.current[field.key]?.();
2633
+ });
2634
+ },
2635
+ [flattenedFieldsWithDependencies]
2636
+ );
2637
+ const validateForm = useCallback(async () => {
2638
+ const processedFields = await Promise.all(
2639
+ flattenedFields.map(async (field) => {
2640
+ if (!field.when) {
2641
+ return field;
2642
+ }
2643
+ const isVisible = await runExpression(field.when, {
2644
+ ...defaultState,
2645
+ ...state
2646
+ });
2647
+ return isVisible ? field : null;
2648
+ })
2649
+ );
2650
+ const fieldsToCheck = processedFields.filter(Boolean);
2651
+ let errorCount = 0;
2652
+ for (const field of fieldsToCheck) {
2653
+ if (field && field.required && !doesValueExist(field, state[field.key])) {
2654
+ setErrors((prev) => ({
2655
+ ...prev,
2656
+ [field.key]: "This field is required"
2657
+ }));
2658
+ errorCount++;
2659
+ } else if (field?.validation_pattern) {
2660
+ const isValid = new RegExp(field.validation_pattern).test(
2661
+ state[field.key]?.value ?? field.default ?? ""
2662
+ );
2663
+ if (!isValid) {
2664
+ setErrors((prev) => ({
2665
+ ...prev,
2666
+ [field.key]: `Value must match the pattern ${field.validation_pattern ?? ""}`
2667
+ }));
2668
+ errorCount++;
2669
+ }
2670
+ } else {
2671
+ if (field) {
2672
+ setErrors((prev) => ({
2673
+ ...prev,
2674
+ [field.key]: void 0
2675
+ }));
2676
+ }
2677
+ }
2678
+ }
2679
+ return {
2680
+ isValid: errorCount === 0,
2681
+ includedKeys: fieldsToCheck.map((f) => f?.key).filter((k) => Boolean(k))
2682
+ };
2683
+ }, [state, flattenedFields, defaultState]);
2684
+ useEffect(() => {
2685
+ resetIdleTimeout();
2686
+ }, [resetIdleTimeout]);
2687
+ const waitingExpressionCount = Object.values(waitingExpressions).filter(Boolean).length;
2688
+ if (!expressionsStarted && whenClauses.length === 0) {
2689
+ setExpressionsStarted(true);
2690
+ }
2691
+ if (expressionsStarted && initialExpressionsPending && waitingExpressionCount === 0) {
2692
+ setInitialExpressionsPending(false);
2693
+ }
2694
+ return /* @__PURE__ */ jsx(
2695
+ SetupAssistantProvider,
2696
+ {
2697
+ value: {
2698
+ state,
2699
+ setState,
2700
+ interpolateValue,
2701
+ getNestedFields,
2702
+ reInferFieldsForObject,
2703
+ inferencePending,
2704
+ evaluateExpression,
2705
+ shouldHideField,
2706
+ shouldDisableField,
2707
+ expressionsIdle,
2708
+ getFieldErrorState,
2709
+ validateForm,
2710
+ waitingExpressionCount,
2711
+ initialExpressionsPending,
2712
+ flattenedFields,
2713
+ flattenedFieldsByKey,
2714
+ afterFieldChange,
2715
+ registerFieldResetter,
2716
+ disabledKeys: disabledKeys ?? []
2717
+ },
2718
+ children
2719
+ }
2720
+ );
2721
+ };
2722
+ var useSetupAssistant = () => {
2723
+ const context = useContext(SetupAssistantContext);
2724
+ if (!context) {
2725
+ throw new Error("useSetupAssistant must be used within a SetupAssistantController");
2726
+ }
2727
+ return context;
2728
+ };
2729
+
2730
+ export { PluginBootstrapSuccess, PluginEngineProvider, RouteScriptRunner, SetupAssistantController, TranslationWrapper, useCalendarEvents, useCalendarOptions, useCalendarSourceCustomScript, useCalendarSourceReset, useFloatingFrame, useFloatingFrameCustomScript, useGenericPluginCustomScript, useManualInteraction, usePluginCallback, usePluginCustomHTML, usePluginPage, useRecordDetailCustomScript, useSetupAssistant, useTranslation };
2731
+ //# sourceMappingURL=react.js.map
2732
+ //# sourceMappingURL=react.js.map