@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.
- package/LICENSE.md +674 -0
- package/README.md +160 -0
- package/dist/ThirdPartyScript-DdrF7zh7.d.ts +459 -0
- package/dist/automation-C8hsM_Rc.d.ts +36 -0
- package/dist/chunk-2MW4AGSN.js +2415 -0
- package/dist/chunk-2MW4AGSN.js.map +1 -0
- package/dist/chunk-3LDUYA57.js +61 -0
- package/dist/chunk-3LDUYA57.js.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +29 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-727AYMPR.js +120 -0
- package/dist/chunk-727AYMPR.js.map +1 -0
- package/dist/chunk-GGGXYD3K.js +40 -0
- package/dist/chunk-GGGXYD3K.js.map +1 -0
- package/dist/chunk-LGJYUPYZ.js +3 -0
- package/dist/chunk-LGJYUPYZ.js.map +1 -0
- package/dist/chunk-NXLAROEM.js +730 -0
- package/dist/chunk-NXLAROEM.js.map +1 -0
- package/dist/chunk-P7GQBCEH.js +172 -0
- package/dist/chunk-P7GQBCEH.js.map +1 -0
- package/dist/chunk-UOTNQL56.js +46 -0
- package/dist/chunk-UOTNQL56.js.map +1 -0
- package/dist/chunk-VYBEGZLN.js +319 -0
- package/dist/chunk-VYBEGZLN.js.map +1 -0
- package/dist/chunk-XKKZPZMB.js +121 -0
- package/dist/chunk-XKKZPZMB.js.map +1 -0
- package/dist/communication.d.ts +13 -0
- package/dist/communication.js +6 -0
- package/dist/communication.js.map +1 -0
- package/dist/contexts/base.d.ts +99 -0
- package/dist/contexts/base.js +7 -0
- package/dist/contexts/base.js.map +1 -0
- package/dist/contexts/floatingFrame.d.ts +18 -0
- package/dist/contexts/floatingFrame.js +8 -0
- package/dist/contexts/floatingFrame.js.map +1 -0
- package/dist/contexts/recordDetail.d.ts +32 -0
- package/dist/contexts/recordDetail.js +8 -0
- package/dist/contexts/recordDetail.js.map +1 -0
- package/dist/floatingFrames-BzaoL7cq.d.ts +8 -0
- package/dist/generic-RiEgdDAq.d.ts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/merge-translations.js +28 -0
- package/dist/react.d.ts +367 -0
- package/dist/react.js +2732 -0
- package/dist/react.js.map +1 -0
- package/dist/routeScript-kttY2YZh.d.ts +303 -0
- package/dist/translation.json +8 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +40 -0
- package/dist/util.js +87 -0
- package/dist/util.js.map +1 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +19 -0
- package/dist/vite.js.map +1 -0
- package/dist/workers/calendarSource.worker.d.ts +2 -0
- package/dist/workers/calendarSource.worker.js +62 -0
- package/dist/workers/calendarSource.worker.js.map +1 -0
- package/dist/workers/expression.worker.d.ts +2 -0
- package/dist/workers/expression.worker.js +35 -0
- package/dist/workers/expression.worker.js.map +1 -0
- package/dist/workers/floatingFrame.worker.d.ts +2 -0
- package/dist/workers/floatingFrame.worker.js +57 -0
- package/dist/workers/floatingFrame.worker.js.map +1 -0
- package/dist/workers/generic.worker.d.ts +2 -0
- package/dist/workers/generic.worker.js +57 -0
- package/dist/workers/generic.worker.js.map +1 -0
- package/dist/workers/recordDetail.worker.d.ts +2 -0
- package/dist/workers/recordDetail.worker.js +59 -0
- package/dist/workers/recordDetail.worker.js.map +1 -0
- 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
|