api_binding_ui 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -384
- package/SKILL.md +49 -294
- package/dist/framework/contract.d.ts +268 -0
- package/dist/framework/engine.d.ts +88 -0
- package/dist/framework/expr.d.ts +34 -0
- package/dist/framework/loadCatalog.d.ts +6 -0
- package/dist/framework/names.d.ts +7 -0
- package/dist/framework/openapi.d.ts +7 -0
- package/dist/framework/runner.d.ts +33 -0
- package/dist/framework/schema.d.ts +22 -0
- package/dist/framework/store.d.ts +17 -0
- package/dist/framework/validate.d.ts +7 -0
- package/dist/index-e9twc25y.js +754 -0
- package/dist/index-e9twc25y.js.map +16 -0
- package/dist/index.d.ts +11 -26
- package/dist/index.js +345 -3790
- package/dist/index.js.map +14 -0
- package/dist/runtime/BindingProvider.d.ts +42 -0
- package/dist/runtime/BindingScreen.d.ts +23 -0
- package/dist/runtime/hooks.d.ts +22 -0
- package/dist/runtime/screenStore.d.ts +7 -0
- package/dist/studio/ApiPicker.d.ts +15 -0
- package/dist/studio/BindingStudio.d.ts +21 -0
- package/dist/studio/BindingWidget.d.ts +9 -0
- package/dist/studio/Collapse.d.ts +6 -0
- package/dist/studio/ComponentList.d.ts +10 -0
- package/dist/studio/FindAnything.d.ts +7 -0
- package/dist/studio/Inspector.d.ts +12 -0
- package/dist/studio/Navigator.d.ts +17 -0
- package/dist/studio/SettingsPanel.d.ts +15 -0
- package/dist/studio/Tip.d.ts +23 -0
- package/dist/studio/editors/ConsumeEditor.d.ts +4 -0
- package/dist/studio/editors/RequestInputs.d.ts +11 -0
- package/dist/studio/editors/ScopedEditor.d.ts +3 -0
- package/dist/studio/editors/SlotSection.d.ts +3 -0
- package/dist/studio/editors/SourceSelect.d.ts +10 -0
- package/dist/studio/editors/StepCard.d.ts +12 -0
- package/dist/studio/editors/TriggerEditor.d.ts +4 -0
- package/dist/studio/editors/types.d.ts +14 -0
- package/dist/studio/icons.d.ts +65 -0
- package/dist/studio/index.css +2021 -0
- package/dist/studio/index.d.ts +3 -0
- package/dist/studio/index.js +2806 -0
- package/dist/studio/index.js.map +33 -0
- package/dist/studio/useBindings.d.ts +20 -0
- package/dist/studio/useCatalog.d.ts +15 -0
- package/dist/studio/useGlide.d.ts +11 -0
- package/dist/studio/useSettings.d.ts +8 -0
- package/package.json +20 -9
- package/spec/FRAMEWORK.md +271 -0
- package/dist/api-binding-ui.css +0 -244
- package/dist/components/ApiBindingWorkspace.d.ts +0 -10
- package/dist/components/ApiMappings.d.ts +0 -21
- package/dist/components/BindApiButton.d.ts +0 -41
- package/dist/components/RequestPreview.d.ts +0 -7
- package/dist/components/SimulationDialog.d.ts +0 -8
- package/dist/components/WorkspaceContext.d.ts +0 -7
- package/dist/components/ui/AppGrid.d.ts +0 -27
- package/dist/components/ui/Badge.d.ts +0 -9
- package/dist/components/ui/Banner.d.ts +0 -8
- package/dist/components/ui/Button.d.ts +0 -31
- package/dist/components/ui/Card.d.ts +0 -14
- package/dist/components/ui/Dialog.d.ts +0 -61
- package/dist/components/ui/Icons.d.ts +0 -21
- package/dist/components/ui/Input.d.ts +0 -31
- package/dist/core/bindingContext.d.ts +0 -67
- package/dist/core/catalog.d.ts +0 -18
- package/dist/core/componentId.d.ts +0 -12
- package/dist/core/model.d.ts +0 -57
- package/dist/core/transport.d.ts +0 -33
- package/dist/core/valueCoercion.d.ts +0 -14
- package/dist/hooks/useAppliedBinding.d.ts +0 -6
- package/dist/index.css +0 -244
- package/dist/style.css +0 -244
- package/dist/types.d.ts +0 -232
package/dist/index.js
CHANGED
|
@@ -1,3790 +1,345 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
type
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
"
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
t = t.replace(/\[\]$/, "").trim();
|
|
347
|
-
array = true;
|
|
348
|
-
}
|
|
349
|
-
return { base: t, array };
|
|
350
|
-
}
|
|
351
|
-
function coerceScalar(text, base) {
|
|
352
|
-
const t = text.trim();
|
|
353
|
-
if (/^number$/.test(base)) {
|
|
354
|
-
if (t === "")
|
|
355
|
-
return;
|
|
356
|
-
const n = Number(t);
|
|
357
|
-
return Number.isFinite(n) ? n : undefined;
|
|
358
|
-
}
|
|
359
|
-
if (/^boolean$/.test(base)) {
|
|
360
|
-
const l = t.toLowerCase();
|
|
361
|
-
return TRUE.has(l) ? true : FALSE.has(l) ? false : undefined;
|
|
362
|
-
}
|
|
363
|
-
return text;
|
|
364
|
-
}
|
|
365
|
-
function coerce(text, type) {
|
|
366
|
-
const { base, array } = baseType(type);
|
|
367
|
-
if (!array)
|
|
368
|
-
return coerceScalar(text, base);
|
|
369
|
-
const items = text.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
370
|
-
const out = items.map((s) => coerceScalar(s, base));
|
|
371
|
-
if (out.some((v) => v === undefined))
|
|
372
|
-
return;
|
|
373
|
-
return out.length ? out : undefined;
|
|
374
|
-
}
|
|
375
|
-
function readPath(obj, path) {
|
|
376
|
-
if (path === "")
|
|
377
|
-
return obj;
|
|
378
|
-
return path.split(".").reduce((curr, key) => {
|
|
379
|
-
if (curr === null || curr === undefined || typeof curr !== "object")
|
|
380
|
-
return;
|
|
381
|
-
return curr[key];
|
|
382
|
-
}, obj);
|
|
383
|
-
}
|
|
384
|
-
var asText = (v) => v === null || v === undefined ? "" : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
385
|
-
var FREE_TEXT_SOURCE2 = "freeText";
|
|
386
|
-
function resolveParam(p, ctx) {
|
|
387
|
-
if (!p.source)
|
|
388
|
-
return;
|
|
389
|
-
let value;
|
|
390
|
-
if (p.source === FREE_TEXT_SOURCE2) {
|
|
391
|
-
value = p.property;
|
|
392
|
-
} else if (ctx.grid && p.source === ctx.grid.gridId) {
|
|
393
|
-
const prefix = `${ctx.grid.gridId}.`;
|
|
394
|
-
if (!p.property.startsWith(prefix) || !ctx.grid.rows.length)
|
|
395
|
-
return;
|
|
396
|
-
const path = p.property.slice(prefix.length);
|
|
397
|
-
const parts = ctx.grid.rows.map((r) => asText(readPath(r, path)));
|
|
398
|
-
value = parts.every((s) => s === "") ? "" : parts.join(",");
|
|
399
|
-
} else {
|
|
400
|
-
const parents = ctx.parents ?? [];
|
|
401
|
-
let parentId;
|
|
402
|
-
let fieldName = p.source;
|
|
403
|
-
for (const parent of parents) {
|
|
404
|
-
if (p.source.startsWith(`${parent.id}.`)) {
|
|
405
|
-
parentId = parent.id;
|
|
406
|
-
fieldName = p.source.slice(parent.id.length + 1);
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
const values = parentId ? parents.find((x) => x.id === parentId)?.values ?? {} : ctx.form;
|
|
411
|
-
value = asText(values[fieldName]);
|
|
412
|
-
}
|
|
413
|
-
return value === "" ? undefined : value;
|
|
414
|
-
}
|
|
415
|
-
function setPath(target, path, value) {
|
|
416
|
-
const keys = path.split(".");
|
|
417
|
-
let cur = target;
|
|
418
|
-
keys.slice(0, -1).forEach((k) => {
|
|
419
|
-
if (!cur[k] || typeof cur[k] !== "object")
|
|
420
|
-
cur[k] = {};
|
|
421
|
-
cur = cur[k];
|
|
422
|
-
});
|
|
423
|
-
cur[keys[keys.length - 1]] = value;
|
|
424
|
-
}
|
|
425
|
-
function setArrayOfObjects(target, name, text, leafType) {
|
|
426
|
-
const i = name.indexOf("[].");
|
|
427
|
-
const head = name.slice(0, i);
|
|
428
|
-
const rest = name.slice(i + 3);
|
|
429
|
-
const values = text.split(",").map((s) => s.trim());
|
|
430
|
-
const coerced = values.map((v) => v === "" ? undefined : coerce(v, leafType));
|
|
431
|
-
if (coerced.some((v, idx) => v === undefined && values[idx] !== ""))
|
|
432
|
-
return false;
|
|
433
|
-
let arr = head.split(".").reduce((o, k) => o?.[k], target);
|
|
434
|
-
if (!Array.isArray(arr)) {
|
|
435
|
-
arr = [];
|
|
436
|
-
setPath(target, head, arr);
|
|
437
|
-
}
|
|
438
|
-
coerced.forEach((v, idx) => {
|
|
439
|
-
if (v === undefined)
|
|
440
|
-
return;
|
|
441
|
-
arr[idx] ??= {};
|
|
442
|
-
if (rest.includes("[]."))
|
|
443
|
-
setArrayOfObjects(arr[idx], rest, String(v), leafType);
|
|
444
|
-
else
|
|
445
|
-
setPath(arr[idx], rest, v);
|
|
446
|
-
});
|
|
447
|
-
return true;
|
|
448
|
-
}
|
|
449
|
-
function buildRequest(mappings, ctx, spec) {
|
|
450
|
-
const out = { data: {}, params: {}, path: {} };
|
|
451
|
-
const byName = new Map(spec.map((p) => [p.name, p]));
|
|
452
|
-
for (const m of mappings) {
|
|
453
|
-
const text = resolveParam(m, ctx);
|
|
454
|
-
if (text === undefined)
|
|
455
|
-
continue;
|
|
456
|
-
const s = byName.get(m.param);
|
|
457
|
-
const type = s?.type ?? "string";
|
|
458
|
-
const target = s?.in === "query" ? out.params : s?.in === "path" ? out.path : out.data;
|
|
459
|
-
if (m.param.includes("[].")) {
|
|
460
|
-
setArrayOfObjects(target, m.param, text, type);
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
const value = coerce(text, type);
|
|
464
|
-
if (value === undefined)
|
|
465
|
-
continue;
|
|
466
|
-
setPath(target, m.param, value);
|
|
467
|
-
}
|
|
468
|
-
return out;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// src/core/model.ts
|
|
472
|
-
var CONFIG_VERSION = 1;
|
|
473
|
-
var draftKey = (key) => `api-binding:draft:${key}`;
|
|
474
|
-
var appliedKey = (key) => `api-binding:applied:${key}`;
|
|
475
|
-
var fingerprint = (config) => JSON.stringify(config);
|
|
476
|
-
var SUBMIT = componentId({ kind: "button", name: "Submit" });
|
|
477
|
-
var CANCEL = componentId({ kind: "button", name: "Cancel" });
|
|
478
|
-
var CUSTOMER_PARAM = "customerIds";
|
|
479
|
-
var seq = 0;
|
|
480
|
-
var newCallId = () => `call-${Date.now().toString(36)}-${(seq++).toString(36)}`;
|
|
481
|
-
var definitionFor = (context, api, method) => context.definitions.find((d) => d.api === api && (!method || d.method === method));
|
|
482
|
-
var methodsFor = (context, api) => context.definitions.filter((d) => d.api === api).map((d) => d.method);
|
|
483
|
-
var apisOf = (context) => [...new Set(context.definitions.map((d) => d.api))];
|
|
484
|
-
var fieldId = (name) => componentId({ kind: "field", name });
|
|
485
|
-
function defaultField(c) {
|
|
486
|
-
return {
|
|
487
|
-
name: c.name,
|
|
488
|
-
label: c.label,
|
|
489
|
-
required: !!c.required,
|
|
490
|
-
input: c.name === "subject" || c.name === "notes" ? "template" : "user",
|
|
491
|
-
excluded: false
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function reconcileCallParams(call, context) {
|
|
495
|
-
const def = definitionFor(context, call.api, call.method);
|
|
496
|
-
if (!def)
|
|
497
|
-
return call;
|
|
498
|
-
const saved = new Map(call.params.map((p) => [p.param, p]));
|
|
499
|
-
const params = def.params.map((p) => ({
|
|
500
|
-
param: p.name,
|
|
501
|
-
required: p.required,
|
|
502
|
-
source: saved.get(p.name)?.source ?? "",
|
|
503
|
-
property: saved.get(p.name)?.property ?? ""
|
|
504
|
-
}));
|
|
505
|
-
for (const p of call.params) {
|
|
506
|
-
if (!def.params.some((d) => d.name === p.param))
|
|
507
|
-
params.push(p);
|
|
508
|
-
}
|
|
509
|
-
return JSON.stringify(params) === JSON.stringify(call.params) ? call : { ...call, params };
|
|
510
|
-
}
|
|
511
|
-
function fromFile(file, context) {
|
|
512
|
-
if (!file)
|
|
513
|
-
return [];
|
|
514
|
-
const rows = (purpose) => (file[purpose] ?? []).map((r, i) => ({
|
|
515
|
-
id: `${purpose}-${i + 1}`,
|
|
516
|
-
purpose,
|
|
517
|
-
component: normalizeComponent(r.component),
|
|
518
|
-
api: r.api,
|
|
519
|
-
method: r.method || methodsFor(context, r.api)[0] || "",
|
|
520
|
-
timing: purpose === "read" ? r.timing || "lazy" : "",
|
|
521
|
-
...purpose === "read" ? { freshness: r.freshness || "fresh" } : {},
|
|
522
|
-
...purpose === "read" && r.pollSeconds ? { pollSeconds: r.pollSeconds } : {},
|
|
523
|
-
params: r.params.map((p) => ({
|
|
524
|
-
...p,
|
|
525
|
-
source: normalizeComponent(p.source),
|
|
526
|
-
property: p.source === FREE_TEXT_SOURCE2 ? p.property : normalizeComponent(p.property)
|
|
527
|
-
}))
|
|
528
|
-
}));
|
|
529
|
-
return [...rows("read"), ...rows("write")].map((c) => reconcileCallParams(c, context));
|
|
530
|
-
}
|
|
531
|
-
function proposeConfig(context) {
|
|
532
|
-
return {
|
|
533
|
-
version: 2,
|
|
534
|
-
customerRule: "each",
|
|
535
|
-
success: "close",
|
|
536
|
-
templateFills: true,
|
|
537
|
-
fields: context.snapshot.components.map(defaultField),
|
|
538
|
-
calls: fromFile(context.mappings, context)
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
function reconcileConfig(config, context) {
|
|
542
|
-
const fields = context.snapshot.components.map((c) => {
|
|
543
|
-
const old = config.fields.find((f) => f.name === c.name);
|
|
544
|
-
return old ? { ...old, label: c.label, required: !!c.required } : defaultField(c);
|
|
545
|
-
});
|
|
546
|
-
const next = {
|
|
547
|
-
...config,
|
|
548
|
-
fields,
|
|
549
|
-
calls: config.calls.map((c) => reconcileCallParams(c, context))
|
|
550
|
-
};
|
|
551
|
-
return fingerprint(next) === fingerprint(config) ? config : next;
|
|
552
|
-
}
|
|
553
|
-
function migrateConfig(raw, context) {
|
|
554
|
-
if (!raw || typeof raw !== "object")
|
|
555
|
-
return null;
|
|
556
|
-
const value = raw;
|
|
557
|
-
if (value.version === 2 && Array.isArray(value.calls) && Array.isArray(value.fields)) {
|
|
558
|
-
return reconcileConfig(value, context);
|
|
559
|
-
}
|
|
560
|
-
if (!Array.isArray(value.connections))
|
|
561
|
-
return null;
|
|
562
|
-
const old = value;
|
|
563
|
-
const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
564
|
-
const base = proposeConfig(context);
|
|
565
|
-
let calls = base.calls;
|
|
566
|
-
const grid = context.snapshot.grid?.id;
|
|
567
|
-
const conflicts = [];
|
|
568
|
-
const legacy = new Set(old.connections.map((c) => c.name));
|
|
569
|
-
if (has(old, "saveApi") && !old.saveApi) {
|
|
570
|
-
calls = calls.filter((c) => !(c.purpose === "write" && c.component === SUBMIT));
|
|
571
|
-
} else if (old.saveApi || !has(old, "saveApi")) {
|
|
572
|
-
let submit = calls.find((c) => c.purpose === "write" && c.component === SUBMIT);
|
|
573
|
-
if (!submit && old.saveApi) {
|
|
574
|
-
submit = { id: "write-1", purpose: "write", component: SUBMIT, api: old.saveApi, method: "", timing: "", params: [] };
|
|
575
|
-
calls = [...calls, submit];
|
|
576
|
-
}
|
|
577
|
-
if (submit) {
|
|
578
|
-
const api = old.saveApi || submit.api;
|
|
579
|
-
const pick = methodsFor(context, api).includes("POST") ? "POST" : methodsFor(context, api)[0] ?? "";
|
|
580
|
-
const patched = reconcileCallParams({ ...submit, api, method: submit.api === api && submit.method ? submit.method : pick }, context);
|
|
581
|
-
const params = patched.params.map((p) => {
|
|
582
|
-
let next = legacy.has(p.source) ? { ...p, source: "", property: "" } : p;
|
|
583
|
-
const claim = old.connections.filter((c) => c.destination === p.param && !c.excluded);
|
|
584
|
-
if (claim.length === 1) {
|
|
585
|
-
next = { ...next, source: claim[0].name, property: claim[0].name };
|
|
586
|
-
} else if (claim.length > 1) {
|
|
587
|
-
conflicts.push({ callId: patched.id, param: p.param, fields: claim.map((c) => c.name) });
|
|
588
|
-
next = { ...next, source: "", property: "" };
|
|
589
|
-
}
|
|
590
|
-
if (has(old, "customerProperty") && grid && next.source === grid && p.param === CUSTOMER_PARAM) {
|
|
591
|
-
next = { ...next, property: old.customerProperty ?? "" };
|
|
592
|
-
}
|
|
593
|
-
return next;
|
|
594
|
-
});
|
|
595
|
-
calls = calls.map((c) => c.id === submit.id ? { ...patched, params } : c);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
for (const conn of old.connections) {
|
|
599
|
-
if (!has(conn, "readApi"))
|
|
600
|
-
continue;
|
|
601
|
-
if (!conn.readApi) {
|
|
602
|
-
calls = calls.filter((c) => !(c.purpose === "read" && c.component === conn.name));
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
const existing = calls.find((c) => c.purpose === "read" && c.component === conn.name);
|
|
606
|
-
if (existing) {
|
|
607
|
-
calls = calls.map((c) => c === existing ? reconcileCallParams({ ...c, api: conn.readApi, method: c.api === conn.readApi ? c.method : methodsFor(context, conn.readApi)[0] ?? "" }, context) : c);
|
|
608
|
-
} else {
|
|
609
|
-
calls = [
|
|
610
|
-
...calls,
|
|
611
|
-
reconcileCallParams({
|
|
612
|
-
id: newCallId(),
|
|
613
|
-
purpose: "read",
|
|
614
|
-
component: conn.name,
|
|
615
|
-
api: conn.readApi,
|
|
616
|
-
method: methodsFor(context, conn.readApi)[0] ?? "",
|
|
617
|
-
timing: "lazy",
|
|
618
|
-
freshness: "fresh",
|
|
619
|
-
params: []
|
|
620
|
-
}, context)
|
|
621
|
-
];
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
const fields = base.fields.map((f) => {
|
|
625
|
-
const c = old.connections.find((x) => x.name === f.name);
|
|
626
|
-
return c ? { ...f, input: c.input, excluded: c.excluded } : f;
|
|
627
|
-
});
|
|
628
|
-
return {
|
|
629
|
-
version: 2,
|
|
630
|
-
customerRule: old.customerRule ?? "each",
|
|
631
|
-
success: old.success ?? "close",
|
|
632
|
-
templateFills: old.templateFills ?? true,
|
|
633
|
-
fields,
|
|
634
|
-
calls,
|
|
635
|
-
...conflicts.length ? { conflicts } : {}
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
function callsFor(config, component) {
|
|
639
|
-
if (!component)
|
|
640
|
-
return config.calls;
|
|
641
|
-
return config.calls.filter((c) => c.component === component || c.params.some((p) => p.source === component));
|
|
642
|
-
}
|
|
643
|
-
function changeCallTarget(call, api, method, context) {
|
|
644
|
-
const methods = methodsFor(context, api);
|
|
645
|
-
const m = method && methods.includes(method) ? method : methods[0] ?? "";
|
|
646
|
-
const def = definitionFor(context, api, m);
|
|
647
|
-
const saved = new Map(call.params.map((p) => [p.param, p]));
|
|
648
|
-
const params = (def?.params ?? []).map((p) => ({
|
|
649
|
-
param: p.name,
|
|
650
|
-
required: p.required,
|
|
651
|
-
source: saved.get(p.name)?.source ?? "",
|
|
652
|
-
property: saved.get(p.name)?.property ?? ""
|
|
653
|
-
}));
|
|
654
|
-
const read = call.purpose === "read";
|
|
655
|
-
return {
|
|
656
|
-
...call,
|
|
657
|
-
api,
|
|
658
|
-
method: m,
|
|
659
|
-
params,
|
|
660
|
-
...read ? { timing: call.timing || "lazy", freshness: call.freshness || "fresh" } : {}
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
function droppedBy(call, api, method, context) {
|
|
664
|
-
const next = changeCallTarget(call, api, method, context);
|
|
665
|
-
const kept = new Set(next.params.map((p) => p.param));
|
|
666
|
-
return call.params.filter((p) => p.source && !kept.has(p.param)).length;
|
|
667
|
-
}
|
|
668
|
-
var populated = (call) => call.params.some((p) => !!p.source);
|
|
669
|
-
function changePurpose(call, purpose) {
|
|
670
|
-
if (purpose === call.purpose)
|
|
671
|
-
return call;
|
|
672
|
-
return purpose === "read" ? { ...call, purpose, timing: "lazy", freshness: "fresh" } : { ...call, purpose, timing: "", freshness: undefined, pollSeconds: undefined };
|
|
673
|
-
}
|
|
674
|
-
function newCall(purpose, component) {
|
|
675
|
-
return {
|
|
676
|
-
id: newCallId(),
|
|
677
|
-
purpose,
|
|
678
|
-
component,
|
|
679
|
-
api: "",
|
|
680
|
-
method: "",
|
|
681
|
-
timing: purpose === "read" ? "lazy" : "",
|
|
682
|
-
...purpose === "read" ? { freshness: "fresh" } : {},
|
|
683
|
-
params: []
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
function moveCall(calls, id, delta) {
|
|
687
|
-
const i = calls.findIndex((c) => c.id === id);
|
|
688
|
-
const j = i + delta;
|
|
689
|
-
if (i < 0 || j < 0 || j >= calls.length)
|
|
690
|
-
return calls;
|
|
691
|
-
const next = [...calls];
|
|
692
|
-
[next[i], next[j]] = [next[j], next[i]];
|
|
693
|
-
return next;
|
|
694
|
-
}
|
|
695
|
-
function setParamSource(p, source, gridId) {
|
|
696
|
-
if (source === FREE_TEXT_SOURCE2)
|
|
697
|
-
return { ...p, source, property: "" };
|
|
698
|
-
if (gridId && source === gridId)
|
|
699
|
-
return { ...p, source, property: "" };
|
|
700
|
-
return { ...p, source, property: source };
|
|
701
|
-
}
|
|
702
|
-
var gridPaths = (context) => context.snapshot.grid ? gridPropertyPaths({ gridId: context.snapshot.grid.id, rows: context.snapshot.grid.rows }) : [];
|
|
703
|
-
var componentLabel = (context, id) => contextComponents(context.snapshot).find((o) => o.value === id)?.label ?? (id || "No target");
|
|
704
|
-
var endpointLabel = (call) => call.api ? `${call.method || "?"} ${call.api}` : "No API selected";
|
|
705
|
-
function callIssues(call, index, context) {
|
|
706
|
-
const out = [];
|
|
707
|
-
const name = `Call ${index + 1} (${call.purpose}${call.api ? ` ${endpointLabel(call)}` : ""})`;
|
|
708
|
-
const targets = new Set(contextComponents(context.snapshot).map((o) => o.value));
|
|
709
|
-
const sources = new Set(sourceComponents(context.snapshot).map((o) => o.value));
|
|
710
|
-
const paths = new Set(gridPaths(context));
|
|
711
|
-
const gridId = context.snapshot.grid?.id;
|
|
712
|
-
if (!call.component)
|
|
713
|
-
out.push(`${name}: choose which component it belongs to.`);
|
|
714
|
-
else if (!targets.has(call.component))
|
|
715
|
-
out.push(`${name}: target "${call.component}" is not in this window.`);
|
|
716
|
-
if (!call.api) {
|
|
717
|
-
out.push(`${name}: choose an API.`);
|
|
718
|
-
return out;
|
|
719
|
-
}
|
|
720
|
-
if (!apisOf(context).includes(call.api)) {
|
|
721
|
-
out.push(`${name}: API "${call.api}" is not registered.`);
|
|
722
|
-
return out;
|
|
723
|
-
}
|
|
724
|
-
if (!call.method || !methodsFor(context, call.api).includes(call.method)) {
|
|
725
|
-
out.push(`${name}: method "${call.method || "none"}" is not registered for this API.`);
|
|
726
|
-
return out;
|
|
727
|
-
}
|
|
728
|
-
const def = definitionFor(context, call.api, call.method);
|
|
729
|
-
if (def.available === false)
|
|
730
|
-
out.push(`${name}: unavailable — ${def.problem || "generated hook/contract missing"}.`);
|
|
731
|
-
if (call.purpose === "read") {
|
|
732
|
-
if (call.timing !== "lazy" && call.timing !== "prefetch")
|
|
733
|
-
out.push(`${name}: choose when it loads.`);
|
|
734
|
-
if (!call.freshness)
|
|
735
|
-
out.push(`${name}: choose freshness.`);
|
|
736
|
-
if (call.freshness === "poll" && !(Number.isInteger(call.pollSeconds) && (call.pollSeconds ?? 0) > 0)) {
|
|
737
|
-
out.push(`${name}: poll interval must be a whole number of seconds above 0.`);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
for (const p of call.params) {
|
|
741
|
-
const spec = def.params.find((d) => d.name === p.param);
|
|
742
|
-
if (!spec) {
|
|
743
|
-
out.push(`${name}: "${p.param}" is no longer in the API contract — remove it or change the API.`);
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
if (!p.source) {
|
|
747
|
-
if (spec.required)
|
|
748
|
-
out.push(`${name}: required "${p.param}" has no source.`);
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
if (p.source === FREE_TEXT_SOURCE2) {
|
|
752
|
-
if (p.property === "") {
|
|
753
|
-
if (spec.required)
|
|
754
|
-
out.push(`${name}: required "${p.param}" has an empty fixed value.`);
|
|
755
|
-
} else if (!p.param.includes("[].") && coerce(p.property, spec.type) === undefined) {
|
|
756
|
-
out.push(`${name}: "${p.property}" is not a valid ${spec.type} for "${p.param}".`);
|
|
757
|
-
}
|
|
758
|
-
continue;
|
|
759
|
-
}
|
|
760
|
-
if (!sources.has(p.source)) {
|
|
761
|
-
out.push(`${name}: "${p.param}" source "${p.source}" is not in this window.`);
|
|
762
|
-
continue;
|
|
763
|
-
}
|
|
764
|
-
if (gridId && p.source === gridId && !paths.has(p.property)) {
|
|
765
|
-
out.push(`${name}: "${p.param}" needs a valid grid property${p.property ? ` ("${p.property}" not found)` : ""}.`);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
return out;
|
|
769
|
-
}
|
|
770
|
-
var fieldUsed = (config, name) => config.calls.some((c) => c.component === name || c.params.some((p) => p.source === name));
|
|
771
|
-
function issuesFor(config, context) {
|
|
772
|
-
const issues = [];
|
|
773
|
-
config.calls.forEach((c, i) => issues.push(...callIssues(c, i, context)));
|
|
774
|
-
if (context.snapshot.buttons.includes("Submit") && !config.calls.some((c) => c.purpose === "write" && c.component === SUBMIT)) {
|
|
775
|
-
issues.push("Submit has no write API mapping.");
|
|
776
|
-
}
|
|
777
|
-
for (const field of config.fields) {
|
|
778
|
-
const component = context.snapshot.components.find((c) => c.name === field.name);
|
|
779
|
-
if (field.excluded) {
|
|
780
|
-
if (field.required)
|
|
781
|
-
issues.push(`${field.label} is required and cannot be excluded.`);
|
|
782
|
-
if (fieldUsed(config, field.name))
|
|
783
|
-
issues.push(`${field.label} is excluded but still used by an API mapping.`);
|
|
784
|
-
continue;
|
|
785
|
-
}
|
|
786
|
-
if ((component?.kind ?? component?.type) === "checkbox" && !fieldUsed(config, field.name)) {
|
|
787
|
-
issues.push(`${field.label} has no supported action. Resolve it or explicitly exclude it.`);
|
|
788
|
-
}
|
|
789
|
-
if (component && (component.readable || component.kind === "dropdown" || component.type === "combobox") && component.optionsSource !== "static" && !config.calls.some((c) => c.purpose === "read" && c.component === field.name)) {
|
|
790
|
-
issues.push(`${field.label} needs a load connection.`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
for (const k of config.conflicts ?? []) {
|
|
794
|
-
const i = config.calls.findIndex((c) => c.id === k.callId);
|
|
795
|
-
const p = config.calls[i]?.params.find((x) => x.param === k.param);
|
|
796
|
-
if (p && !p.source) {
|
|
797
|
-
issues.push(`Call ${i + 1} (${endpointLabel(config.calls[i])}): "${k.param}" was claimed by ${k.fields.join(" and ")} in an older draft — choose its source.`);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
return [...new Set(issues)];
|
|
801
|
-
}
|
|
802
|
-
function actionContext(context, form, rows) {
|
|
803
|
-
const grid = context.snapshot.grid;
|
|
804
|
-
return {
|
|
805
|
-
form,
|
|
806
|
-
...grid ? { grid: { gridId: grid.id, rows: rows ?? grid.rows } } : {},
|
|
807
|
-
parents: (context.snapshot.contexts ?? []).map((p) => ({
|
|
808
|
-
id: p.id,
|
|
809
|
-
values: Object.fromEntries(p.components.map((c) => [c.name, c.value ?? ""]))
|
|
810
|
-
}))
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
var snapshotForm = (context) => Object.fromEntries(context.snapshot.components.map((c) => [c.name, c.value ?? ""]));
|
|
814
|
-
function resolutionIssues(call, index, def, ctx) {
|
|
815
|
-
if (!def)
|
|
816
|
-
return [];
|
|
817
|
-
const out = [];
|
|
818
|
-
const name = `Call ${index + 1} (${call.purpose} ${endpointLabel(call)})`;
|
|
819
|
-
for (const spec of def.params) {
|
|
820
|
-
const m = call.params.find((p) => p.param === spec.name);
|
|
821
|
-
const text = m ? resolveParam(m, ctx) : undefined;
|
|
822
|
-
if (text === undefined || text === "") {
|
|
823
|
-
if (spec.required && m?.source)
|
|
824
|
-
out.push(`${name}: required "${spec.name}" resolves to no value.`);
|
|
825
|
-
continue;
|
|
826
|
-
}
|
|
827
|
-
if (!spec.name.includes("[].") && coerce(text, spec.type) === undefined) {
|
|
828
|
-
out.push(`${name}: "${text}" is not a valid ${spec.type} for "${spec.name}", so it would not be sent.`);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return out;
|
|
832
|
-
}
|
|
833
|
-
function previewRequests(config, context, ctx, filter) {
|
|
834
|
-
return config.calls.map((c, i) => ({ c, i })).filter(({ c }) => !filter || filter(c)).map(({ c, i }) => {
|
|
835
|
-
const def = definitionFor(context, c.api, c.method);
|
|
836
|
-
const parts = buildRequest(c.params, ctx, def?.params ?? []);
|
|
837
|
-
return {
|
|
838
|
-
order: i + 1,
|
|
839
|
-
id: c.id,
|
|
840
|
-
purpose: c.purpose,
|
|
841
|
-
target: componentLabel(context, c.component),
|
|
842
|
-
method: c.method ?? "",
|
|
843
|
-
api: c.api,
|
|
844
|
-
hook: def?.hook ?? "",
|
|
845
|
-
available: !!def && def.available !== false,
|
|
846
|
-
parts,
|
|
847
|
-
issues: [...callIssues(c, i, context), ...resolutionIssues(c, i, def, ctx)]
|
|
848
|
-
};
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
var isNoteContract = (def) => !!def && ["subject", "description", "noteType"].every((n) => def.params.some((p) => p.name === n));
|
|
852
|
-
function isSimulated(call, context) {
|
|
853
|
-
const def = definitionFor(context, call.api, call.method);
|
|
854
|
-
if (!def || def.available === false)
|
|
855
|
-
return false;
|
|
856
|
-
if (call.purpose === "write") {
|
|
857
|
-
return call.component === SUBMIT && call.method === "POST" && isNoteContract(def) && def.params.some((p) => p.name === CUSTOMER_PARAM);
|
|
858
|
-
}
|
|
859
|
-
return !!context.options[call.component]?.length && !!context.mappings?.read.some((r) => normalizeComponent(r.component) === call.component && r.api === call.api && (r.method || call.method) === call.method);
|
|
860
|
-
}
|
|
861
|
-
function simulatorGaps(config, context) {
|
|
862
|
-
return config.calls.filter((c) => c.api && !isSimulated(c, context)).map((c) => c.purpose === "write" ? `No simulator for ${endpointLabel(c)} on ${componentLabel(context, c.component)}; only its request preview can be shown, so this binding cannot be verified here.` : `${endpointLabel(c)} for ${componentLabel(context, c.component)} is not tested: no captured response exists for it, so this binding cannot be verified here.`);
|
|
863
|
-
}
|
|
864
|
-
function simulationErrors(config, input, customers) {
|
|
865
|
-
const errors = [];
|
|
866
|
-
const hasGridParam = config.calls.some((c) => c.params.some((p) => p.param === CUSTOMER_PARAM || p.source.toLowerCase().includes("grid")));
|
|
867
|
-
if (hasGridParam) {
|
|
868
|
-
if (!customers)
|
|
869
|
-
errors.push("Select at least one customer.");
|
|
870
|
-
if (config.customerRule === "single" && customers !== 1)
|
|
871
|
-
errors.push("This binding requires exactly one customer.");
|
|
872
|
-
}
|
|
873
|
-
const required = (name) => config.fields.find((f) => f.name === name && !f.excluded && f.required);
|
|
874
|
-
if (required("notesType") && !input.notesType)
|
|
875
|
-
errors.push("Notes Type is required.");
|
|
876
|
-
if (required("subject") && !String(input.subject ?? "").trim())
|
|
877
|
-
errors.push("Subject is required.");
|
|
878
|
-
if (required("notes") && !String(input.notes ?? "").trim())
|
|
879
|
-
errors.push("Description is required.");
|
|
880
|
-
for (const field of config.fields) {
|
|
881
|
-
if (field.excluded || !field.required)
|
|
882
|
-
continue;
|
|
883
|
-
if (field.name === "notesType" || field.name === "subject" || field.name === "notes")
|
|
884
|
-
continue;
|
|
885
|
-
const val = input[field.name];
|
|
886
|
-
if (val === undefined || val === null || String(val).trim() === "") {
|
|
887
|
-
errors.push(`${field.label} is required.`);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return errors;
|
|
891
|
-
}
|
|
892
|
-
var simulationForm = (input) => {
|
|
893
|
-
const form = {
|
|
894
|
-
notesType: String(input.notesType ?? ""),
|
|
895
|
-
template: String(input.template ?? ""),
|
|
896
|
-
subject: String(input.subject ?? ""),
|
|
897
|
-
notes: String(input.notes ?? ""),
|
|
898
|
-
addCallLog: String(input.addCallLog ?? "")
|
|
899
|
-
};
|
|
900
|
-
for (const [k, v] of Object.entries(input)) {
|
|
901
|
-
form[k] = typeof v === "boolean" ? String(v) : String(v ?? "");
|
|
902
|
-
}
|
|
903
|
-
return form;
|
|
904
|
-
};
|
|
905
|
-
function simulateSave(config, context, input, rows, fail) {
|
|
906
|
-
const ctx = actionContext(context, simulationForm(input), rows);
|
|
907
|
-
const requests = previewRequests(config, context, ctx, (c) => c.purpose === "write" && c.component === SUBMIT);
|
|
908
|
-
const errors = [...simulationErrors(config, input, rows.length), ...simulatorGaps(config, context)];
|
|
909
|
-
if (!requests.length)
|
|
910
|
-
errors.push("Submit has no write API mapping.");
|
|
911
|
-
errors.push(...requests.flatMap((r) => r.issues));
|
|
912
|
-
if (errors.length)
|
|
913
|
-
return { notes: [], errors: [...new Set(errors)], requests };
|
|
914
|
-
if (fail) {
|
|
915
|
-
return {
|
|
916
|
-
notes: [],
|
|
917
|
-
errors: ["Simulated save failed. Your entered information is preserved; retry when ready."],
|
|
918
|
-
requests
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
const notes = [];
|
|
922
|
-
for (const r of requests) {
|
|
923
|
-
const d = r.parts.data;
|
|
924
|
-
const def = definitionFor(context, r.api, r.method);
|
|
925
|
-
if (isNoteContract(def)) {
|
|
926
|
-
if (typeof d.subject !== "string" || !d.subject.trim() || typeof d.description !== "string" || !d.description.trim() || typeof d.noteType !== "number") {
|
|
927
|
-
return {
|
|
928
|
-
notes: [],
|
|
929
|
-
errors: [`${endpointLabel(r)} would not send a valid note (subject, description, noteType). Review its request mappings.`],
|
|
930
|
-
requests
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
const ids = d[CUSTOMER_PARAM];
|
|
934
|
-
if (!Array.isArray(ids) || !ids.length || !ids.every((x) => typeof x === "number" && Number.isFinite(x))) {
|
|
935
|
-
return {
|
|
936
|
-
notes: [],
|
|
937
|
-
errors: [`${endpointLabel(r)} would not send valid customer IDs ("${CUSTOMER_PARAM}"). Review its customer identifier.`],
|
|
938
|
-
requests
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
const customers = ids;
|
|
942
|
-
customers.forEach((customerId) => notes.push({
|
|
943
|
-
id: `sample-note-${notes.length + 1}`,
|
|
944
|
-
customerId,
|
|
945
|
-
noteType: d.noteType,
|
|
946
|
-
subject: d.subject,
|
|
947
|
-
description: d.description
|
|
948
|
-
}));
|
|
949
|
-
} else {
|
|
950
|
-
notes.push({
|
|
951
|
-
id: `sample-record-${notes.length + 1}`,
|
|
952
|
-
...d
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
return { notes, errors: [], requests };
|
|
957
|
-
}
|
|
958
|
-
function canApply(config, context, verified) {
|
|
959
|
-
return issuesFor(config, context).length === 0 && simulatorGaps(config, context).length === 0 && verified === fingerprint(config);
|
|
960
|
-
}
|
|
961
|
-
function fieldSummary(config, context, name) {
|
|
962
|
-
const reads = config.calls.filter((c) => c.purpose === "read" && c.component === name).map((c) => `${endpointLabel(c)} · ${c.timing === "prefetch" ? "On Window Load" : "Lazy on Click"}`);
|
|
963
|
-
const field = config.fields.find((f) => f.name === name);
|
|
964
|
-
const sends = config.calls.flatMap((c) => c.params.filter((p) => p.source === name).map((p) => `${endpointLabel(c)} → ${p.param}`));
|
|
965
|
-
const input = field?.input === "template" && config.templateFills ? "User input or template" : "User input";
|
|
966
|
-
return {
|
|
967
|
-
from: field?.excluded ? "Excluded by choice" : [...reads, ...reads.length ? [] : [input]].join("; "),
|
|
968
|
-
to: field?.excluded ? "Hidden in Studio preview" : sends.join("; ") || (name === "template" && config.templateFills ? "Fills Subject and Description · not saved" : "Not sent by any API")
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
var isButton = isButtonId;
|
|
972
|
-
// src/core/transport.ts
|
|
973
|
-
var contextKey = (key) => `api-binding:context:${key}`;
|
|
974
|
-
var latestContexts = new Map;
|
|
975
|
-
var hostListening = false;
|
|
976
|
-
function storeBindingContext(context) {
|
|
977
|
-
try {
|
|
978
|
-
const key = context.snapshot.key;
|
|
979
|
-
const json = JSON.stringify(context);
|
|
980
|
-
localStorage.setItem(contextKey(key), json);
|
|
981
|
-
storeSnapshot(context.snapshot);
|
|
982
|
-
} catch {}
|
|
983
|
-
}
|
|
984
|
-
function readBindingContext(key) {
|
|
985
|
-
try {
|
|
986
|
-
const raw = localStorage.getItem(contextKey(key)) || localStorage.getItem(`api-binding-v2:context:${key}`);
|
|
987
|
-
return raw ? JSON.parse(raw) : null;
|
|
988
|
-
} catch {
|
|
989
|
-
return null;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
function validBindingContext(value, key) {
|
|
993
|
-
if (!value || typeof value !== "object")
|
|
994
|
-
return false;
|
|
995
|
-
const context = value;
|
|
996
|
-
return context.snapshot?.key === key && Array.isArray(context.snapshot.components) && !!context.options;
|
|
997
|
-
}
|
|
998
|
-
function openBindingStudio(context, options) {
|
|
999
|
-
const key = context.snapshot.key;
|
|
1000
|
-
storeBindingContext(context);
|
|
1001
|
-
latestContexts.set(key, context);
|
|
1002
|
-
if (!hostListening) {
|
|
1003
|
-
hostListening = true;
|
|
1004
|
-
window.addEventListener("message", (event) => {
|
|
1005
|
-
if (event.origin !== window.location.origin)
|
|
1006
|
-
return;
|
|
1007
|
-
const data = event.data;
|
|
1008
|
-
if ((data?.type === BINDING_READY || data?.type === "api-binding-v2-ready") && data.key) {
|
|
1009
|
-
const current = latestContexts.get(data.key);
|
|
1010
|
-
if (current) {
|
|
1011
|
-
const msg = { type: BINDING_CONTEXT, context: current };
|
|
1012
|
-
event.source?.postMessage(msg, event.origin);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
const tabName = `api-binding:${key}`;
|
|
1018
|
-
const routeBase = options?.routeBase || "/api-binding";
|
|
1019
|
-
const url = `${routeBase}/${encodeURIComponent(key)}`;
|
|
1020
|
-
const tab = window.open("", tabName, options?.features);
|
|
1021
|
-
if (!tab) {
|
|
1022
|
-
throw new Error("Your browser blocked the API Binding tab. Please allow popups for this site and try again.");
|
|
1023
|
-
}
|
|
1024
|
-
try {
|
|
1025
|
-
if (tab.location.href === "about:blank") {
|
|
1026
|
-
tab.location.href = url;
|
|
1027
|
-
}
|
|
1028
|
-
} catch {
|
|
1029
|
-
tab.location.href = url;
|
|
1030
|
-
}
|
|
1031
|
-
const msg = { type: BINDING_CONTEXT, context };
|
|
1032
|
-
try {
|
|
1033
|
-
tab.postMessage(msg, window.location.origin);
|
|
1034
|
-
} catch {}
|
|
1035
|
-
tab.focus();
|
|
1036
|
-
try {
|
|
1037
|
-
const channel = new BroadcastChannel(BINDING_CHANNEL);
|
|
1038
|
-
channel.postMessage({ key });
|
|
1039
|
-
channel.close();
|
|
1040
|
-
} catch {}
|
|
1041
|
-
return tab;
|
|
1042
|
-
}
|
|
1043
|
-
function notifyApplied(key, config) {
|
|
1044
|
-
const payload = {
|
|
1045
|
-
version: CONFIG_VERSION,
|
|
1046
|
-
config,
|
|
1047
|
-
appliedAt: new Date().toISOString()
|
|
1048
|
-
};
|
|
1049
|
-
const rawPayload = JSON.stringify(payload);
|
|
1050
|
-
try {
|
|
1051
|
-
localStorage.setItem(appliedKey(key), rawPayload);
|
|
1052
|
-
localStorage.setItem(`api-binding:applied:${key}`, rawPayload);
|
|
1053
|
-
} catch {}
|
|
1054
|
-
const msg = {
|
|
1055
|
-
type: BINDING_APPLIED,
|
|
1056
|
-
key,
|
|
1057
|
-
config
|
|
1058
|
-
};
|
|
1059
|
-
try {
|
|
1060
|
-
window.opener?.postMessage(msg, window.location.origin);
|
|
1061
|
-
window.opener?.postMessage({ type: "api-binding-v2-applied", key, config }, window.location.origin);
|
|
1062
|
-
} catch {}
|
|
1063
|
-
try {
|
|
1064
|
-
const channel = new BroadcastChannel(BINDING_CHANNEL);
|
|
1065
|
-
channel.postMessage(msg);
|
|
1066
|
-
channel.close();
|
|
1067
|
-
} catch {}
|
|
1068
|
-
}
|
|
1069
|
-
function notifySelectionUpdate(key, grid) {
|
|
1070
|
-
const msg = {
|
|
1071
|
-
type: BINDING_SELECTION_UPDATE,
|
|
1072
|
-
key,
|
|
1073
|
-
grid
|
|
1074
|
-
};
|
|
1075
|
-
try {
|
|
1076
|
-
const channel = new BroadcastChannel(BINDING_CHANNEL);
|
|
1077
|
-
channel.postMessage(msg);
|
|
1078
|
-
channel.close();
|
|
1079
|
-
} catch {}
|
|
1080
|
-
}
|
|
1081
|
-
var validContext = validBindingContext;
|
|
1082
|
-
// src/core/catalog.ts
|
|
1083
|
-
function friendlyLabel(operationId) {
|
|
1084
|
-
if (!operationId)
|
|
1085
|
-
return;
|
|
1086
|
-
return operationId.split(".").filter((p) => !/^v\d+$/i.test(p)).map((p) => p.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (c) => c.toUpperCase())).join(" ");
|
|
1087
|
-
}
|
|
1088
|
-
function buildCatalogFromRegistry({
|
|
1089
|
-
bindings,
|
|
1090
|
-
hookResolver = () => {
|
|
1091
|
-
return;
|
|
1092
|
-
},
|
|
1093
|
-
paramsResolver = () => [],
|
|
1094
|
-
availabilityChecker = () => ({ available: true })
|
|
1095
|
-
}) {
|
|
1096
|
-
const definitions = [];
|
|
1097
|
-
for (const [api, methodsObj] of Object.entries(bindings)) {
|
|
1098
|
-
for (const [method, meta] of Object.entries(methodsObj)) {
|
|
1099
|
-
const hook = hookResolver(api, method) ?? "";
|
|
1100
|
-
const params = paramsResolver(api, method);
|
|
1101
|
-
const status = availabilityChecker(api, method);
|
|
1102
|
-
definitions.push({
|
|
1103
|
-
api,
|
|
1104
|
-
method: method.toUpperCase(),
|
|
1105
|
-
hook,
|
|
1106
|
-
params,
|
|
1107
|
-
label: friendlyLabel(meta?.operationId),
|
|
1108
|
-
available: status.available,
|
|
1109
|
-
...status.problem ? { problem: status.problem } : {}
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
return definitions;
|
|
1114
|
-
}
|
|
1115
|
-
// src/hooks/useAppliedBinding.ts
|
|
1116
|
-
import * as React from "react";
|
|
1117
|
-
function useAppliedBinding(pageKey) {
|
|
1118
|
-
const [applied, setApplied] = React.useState(() => {
|
|
1119
|
-
try {
|
|
1120
|
-
const raw = localStorage.getItem(appliedKey(pageKey));
|
|
1121
|
-
return raw ? JSON.parse(raw) : null;
|
|
1122
|
-
} catch {
|
|
1123
|
-
return null;
|
|
1124
|
-
}
|
|
1125
|
-
});
|
|
1126
|
-
React.useEffect(() => {
|
|
1127
|
-
function check() {
|
|
1128
|
-
try {
|
|
1129
|
-
const raw = localStorage.getItem(appliedKey(pageKey));
|
|
1130
|
-
if (raw)
|
|
1131
|
-
setApplied(JSON.parse(raw));
|
|
1132
|
-
} catch {}
|
|
1133
|
-
}
|
|
1134
|
-
const onMessage = (event) => {
|
|
1135
|
-
if (event.origin === window.location.origin) {
|
|
1136
|
-
const data = event.data;
|
|
1137
|
-
if (data?.type === BINDING_APPLIED && data.key === pageKey) {
|
|
1138
|
-
check();
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
};
|
|
1142
|
-
const onStorage = (event) => {
|
|
1143
|
-
if (event.key === appliedKey(pageKey)) {
|
|
1144
|
-
check();
|
|
1145
|
-
}
|
|
1146
|
-
};
|
|
1147
|
-
window.addEventListener("message", onMessage);
|
|
1148
|
-
window.addEventListener("storage", onStorage);
|
|
1149
|
-
let channel;
|
|
1150
|
-
try {
|
|
1151
|
-
channel = new BroadcastChannel(BINDING_CHANNEL);
|
|
1152
|
-
channel.onmessage = (event) => {
|
|
1153
|
-
if (event.data?.type === BINDING_APPLIED && event.data?.key === pageKey) {
|
|
1154
|
-
check();
|
|
1155
|
-
}
|
|
1156
|
-
};
|
|
1157
|
-
} catch {}
|
|
1158
|
-
return () => {
|
|
1159
|
-
window.removeEventListener("message", onMessage);
|
|
1160
|
-
window.removeEventListener("storage", onStorage);
|
|
1161
|
-
channel?.close();
|
|
1162
|
-
};
|
|
1163
|
-
}, [pageKey]);
|
|
1164
|
-
return {
|
|
1165
|
-
appliedConfig: applied?.config ?? null,
|
|
1166
|
-
isApplied: !!applied,
|
|
1167
|
-
appliedAt: applied?.appliedAt ?? null
|
|
1168
|
-
};
|
|
1169
|
-
}
|
|
1170
|
-
// src/components/BindApiButton.tsx
|
|
1171
|
-
import * as React3 from "react";
|
|
1172
|
-
|
|
1173
|
-
// src/components/ui/Icons.tsx
|
|
1174
|
-
import * as React2 from "react";
|
|
1175
|
-
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
1176
|
-
var base = (path) => React2.forwardRef(({ size = 16, className = "", ...props }, ref) => /* @__PURE__ */ jsx("svg", {
|
|
1177
|
-
ref,
|
|
1178
|
-
width: size,
|
|
1179
|
-
height: size,
|
|
1180
|
-
viewBox: "0 0 24 24",
|
|
1181
|
-
fill: "none",
|
|
1182
|
-
stroke: "currentColor",
|
|
1183
|
-
strokeWidth: "2",
|
|
1184
|
-
strokeLinecap: "round",
|
|
1185
|
-
strokeLinejoin: "round",
|
|
1186
|
-
className,
|
|
1187
|
-
...props,
|
|
1188
|
-
children: path
|
|
1189
|
-
}));
|
|
1190
|
-
var LinkIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1191
|
-
children: [
|
|
1192
|
-
/* @__PURE__ */ jsx("path", {
|
|
1193
|
-
d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"
|
|
1194
|
-
}),
|
|
1195
|
-
/* @__PURE__ */ jsx("path", {
|
|
1196
|
-
d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
|
|
1197
|
-
})
|
|
1198
|
-
]
|
|
1199
|
-
}));
|
|
1200
|
-
var PencilIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1201
|
-
children: [
|
|
1202
|
-
/* @__PURE__ */ jsx("path", {
|
|
1203
|
-
d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"
|
|
1204
|
-
}),
|
|
1205
|
-
/* @__PURE__ */ jsx("path", {
|
|
1206
|
-
d: "m15 5 4 4"
|
|
1207
|
-
})
|
|
1208
|
-
]
|
|
1209
|
-
}));
|
|
1210
|
-
var SettingsIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1211
|
-
children: [
|
|
1212
|
-
/* @__PURE__ */ jsx("path", {
|
|
1213
|
-
d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"
|
|
1214
|
-
}),
|
|
1215
|
-
/* @__PURE__ */ jsx("circle", {
|
|
1216
|
-
cx: "12",
|
|
1217
|
-
cy: "12",
|
|
1218
|
-
r: "3"
|
|
1219
|
-
})
|
|
1220
|
-
]
|
|
1221
|
-
}));
|
|
1222
|
-
var PlusIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1223
|
-
children: [
|
|
1224
|
-
/* @__PURE__ */ jsx("path", {
|
|
1225
|
-
d: "M5 12h14"
|
|
1226
|
-
}),
|
|
1227
|
-
/* @__PURE__ */ jsx("path", {
|
|
1228
|
-
d: "M12 5v14"
|
|
1229
|
-
})
|
|
1230
|
-
]
|
|
1231
|
-
}));
|
|
1232
|
-
var Trash2Icon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1233
|
-
children: [
|
|
1234
|
-
/* @__PURE__ */ jsx("path", {
|
|
1235
|
-
d: "M3 6h18"
|
|
1236
|
-
}),
|
|
1237
|
-
/* @__PURE__ */ jsx("path", {
|
|
1238
|
-
d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"
|
|
1239
|
-
}),
|
|
1240
|
-
/* @__PURE__ */ jsx("path", {
|
|
1241
|
-
d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"
|
|
1242
|
-
}),
|
|
1243
|
-
/* @__PURE__ */ jsx("line", {
|
|
1244
|
-
x1: "10",
|
|
1245
|
-
x2: "10",
|
|
1246
|
-
y1: "11",
|
|
1247
|
-
y2: "17"
|
|
1248
|
-
}),
|
|
1249
|
-
/* @__PURE__ */ jsx("line", {
|
|
1250
|
-
x1: "14",
|
|
1251
|
-
x2: "14",
|
|
1252
|
-
y1: "11",
|
|
1253
|
-
y2: "17"
|
|
1254
|
-
})
|
|
1255
|
-
]
|
|
1256
|
-
}));
|
|
1257
|
-
var ArrowUpIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1258
|
-
children: [
|
|
1259
|
-
/* @__PURE__ */ jsx("path", {
|
|
1260
|
-
d: "m5 12 7-7 7 7"
|
|
1261
|
-
}),
|
|
1262
|
-
/* @__PURE__ */ jsx("path", {
|
|
1263
|
-
d: "M12 19V5"
|
|
1264
|
-
})
|
|
1265
|
-
]
|
|
1266
|
-
}));
|
|
1267
|
-
var ArrowDownIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1268
|
-
children: [
|
|
1269
|
-
/* @__PURE__ */ jsx("path", {
|
|
1270
|
-
d: "M12 5v14"
|
|
1271
|
-
}),
|
|
1272
|
-
/* @__PURE__ */ jsx("path", {
|
|
1273
|
-
d: "m19 12-7 7-7-7"
|
|
1274
|
-
})
|
|
1275
|
-
]
|
|
1276
|
-
}));
|
|
1277
|
-
var PlayIcon = base(/* @__PURE__ */ jsx("polygon", {
|
|
1278
|
-
points: "6 3 20 12 6 21 6 3"
|
|
1279
|
-
}));
|
|
1280
|
-
var CheckIcon = base(/* @__PURE__ */ jsx("polyline", {
|
|
1281
|
-
points: "20 6 9 17 4 12"
|
|
1282
|
-
}));
|
|
1283
|
-
var XIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1284
|
-
children: [
|
|
1285
|
-
/* @__PURE__ */ jsx("path", {
|
|
1286
|
-
d: "M18 6 6 18"
|
|
1287
|
-
}),
|
|
1288
|
-
/* @__PURE__ */ jsx("path", {
|
|
1289
|
-
d: "m6 6 12 12"
|
|
1290
|
-
})
|
|
1291
|
-
]
|
|
1292
|
-
}));
|
|
1293
|
-
var SearchIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1294
|
-
children: [
|
|
1295
|
-
/* @__PURE__ */ jsx("circle", {
|
|
1296
|
-
cx: "11",
|
|
1297
|
-
cy: "11",
|
|
1298
|
-
r: "8"
|
|
1299
|
-
}),
|
|
1300
|
-
/* @__PURE__ */ jsx("path", {
|
|
1301
|
-
d: "m21 21-4.3-4.3"
|
|
1302
|
-
})
|
|
1303
|
-
]
|
|
1304
|
-
}));
|
|
1305
|
-
var AlertTriangleIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1306
|
-
children: [
|
|
1307
|
-
/* @__PURE__ */ jsx("path", {
|
|
1308
|
-
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"
|
|
1309
|
-
}),
|
|
1310
|
-
/* @__PURE__ */ jsx("line", {
|
|
1311
|
-
x1: "12",
|
|
1312
|
-
x2: "12",
|
|
1313
|
-
y1: "9",
|
|
1314
|
-
y2: "13"
|
|
1315
|
-
}),
|
|
1316
|
-
/* @__PURE__ */ jsx("line", {
|
|
1317
|
-
x1: "12",
|
|
1318
|
-
x2: "12.01",
|
|
1319
|
-
y1: "17",
|
|
1320
|
-
y2: "17"
|
|
1321
|
-
})
|
|
1322
|
-
]
|
|
1323
|
-
}));
|
|
1324
|
-
var AlertCircleIcon = base(/* @__PURE__ */ jsxs(Fragment, {
|
|
1325
|
-
children: [
|
|
1326
|
-
/* @__PURE__ */ jsx("circle", {
|
|
1327
|
-
cx: "12",
|
|
1328
|
-
cy: "12",
|
|
1329
|
-
r: "10"
|
|
1330
|
-
}),
|
|
1331
|
-
/* @__PURE__ */ jsx("line", {
|
|
1332
|
-
x1: "12",
|
|
1333
|
-
x2: "12",
|
|
1334
|
-
y1: "8",
|
|
1335
|
-
y2: "12"
|
|
1336
|
-
}),
|
|
1337
|
-
/* @__PURE__ */ jsx("line", {
|
|
1338
|
-
x1: "12",
|
|
1339
|
-
x2: "12.01",
|
|
1340
|
-
y1: "16",
|
|
1341
|
-
y2: "16"
|
|
1342
|
-
})
|
|
1343
|
-
]
|
|
1344
|
-
}));
|
|
1345
|
-
var iconMap = {
|
|
1346
|
-
Link: LinkIcon,
|
|
1347
|
-
Pencil: PencilIcon,
|
|
1348
|
-
Settings: SettingsIcon,
|
|
1349
|
-
Plus: PlusIcon,
|
|
1350
|
-
Trash2: Trash2Icon,
|
|
1351
|
-
ArrowUp: ArrowUpIcon,
|
|
1352
|
-
ArrowDown: ArrowDownIcon,
|
|
1353
|
-
Play: PlayIcon,
|
|
1354
|
-
Check: CheckIcon,
|
|
1355
|
-
X: XIcon,
|
|
1356
|
-
Search: SearchIcon,
|
|
1357
|
-
AlertTriangle: AlertTriangleIcon,
|
|
1358
|
-
AlertCircle: AlertCircleIcon
|
|
1359
|
-
};
|
|
1360
|
-
function renderIcon(icon, props) {
|
|
1361
|
-
if (!icon)
|
|
1362
|
-
return null;
|
|
1363
|
-
if (typeof icon === "string") {
|
|
1364
|
-
const Component = iconMap[icon];
|
|
1365
|
-
return Component ? /* @__PURE__ */ jsx(Component, {
|
|
1366
|
-
...props
|
|
1367
|
-
}) : null;
|
|
1368
|
-
}
|
|
1369
|
-
return icon;
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
// src/components/ui/Button.tsx
|
|
1373
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1374
|
-
function Button({
|
|
1375
|
-
variant = "secondary",
|
|
1376
|
-
size = "md",
|
|
1377
|
-
icon,
|
|
1378
|
-
tooltip,
|
|
1379
|
-
children,
|
|
1380
|
-
className = "",
|
|
1381
|
-
type = "button",
|
|
1382
|
-
...rest
|
|
1383
|
-
}) {
|
|
1384
|
-
const iconOnly = Boolean(icon) && (children === undefined || children === null || children === false);
|
|
1385
|
-
return /* @__PURE__ */ jsxs2("button", {
|
|
1386
|
-
type,
|
|
1387
|
-
className: `ds-button ds-button--${size} ds-button--${variant} ${iconOnly ? "ds-button--icon-only" : ""} ${className}`,
|
|
1388
|
-
title: typeof tooltip === "string" ? tooltip : undefined,
|
|
1389
|
-
...rest,
|
|
1390
|
-
children: [
|
|
1391
|
-
icon && /* @__PURE__ */ jsx2("span", {
|
|
1392
|
-
className: "ds-button__icon",
|
|
1393
|
-
children: renderIcon(icon)
|
|
1394
|
-
}),
|
|
1395
|
-
children && /* @__PURE__ */ jsx2("span", {
|
|
1396
|
-
children
|
|
1397
|
-
})
|
|
1398
|
-
]
|
|
1399
|
-
});
|
|
1400
|
-
}
|
|
1401
|
-
function ButtonBar({ justify, children, className = "" }) {
|
|
1402
|
-
return /* @__PURE__ */ jsx2("div", {
|
|
1403
|
-
className: `ds-button-bar ${justify === "between" ? "ds-button-bar--justify-between" : ""} ${className}`,
|
|
1404
|
-
children
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
function ButtonGroup({
|
|
1408
|
-
value,
|
|
1409
|
-
onChange,
|
|
1410
|
-
options = [],
|
|
1411
|
-
"aria-label": ariaLabel,
|
|
1412
|
-
className = ""
|
|
1413
|
-
}) {
|
|
1414
|
-
return /* @__PURE__ */ jsx2("div", {
|
|
1415
|
-
className: `ds-button-group ${className}`,
|
|
1416
|
-
role: "group",
|
|
1417
|
-
"aria-label": ariaLabel,
|
|
1418
|
-
children: options.map((opt) => /* @__PURE__ */ jsx2("button", {
|
|
1419
|
-
type: "button",
|
|
1420
|
-
className: `ds-button-group__button ${opt.value === value ? "ds-button-group__button--selected active" : ""}`,
|
|
1421
|
-
onClick: () => onChange(opt.value),
|
|
1422
|
-
children: opt.label
|
|
1423
|
-
}, opt.value))
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
// src/components/BindApiButton.tsx
|
|
1428
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1429
|
-
function BindApiButton({
|
|
1430
|
-
appId = "app",
|
|
1431
|
-
windowId,
|
|
1432
|
-
source,
|
|
1433
|
-
parent,
|
|
1434
|
-
grid,
|
|
1435
|
-
parents,
|
|
1436
|
-
kind,
|
|
1437
|
-
trigger,
|
|
1438
|
-
options = {},
|
|
1439
|
-
definitions = [],
|
|
1440
|
-
mappingFile = null,
|
|
1441
|
-
routeBase = "/api-binding",
|
|
1442
|
-
containerRef,
|
|
1443
|
-
containerSelector,
|
|
1444
|
-
rootElement,
|
|
1445
|
-
formId,
|
|
1446
|
-
components: explicitComponents,
|
|
1447
|
-
buttons: explicitButtons,
|
|
1448
|
-
className = "",
|
|
1449
|
-
children = "Bind API"
|
|
1450
|
-
}) {
|
|
1451
|
-
const ref = React3.useRef(null);
|
|
1452
|
-
const [error, setError] = React3.useState("");
|
|
1453
|
-
function handleClick(e) {
|
|
1454
|
-
e.preventDefault();
|
|
1455
|
-
e.stopPropagation();
|
|
1456
|
-
try {
|
|
1457
|
-
const ids = windowIds({
|
|
1458
|
-
appId,
|
|
1459
|
-
windowId,
|
|
1460
|
-
gridId: grid?.gridId,
|
|
1461
|
-
parentWindowIds: parents?.map((p) => p.id)
|
|
1462
|
-
});
|
|
1463
|
-
const root = findEnclosingContextElement(ref.current, {
|
|
1464
|
-
containerRef,
|
|
1465
|
-
containerSelector,
|
|
1466
|
-
rootElement,
|
|
1467
|
-
formId
|
|
1468
|
-
});
|
|
1469
|
-
const resolvedKind = kind ?? (root?.closest('[role="dialog"], dialog, .ds-dialog') ? "dialog" : root?.closest("[data-drawer], aside, .drawer") ? "drawer" : "screen");
|
|
1470
|
-
const snapshot = buildSnapshot({
|
|
1471
|
-
ids,
|
|
1472
|
-
source,
|
|
1473
|
-
parent,
|
|
1474
|
-
root,
|
|
1475
|
-
grid,
|
|
1476
|
-
kind: resolvedKind,
|
|
1477
|
-
trigger,
|
|
1478
|
-
contexts: parents,
|
|
1479
|
-
components: explicitComponents,
|
|
1480
|
-
buttons: explicitButtons
|
|
1481
|
-
});
|
|
1482
|
-
openBindingStudio({
|
|
1483
|
-
snapshot,
|
|
1484
|
-
mappings: mappingFile,
|
|
1485
|
-
options,
|
|
1486
|
-
definitions
|
|
1487
|
-
}, { routeBase });
|
|
1488
|
-
setError("");
|
|
1489
|
-
} catch (err) {
|
|
1490
|
-
const msg = err instanceof Error ? err.message : "Could not open API Binding Studio.";
|
|
1491
|
-
setError(msg);
|
|
1492
|
-
console.error("[BindApiButton] Error:", err);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
return /* @__PURE__ */ jsxs3("span", {
|
|
1496
|
-
ref,
|
|
1497
|
-
className,
|
|
1498
|
-
children: [
|
|
1499
|
-
/* @__PURE__ */ jsx3(Button, {
|
|
1500
|
-
size: "sm",
|
|
1501
|
-
icon: "Link",
|
|
1502
|
-
onClick: handleClick,
|
|
1503
|
-
tooltip: error || undefined,
|
|
1504
|
-
children
|
|
1505
|
-
}),
|
|
1506
|
-
error && /* @__PURE__ */ jsx3("span", {
|
|
1507
|
-
role: "alert",
|
|
1508
|
-
className: "binding-error",
|
|
1509
|
-
style: { marginLeft: "0.5rem" },
|
|
1510
|
-
children: error
|
|
1511
|
-
})
|
|
1512
|
-
]
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
// src/components/ApiBindingWorkspace.tsx
|
|
1516
|
-
import * as React9 from "react";
|
|
1517
|
-
|
|
1518
|
-
// src/components/WorkspaceContext.tsx
|
|
1519
|
-
import * as React6 from "react";
|
|
1520
|
-
|
|
1521
|
-
// src/components/ui/Badge.tsx
|
|
1522
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1523
|
-
var TONES = {
|
|
1524
|
-
neutral: {
|
|
1525
|
-
bg: "var(--ds-badge-neutral-background, var(--ds-color-surface, #f8fafc))",
|
|
1526
|
-
fg: "var(--ds-badge-neutral-foreground, var(--ds-color-muted-foreground, #64748b))",
|
|
1527
|
-
border: "var(--ds-badge-neutral-border, var(--ds-color-border, #e2e8f0))"
|
|
1528
|
-
},
|
|
1529
|
-
warning: {
|
|
1530
|
-
bg: "var(--ds-badge-warning-background, #fff7ed)",
|
|
1531
|
-
fg: "var(--ds-badge-warning-foreground, #9a3412)",
|
|
1532
|
-
border: "var(--ds-badge-warning-border, #fed7aa)"
|
|
1533
|
-
},
|
|
1534
|
-
success: {
|
|
1535
|
-
bg: "var(--ds-badge-success-background, #f0fdf4)",
|
|
1536
|
-
fg: "var(--ds-badge-success-foreground, #166534)",
|
|
1537
|
-
border: "var(--ds-badge-success-border, #bbf7d0)"
|
|
1538
|
-
},
|
|
1539
|
-
danger: {
|
|
1540
|
-
bg: "var(--ds-badge-danger-background, #fef2f2)",
|
|
1541
|
-
fg: "var(--ds-badge-danger-foreground, #991b1b)",
|
|
1542
|
-
border: "var(--ds-badge-danger-border, #fecaca)"
|
|
1543
|
-
},
|
|
1544
|
-
info: {
|
|
1545
|
-
bg: "var(--ds-badge-info-background, #eff6ff)",
|
|
1546
|
-
fg: "var(--ds-badge-info-foreground, #1e40af)",
|
|
1547
|
-
border: "var(--ds-badge-info-border, #bfdbfe)"
|
|
1548
|
-
},
|
|
1549
|
-
brand: {
|
|
1550
|
-
bg: "var(--ds-badge-brand-background, #fff7ed)",
|
|
1551
|
-
fg: "var(--ds-badge-brand-foreground, #c2410c)",
|
|
1552
|
-
border: "var(--ds-badge-brand-border, #fdba74)"
|
|
1553
|
-
}
|
|
1554
|
-
};
|
|
1555
|
-
function Badge({ variant = "neutral", children, className = "", style, ...rest }) {
|
|
1556
|
-
const tone = TONES[variant] ?? TONES.neutral;
|
|
1557
|
-
return /* @__PURE__ */ jsx4("span", {
|
|
1558
|
-
className: `ds-badge ds-badge--${variant} ${className}`,
|
|
1559
|
-
style: {
|
|
1560
|
-
display: "inline-flex",
|
|
1561
|
-
alignItems: "center",
|
|
1562
|
-
borderWidth: "1px",
|
|
1563
|
-
borderStyle: "solid",
|
|
1564
|
-
borderColor: tone.border,
|
|
1565
|
-
backgroundColor: tone.bg,
|
|
1566
|
-
color: tone.fg,
|
|
1567
|
-
borderRadius: "var(--ds-badge-radius, 4px)",
|
|
1568
|
-
height: "var(--ds-badge-height-sm, 20px)",
|
|
1569
|
-
padding: "0 var(--ds-badge-padding-x-sm, 8px)",
|
|
1570
|
-
fontSize: "var(--ds-badge-font-size-sm, 12px)",
|
|
1571
|
-
fontWeight: 500,
|
|
1572
|
-
lineHeight: 1,
|
|
1573
|
-
whiteSpace: "nowrap",
|
|
1574
|
-
...style
|
|
1575
|
-
},
|
|
1576
|
-
...rest,
|
|
1577
|
-
children
|
|
1578
|
-
});
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
// src/components/ui/AppGrid.tsx
|
|
1582
|
-
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1583
|
-
function createActionColumn(spec) {
|
|
1584
|
-
return {
|
|
1585
|
-
type: "action",
|
|
1586
|
-
headerName: spec.headerName,
|
|
1587
|
-
labelField: spec.labelField,
|
|
1588
|
-
icon: spec.icon,
|
|
1589
|
-
onClick: spec.onClick
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
function AppGrid({
|
|
1593
|
-
rows = [],
|
|
1594
|
-
columns = [],
|
|
1595
|
-
getRowKey = (_, i) => i,
|
|
1596
|
-
className = ""
|
|
1597
|
-
}) {
|
|
1598
|
-
return /* @__PURE__ */ jsx5("div", {
|
|
1599
|
-
className: `ds-grid ${className}`,
|
|
1600
|
-
style: {
|
|
1601
|
-
border: "1px solid var(--ds-color-border, #e2e8f0)",
|
|
1602
|
-
borderRadius: "var(--ds-radius-md, 6px)",
|
|
1603
|
-
overflow: "hidden",
|
|
1604
|
-
backgroundColor: "var(--ds-color-surface, #ffffff)",
|
|
1605
|
-
width: "100%"
|
|
1606
|
-
},
|
|
1607
|
-
children: /* @__PURE__ */ jsx5("div", {
|
|
1608
|
-
style: { width: "100%", overflowX: "auto" },
|
|
1609
|
-
children: /* @__PURE__ */ jsxs4("table", {
|
|
1610
|
-
className: "ds-grid__table",
|
|
1611
|
-
style: {
|
|
1612
|
-
width: "100%",
|
|
1613
|
-
borderCollapse: "collapse",
|
|
1614
|
-
fontSize: "13px",
|
|
1615
|
-
textAlign: "left"
|
|
1616
|
-
},
|
|
1617
|
-
children: [
|
|
1618
|
-
/* @__PURE__ */ jsx5("thead", {
|
|
1619
|
-
children: /* @__PURE__ */ jsx5("tr", {
|
|
1620
|
-
className: "ds-grid__header-row",
|
|
1621
|
-
style: {
|
|
1622
|
-
backgroundColor: "var(--ds-grid-header-background, #f8fafc)",
|
|
1623
|
-
borderBottom: "1px solid var(--ds-color-border, #e2e8f0)"
|
|
1624
|
-
},
|
|
1625
|
-
children: columns.map((col, idx) => /* @__PURE__ */ jsx5("th", {
|
|
1626
|
-
className: `ds-grid__header-cell ${col.fill ? "ds-grid__header-cell--fill" : ""}`,
|
|
1627
|
-
style: {
|
|
1628
|
-
padding: "8px 12px",
|
|
1629
|
-
fontSize: "12px",
|
|
1630
|
-
fontWeight: 600,
|
|
1631
|
-
color: "var(--ds-grid-header-foreground, #475569)",
|
|
1632
|
-
borderRight: idx < columns.length - 1 ? "1px solid var(--ds-color-border, #e2e8f0)" : "none",
|
|
1633
|
-
width: col.fill ? undefined : col.type === "action" ? "120px" : undefined
|
|
1634
|
-
},
|
|
1635
|
-
children: /* @__PURE__ */ jsx5("div", {
|
|
1636
|
-
className: "ds-grid__header-cell-label",
|
|
1637
|
-
children: col.headerName
|
|
1638
|
-
})
|
|
1639
|
-
}, `${String(col.field ?? idx)}`))
|
|
1640
|
-
})
|
|
1641
|
-
}),
|
|
1642
|
-
/* @__PURE__ */ jsx5("tbody", {
|
|
1643
|
-
children: rows.map((row, rowIdx) => /* @__PURE__ */ jsx5("tr", {
|
|
1644
|
-
className: "ds-grid__row",
|
|
1645
|
-
style: {
|
|
1646
|
-
borderBottom: rowIdx < rows.length - 1 ? "1px solid var(--ds-color-border, #e2e8f0)" : "none",
|
|
1647
|
-
backgroundColor: rowIdx % 2 === 1 ? "var(--ds-grid-row-alt, #fafbfc)" : "#ffffff"
|
|
1648
|
-
},
|
|
1649
|
-
children: columns.map((col, colIdx) => {
|
|
1650
|
-
let content = null;
|
|
1651
|
-
if (col.type === "action") {
|
|
1652
|
-
const label = col.labelField ? String(row[col.labelField] ?? "") : "Edit";
|
|
1653
|
-
content = /* @__PURE__ */ jsx5(Button, {
|
|
1654
|
-
size: "sm",
|
|
1655
|
-
variant: "secondary",
|
|
1656
|
-
icon: col.icon,
|
|
1657
|
-
onClick: () => col.onClick?.(row),
|
|
1658
|
-
children: label
|
|
1659
|
-
});
|
|
1660
|
-
} else if (col.type === "badge") {
|
|
1661
|
-
const val = String(row[col.field] ?? "");
|
|
1662
|
-
const variant = col.badgeVariants?.[val] ?? "neutral";
|
|
1663
|
-
content = /* @__PURE__ */ jsx5(Badge, {
|
|
1664
|
-
variant,
|
|
1665
|
-
children: val
|
|
1666
|
-
});
|
|
1667
|
-
} else if (col.render) {
|
|
1668
|
-
content = col.render(row);
|
|
1669
|
-
} else {
|
|
1670
|
-
const val = row[col.field];
|
|
1671
|
-
content = val !== undefined && val !== null ? String(val) : "—";
|
|
1672
|
-
}
|
|
1673
|
-
return /* @__PURE__ */ jsx5("td", {
|
|
1674
|
-
className: `ds-grid__cell ${col.fill ? "ds-grid__cell--fill" : ""} ${col.type === "number" ? "ds-grid__cell--numeric" : ""}`,
|
|
1675
|
-
style: {
|
|
1676
|
-
padding: "8px 12px",
|
|
1677
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1678
|
-
borderRight: colIdx < columns.length - 1 ? "1px solid var(--ds-color-border, #e2e8f0)" : "none"
|
|
1679
|
-
},
|
|
1680
|
-
children: content
|
|
1681
|
-
}, `${String(col.field ?? colIdx)}`);
|
|
1682
|
-
})
|
|
1683
|
-
}, String(getRowKey(row, rowIdx))))
|
|
1684
|
-
})
|
|
1685
|
-
]
|
|
1686
|
-
})
|
|
1687
|
-
})
|
|
1688
|
-
});
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
// src/components/ui/Banner.tsx
|
|
1692
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1693
|
-
function Banner({ tone = "caution", children, className = "" }) {
|
|
1694
|
-
return /* @__PURE__ */ jsx6("div", {
|
|
1695
|
-
className: `ds-banner ds-banner--${tone} ${className}`,
|
|
1696
|
-
role: "status",
|
|
1697
|
-
children: /* @__PURE__ */ jsx6("div", {
|
|
1698
|
-
className: "ds-banner__content",
|
|
1699
|
-
children
|
|
1700
|
-
})
|
|
1701
|
-
});
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
// src/components/ui/Card.tsx
|
|
1705
|
-
import * as React4 from "react";
|
|
1706
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1707
|
-
function DetailCard({
|
|
1708
|
-
title,
|
|
1709
|
-
subtitle,
|
|
1710
|
-
actions,
|
|
1711
|
-
fields = [],
|
|
1712
|
-
dense = true,
|
|
1713
|
-
className = ""
|
|
1714
|
-
}) {
|
|
1715
|
-
const showHeader = Boolean(title) || Boolean(subtitle) || Boolean(actions);
|
|
1716
|
-
return /* @__PURE__ */ jsxs5("section", {
|
|
1717
|
-
className: `ds-detail-card ${dense ? "ds-detail-card--dense" : ""} ${className}`,
|
|
1718
|
-
style: {
|
|
1719
|
-
backgroundColor: "var(--ds-detail-card-background, var(--ds-color-surface, #ffffff))",
|
|
1720
|
-
border: "1px solid var(--ds-detail-card-border, var(--ds-color-border, #e2e8f0))",
|
|
1721
|
-
borderRadius: "var(--ds-detail-card-radius, var(--ds-radius-md, 6px))",
|
|
1722
|
-
boxShadow: "var(--ds-detail-card-shadow, none)",
|
|
1723
|
-
display: "flex",
|
|
1724
|
-
flexDirection: "column",
|
|
1725
|
-
minWidth: 0
|
|
1726
|
-
},
|
|
1727
|
-
children: [
|
|
1728
|
-
showHeader && /* @__PURE__ */ jsxs5("div", {
|
|
1729
|
-
className: "ds-detail-card__header",
|
|
1730
|
-
style: {
|
|
1731
|
-
display: "flex",
|
|
1732
|
-
alignItems: "center",
|
|
1733
|
-
justifyContent: "space-between",
|
|
1734
|
-
padding: "12px 16px",
|
|
1735
|
-
borderBottom: "1px solid var(--ds-detail-card-border, var(--ds-color-border, #e2e8f0))"
|
|
1736
|
-
},
|
|
1737
|
-
children: [
|
|
1738
|
-
/* @__PURE__ */ jsxs5("div", {
|
|
1739
|
-
className: "ds-detail-card__heading",
|
|
1740
|
-
style: { minWidth: 0 },
|
|
1741
|
-
children: [
|
|
1742
|
-
title && /* @__PURE__ */ jsx7("h3", {
|
|
1743
|
-
className: "ds-detail-card__title",
|
|
1744
|
-
style: {
|
|
1745
|
-
margin: 0,
|
|
1746
|
-
fontSize: "var(--ds-detail-card-title-font-size, 14px)",
|
|
1747
|
-
fontWeight: 600,
|
|
1748
|
-
color: "var(--ds-color-foreground, #0f172a)"
|
|
1749
|
-
},
|
|
1750
|
-
children: title
|
|
1751
|
-
}),
|
|
1752
|
-
subtitle && /* @__PURE__ */ jsx7("p", {
|
|
1753
|
-
className: "ds-detail-card__subtitle",
|
|
1754
|
-
style: {
|
|
1755
|
-
margin: 0,
|
|
1756
|
-
fontSize: "12px",
|
|
1757
|
-
color: "var(--ds-color-muted-foreground, #64748b)"
|
|
1758
|
-
},
|
|
1759
|
-
children: subtitle
|
|
1760
|
-
})
|
|
1761
|
-
]
|
|
1762
|
-
}),
|
|
1763
|
-
actions && /* @__PURE__ */ jsx7("div", {
|
|
1764
|
-
className: "ds-detail-card__actions",
|
|
1765
|
-
children: actions
|
|
1766
|
-
})
|
|
1767
|
-
]
|
|
1768
|
-
}),
|
|
1769
|
-
/* @__PURE__ */ jsx7("dl", {
|
|
1770
|
-
className: "ds-detail-card__body",
|
|
1771
|
-
style: {
|
|
1772
|
-
margin: 0,
|
|
1773
|
-
padding: "12px 16px",
|
|
1774
|
-
display: "grid",
|
|
1775
|
-
gridTemplateColumns: "max-content 1fr",
|
|
1776
|
-
columnGap: "24px",
|
|
1777
|
-
rowGap: "8px",
|
|
1778
|
-
alignItems: "baseline"
|
|
1779
|
-
},
|
|
1780
|
-
children: fields.map((f, i) => /* @__PURE__ */ jsxs5(React4.Fragment, {
|
|
1781
|
-
children: [
|
|
1782
|
-
/* @__PURE__ */ jsx7("dt", {
|
|
1783
|
-
className: "ds-detail-card__label",
|
|
1784
|
-
style: {
|
|
1785
|
-
fontWeight: 600,
|
|
1786
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1787
|
-
fontSize: "var(--ds-font-size-sm, 13px)",
|
|
1788
|
-
lineHeight: 1.4
|
|
1789
|
-
},
|
|
1790
|
-
children: f.label
|
|
1791
|
-
}),
|
|
1792
|
-
/* @__PURE__ */ jsx7("dd", {
|
|
1793
|
-
className: "ds-detail-card__value",
|
|
1794
|
-
style: {
|
|
1795
|
-
margin: 0,
|
|
1796
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1797
|
-
fontSize: "var(--ds-font-size-sm, 13px)",
|
|
1798
|
-
lineHeight: 1.4
|
|
1799
|
-
},
|
|
1800
|
-
children: f.value !== undefined && f.value !== null && f.value !== "" ? f.value : "—"
|
|
1801
|
-
})
|
|
1802
|
-
]
|
|
1803
|
-
}, `${f.label}-${i}`))
|
|
1804
|
-
})
|
|
1805
|
-
]
|
|
1806
|
-
});
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
// src/components/ui/Dialog.tsx
|
|
1810
|
-
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1811
|
-
function Dialog({
|
|
1812
|
-
open = true,
|
|
1813
|
-
title,
|
|
1814
|
-
description,
|
|
1815
|
-
submitLabel = "Submit",
|
|
1816
|
-
cancelLabel = "Cancel",
|
|
1817
|
-
onCancel,
|
|
1818
|
-
onSubmit,
|
|
1819
|
-
children,
|
|
1820
|
-
size = "large",
|
|
1821
|
-
formId,
|
|
1822
|
-
className = ""
|
|
1823
|
-
}) {
|
|
1824
|
-
if (!open)
|
|
1825
|
-
return null;
|
|
1826
|
-
return /* @__PURE__ */ jsx8("div", {
|
|
1827
|
-
className: "ds-dialog__backdrop",
|
|
1828
|
-
onClick: onCancel,
|
|
1829
|
-
children: /* @__PURE__ */ jsxs6("div", {
|
|
1830
|
-
className: `ds-dialog ${size === "large" ? "ds-dialog--lg" : ""} ${className}`,
|
|
1831
|
-
role: "dialog",
|
|
1832
|
-
"aria-modal": "true",
|
|
1833
|
-
onClick: (e) => e.stopPropagation(),
|
|
1834
|
-
children: [
|
|
1835
|
-
/* @__PURE__ */ jsxs6("div", {
|
|
1836
|
-
className: "ds-dialog__header",
|
|
1837
|
-
children: [
|
|
1838
|
-
/* @__PURE__ */ jsxs6("div", {
|
|
1839
|
-
className: "ds-dialog__heading",
|
|
1840
|
-
children: [
|
|
1841
|
-
/* @__PURE__ */ jsx8("h2", {
|
|
1842
|
-
className: "ds-dialog__title",
|
|
1843
|
-
children: title
|
|
1844
|
-
}),
|
|
1845
|
-
description && /* @__PURE__ */ jsx8("p", {
|
|
1846
|
-
className: "ds-dialog__description",
|
|
1847
|
-
children: description
|
|
1848
|
-
})
|
|
1849
|
-
]
|
|
1850
|
-
}),
|
|
1851
|
-
onCancel && /* @__PURE__ */ jsx8(Button, {
|
|
1852
|
-
variant: "ghost",
|
|
1853
|
-
size: "sm",
|
|
1854
|
-
icon: "X",
|
|
1855
|
-
"aria-label": "Close",
|
|
1856
|
-
onClick: onCancel,
|
|
1857
|
-
className: "ds-dialog__close"
|
|
1858
|
-
})
|
|
1859
|
-
]
|
|
1860
|
-
}),
|
|
1861
|
-
/* @__PURE__ */ jsxs6("form", {
|
|
1862
|
-
id: formId,
|
|
1863
|
-
onSubmit: (e) => {
|
|
1864
|
-
e.preventDefault();
|
|
1865
|
-
onSubmit?.();
|
|
1866
|
-
},
|
|
1867
|
-
className: "ds-dialog__form",
|
|
1868
|
-
children: [
|
|
1869
|
-
/* @__PURE__ */ jsx8("div", {
|
|
1870
|
-
className: "ds-dialog__body",
|
|
1871
|
-
children
|
|
1872
|
-
}),
|
|
1873
|
-
/* @__PURE__ */ jsx8("div", {
|
|
1874
|
-
className: "ds-dialog__footer",
|
|
1875
|
-
children: /* @__PURE__ */ jsxs6(ButtonBar, {
|
|
1876
|
-
justify: "between",
|
|
1877
|
-
children: [
|
|
1878
|
-
onCancel && /* @__PURE__ */ jsx8(Button, {
|
|
1879
|
-
variant: "secondary",
|
|
1880
|
-
onClick: onCancel,
|
|
1881
|
-
children: cancelLabel
|
|
1882
|
-
}),
|
|
1883
|
-
onSubmit && /* @__PURE__ */ jsx8(Button, {
|
|
1884
|
-
variant: "primary",
|
|
1885
|
-
type: "submit",
|
|
1886
|
-
children: submitLabel
|
|
1887
|
-
})
|
|
1888
|
-
]
|
|
1889
|
-
})
|
|
1890
|
-
})
|
|
1891
|
-
]
|
|
1892
|
-
})
|
|
1893
|
-
]
|
|
1894
|
-
})
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1897
|
-
function actionFormDialogTemplateV1(spec) {
|
|
1898
|
-
return spec;
|
|
1899
|
-
}
|
|
1900
|
-
function alertDialogTemplateV1(spec) {
|
|
1901
|
-
return {
|
|
1902
|
-
open: spec.open,
|
|
1903
|
-
title: spec.title,
|
|
1904
|
-
description: spec.message,
|
|
1905
|
-
submitLabel: spec.primaryAction?.label ?? spec.confirmLabel ?? "Confirm",
|
|
1906
|
-
cancelLabel: spec.cancelLabel ?? "Cancel",
|
|
1907
|
-
onSubmit: spec.primaryAction?.onClick ?? spec.onConfirm,
|
|
1908
|
-
onCancel: spec.onCancel,
|
|
1909
|
-
children: null
|
|
1910
|
-
};
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
// src/components/ui/Input.tsx
|
|
1914
|
-
import * as React5 from "react";
|
|
1915
|
-
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1916
|
-
function Input({ label, required, className = "", id, ...rest }) {
|
|
1917
|
-
const generatedId = React5.useId();
|
|
1918
|
-
const inputId = id ?? generatedId;
|
|
1919
|
-
const inputEl = /* @__PURE__ */ jsx9("input", {
|
|
1920
|
-
id: inputId,
|
|
1921
|
-
className: `ds-field__control ds-input ${className}`,
|
|
1922
|
-
required,
|
|
1923
|
-
style: {
|
|
1924
|
-
width: "100%",
|
|
1925
|
-
height: "32px",
|
|
1926
|
-
padding: "4px 8px",
|
|
1927
|
-
fontSize: "13px",
|
|
1928
|
-
fontFamily: "inherit",
|
|
1929
|
-
border: "1px solid var(--ds-color-border, #cbd5e1)",
|
|
1930
|
-
borderRadius: "4px",
|
|
1931
|
-
backgroundColor: "var(--ds-color-surface, #ffffff)",
|
|
1932
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1933
|
-
outline: "none",
|
|
1934
|
-
boxSizing: "border-box"
|
|
1935
|
-
},
|
|
1936
|
-
...rest
|
|
1937
|
-
});
|
|
1938
|
-
if (!label)
|
|
1939
|
-
return inputEl;
|
|
1940
|
-
return /* @__PURE__ */ jsxs7("div", {
|
|
1941
|
-
className: "ds-field-wrapper binding-stack-tight",
|
|
1942
|
-
style: { width: "100%" },
|
|
1943
|
-
children: [
|
|
1944
|
-
/* @__PURE__ */ jsxs7("label", {
|
|
1945
|
-
htmlFor: inputId,
|
|
1946
|
-
className: "ds-field__label",
|
|
1947
|
-
style: {
|
|
1948
|
-
fontSize: "12px",
|
|
1949
|
-
fontWeight: 500,
|
|
1950
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1951
|
-
display: "flex",
|
|
1952
|
-
alignItems: "center",
|
|
1953
|
-
gap: "2px"
|
|
1954
|
-
},
|
|
1955
|
-
children: [
|
|
1956
|
-
label,
|
|
1957
|
-
required && /* @__PURE__ */ jsx9("span", {
|
|
1958
|
-
style: { color: "var(--ds-color-destructive, #ef4444)" },
|
|
1959
|
-
children: "*"
|
|
1960
|
-
})
|
|
1961
|
-
]
|
|
1962
|
-
}),
|
|
1963
|
-
/* @__PURE__ */ jsx9("div", {
|
|
1964
|
-
className: "ds-field",
|
|
1965
|
-
style: { width: "100%" },
|
|
1966
|
-
children: inputEl
|
|
1967
|
-
})
|
|
1968
|
-
]
|
|
1969
|
-
});
|
|
1970
|
-
}
|
|
1971
|
-
function TextArea({ label, required, className = "", id, ...rest }) {
|
|
1972
|
-
const generatedId = React5.useId();
|
|
1973
|
-
const textareaId = id ?? generatedId;
|
|
1974
|
-
const textEl = /* @__PURE__ */ jsx9("textarea", {
|
|
1975
|
-
id: textareaId,
|
|
1976
|
-
className: `ds-field__control ds-textarea ${className}`,
|
|
1977
|
-
required,
|
|
1978
|
-
style: {
|
|
1979
|
-
width: "100%",
|
|
1980
|
-
minHeight: "80px",
|
|
1981
|
-
padding: "6px 8px",
|
|
1982
|
-
fontSize: "13px",
|
|
1983
|
-
fontFamily: "inherit",
|
|
1984
|
-
border: "1px solid var(--ds-color-border, #cbd5e1)",
|
|
1985
|
-
borderRadius: "4px",
|
|
1986
|
-
backgroundColor: "var(--ds-color-surface, #ffffff)",
|
|
1987
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
1988
|
-
outline: "none",
|
|
1989
|
-
resize: "vertical",
|
|
1990
|
-
boxSizing: "border-box"
|
|
1991
|
-
},
|
|
1992
|
-
...rest
|
|
1993
|
-
});
|
|
1994
|
-
if (!label)
|
|
1995
|
-
return textEl;
|
|
1996
|
-
return /* @__PURE__ */ jsxs7("div", {
|
|
1997
|
-
className: "ds-field-wrapper binding-stack-tight",
|
|
1998
|
-
style: { width: "100%" },
|
|
1999
|
-
children: [
|
|
2000
|
-
/* @__PURE__ */ jsxs7("label", {
|
|
2001
|
-
htmlFor: textareaId,
|
|
2002
|
-
className: "ds-field__label",
|
|
2003
|
-
style: {
|
|
2004
|
-
fontSize: "12px",
|
|
2005
|
-
fontWeight: 500,
|
|
2006
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
2007
|
-
display: "flex",
|
|
2008
|
-
alignItems: "center",
|
|
2009
|
-
gap: "2px"
|
|
2010
|
-
},
|
|
2011
|
-
children: [
|
|
2012
|
-
label,
|
|
2013
|
-
required && /* @__PURE__ */ jsx9("span", {
|
|
2014
|
-
style: { color: "var(--ds-color-destructive, #ef4444)" },
|
|
2015
|
-
children: "*"
|
|
2016
|
-
})
|
|
2017
|
-
]
|
|
2018
|
-
}),
|
|
2019
|
-
/* @__PURE__ */ jsx9("div", {
|
|
2020
|
-
className: "ds-field",
|
|
2021
|
-
style: { width: "100%" },
|
|
2022
|
-
children: textEl
|
|
2023
|
-
})
|
|
2024
|
-
]
|
|
2025
|
-
});
|
|
2026
|
-
}
|
|
2027
|
-
function Select({
|
|
2028
|
-
label,
|
|
2029
|
-
required,
|
|
2030
|
-
value,
|
|
2031
|
-
options = [],
|
|
2032
|
-
onChange,
|
|
2033
|
-
placeholder,
|
|
2034
|
-
disabled,
|
|
2035
|
-
className = "",
|
|
2036
|
-
id,
|
|
2037
|
-
...rest
|
|
2038
|
-
}) {
|
|
2039
|
-
const generatedId = React5.useId();
|
|
2040
|
-
const selectId = id ?? generatedId;
|
|
2041
|
-
const selectEl = /* @__PURE__ */ jsxs7("select", {
|
|
2042
|
-
id: selectId,
|
|
2043
|
-
className: `ds-field__control ds-select ${className}`,
|
|
2044
|
-
disabled,
|
|
2045
|
-
value: value ?? "",
|
|
2046
|
-
onChange: (e) => onChange?.(e.target.value || null),
|
|
2047
|
-
style: {
|
|
2048
|
-
width: "100%",
|
|
2049
|
-
height: "32px",
|
|
2050
|
-
padding: "4px 8px",
|
|
2051
|
-
fontSize: "13px",
|
|
2052
|
-
fontFamily: "inherit",
|
|
2053
|
-
border: "1px solid var(--ds-color-border, #cbd5e1)",
|
|
2054
|
-
borderRadius: "4px",
|
|
2055
|
-
backgroundColor: disabled ? "var(--ds-color-surface-subtle, #f8fafc)" : "var(--ds-color-surface, #ffffff)",
|
|
2056
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
2057
|
-
outline: "none",
|
|
2058
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
2059
|
-
boxSizing: "border-box"
|
|
2060
|
-
},
|
|
2061
|
-
...rest,
|
|
2062
|
-
children: [
|
|
2063
|
-
placeholder && /* @__PURE__ */ jsx9("option", {
|
|
2064
|
-
value: "",
|
|
2065
|
-
children: placeholder
|
|
2066
|
-
}),
|
|
2067
|
-
options.map((opt) => /* @__PURE__ */ jsx9("option", {
|
|
2068
|
-
value: opt.value,
|
|
2069
|
-
children: opt.label || opt.value
|
|
2070
|
-
}, opt.value))
|
|
2071
|
-
]
|
|
2072
|
-
});
|
|
2073
|
-
if (!label)
|
|
2074
|
-
return selectEl;
|
|
2075
|
-
return /* @__PURE__ */ jsxs7("div", {
|
|
2076
|
-
className: "ds-field-wrapper binding-stack-tight",
|
|
2077
|
-
style: { width: "100%" },
|
|
2078
|
-
children: [
|
|
2079
|
-
/* @__PURE__ */ jsxs7("label", {
|
|
2080
|
-
htmlFor: selectId,
|
|
2081
|
-
className: "ds-field__label",
|
|
2082
|
-
style: {
|
|
2083
|
-
fontSize: "12px",
|
|
2084
|
-
fontWeight: 500,
|
|
2085
|
-
color: "var(--ds-color-foreground, #0f172a)",
|
|
2086
|
-
display: "flex",
|
|
2087
|
-
alignItems: "center",
|
|
2088
|
-
gap: "2px"
|
|
2089
|
-
},
|
|
2090
|
-
children: [
|
|
2091
|
-
label,
|
|
2092
|
-
required && /* @__PURE__ */ jsx9("span", {
|
|
2093
|
-
style: { color: "var(--ds-color-destructive, #ef4444)" },
|
|
2094
|
-
children: "*"
|
|
2095
|
-
})
|
|
2096
|
-
]
|
|
2097
|
-
}),
|
|
2098
|
-
/* @__PURE__ */ jsx9("div", {
|
|
2099
|
-
className: "ds-field",
|
|
2100
|
-
style: { width: "100%" },
|
|
2101
|
-
children: selectEl
|
|
2102
|
-
})
|
|
2103
|
-
]
|
|
2104
|
-
});
|
|
2105
|
-
}
|
|
2106
|
-
function Checkbox({ label, checked = false, onChange, disabled, className = "", id, ...rest }) {
|
|
2107
|
-
const generatedId = React5.useId();
|
|
2108
|
-
const checkboxId = id ?? generatedId;
|
|
2109
|
-
return /* @__PURE__ */ jsxs7("label", {
|
|
2110
|
-
htmlFor: checkboxId,
|
|
2111
|
-
className: `ds-checkbox-wrapper ${className}`,
|
|
2112
|
-
style: {
|
|
2113
|
-
display: "inline-flex",
|
|
2114
|
-
alignItems: "center",
|
|
2115
|
-
gap: "8px",
|
|
2116
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
2117
|
-
fontSize: "13px",
|
|
2118
|
-
color: "var(--ds-color-foreground, #0f172a)"
|
|
2119
|
-
},
|
|
2120
|
-
children: [
|
|
2121
|
-
/* @__PURE__ */ jsx9("input", {
|
|
2122
|
-
type: "checkbox",
|
|
2123
|
-
id: checkboxId,
|
|
2124
|
-
className: "ds-checkbox",
|
|
2125
|
-
checked,
|
|
2126
|
-
disabled,
|
|
2127
|
-
onChange: (e) => onChange?.(e.target.checked),
|
|
2128
|
-
style: {
|
|
2129
|
-
width: "16px",
|
|
2130
|
-
height: "16px",
|
|
2131
|
-
accentColor: "var(--ds-color-primary, #0284c7)",
|
|
2132
|
-
cursor: disabled ? "not-allowed" : "pointer"
|
|
2133
|
-
},
|
|
2134
|
-
...rest
|
|
2135
|
-
}),
|
|
2136
|
-
/* @__PURE__ */ jsx9("span", {
|
|
2137
|
-
children: label
|
|
2138
|
-
})
|
|
2139
|
-
]
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
// src/components/WorkspaceContext.tsx
|
|
2144
|
-
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2145
|
-
var defaultComponents = {
|
|
2146
|
-
AppGrid,
|
|
2147
|
-
Button,
|
|
2148
|
-
ButtonBar,
|
|
2149
|
-
ButtonGroup,
|
|
2150
|
-
Badge,
|
|
2151
|
-
Banner,
|
|
2152
|
-
DetailCard,
|
|
2153
|
-
Select,
|
|
2154
|
-
Input,
|
|
2155
|
-
TextArea,
|
|
2156
|
-
Checkbox,
|
|
2157
|
-
Dialog,
|
|
2158
|
-
createActionColumn
|
|
2159
|
-
};
|
|
2160
|
-
var WorkspaceComponentsContext = React6.createContext(defaultComponents);
|
|
2161
|
-
function WorkspaceComponentsProvider({
|
|
2162
|
-
components,
|
|
2163
|
-
children
|
|
2164
|
-
}) {
|
|
2165
|
-
const merged = React6.useMemo(() => {
|
|
2166
|
-
return {
|
|
2167
|
-
AppGrid: components?.AppGrid ?? defaultComponents.AppGrid,
|
|
2168
|
-
Button: components?.Button ?? defaultComponents.Button,
|
|
2169
|
-
ButtonBar: components?.ButtonBar ?? defaultComponents.ButtonBar,
|
|
2170
|
-
ButtonGroup: components?.ButtonGroup ?? defaultComponents.ButtonGroup,
|
|
2171
|
-
Badge: components?.Badge ?? defaultComponents.Badge,
|
|
2172
|
-
Banner: components?.Banner ?? defaultComponents.Banner,
|
|
2173
|
-
DetailCard: components?.DetailCard ?? defaultComponents.DetailCard,
|
|
2174
|
-
Select: components?.Select ?? defaultComponents.Select,
|
|
2175
|
-
Input: components?.Input ?? defaultComponents.Input,
|
|
2176
|
-
TextArea: components?.TextArea ?? defaultComponents.TextArea,
|
|
2177
|
-
Checkbox: components?.Checkbox ?? defaultComponents.Checkbox,
|
|
2178
|
-
Dialog: components?.Dialog ?? defaultComponents.Dialog,
|
|
2179
|
-
createActionColumn: components?.createActionColumn ?? defaultComponents.createActionColumn
|
|
2180
|
-
};
|
|
2181
|
-
}, [components]);
|
|
2182
|
-
return /* @__PURE__ */ jsx10(WorkspaceComponentsContext.Provider, {
|
|
2183
|
-
value: merged,
|
|
2184
|
-
children
|
|
2185
|
-
});
|
|
2186
|
-
}
|
|
2187
|
-
function useWorkspaceComponents() {
|
|
2188
|
-
return React6.useContext(WorkspaceComponentsContext);
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
// src/components/ApiMappings.tsx
|
|
2192
|
-
import * as React7 from "react";
|
|
2193
|
-
import { jsx as jsx11, jsxs as jsxs8, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
2194
|
-
var TIMING_OPTIONS = [
|
|
2195
|
-
{ value: "lazy", label: "Lazy on Click" },
|
|
2196
|
-
{ value: "prefetch", label: "On Window Load" }
|
|
2197
|
-
];
|
|
2198
|
-
var FRESHNESS_OPTIONS = [
|
|
2199
|
-
{ value: "fresh", label: "Fresh on every open" },
|
|
2200
|
-
{ value: "reuse", label: "Reuse cached response" },
|
|
2201
|
-
{ value: "poll", label: "Poll on interval" }
|
|
2202
|
-
];
|
|
2203
|
-
function ApiMappings({
|
|
2204
|
-
config,
|
|
2205
|
-
context,
|
|
2206
|
-
focus,
|
|
2207
|
-
defaultTarget,
|
|
2208
|
-
onChange
|
|
2209
|
-
}) {
|
|
2210
|
-
const { Badge, Banner, Button, ButtonBar, Dialog, Input, Select } = useWorkspaceComponents();
|
|
2211
|
-
const [confirm, setConfirm] = React7.useState(null);
|
|
2212
|
-
const targets = contextComponents(context.snapshot);
|
|
2213
|
-
const sources = [...sourceComponents(context.snapshot), FREE_TEXT_OPTION];
|
|
2214
|
-
const paths = gridPaths(context);
|
|
2215
|
-
const gridId = context.snapshot.grid?.id;
|
|
2216
|
-
const shown = config.calls.map((call, index) => ({ call, index })).filter(({ call }) => !focus || call.component === focus || call.params.some((p) => p.source === focus));
|
|
2217
|
-
const update = (id, f) => onChange((old) => ({ ...old, calls: old.calls.map((c) => c.id === id ? f(c) : c) }));
|
|
2218
|
-
const ask = (c, run) => c ? setConfirm(c) : run();
|
|
2219
|
-
function changeApi(call, api, method) {
|
|
2220
|
-
const run = () => update(call.id, (c) => changeCallTarget(c, api, method, context));
|
|
2221
|
-
const lost = droppedBy(call, api, method, context);
|
|
2222
|
-
ask(lost > 0 ? {
|
|
2223
|
-
title: "Change API Mapping?",
|
|
2224
|
-
label: "Change API",
|
|
2225
|
-
message: `${lost} mapped request ${lost === 1 ? "property is" : "properties are"} not in ${method ?? ""} ${api} and will be discarded. Mappings with the same name are kept.`,
|
|
2226
|
-
run
|
|
2227
|
-
} : null, run);
|
|
2228
|
-
}
|
|
2229
|
-
function remove(call, index) {
|
|
2230
|
-
const run = () => onChange((old) => ({ ...old, calls: old.calls.filter((c) => c.id !== call.id) }));
|
|
2231
|
-
ask(populated(call) ? {
|
|
2232
|
-
title: "Remove API Mapping?",
|
|
2233
|
-
label: "Remove",
|
|
2234
|
-
message: `Call ${index + 1} (${endpointLabel(call)}) has mapped request properties that will be discarded.`,
|
|
2235
|
-
run
|
|
2236
|
-
} : null, run);
|
|
2237
|
-
}
|
|
2238
|
-
function clearAll() {
|
|
2239
|
-
const ids = new Set(shown.map((s) => s.call.id));
|
|
2240
|
-
const run = () => onChange((old) => ({ ...old, calls: old.calls.filter((c) => !ids.has(c.id)) }));
|
|
2241
|
-
ask(shown.some((s) => populated(s.call)) ? {
|
|
2242
|
-
title: "Clear API Mappings?",
|
|
2243
|
-
label: "Clear Mappings",
|
|
2244
|
-
message: `${ids.size} API ${ids.size === 1 ? "mapping" : "mappings"} and their request properties will be discarded.`,
|
|
2245
|
-
run
|
|
2246
|
-
} : null, run);
|
|
2247
|
-
}
|
|
2248
|
-
const add = (purpose) => onChange((old) => ({ ...old, calls: [...old.calls, newCall(purpose, defaultTarget ?? focus ?? "")] }));
|
|
2249
|
-
return /* @__PURE__ */ jsxs8("div", {
|
|
2250
|
-
className: "binding-stack",
|
|
2251
|
-
"aria-label": "API mappings",
|
|
2252
|
-
children: [
|
|
2253
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2254
|
-
className: "binding-section-title",
|
|
2255
|
-
children: [
|
|
2256
|
-
/* @__PURE__ */ jsx11("h3", {
|
|
2257
|
-
children: "API mappings"
|
|
2258
|
-
}),
|
|
2259
|
-
/* @__PURE__ */ jsxs8(ButtonBar, {
|
|
2260
|
-
children: [
|
|
2261
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2262
|
-
icon: "Plus",
|
|
2263
|
-
onClick: () => add("read"),
|
|
2264
|
-
children: "Add Read Call"
|
|
2265
|
-
}),
|
|
2266
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2267
|
-
icon: "Plus",
|
|
2268
|
-
onClick: () => add("write"),
|
|
2269
|
-
children: "Add Write Call"
|
|
2270
|
-
}),
|
|
2271
|
-
shown.length > 0 && /* @__PURE__ */ jsx11(Button, {
|
|
2272
|
-
icon: "Trash2",
|
|
2273
|
-
variant: "ghost",
|
|
2274
|
-
onClick: clearAll,
|
|
2275
|
-
children: "Clear Mappings"
|
|
2276
|
-
})
|
|
2277
|
-
]
|
|
2278
|
-
})
|
|
2279
|
-
]
|
|
2280
|
-
}),
|
|
2281
|
-
shown.length === 0 && /* @__PURE__ */ jsxs8("p", {
|
|
2282
|
-
className: "binding-muted",
|
|
2283
|
-
children: [
|
|
2284
|
-
"No API mappings ",
|
|
2285
|
-
focus ? "for this component yet" : "yet",
|
|
2286
|
-
". Add a read or write call."
|
|
2287
|
-
]
|
|
2288
|
-
}),
|
|
2289
|
-
shown.map(({ call, index }) => {
|
|
2290
|
-
const def = definitionFor(context, call.api, call.method);
|
|
2291
|
-
const issues = callIssues(call, index, context);
|
|
2292
|
-
const related = focus && call.component !== focus;
|
|
2293
|
-
return /* @__PURE__ */ jsxs8("section", {
|
|
2294
|
-
className: "binding-call",
|
|
2295
|
-
"aria-label": `Call ${index + 1}`,
|
|
2296
|
-
children: [
|
|
2297
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2298
|
-
className: "binding-section-title",
|
|
2299
|
-
children: [
|
|
2300
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2301
|
-
className: "binding-stack-tight",
|
|
2302
|
-
children: [
|
|
2303
|
-
/* @__PURE__ */ jsxs8("strong", {
|
|
2304
|
-
children: [
|
|
2305
|
-
index + 1,
|
|
2306
|
-
". ",
|
|
2307
|
-
call.purpose === "read" ? "Read" : "Write",
|
|
2308
|
-
" ·",
|
|
2309
|
-
" ",
|
|
2310
|
-
targets.find((t) => t.value === call.component)?.label ?? (call.component || "No target")
|
|
2311
|
-
]
|
|
2312
|
-
}),
|
|
2313
|
-
/* @__PURE__ */ jsxs8("code", {
|
|
2314
|
-
className: "binding-endpoint",
|
|
2315
|
-
children: [
|
|
2316
|
-
endpointLabel(call),
|
|
2317
|
-
def?.hook ? ` · ${def.hook}` : ""
|
|
2318
|
-
]
|
|
2319
|
-
})
|
|
2320
|
-
]
|
|
2321
|
-
}),
|
|
2322
|
-
/* @__PURE__ */ jsxs8(ButtonBar, {
|
|
2323
|
-
children: [
|
|
2324
|
-
related && /* @__PURE__ */ jsx11(Badge, {
|
|
2325
|
-
children: "Sends this field"
|
|
2326
|
-
}),
|
|
2327
|
-
def?.available === false && /* @__PURE__ */ jsx11(Badge, {
|
|
2328
|
-
variant: "danger",
|
|
2329
|
-
children: "Unavailable"
|
|
2330
|
-
}),
|
|
2331
|
-
/* @__PURE__ */ jsx11(Badge, {
|
|
2332
|
-
variant: issues.length ? "warning" : "success",
|
|
2333
|
-
children: issues.length ? `${issues.length} to resolve` : "Ready"
|
|
2334
|
-
}),
|
|
2335
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2336
|
-
icon: "ArrowUp",
|
|
2337
|
-
"aria-label": `Move Call ${index + 1} Up`,
|
|
2338
|
-
tooltip: "Move Up",
|
|
2339
|
-
disabled: index === 0,
|
|
2340
|
-
onClick: () => onChange((old) => ({ ...old, calls: moveCall(old.calls, call.id, -1) }))
|
|
2341
|
-
}),
|
|
2342
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2343
|
-
icon: "ArrowDown",
|
|
2344
|
-
"aria-label": `Move Call ${index + 1} Down`,
|
|
2345
|
-
tooltip: "Move Down",
|
|
2346
|
-
disabled: index === config.calls.length - 1,
|
|
2347
|
-
onClick: () => onChange((old) => ({ ...old, calls: moveCall(old.calls, call.id, 1) }))
|
|
2348
|
-
}),
|
|
2349
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2350
|
-
icon: "Trash2",
|
|
2351
|
-
"aria-label": `Remove Call ${index + 1}`,
|
|
2352
|
-
tooltip: "Remove",
|
|
2353
|
-
onClick: () => remove(call, index)
|
|
2354
|
-
})
|
|
2355
|
-
]
|
|
2356
|
-
})
|
|
2357
|
-
]
|
|
2358
|
-
}),
|
|
2359
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2360
|
-
className: "binding-call-grid",
|
|
2361
|
-
children: [
|
|
2362
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2363
|
-
label: "Purpose",
|
|
2364
|
-
value: call.purpose,
|
|
2365
|
-
options: [
|
|
2366
|
-
{ value: "read", label: "Read (load data)" },
|
|
2367
|
-
{ value: "write", label: "Write (send data)" }
|
|
2368
|
-
],
|
|
2369
|
-
onChange: (v) => update(call.id, (c) => changePurpose(c, v === "write" ? "write" : "read"))
|
|
2370
|
-
}),
|
|
2371
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2372
|
-
label: "Component",
|
|
2373
|
-
value: call.component || null,
|
|
2374
|
-
placeholder: "Choose a component",
|
|
2375
|
-
options: [
|
|
2376
|
-
...targets,
|
|
2377
|
-
...call.component && !targets.some((t) => t.value === call.component) ? [{ value: call.component, label: `${call.component} (missing)` }] : []
|
|
2378
|
-
],
|
|
2379
|
-
onChange: (v) => update(call.id, (c) => ({ ...c, component: String(v ?? "") }))
|
|
2380
|
-
}),
|
|
2381
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2382
|
-
label: "API",
|
|
2383
|
-
value: call.api || null,
|
|
2384
|
-
placeholder: "Choose an API",
|
|
2385
|
-
options: [
|
|
2386
|
-
...apisOf(context).map((api) => {
|
|
2387
|
-
const defs = context.definitions.filter((d) => d.api === api);
|
|
2388
|
-
return {
|
|
2389
|
-
value: api,
|
|
2390
|
-
label: api,
|
|
2391
|
-
description: defs.map((d) => `${d.method}${d.label ? ` ${d.label}` : ""}`).join(" · "),
|
|
2392
|
-
...defs.every((d) => d.available === false) ? { badges: [{ label: "Unavailable", variant: "danger" }] } : {}
|
|
2393
|
-
};
|
|
2394
|
-
}),
|
|
2395
|
-
...call.api && !apisOf(context).includes(call.api) ? [{ value: call.api, label: `${call.api} (not registered)` }] : []
|
|
2396
|
-
],
|
|
2397
|
-
onChange: (v) => changeApi(call, String(v ?? ""))
|
|
2398
|
-
}),
|
|
2399
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2400
|
-
label: "Method",
|
|
2401
|
-
value: call.method || null,
|
|
2402
|
-
disabled: !call.api,
|
|
2403
|
-
placeholder: "Choose a method",
|
|
2404
|
-
options: methodsFor(context, call.api).map((m) => {
|
|
2405
|
-
const d = definitionFor(context, call.api, m);
|
|
2406
|
-
return {
|
|
2407
|
-
value: m,
|
|
2408
|
-
label: m,
|
|
2409
|
-
description: d?.label,
|
|
2410
|
-
...d?.available === false ? { badges: [{ label: "Unavailable", variant: "danger" }] } : {}
|
|
2411
|
-
};
|
|
2412
|
-
}),
|
|
2413
|
-
onChange: (v) => changeApi(call, call.api, String(v ?? ""))
|
|
2414
|
-
}),
|
|
2415
|
-
call.purpose === "read" && /* @__PURE__ */ jsxs8(Fragment3, {
|
|
2416
|
-
children: [
|
|
2417
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2418
|
-
label: "Load Timing",
|
|
2419
|
-
value: call.timing || null,
|
|
2420
|
-
options: TIMING_OPTIONS,
|
|
2421
|
-
onChange: (v) => update(call.id, (c) => ({ ...c, timing: v === "prefetch" ? "prefetch" : "lazy" }))
|
|
2422
|
-
}),
|
|
2423
|
-
/* @__PURE__ */ jsx11(Select, {
|
|
2424
|
-
label: "Freshness",
|
|
2425
|
-
value: call.freshness || null,
|
|
2426
|
-
options: FRESHNESS_OPTIONS,
|
|
2427
|
-
onChange: (v) => update(call.id, (c) => ({
|
|
2428
|
-
...c,
|
|
2429
|
-
freshness: v ?? "fresh",
|
|
2430
|
-
pollSeconds: v === "poll" ? c.pollSeconds : undefined
|
|
2431
|
-
}))
|
|
2432
|
-
}),
|
|
2433
|
-
call.freshness === "poll" && /* @__PURE__ */ jsx11(Input, {
|
|
2434
|
-
label: "Poll Every (s)",
|
|
2435
|
-
type: "number",
|
|
2436
|
-
min: 1,
|
|
2437
|
-
value: call.pollSeconds ?? "",
|
|
2438
|
-
onChange: (e) => {
|
|
2439
|
-
const n = Number(e.target.value);
|
|
2440
|
-
update(call.id, (c) => ({ ...c, pollSeconds: e.target.value === "" ? undefined : n }));
|
|
2441
|
-
}
|
|
2442
|
-
})
|
|
2443
|
-
]
|
|
2444
|
-
})
|
|
2445
|
-
]
|
|
2446
|
-
}),
|
|
2447
|
-
def?.available === false && /* @__PURE__ */ jsxs8(Banner, {
|
|
2448
|
-
tone: "caution",
|
|
2449
|
-
children: [
|
|
2450
|
-
"This API is registered but cannot run: ",
|
|
2451
|
-
def.problem
|
|
2452
|
-
]
|
|
2453
|
-
}),
|
|
2454
|
-
call.api && def && call.params.length === 0 && /* @__PURE__ */ jsx11("p", {
|
|
2455
|
-
className: "binding-muted",
|
|
2456
|
-
children: "This API takes no request properties."
|
|
2457
|
-
}),
|
|
2458
|
-
call.params.length > 0 && /* @__PURE__ */ jsxs8("div", {
|
|
2459
|
-
className: "binding-params",
|
|
2460
|
-
role: "table",
|
|
2461
|
-
"aria-label": `Request properties for call ${index + 1}`,
|
|
2462
|
-
children: [
|
|
2463
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2464
|
-
className: "binding-param binding-muted",
|
|
2465
|
-
role: "row",
|
|
2466
|
-
children: [
|
|
2467
|
-
/* @__PURE__ */ jsx11("span", {
|
|
2468
|
-
role: "columnheader",
|
|
2469
|
-
children: "Request property"
|
|
2470
|
-
}),
|
|
2471
|
-
/* @__PURE__ */ jsx11("span", {
|
|
2472
|
-
role: "columnheader",
|
|
2473
|
-
children: "Source"
|
|
2474
|
-
}),
|
|
2475
|
-
/* @__PURE__ */ jsx11("span", {
|
|
2476
|
-
role: "columnheader",
|
|
2477
|
-
children: "Property / value"
|
|
2478
|
-
})
|
|
2479
|
-
]
|
|
2480
|
-
}),
|
|
2481
|
-
call.params.map((p) => {
|
|
2482
|
-
const spec = def?.params.find((d) => d.name === p.param);
|
|
2483
|
-
return /* @__PURE__ */ jsxs8("div", {
|
|
2484
|
-
className: "binding-param",
|
|
2485
|
-
role: "row",
|
|
2486
|
-
children: [
|
|
2487
|
-
/* @__PURE__ */ jsxs8("div", {
|
|
2488
|
-
className: "binding-stack-tight",
|
|
2489
|
-
role: "cell",
|
|
2490
|
-
children: [
|
|
2491
|
-
/* @__PURE__ */ jsxs8("span", {
|
|
2492
|
-
children: [
|
|
2493
|
-
p.param,
|
|
2494
|
-
spec?.required ? " *" : ""
|
|
2495
|
-
]
|
|
2496
|
-
}),
|
|
2497
|
-
/* @__PURE__ */ jsx11("span", {
|
|
2498
|
-
className: "binding-muted",
|
|
2499
|
-
children: spec ? `${spec.type} · ${spec.in ?? "body"}${spec.required ? " · required" : ""}` : "Not in contract"
|
|
2500
|
-
}),
|
|
2501
|
-
!spec && /* @__PURE__ */ jsx11(Button, {
|
|
2502
|
-
size: "sm",
|
|
2503
|
-
variant: "ghost",
|
|
2504
|
-
onClick: () => update(call.id, (c) => ({ ...c, params: c.params.filter((x) => x.param !== p.param) })),
|
|
2505
|
-
children: "Remove Property"
|
|
2506
|
-
})
|
|
2507
|
-
]
|
|
2508
|
-
}),
|
|
2509
|
-
/* @__PURE__ */ jsx11("div", {
|
|
2510
|
-
role: "cell",
|
|
2511
|
-
children: /* @__PURE__ */ jsx11(Select, {
|
|
2512
|
-
"aria-label": `Source for ${p.param}`,
|
|
2513
|
-
value: p.source || null,
|
|
2514
|
-
placeholder: "Not sent",
|
|
2515
|
-
options: [{ value: "", label: "Not sent" }, ...sources],
|
|
2516
|
-
onChange: (v) => update(call.id, (c) => ({
|
|
2517
|
-
...c,
|
|
2518
|
-
params: c.params.map((x) => x.param === p.param ? v ? setParamSource(x, String(v), gridId) : { ...x, source: "", property: "" } : x)
|
|
2519
|
-
}))
|
|
2520
|
-
})
|
|
2521
|
-
}),
|
|
2522
|
-
/* @__PURE__ */ jsx11("div", {
|
|
2523
|
-
role: "cell",
|
|
2524
|
-
children: p.source === FREE_TEXT_SOURCE ? /* @__PURE__ */ jsx11(Input, {
|
|
2525
|
-
"aria-label": `Fixed value for ${p.param}`,
|
|
2526
|
-
value: p.property,
|
|
2527
|
-
placeholder: spec?.type === "boolean" ? "true or false" : "Fixed value",
|
|
2528
|
-
onChange: (e) => {
|
|
2529
|
-
const value = e.target.value;
|
|
2530
|
-
update(call.id, (c) => ({
|
|
2531
|
-
...c,
|
|
2532
|
-
params: c.params.map((x) => x.param === p.param ? { ...x, property: value } : x)
|
|
2533
|
-
}));
|
|
2534
|
-
}
|
|
2535
|
-
}) : gridId && p.source === gridId ? /* @__PURE__ */ jsx11(Select, {
|
|
2536
|
-
"aria-label": `Grid property for ${p.param}`,
|
|
2537
|
-
value: p.property || null,
|
|
2538
|
-
placeholder: "Choose a grid property",
|
|
2539
|
-
options: paths.map((path) => ({
|
|
2540
|
-
value: path,
|
|
2541
|
-
label: path.slice(gridId.length + 1),
|
|
2542
|
-
description: `First row: ${String(path.slice(gridId.length + 1).split(".").reduce((o, k) => o?.[k], context.snapshot.grid?.rows[0]) ?? "—")}`
|
|
2543
|
-
})),
|
|
2544
|
-
onChange: (v) => update(call.id, (c) => ({
|
|
2545
|
-
...c,
|
|
2546
|
-
params: c.params.map((x) => x.param === p.param ? { ...x, property: String(v ?? "") } : x)
|
|
2547
|
-
}))
|
|
2548
|
-
}) : /* @__PURE__ */ jsx11("span", {
|
|
2549
|
-
className: "binding-muted",
|
|
2550
|
-
children: p.source ? p.property : "—"
|
|
2551
|
-
})
|
|
2552
|
-
})
|
|
2553
|
-
]
|
|
2554
|
-
}, p.param);
|
|
2555
|
-
})
|
|
2556
|
-
]
|
|
2557
|
-
}),
|
|
2558
|
-
issues.length > 0 && /* @__PURE__ */ jsx11("div", {
|
|
2559
|
-
role: "status",
|
|
2560
|
-
children: issues.map((i) => /* @__PURE__ */ jsx11("p", {
|
|
2561
|
-
className: "binding-error",
|
|
2562
|
-
children: i
|
|
2563
|
-
}, i))
|
|
2564
|
-
})
|
|
2565
|
-
]
|
|
2566
|
-
}, call.id);
|
|
2567
|
-
}),
|
|
2568
|
-
confirm && /* @__PURE__ */ jsx11(Dialog, {
|
|
2569
|
-
...alertDialogTemplateV1({
|
|
2570
|
-
open: true,
|
|
2571
|
-
kind: "destructive",
|
|
2572
|
-
title: confirm.title,
|
|
2573
|
-
message: confirm.message,
|
|
2574
|
-
onCancel: () => setConfirm(null),
|
|
2575
|
-
primaryAction: {
|
|
2576
|
-
label: confirm.label,
|
|
2577
|
-
onClick: () => {
|
|
2578
|
-
confirm.run();
|
|
2579
|
-
setConfirm(null);
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
})
|
|
2583
|
-
})
|
|
2584
|
-
]
|
|
2585
|
-
});
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
// src/components/RequestPreview.tsx
|
|
2589
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2590
|
-
var json = (o) => Object.keys(o).length ? JSON.stringify(o, null, 2) : "—";
|
|
2591
|
-
function RequestPreview({ title, requests }) {
|
|
2592
|
-
const { Badge } = useWorkspaceComponents();
|
|
2593
|
-
return /* @__PURE__ */ jsxs9("div", {
|
|
2594
|
-
className: "binding-stack",
|
|
2595
|
-
"aria-label": title,
|
|
2596
|
-
children: [
|
|
2597
|
-
/* @__PURE__ */ jsx12("h3", {
|
|
2598
|
-
children: title
|
|
2599
|
-
}),
|
|
2600
|
-
requests.length === 0 && /* @__PURE__ */ jsx12("p", {
|
|
2601
|
-
className: "binding-muted",
|
|
2602
|
-
children: "No calls to preview."
|
|
2603
|
-
}),
|
|
2604
|
-
requests.map((r) => /* @__PURE__ */ jsxs9("div", {
|
|
2605
|
-
className: "binding-call",
|
|
2606
|
-
children: [
|
|
2607
|
-
/* @__PURE__ */ jsxs9("div", {
|
|
2608
|
-
className: "binding-section-title",
|
|
2609
|
-
children: [
|
|
2610
|
-
/* @__PURE__ */ jsxs9("strong", {
|
|
2611
|
-
children: [
|
|
2612
|
-
r.order,
|
|
2613
|
-
". ",
|
|
2614
|
-
r.purpose === "read" ? "Read" : "Write",
|
|
2615
|
-
" · ",
|
|
2616
|
-
r.target
|
|
2617
|
-
]
|
|
2618
|
-
}),
|
|
2619
|
-
/* @__PURE__ */ jsx12(Badge, {
|
|
2620
|
-
variant: !r.available ? "danger" : r.issues.length ? "warning" : "neutral",
|
|
2621
|
-
children: !r.available ? "Unavailable" : r.issues.length ? "Incomplete" : "Simulated only"
|
|
2622
|
-
})
|
|
2623
|
-
]
|
|
2624
|
-
}),
|
|
2625
|
-
/* @__PURE__ */ jsxs9("code", {
|
|
2626
|
-
className: "binding-endpoint",
|
|
2627
|
-
children: [
|
|
2628
|
-
r.method,
|
|
2629
|
-
" ",
|
|
2630
|
-
r.api || "No API",
|
|
2631
|
-
r.hook ? ` · ${r.hook}` : ""
|
|
2632
|
-
]
|
|
2633
|
-
}),
|
|
2634
|
-
/* @__PURE__ */ jsxs9("div", {
|
|
2635
|
-
className: "binding-preview-parts",
|
|
2636
|
-
children: [
|
|
2637
|
-
/* @__PURE__ */ jsxs9("div", {
|
|
2638
|
-
children: [
|
|
2639
|
-
/* @__PURE__ */ jsx12("span", {
|
|
2640
|
-
className: "binding-muted",
|
|
2641
|
-
children: "Body"
|
|
2642
|
-
}),
|
|
2643
|
-
/* @__PURE__ */ jsx12("pre", {
|
|
2644
|
-
className: "binding-pre",
|
|
2645
|
-
children: json(r.parts.data)
|
|
2646
|
-
})
|
|
2647
|
-
]
|
|
2648
|
-
}),
|
|
2649
|
-
/* @__PURE__ */ jsxs9("div", {
|
|
2650
|
-
children: [
|
|
2651
|
-
/* @__PURE__ */ jsx12("span", {
|
|
2652
|
-
className: "binding-muted",
|
|
2653
|
-
children: "Query"
|
|
2654
|
-
}),
|
|
2655
|
-
/* @__PURE__ */ jsx12("pre", {
|
|
2656
|
-
className: "binding-pre",
|
|
2657
|
-
children: json(r.parts.params)
|
|
2658
|
-
})
|
|
2659
|
-
]
|
|
2660
|
-
}),
|
|
2661
|
-
/* @__PURE__ */ jsxs9("div", {
|
|
2662
|
-
children: [
|
|
2663
|
-
/* @__PURE__ */ jsx12("span", {
|
|
2664
|
-
className: "binding-muted",
|
|
2665
|
-
children: "Path"
|
|
2666
|
-
}),
|
|
2667
|
-
/* @__PURE__ */ jsx12("pre", {
|
|
2668
|
-
className: "binding-pre",
|
|
2669
|
-
children: json(r.parts.path)
|
|
2670
|
-
})
|
|
2671
|
-
]
|
|
2672
|
-
})
|
|
2673
|
-
]
|
|
2674
|
-
})
|
|
2675
|
-
]
|
|
2676
|
-
}, r.id))
|
|
2677
|
-
]
|
|
2678
|
-
});
|
|
2679
|
-
}
|
|
2680
|
-
|
|
2681
|
-
// src/components/SimulationDialog.tsx
|
|
2682
|
-
import * as React8 from "react";
|
|
2683
|
-
import { jsx as jsx13, jsxs as jsxs10, Fragment as Fragment4 } from "react/jsx-runtime";
|
|
2684
|
-
function SimulationDialog({
|
|
2685
|
-
config,
|
|
2686
|
-
context,
|
|
2687
|
-
onClose,
|
|
2688
|
-
onVerified
|
|
2689
|
-
}) {
|
|
2690
|
-
const {
|
|
2691
|
-
Banner,
|
|
2692
|
-
Button,
|
|
2693
|
-
ButtonBar,
|
|
2694
|
-
ButtonGroup,
|
|
2695
|
-
DetailCard,
|
|
2696
|
-
Dialog,
|
|
2697
|
-
Checkbox,
|
|
2698
|
-
Input,
|
|
2699
|
-
Select,
|
|
2700
|
-
TextArea
|
|
2701
|
-
} = useWorkspaceComponents();
|
|
2702
|
-
const [input, setInput] = React8.useState(() => {
|
|
2703
|
-
const initial = {
|
|
2704
|
-
notesType: "",
|
|
2705
|
-
template: "",
|
|
2706
|
-
subject: "",
|
|
2707
|
-
notes: "",
|
|
2708
|
-
addCallLog: false
|
|
2709
|
-
};
|
|
2710
|
-
for (const comp of context.snapshot.components) {
|
|
2711
|
-
if (comp.kind === "checkbox") {
|
|
2712
|
-
initial[comp.name] = comp.value === "true";
|
|
2713
|
-
} else {
|
|
2714
|
-
initial[comp.name] = comp.value ?? "";
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
return initial;
|
|
2718
|
-
});
|
|
2719
|
-
const [scope, setScope] = React8.useState("one");
|
|
2720
|
-
const [failure, setFailure] = React8.useState(false);
|
|
2721
|
-
const [errors, setErrors] = React8.useState([]);
|
|
2722
|
-
const [saved, setSaved] = React8.useState([]);
|
|
2723
|
-
const [retrieved, setRetrieved] = React8.useState([]);
|
|
2724
|
-
const [message, setMessage] = React8.useState("");
|
|
2725
|
-
const callLog = config.fields.find((c) => c.name === "addCallLog");
|
|
2726
|
-
const source = context.snapshot.grid?.rows ?? [];
|
|
2727
|
-
const example = source[0] ?? {};
|
|
2728
|
-
const rows = scope === "none" ? [] : scope === "one" ? [source[0] ?? example] : source.length > 1 ? source.slice(0, 2) : [example, { ...example, customerName: "Example customer" }];
|
|
2729
|
-
const nameOf = (r) => String(r.customerName ?? r.customerNumber ?? r.id ?? "Example customer");
|
|
2730
|
-
const gaps = simulatorGaps(config, context);
|
|
2731
|
-
const requests = previewRequests(config, context, actionContext(context, simulationForm(input), rows), (c) => c.purpose === "write" && c.component === SUBMIT);
|
|
2732
|
-
function setField(key, value) {
|
|
2733
|
-
setInput((prev) => ({ ...prev, [key]: value }));
|
|
2734
|
-
setErrors([]);
|
|
2735
|
-
}
|
|
2736
|
-
function chooseTemplate(value) {
|
|
2737
|
-
const template = context.options.template?.find((o) => o.value === value);
|
|
2738
|
-
const fromTemplate = (name) => config.templateFills && config.fields.find((c) => c.name === name)?.input === "template";
|
|
2739
|
-
setInput((prev) => ({
|
|
2740
|
-
...prev,
|
|
2741
|
-
template: value ?? "",
|
|
2742
|
-
...template && fromTemplate("subject") ? { subject: template.subject ?? "" } : {},
|
|
2743
|
-
...template && fromTemplate("notes") ? { notes: template.description ?? "" } : {}
|
|
2744
|
-
}));
|
|
2745
|
-
}
|
|
2746
|
-
function submit() {
|
|
2747
|
-
const result = simulateSave(config, context, input, rows, failure);
|
|
2748
|
-
setErrors(result.errors);
|
|
2749
|
-
if (result.errors.length) {
|
|
2750
|
-
setMessage("");
|
|
2751
|
-
return;
|
|
2752
|
-
}
|
|
2753
|
-
setSaved(result.notes);
|
|
2754
|
-
setRetrieved([]);
|
|
2755
|
-
setMessage(config.success === "close" ? "Simulated popup closed after save. Retrieve the saved note to finish the test." : "Saved. The simulated popup stays open. Retrieve the note to finish the test.");
|
|
2756
|
-
}
|
|
2757
|
-
const isNotesContract = context.snapshot.source === "Add Notes" || config.fields.some((f) => f.name === "notesType");
|
|
2758
|
-
return /* @__PURE__ */ jsx13(Dialog, {
|
|
2759
|
-
...actionFormDialogTemplateV1({
|
|
2760
|
-
open: true,
|
|
2761
|
-
title: `Try ${context.snapshot.source || "Add Notes"}`,
|
|
2762
|
-
description: "Simulation — sample data only.",
|
|
2763
|
-
size: "large",
|
|
2764
|
-
formId: "binding-simulation",
|
|
2765
|
-
submitLabel: "Submit",
|
|
2766
|
-
cancelLabel: "Cancel",
|
|
2767
|
-
onCancel: onClose,
|
|
2768
|
-
onSubmit: submit,
|
|
2769
|
-
children: /* @__PURE__ */ jsxs10("div", {
|
|
2770
|
-
className: "binding-simulation",
|
|
2771
|
-
onInvalidCapture: (event) => {
|
|
2772
|
-
event.preventDefault();
|
|
2773
|
-
setErrors([...simulationErrors(config, input, rows.length), ...gaps]);
|
|
2774
|
-
},
|
|
2775
|
-
children: [
|
|
2776
|
-
/* @__PURE__ */ jsxs10("div", {
|
|
2777
|
-
className: "binding-stack",
|
|
2778
|
-
children: [
|
|
2779
|
-
/* @__PURE__ */ jsxs10("p", {
|
|
2780
|
-
children: [
|
|
2781
|
-
"Selected customers: ",
|
|
2782
|
-
rows.length ? rows.map(nameOf).join(", ") : "None"
|
|
2783
|
-
]
|
|
2784
|
-
}),
|
|
2785
|
-
/* @__PURE__ */ jsx13(ButtonGroup, {
|
|
2786
|
-
"aria-label": "Selected customers",
|
|
2787
|
-
value: scope,
|
|
2788
|
-
onChange: setScope,
|
|
2789
|
-
options: [
|
|
2790
|
-
{ value: "one", label: "One Customer" },
|
|
2791
|
-
{ value: "multiple", label: "Multiple Customers" },
|
|
2792
|
-
{ value: "none", label: "No Customers" }
|
|
2793
|
-
]
|
|
2794
|
-
}),
|
|
2795
|
-
/* @__PURE__ */ jsx13(Checkbox, {
|
|
2796
|
-
label: "Simulate a failed save",
|
|
2797
|
-
checked: failure,
|
|
2798
|
-
onChange: setFailure
|
|
2799
|
-
})
|
|
2800
|
-
]
|
|
2801
|
-
}),
|
|
2802
|
-
errors.length > 0 && /* @__PURE__ */ jsx13(Banner, {
|
|
2803
|
-
tone: "caution",
|
|
2804
|
-
children: /* @__PURE__ */ jsx13("div", {
|
|
2805
|
-
role: "alert",
|
|
2806
|
-
children: errors.map((error) => /* @__PURE__ */ jsx13("p", {
|
|
2807
|
-
children: error
|
|
2808
|
-
}, error))
|
|
2809
|
-
})
|
|
2810
|
-
}),
|
|
2811
|
-
isNotesContract ? /* @__PURE__ */ jsxs10(Fragment4, {
|
|
2812
|
-
children: [
|
|
2813
|
-
/* @__PURE__ */ jsx13(Select, {
|
|
2814
|
-
label: "Notes Type",
|
|
2815
|
-
required: true,
|
|
2816
|
-
value: input.notesType || null,
|
|
2817
|
-
onChange: (value) => setField("notesType", String(value ?? "")),
|
|
2818
|
-
options: context.options.notesType ?? []
|
|
2819
|
-
}),
|
|
2820
|
-
/* @__PURE__ */ jsx13(Select, {
|
|
2821
|
-
label: "Template",
|
|
2822
|
-
value: input.template || null,
|
|
2823
|
-
onChange: (value) => chooseTemplate(value),
|
|
2824
|
-
options: context.options.template ?? []
|
|
2825
|
-
}),
|
|
2826
|
-
/* @__PURE__ */ jsx13(Input, {
|
|
2827
|
-
label: "Subject",
|
|
2828
|
-
required: true,
|
|
2829
|
-
value: input.subject,
|
|
2830
|
-
onChange: (event) => setField("subject", event.target.value)
|
|
2831
|
-
}),
|
|
2832
|
-
/* @__PURE__ */ jsx13(TextArea, {
|
|
2833
|
-
label: "Description",
|
|
2834
|
-
required: true,
|
|
2835
|
-
value: input.notes,
|
|
2836
|
-
onChange: (event) => setField("notes", event.target.value)
|
|
2837
|
-
}),
|
|
2838
|
-
callLog && !callLog.excluded && /* @__PURE__ */ jsx13(Checkbox, {
|
|
2839
|
-
label: "Add Call Log",
|
|
2840
|
-
checked: input.addCallLog,
|
|
2841
|
-
onChange: (value) => setField("addCallLog", value)
|
|
2842
|
-
}),
|
|
2843
|
-
context.options.notesType?.length || context.options.template?.length ? /* @__PURE__ */ jsx13("p", {
|
|
2844
|
-
className: "binding-muted",
|
|
2845
|
-
children: "Notes Type and Template choices are samples captured from the open window, not responses fetched by this test."
|
|
2846
|
-
}) : null
|
|
2847
|
-
]
|
|
2848
|
-
}) : config.fields.filter((f) => !f.excluded).map((f) => {
|
|
2849
|
-
const comp = context.snapshot.components.find((c) => c.name === f.name);
|
|
2850
|
-
const fieldKind = comp?.kind ?? (comp?.type === "select" || comp?.type === "combobox" ? "dropdown" : comp?.type === "textarea" ? "text-area" : comp?.type === "checkbox" ? "checkbox" : "text");
|
|
2851
|
-
const opts = context.options[f.name] ?? [];
|
|
2852
|
-
if (fieldKind === "dropdown" || opts.length > 0) {
|
|
2853
|
-
return /* @__PURE__ */ jsx13(Select, {
|
|
2854
|
-
label: f.label,
|
|
2855
|
-
required: f.required,
|
|
2856
|
-
value: String(input[f.name] ?? "") || null,
|
|
2857
|
-
onChange: (val) => setField(f.name, String(val ?? "")),
|
|
2858
|
-
options: opts
|
|
2859
|
-
}, f.name);
|
|
2860
|
-
}
|
|
2861
|
-
if (fieldKind === "text-area") {
|
|
2862
|
-
return /* @__PURE__ */ jsx13(TextArea, {
|
|
2863
|
-
label: f.label,
|
|
2864
|
-
required: f.required,
|
|
2865
|
-
value: String(input[f.name] ?? ""),
|
|
2866
|
-
onChange: (event) => setField(f.name, event.target.value)
|
|
2867
|
-
}, f.name);
|
|
2868
|
-
}
|
|
2869
|
-
if (fieldKind === "checkbox") {
|
|
2870
|
-
return /* @__PURE__ */ jsx13(Checkbox, {
|
|
2871
|
-
label: f.label,
|
|
2872
|
-
checked: Boolean(input[f.name]),
|
|
2873
|
-
onChange: (val) => setField(f.name, val)
|
|
2874
|
-
}, f.name);
|
|
2875
|
-
}
|
|
2876
|
-
return /* @__PURE__ */ jsx13(Input, {
|
|
2877
|
-
label: f.label,
|
|
2878
|
-
required: f.required,
|
|
2879
|
-
value: String(input[f.name] ?? ""),
|
|
2880
|
-
onChange: (event) => setField(f.name, event.target.value)
|
|
2881
|
-
}, f.name);
|
|
2882
|
-
}),
|
|
2883
|
-
gaps.length > 0 && /* @__PURE__ */ jsx13(Banner, {
|
|
2884
|
-
tone: "caution",
|
|
2885
|
-
children: /* @__PURE__ */ jsx13("div", {
|
|
2886
|
-
children: gaps.map((g) => /* @__PURE__ */ jsx13("p", {
|
|
2887
|
-
children: g
|
|
2888
|
-
}, g))
|
|
2889
|
-
})
|
|
2890
|
-
}),
|
|
2891
|
-
/* @__PURE__ */ jsx13(RequestPreview, {
|
|
2892
|
-
title: "Request preview (sample values)",
|
|
2893
|
-
requests
|
|
2894
|
-
}),
|
|
2895
|
-
message && /* @__PURE__ */ jsx13(Banner, {
|
|
2896
|
-
tone: "resolved",
|
|
2897
|
-
children: /* @__PURE__ */ jsx13("p", {
|
|
2898
|
-
role: "status",
|
|
2899
|
-
children: message
|
|
2900
|
-
})
|
|
2901
|
-
}),
|
|
2902
|
-
saved.length > 0 && /* @__PURE__ */ jsx13(ButtonBar, {
|
|
2903
|
-
children: /* @__PURE__ */ jsx13(Button, {
|
|
2904
|
-
icon: "Search",
|
|
2905
|
-
onClick: () => {
|
|
2906
|
-
const readback = JSON.parse(JSON.stringify(saved));
|
|
2907
|
-
setRetrieved(readback);
|
|
2908
|
-
if (readback.length === saved.length && readback.every((note) => note.subject && note.description)) {
|
|
2909
|
-
onVerified(fingerprint(config));
|
|
2910
|
-
}
|
|
2911
|
-
},
|
|
2912
|
-
children: "Retrieve Saved Note"
|
|
2913
|
-
})
|
|
2914
|
-
}),
|
|
2915
|
-
retrieved.map((note, i) => /* @__PURE__ */ jsx13(DetailCard, {
|
|
2916
|
-
title: `Retrieved note ${i + 1} (${note.customerName || note.customerNumber || "Customer"})`,
|
|
2917
|
-
dense: true,
|
|
2918
|
-
fields: [
|
|
2919
|
-
{ label: "Notes Type", value: String(note.notesType ?? "") },
|
|
2920
|
-
{ label: "Subject", value: String(note.subject ?? "") },
|
|
2921
|
-
{ label: "Description", value: String(note.description ?? "") }
|
|
2922
|
-
]
|
|
2923
|
-
}, i))
|
|
2924
|
-
]
|
|
2925
|
-
})
|
|
2926
|
-
})
|
|
2927
|
-
});
|
|
2928
|
-
}
|
|
2929
|
-
|
|
2930
|
-
// src/components/ApiBindingWorkspace.tsx
|
|
2931
|
-
import { jsx as jsx14, jsxs as jsxs11, Fragment as Fragment6 } from "react/jsx-runtime";
|
|
2932
|
-
function loadDraft(key) {
|
|
2933
|
-
try {
|
|
2934
|
-
const raw = localStorage.getItem(draftKey(key)) || localStorage.getItem(`api-binding-v2:draft:${key}`) || localStorage.getItem(`api-binding:draft:${key}`);
|
|
2935
|
-
return raw ? JSON.parse(raw) : null;
|
|
2936
|
-
} catch {
|
|
2937
|
-
return null;
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
function ApiBindingWorkspace({
|
|
2941
|
-
pageKey,
|
|
2942
|
-
initialContext,
|
|
2943
|
-
components
|
|
2944
|
-
}) {
|
|
2945
|
-
return /* @__PURE__ */ jsx14(WorkspaceComponentsProvider, {
|
|
2946
|
-
components,
|
|
2947
|
-
children: /* @__PURE__ */ jsx14(ApiBindingWorkspaceInner, {
|
|
2948
|
-
pageKey,
|
|
2949
|
-
initialContext
|
|
2950
|
-
})
|
|
2951
|
-
});
|
|
2952
|
-
}
|
|
2953
|
-
function ApiBindingWorkspaceInner({
|
|
2954
|
-
pageKey,
|
|
2955
|
-
initialContext
|
|
2956
|
-
}) {
|
|
2957
|
-
const {
|
|
2958
|
-
AppGrid,
|
|
2959
|
-
Button,
|
|
2960
|
-
ButtonBar,
|
|
2961
|
-
Badge,
|
|
2962
|
-
Banner,
|
|
2963
|
-
Select,
|
|
2964
|
-
Input,
|
|
2965
|
-
TextArea,
|
|
2966
|
-
Checkbox,
|
|
2967
|
-
DetailCard,
|
|
2968
|
-
createActionColumn
|
|
2969
|
-
} = useWorkspaceComponents();
|
|
2970
|
-
const [context, setContext] = React9.useState(initialContext ?? null);
|
|
2971
|
-
const [config, setConfig] = React9.useState(null);
|
|
2972
|
-
const [verified, setVerified] = React9.useState(null);
|
|
2973
|
-
const [editing, setEditing] = React9.useState(null);
|
|
2974
|
-
const [simulation, setSimulation] = React9.useState(false);
|
|
2975
|
-
const [applied, setApplied] = React9.useState(false);
|
|
2976
|
-
const [showCustomers, setShowCustomers] = React9.useState(false);
|
|
2977
|
-
const [storageError, setStorageError] = React9.useState("");
|
|
2978
|
-
const loaded = React9.useRef(false);
|
|
2979
|
-
React9.useEffect(() => {
|
|
2980
|
-
loaded.current = false;
|
|
2981
|
-
function receive(raw) {
|
|
2982
|
-
if (!validBindingContext(raw, pageKey))
|
|
2983
|
-
return;
|
|
2984
|
-
try {
|
|
2985
|
-
storeBindingContext(raw);
|
|
2986
|
-
} catch {
|
|
2987
|
-
setStorageError("Browser storage is unavailable. Changes cannot be kept after refresh.");
|
|
2988
|
-
}
|
|
2989
|
-
const value = raw;
|
|
2990
|
-
setContext(value);
|
|
2991
|
-
if (!loaded.current) {
|
|
2992
|
-
const draft = loadDraft(pageKey);
|
|
2993
|
-
const initial = draft && migrateConfig(draft.config, value) || proposeConfig(value);
|
|
2994
|
-
setConfig(initial);
|
|
2995
|
-
setVerified(draft?.version === CONFIG_VERSION && draft.verified === fingerprint(initial) ? draft.verified : null);
|
|
2996
|
-
try {
|
|
2997
|
-
const stored = localStorage.getItem(appliedKey(pageKey)) || localStorage.getItem(`api-binding:applied:${pageKey}`) || localStorage.getItem(`api-binding-v2:applied:${pageKey}`);
|
|
2998
|
-
const appliedConfig = stored ? migrateConfig(JSON.parse(stored).config, value) : null;
|
|
2999
|
-
setApplied(!!appliedConfig && fingerprint(appliedConfig) === fingerprint(initial));
|
|
3000
|
-
} catch {
|
|
3001
|
-
setApplied(false);
|
|
3002
|
-
}
|
|
3003
|
-
loaded.current = true;
|
|
3004
|
-
} else {
|
|
3005
|
-
setConfig((previous) => previous ? reconcileConfig(previous, value) : proposeConfig(value));
|
|
3006
|
-
}
|
|
3007
|
-
}
|
|
3008
|
-
const local = readBindingContext(pageKey);
|
|
3009
|
-
if (local)
|
|
3010
|
-
receive(local);
|
|
3011
|
-
else if (initialContext && validBindingContext(initialContext, pageKey))
|
|
3012
|
-
receive(initialContext);
|
|
3013
|
-
const onMessage = (event) => {
|
|
3014
|
-
if (event.origin === window.location.origin && (event.data?.type === BINDING_CONTEXT || event.data?.type === "api-binding-v2-context")) {
|
|
3015
|
-
receive(event.data.context);
|
|
3016
|
-
}
|
|
3017
|
-
};
|
|
3018
|
-
const onStorage = (event) => {
|
|
3019
|
-
if (event.key === contextKey(pageKey) || event.key === `api-binding:context:${pageKey}` || event.key === `api-binding-v2:context:${pageKey}`) {
|
|
3020
|
-
receive(readBindingContext(pageKey));
|
|
3021
|
-
}
|
|
3022
|
-
};
|
|
3023
|
-
window.addEventListener("message", onMessage);
|
|
3024
|
-
window.addEventListener("storage", onStorage);
|
|
3025
|
-
let channel;
|
|
3026
|
-
try {
|
|
3027
|
-
channel = new BroadcastChannel(BINDING_CHANNEL);
|
|
3028
|
-
channel.onmessage = (event) => {
|
|
3029
|
-
if (event.data?.key === pageKey) {
|
|
3030
|
-
if (event.data?.type === BINDING_SELECTION_UPDATE && event.data?.grid) {
|
|
3031
|
-
setContext((prev) => prev ? {
|
|
3032
|
-
...prev,
|
|
3033
|
-
snapshot: {
|
|
3034
|
-
...prev.snapshot,
|
|
3035
|
-
grid: {
|
|
3036
|
-
id: event.data.grid.id,
|
|
3037
|
-
label: prev.snapshot.grid?.label,
|
|
3038
|
-
columns: prev.snapshot.grid?.columns ?? [],
|
|
3039
|
-
rows: event.data.grid.rows
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
} : prev);
|
|
3043
|
-
} else {
|
|
3044
|
-
receive(readBindingContext(pageKey));
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
};
|
|
3048
|
-
} catch {}
|
|
3049
|
-
try {
|
|
3050
|
-
window.opener?.postMessage({ type: BINDING_READY, key: pageKey }, window.location.origin);
|
|
3051
|
-
window.opener?.postMessage({ type: "api-binding-v2-ready", key: pageKey }, window.location.origin);
|
|
3052
|
-
} catch {}
|
|
3053
|
-
return () => {
|
|
3054
|
-
window.removeEventListener("message", onMessage);
|
|
3055
|
-
window.removeEventListener("storage", onStorage);
|
|
3056
|
-
channel?.close();
|
|
3057
|
-
};
|
|
3058
|
-
}, [pageKey, initialContext]);
|
|
3059
|
-
React9.useEffect(() => {
|
|
3060
|
-
if (!config)
|
|
3061
|
-
return;
|
|
3062
|
-
try {
|
|
3063
|
-
const payload = JSON.stringify({ version: CONFIG_VERSION, config, verified });
|
|
3064
|
-
localStorage.setItem(draftKey(pageKey), payload);
|
|
3065
|
-
} catch {
|
|
3066
|
-
setStorageError("Browser storage is unavailable. Your changes have not been saved.");
|
|
3067
|
-
}
|
|
3068
|
-
}, [config, verified, pageKey]);
|
|
3069
|
-
function change(updater) {
|
|
3070
|
-
setConfig((old) => {
|
|
3071
|
-
if (!old)
|
|
3072
|
-
return old;
|
|
3073
|
-
const next = updater(old);
|
|
3074
|
-
if (fingerprint(next) !== fingerprint(old)) {
|
|
3075
|
-
setVerified(null);
|
|
3076
|
-
setApplied(false);
|
|
3077
|
-
}
|
|
3078
|
-
return next;
|
|
3079
|
-
});
|
|
3080
|
-
}
|
|
3081
|
-
function updateField(name, patch) {
|
|
3082
|
-
change((old) => ({
|
|
3083
|
-
...old,
|
|
3084
|
-
fields: old.fields.map((c) => c.name === name ? { ...c, ...patch } : c)
|
|
3085
|
-
}));
|
|
3086
|
-
}
|
|
3087
|
-
if (!context || !config) {
|
|
3088
|
-
return /* @__PURE__ */ jsx14("main", {
|
|
3089
|
-
className: "binding-workspace",
|
|
3090
|
-
children: /* @__PURE__ */ jsxs11("div", {
|
|
3091
|
-
className: "binding-empty",
|
|
3092
|
-
children: [
|
|
3093
|
-
/* @__PURE__ */ jsxs11("h1", {
|
|
3094
|
-
children: [
|
|
3095
|
-
"Bind ",
|
|
3096
|
-
pageKey.includes("worklist-add-notes") ? "Add Notes" : "Add Notes"
|
|
3097
|
-
]
|
|
3098
|
-
}),
|
|
3099
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3100
|
-
children: "Open Add Notes and choose Bind API to bring its fields and selected customers here."
|
|
3101
|
-
})
|
|
3102
|
-
]
|
|
3103
|
-
})
|
|
3104
|
-
});
|
|
3105
|
-
}
|
|
3106
|
-
const issues = issuesFor(config, context);
|
|
3107
|
-
const gaps = simulatorGaps(config, context);
|
|
3108
|
-
const grid = context.snapshot.grid;
|
|
3109
|
-
const submitCalls = config.calls.filter((c) => c.purpose === "write" && c.component === SUBMIT);
|
|
3110
|
-
const customerParams = submitCalls.filter((c) => isNoteContract(definitionFor(context, c.api, c.method))).flatMap((c) => c.params.filter((p) => p.param === CUSTOMER_PARAM && grid && p.source === grid.id).map((p) => ({ call: c, param: p })));
|
|
3111
|
-
const cancelCalls = config.calls.filter((c) => c.component === CANCEL);
|
|
3112
|
-
const customerOptions = gridPaths(context).map((path) => {
|
|
3113
|
-
const key = path.slice((grid?.id.length ?? 0) + 1);
|
|
3114
|
-
return {
|
|
3115
|
-
value: path,
|
|
3116
|
-
label: key,
|
|
3117
|
-
description: `First selected customer: ${String(key.split(".").reduce((o, k) => o?.[k], grid?.rows[0]) ?? "—")}`
|
|
3118
|
-
};
|
|
3119
|
-
});
|
|
3120
|
-
const selectedField = config.fields.find((c) => c.name === editing);
|
|
3121
|
-
const fieldIssues = (name, label) => issues.filter((i) => i.includes(label) || config.calls.some((c, idx) => (c.component === name || c.params.some((p) => p.source === name)) && i.startsWith(`Call ${idx + 1} `)));
|
|
3122
|
-
const rows = config.fields.map((field) => {
|
|
3123
|
-
const s = fieldSummary(config, context, field.name);
|
|
3124
|
-
return {
|
|
3125
|
-
id: field.name,
|
|
3126
|
-
label: field.label,
|
|
3127
|
-
from: s.from,
|
|
3128
|
-
to: s.to,
|
|
3129
|
-
action: fieldIssues(field.name, field.label).length ? "Resolve" : "Change"
|
|
3130
|
-
};
|
|
3131
|
-
});
|
|
3132
|
-
const columns = [
|
|
3133
|
-
{ field: "label", headerName: "Popup field" },
|
|
3134
|
-
{ field: "from", headerName: "Gets its value from", fill: true },
|
|
3135
|
-
{ field: "to", headerName: "Saves to / Used for", fill: true },
|
|
3136
|
-
createActionColumn({
|
|
3137
|
-
headerName: "Change or Resolve",
|
|
3138
|
-
labelField: "action",
|
|
3139
|
-
icon: "Pencil",
|
|
3140
|
-
onClick: (row) => setEditing(row.id)
|
|
3141
|
-
})
|
|
3142
|
-
];
|
|
3143
|
-
const callRows = config.calls.map((c, i) => ({
|
|
3144
|
-
id: c.id,
|
|
3145
|
-
order: i + 1,
|
|
3146
|
-
purpose: c.purpose === "read" ? "Read" : "Write",
|
|
3147
|
-
target: componentLabel(context, c.component),
|
|
3148
|
-
endpoint: endpointLabel(c),
|
|
3149
|
-
mapped: `${c.params.filter((p) => p.source).length} of ${c.params.length}`,
|
|
3150
|
-
status: issues.some((x) => x.startsWith(`Call ${i + 1} `)) ? "Needs attention" : "Ready"
|
|
3151
|
-
}));
|
|
3152
|
-
const callColumns = [
|
|
3153
|
-
{ field: "order", headerName: "Order", type: "number" },
|
|
3154
|
-
{ field: "purpose", headerName: "Purpose" },
|
|
3155
|
-
{ field: "target", headerName: "Component" },
|
|
3156
|
-
{ field: "endpoint", headerName: "Endpoint", fill: true },
|
|
3157
|
-
{ field: "mapped", headerName: "Mapped" },
|
|
3158
|
-
{
|
|
3159
|
-
field: "status",
|
|
3160
|
-
headerName: "Status",
|
|
3161
|
-
type: "badge",
|
|
3162
|
-
badgeVariants: { Ready: "success", "Needs attention": "warning" }
|
|
3163
|
-
}
|
|
3164
|
-
];
|
|
3165
|
-
const customerNames = grid?.rows.map((row) => String(row.customerName ?? row.customerNumber ?? row.id ?? "Selected customer")) ?? [];
|
|
3166
|
-
const field = (name) => config.fields.find((c) => c.name === name);
|
|
3167
|
-
const callLog = field("addCallLog");
|
|
3168
|
-
const inputValue = (name) => context.snapshot.components.find((c) => c.name === name)?.value ?? "";
|
|
3169
|
-
const previewTemplate = context.options.template?.find((option) => option.value === inputValue("template")) ?? context.options.template?.[0];
|
|
3170
|
-
const previewValue = (name) => inputValue(name) || (config.templateFills && field(name)?.input === "template" ? (name === "subject" ? previewTemplate?.subject : previewTemplate?.description) ?? "" : "");
|
|
3171
|
-
const liveCtx = actionContext(context, snapshotForm(context));
|
|
3172
|
-
const panelTitle = editing === "customers" ? "Customer context" : editing === "actions" ? "Submit behavior" : editing === "mappings" ? "Manage API mappings" : selectedField?.label;
|
|
3173
|
-
const focus = editing === "actions" ? SUBMIT : selectedField ? selectedField.name : undefined;
|
|
3174
|
-
return /* @__PURE__ */ jsxs11("main", {
|
|
3175
|
-
className: "binding-workspace",
|
|
3176
|
-
children: [
|
|
3177
|
-
/* @__PURE__ */ jsxs11("header", {
|
|
3178
|
-
className: "binding-header",
|
|
3179
|
-
children: [
|
|
3180
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3181
|
-
className: "binding-stack",
|
|
3182
|
-
children: [
|
|
3183
|
-
/* @__PURE__ */ jsxs11("h1", {
|
|
3184
|
-
children: [
|
|
3185
|
-
"Bind ",
|
|
3186
|
-
context.snapshot.source || "Add Notes"
|
|
3187
|
-
]
|
|
3188
|
-
}),
|
|
3189
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3190
|
-
className: "binding-muted",
|
|
3191
|
-
children: "Review how this popup loads and saves information."
|
|
3192
|
-
})
|
|
3193
|
-
]
|
|
3194
|
-
}),
|
|
3195
|
-
/* @__PURE__ */ jsx14(Badge, {
|
|
3196
|
-
variant: issues.length ? "warning" : "success",
|
|
3197
|
-
children: issues.length ? `${issues.length} connections to resolve` : "Connections ready"
|
|
3198
|
-
})
|
|
3199
|
-
]
|
|
3200
|
-
}),
|
|
3201
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3202
|
-
className: "binding-body",
|
|
3203
|
-
children: [
|
|
3204
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3205
|
-
className: "binding-columns",
|
|
3206
|
-
children: [
|
|
3207
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3208
|
-
className: "binding-main",
|
|
3209
|
-
children: [
|
|
3210
|
-
/* @__PURE__ */ jsx14("section", {
|
|
3211
|
-
className: "binding-section",
|
|
3212
|
-
"aria-label": "Customer context",
|
|
3213
|
-
children: /* @__PURE__ */ jsx14(DetailCard, {
|
|
3214
|
-
title: "Customer context",
|
|
3215
|
-
dense: true,
|
|
3216
|
-
actions: grid ? /* @__PURE__ */ jsx14(Button, {
|
|
3217
|
-
icon: "Pencil",
|
|
3218
|
-
onClick: () => setEditing("customers"),
|
|
3219
|
-
children: "Edit"
|
|
3220
|
-
}) : undefined,
|
|
3221
|
-
fields: [
|
|
3222
|
-
{
|
|
3223
|
-
label: "Customer source",
|
|
3224
|
-
value: grid ? `${grid.label || grid.id} · selected rows` : "No source grid connected"
|
|
3225
|
-
},
|
|
3226
|
-
{
|
|
3227
|
-
label: "Save behavior",
|
|
3228
|
-
value: config.customerRule === "each" ? "One note per selected customer" : "Require exactly one customer"
|
|
3229
|
-
},
|
|
3230
|
-
{
|
|
3231
|
-
label: "Customer identifier",
|
|
3232
|
-
value: customerParams.length ? customerParams.map((x) => `${x.param.property || "not chosen"} → ${x.param.param}`).join("; ") : "No Submit request property uses the selection"
|
|
3233
|
-
},
|
|
3234
|
-
{
|
|
3235
|
-
label: "Current selection",
|
|
3236
|
-
value: customerNames.length === 0 ? "No customers selected in the worklist" : customerNames.length === 1 ? customerNames[0] : /* @__PURE__ */ jsxs11("div", {
|
|
3237
|
-
className: "binding-stack-tight",
|
|
3238
|
-
children: [
|
|
3239
|
-
/* @__PURE__ */ jsxs11("span", {
|
|
3240
|
-
children: [
|
|
3241
|
-
customerNames.length,
|
|
3242
|
-
" customers selected"
|
|
3243
|
-
]
|
|
3244
|
-
}),
|
|
3245
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3246
|
-
variant: "ghost",
|
|
3247
|
-
size: "sm",
|
|
3248
|
-
"aria-expanded": showCustomers,
|
|
3249
|
-
onClick: () => setShowCustomers((v) => !v),
|
|
3250
|
-
children: showCustomers ? "Hide Customers" : "Show Customers"
|
|
3251
|
-
}),
|
|
3252
|
-
showCustomers && /* @__PURE__ */ jsx14("ul", {
|
|
3253
|
-
className: "binding-list",
|
|
3254
|
-
children: customerNames.map((name, i) => /* @__PURE__ */ jsx14("li", {
|
|
3255
|
-
children: name
|
|
3256
|
-
}, `${name}-${i}`))
|
|
3257
|
-
})
|
|
3258
|
-
]
|
|
3259
|
-
})
|
|
3260
|
-
}
|
|
3261
|
-
]
|
|
3262
|
-
})
|
|
3263
|
-
}),
|
|
3264
|
-
/* @__PURE__ */ jsxs11("section", {
|
|
3265
|
-
className: "binding-section",
|
|
3266
|
-
"aria-labelledby": "fields-heading",
|
|
3267
|
-
children: [
|
|
3268
|
-
/* @__PURE__ */ jsx14("h2", {
|
|
3269
|
-
id: "fields-heading",
|
|
3270
|
-
children: "Field connections"
|
|
3271
|
-
}),
|
|
3272
|
-
/* @__PURE__ */ jsx14(AppGrid, {
|
|
3273
|
-
rows,
|
|
3274
|
-
getRowKey: (row) => row.id,
|
|
3275
|
-
columns,
|
|
3276
|
-
filtering: false,
|
|
3277
|
-
height: "auto"
|
|
3278
|
-
})
|
|
3279
|
-
]
|
|
3280
|
-
}),
|
|
3281
|
-
/* @__PURE__ */ jsxs11("section", {
|
|
3282
|
-
className: "binding-section",
|
|
3283
|
-
"aria-labelledby": "actions-heading",
|
|
3284
|
-
children: [
|
|
3285
|
-
/* @__PURE__ */ jsx14("h2", {
|
|
3286
|
-
id: "actions-heading",
|
|
3287
|
-
children: "Action behavior"
|
|
3288
|
-
}),
|
|
3289
|
-
/* @__PURE__ */ jsx14(DetailCard, {
|
|
3290
|
-
title: "Submit",
|
|
3291
|
-
dense: true,
|
|
3292
|
-
actions: /* @__PURE__ */ jsx14(Button, {
|
|
3293
|
-
icon: "Pencil",
|
|
3294
|
-
onClick: () => setEditing("actions"),
|
|
3295
|
-
children: "Edit"
|
|
3296
|
-
}),
|
|
3297
|
-
fields: [
|
|
3298
|
-
{ label: "Before saving", value: "Validate required fields" },
|
|
3299
|
-
{
|
|
3300
|
-
label: "Save operation",
|
|
3301
|
-
value: submitCalls.length ? submitCalls.map((c) => `${endpointLabel(c)} (${config.customerRule === "each" ? "one note per selected customer" : "one customer"})`).join(" then ") : "Save connection missing"
|
|
3302
|
-
},
|
|
3303
|
-
{ label: "On success", value: config.success === "close" ? "Close popup" : "Keep popup open" },
|
|
3304
|
-
{ label: "On failure", value: "Keep popup open, preserve entered values, allow retry" }
|
|
3305
|
-
]
|
|
3306
|
-
}),
|
|
3307
|
-
cancelCalls.length ? /* @__PURE__ */ jsx14(DetailCard, {
|
|
3308
|
-
title: "Cancel",
|
|
3309
|
-
subtitle: "Configured API mappings",
|
|
3310
|
-
dense: true,
|
|
3311
|
-
fields: [
|
|
3312
|
-
{
|
|
3313
|
-
label: "API calls",
|
|
3314
|
-
value: cancelCalls.map((c) => `${c.purpose === "read" ? "Read" : "Write"} ${endpointLabel(c)}`).join(" then ")
|
|
3315
|
-
},
|
|
3316
|
-
{ label: "Then", value: "Close popup" }
|
|
3317
|
-
]
|
|
3318
|
-
}) : /* @__PURE__ */ jsx14(DetailCard, {
|
|
3319
|
-
title: "Cancel",
|
|
3320
|
-
subtitle: "Fixed behavior",
|
|
3321
|
-
dense: true,
|
|
3322
|
-
fields: [{ label: "Behavior", value: "Close without saving" }]
|
|
3323
|
-
})
|
|
3324
|
-
]
|
|
3325
|
-
}),
|
|
3326
|
-
/* @__PURE__ */ jsxs11("section", {
|
|
3327
|
-
className: "binding-section",
|
|
3328
|
-
"aria-labelledby": "calls-heading",
|
|
3329
|
-
children: [
|
|
3330
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3331
|
-
className: "binding-section-title",
|
|
3332
|
-
children: [
|
|
3333
|
-
/* @__PURE__ */ jsx14("h2", {
|
|
3334
|
-
id: "calls-heading",
|
|
3335
|
-
children: "API mappings"
|
|
3336
|
-
}),
|
|
3337
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3338
|
-
icon: "Settings",
|
|
3339
|
-
onClick: () => setEditing("mappings"),
|
|
3340
|
-
children: "Manage API Mappings"
|
|
3341
|
-
})
|
|
3342
|
-
]
|
|
3343
|
-
}),
|
|
3344
|
-
callRows.length ? /* @__PURE__ */ jsx14(AppGrid, {
|
|
3345
|
-
rows: callRows,
|
|
3346
|
-
getRowKey: (r) => r.id,
|
|
3347
|
-
columns: callColumns,
|
|
3348
|
-
filtering: false,
|
|
3349
|
-
height: "auto"
|
|
3350
|
-
}) : /* @__PURE__ */ jsx14("p", {
|
|
3351
|
-
className: "binding-muted",
|
|
3352
|
-
children: "No API mappings yet."
|
|
3353
|
-
})
|
|
3354
|
-
]
|
|
3355
|
-
}),
|
|
3356
|
-
issues.length > 0 && /* @__PURE__ */ jsx14(Banner, {
|
|
3357
|
-
tone: "caution",
|
|
3358
|
-
children: /* @__PURE__ */ jsx14("div", {
|
|
3359
|
-
children: issues.map((issue) => /* @__PURE__ */ jsx14("p", {
|
|
3360
|
-
children: issue
|
|
3361
|
-
}, issue))
|
|
3362
|
-
})
|
|
3363
|
-
}),
|
|
3364
|
-
storageError && /* @__PURE__ */ jsx14(Banner, {
|
|
3365
|
-
tone: "caution",
|
|
3366
|
-
children: storageError
|
|
3367
|
-
})
|
|
3368
|
-
]
|
|
3369
|
-
}),
|
|
3370
|
-
/* @__PURE__ */ jsxs11("aside", {
|
|
3371
|
-
className: "binding-preview",
|
|
3372
|
-
"aria-label": `Read-only ${context.snapshot.source || "Add Notes"} preview`,
|
|
3373
|
-
children: [
|
|
3374
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3375
|
-
className: "binding-section-title",
|
|
3376
|
-
children: [
|
|
3377
|
-
/* @__PURE__ */ jsx14("h2", {
|
|
3378
|
-
children: context.snapshot.source || "Add Notes"
|
|
3379
|
-
}),
|
|
3380
|
-
/* @__PURE__ */ jsx14(Badge, {
|
|
3381
|
-
children: "Read-only preview"
|
|
3382
|
-
})
|
|
3383
|
-
]
|
|
3384
|
-
}),
|
|
3385
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3386
|
-
className: "binding-muted",
|
|
3387
|
-
children: config.customerRule === "each" ? "One note per selected customer" : "Exactly one customer"
|
|
3388
|
-
}),
|
|
3389
|
-
context.snapshot.source === "Add Notes" || config.fields.some((f) => f.name === "notesType") ? /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3390
|
-
children: [
|
|
3391
|
-
/* @__PURE__ */ jsx14(Select, {
|
|
3392
|
-
label: "Notes Type",
|
|
3393
|
-
required: true,
|
|
3394
|
-
disabled: true,
|
|
3395
|
-
value: inputValue("notesType") || context.options.notesType?.[0]?.value || null,
|
|
3396
|
-
options: context.options.notesType ?? []
|
|
3397
|
-
}),
|
|
3398
|
-
/* @__PURE__ */ jsx14(Select, {
|
|
3399
|
-
label: "Template",
|
|
3400
|
-
disabled: true,
|
|
3401
|
-
value: config.templateFills ? previewTemplate?.value ?? null : null,
|
|
3402
|
-
options: context.options.template ?? []
|
|
3403
|
-
}),
|
|
3404
|
-
/* @__PURE__ */ jsx14(Input, {
|
|
3405
|
-
label: "Subject",
|
|
3406
|
-
required: true,
|
|
3407
|
-
readOnly: true,
|
|
3408
|
-
value: previewValue("subject")
|
|
3409
|
-
}),
|
|
3410
|
-
/* @__PURE__ */ jsx14(TextArea, {
|
|
3411
|
-
label: "Description",
|
|
3412
|
-
required: true,
|
|
3413
|
-
readOnly: true,
|
|
3414
|
-
value: previewValue("notes")
|
|
3415
|
-
}),
|
|
3416
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3417
|
-
className: "binding-muted",
|
|
3418
|
-
children: "Preview uses your entered values or sample template content."
|
|
3419
|
-
}),
|
|
3420
|
-
callLog && !callLog.excluded && /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3421
|
-
children: [
|
|
3422
|
-
/* @__PURE__ */ jsx14(Checkbox, {
|
|
3423
|
-
label: "Add Call Log",
|
|
3424
|
-
disabled: true,
|
|
3425
|
-
checked: false
|
|
3426
|
-
}),
|
|
3427
|
-
issues.some((i) => i.startsWith(callLog.label)) && /* @__PURE__ */ jsx14(Badge, {
|
|
3428
|
-
variant: "warning",
|
|
3429
|
-
children: "Connection unresolved"
|
|
3430
|
-
})
|
|
3431
|
-
]
|
|
3432
|
-
}),
|
|
3433
|
-
/* @__PURE__ */ jsxs11(ButtonBar, {
|
|
3434
|
-
justify: "between",
|
|
3435
|
-
children: [
|
|
3436
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3437
|
-
disabled: true,
|
|
3438
|
-
children: "Cancel"
|
|
3439
|
-
}),
|
|
3440
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3441
|
-
variant: "primary",
|
|
3442
|
-
disabled: true,
|
|
3443
|
-
children: "Submit"
|
|
3444
|
-
})
|
|
3445
|
-
]
|
|
3446
|
-
})
|
|
3447
|
-
]
|
|
3448
|
-
}) : /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3449
|
-
children: [
|
|
3450
|
-
config.fields.filter((f) => !f.excluded).map((f) => {
|
|
3451
|
-
const comp = context.snapshot.components.find((c) => c.name === f.name);
|
|
3452
|
-
const fieldKind = comp?.kind ?? (comp?.type === "select" || comp?.type === "combobox" ? "dropdown" : comp?.type === "textarea" ? "text-area" : comp?.type === "checkbox" ? "checkbox" : "text");
|
|
3453
|
-
const opts = context.options[f.name] ?? [];
|
|
3454
|
-
if (fieldKind === "dropdown" || opts.length > 0) {
|
|
3455
|
-
return /* @__PURE__ */ jsx14(Select, {
|
|
3456
|
-
label: f.label,
|
|
3457
|
-
required: f.required,
|
|
3458
|
-
disabled: true,
|
|
3459
|
-
value: inputValue(f.name) || opts[0]?.value || null,
|
|
3460
|
-
options: opts
|
|
3461
|
-
}, f.name);
|
|
3462
|
-
}
|
|
3463
|
-
if (fieldKind === "text-area") {
|
|
3464
|
-
return /* @__PURE__ */ jsx14(TextArea, {
|
|
3465
|
-
label: f.label,
|
|
3466
|
-
required: f.required,
|
|
3467
|
-
readOnly: true,
|
|
3468
|
-
value: previewValue(f.name)
|
|
3469
|
-
}, f.name);
|
|
3470
|
-
}
|
|
3471
|
-
if (fieldKind === "checkbox") {
|
|
3472
|
-
return /* @__PURE__ */ jsxs11(React9.Fragment, {
|
|
3473
|
-
children: [
|
|
3474
|
-
/* @__PURE__ */ jsx14(Checkbox, {
|
|
3475
|
-
label: f.label,
|
|
3476
|
-
disabled: true,
|
|
3477
|
-
checked: comp?.value === "true"
|
|
3478
|
-
}),
|
|
3479
|
-
issues.some((i) => i.startsWith(f.label)) && /* @__PURE__ */ jsx14(Badge, {
|
|
3480
|
-
variant: "warning",
|
|
3481
|
-
children: "Connection unresolved"
|
|
3482
|
-
})
|
|
3483
|
-
]
|
|
3484
|
-
}, f.name);
|
|
3485
|
-
}
|
|
3486
|
-
return /* @__PURE__ */ jsx14(Input, {
|
|
3487
|
-
label: f.label,
|
|
3488
|
-
required: f.required,
|
|
3489
|
-
readOnly: true,
|
|
3490
|
-
value: previewValue(f.name)
|
|
3491
|
-
}, f.name);
|
|
3492
|
-
}),
|
|
3493
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3494
|
-
className: "binding-muted",
|
|
3495
|
-
children: "Preview uses your entered values or sample data."
|
|
3496
|
-
}),
|
|
3497
|
-
/* @__PURE__ */ jsx14(ButtonBar, {
|
|
3498
|
-
justify: "between",
|
|
3499
|
-
children: (context.snapshot.buttons && context.snapshot.buttons.length > 0 ? context.snapshot.buttons : ["Cancel", "Submit"]).map((btnText, idx) => /* @__PURE__ */ jsx14(Button, {
|
|
3500
|
-
variant: idx === (context.snapshot.buttons?.length ?? 2) - 1 ? "primary" : "secondary",
|
|
3501
|
-
disabled: true,
|
|
3502
|
-
children: btnText
|
|
3503
|
-
}, btnText))
|
|
3504
|
-
})
|
|
3505
|
-
]
|
|
3506
|
-
}),
|
|
3507
|
-
/* @__PURE__ */ jsxs11("p", {
|
|
3508
|
-
className: "binding-muted",
|
|
3509
|
-
children: [
|
|
3510
|
-
"After save: ",
|
|
3511
|
-
config.success === "close" ? "close popup" : "keep popup open",
|
|
3512
|
-
"."
|
|
3513
|
-
]
|
|
3514
|
-
})
|
|
3515
|
-
]
|
|
3516
|
-
})
|
|
3517
|
-
]
|
|
3518
|
-
}),
|
|
3519
|
-
editing && /* @__PURE__ */ jsxs11("aside", {
|
|
3520
|
-
className: `binding-panel${editing !== "customers" ? " binding-panel-wide" : ""}`,
|
|
3521
|
-
"aria-label": "Edit connection",
|
|
3522
|
-
children: [
|
|
3523
|
-
/* @__PURE__ */ jsxs11("div", {
|
|
3524
|
-
className: "binding-section-title",
|
|
3525
|
-
children: [
|
|
3526
|
-
/* @__PURE__ */ jsx14("h2", {
|
|
3527
|
-
children: panelTitle
|
|
3528
|
-
}),
|
|
3529
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3530
|
-
icon: "X",
|
|
3531
|
-
tooltip: "Close Panel",
|
|
3532
|
-
"aria-label": "Close Panel",
|
|
3533
|
-
onClick: () => setEditing(null)
|
|
3534
|
-
})
|
|
3535
|
-
]
|
|
3536
|
-
}),
|
|
3537
|
-
editing === "customers" && /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3538
|
-
children: [
|
|
3539
|
-
/* @__PURE__ */ jsx14(Select, {
|
|
3540
|
-
label: "Selection rule",
|
|
3541
|
-
value: config.customerRule,
|
|
3542
|
-
options: [
|
|
3543
|
-
{ value: "each", label: "One note per selected customer" },
|
|
3544
|
-
{ value: "single", label: "Require exactly one customer" }
|
|
3545
|
-
],
|
|
3546
|
-
onChange: (value) => change((old) => ({ ...old, customerRule: value === "single" ? "single" : "each" }))
|
|
3547
|
-
}),
|
|
3548
|
-
customerParams.length > 0 && grid ? customerParams.map(({ call, param }) => /* @__PURE__ */ jsx14(Select, {
|
|
3549
|
-
label: customerParams.length > 1 ? `Customer identifier · ${endpointLabel(call)} (call ${config.calls.indexOf(call) + 1})` : "Customer identifier",
|
|
3550
|
-
value: param.property || null,
|
|
3551
|
-
options: customerOptions,
|
|
3552
|
-
onChange: (value) => change((old) => ({
|
|
3553
|
-
...old,
|
|
3554
|
-
calls: old.calls.map((c) => c.id === call.id ? {
|
|
3555
|
-
...c,
|
|
3556
|
-
params: c.params.map((p) => p.param === CUSTOMER_PARAM && p.source === grid.id ? { ...p, property: String(value ?? "") } : p)
|
|
3557
|
-
} : c)
|
|
3558
|
-
}))
|
|
3559
|
-
}, call.id)) : /* @__PURE__ */ jsx14("p", {
|
|
3560
|
-
className: "binding-muted",
|
|
3561
|
-
children: "No Submit request property takes the selected customers. Map one to the grid in the Submit API mappings."
|
|
3562
|
-
}),
|
|
3563
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3564
|
-
children: config.customerRule === "single" ? "Submitting with no customers or multiple customers will be blocked." : "Submitting with multiple customers saves the same note separately for each customer."
|
|
3565
|
-
})
|
|
3566
|
-
]
|
|
3567
|
-
}),
|
|
3568
|
-
editing === "actions" && /* @__PURE__ */ jsx14(Select, {
|
|
3569
|
-
label: "After a successful save",
|
|
3570
|
-
value: config.success,
|
|
3571
|
-
options: [
|
|
3572
|
-
{ value: "close", label: "Close the popup" },
|
|
3573
|
-
{ value: "keep", label: "Keep the popup open" }
|
|
3574
|
-
],
|
|
3575
|
-
onChange: (value) => change((old) => ({ ...old, success: value === "keep" ? "keep" : "close" }))
|
|
3576
|
-
}),
|
|
3577
|
-
selectedField && /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3578
|
-
children: [
|
|
3579
|
-
selectedField.name === "template" && /* @__PURE__ */ jsx14(Checkbox, {
|
|
3580
|
-
label: "Fill Subject and Description from the selected template",
|
|
3581
|
-
checked: config.templateFills,
|
|
3582
|
-
onChange: (value) => change((old) => ({ ...old, templateFills: value }))
|
|
3583
|
-
}),
|
|
3584
|
-
(selectedField.name === "subject" || selectedField.name === "notes") && /* @__PURE__ */ jsx14(Select, {
|
|
3585
|
-
label: "Gets its value from",
|
|
3586
|
-
value: selectedField.input,
|
|
3587
|
-
options: [
|
|
3588
|
-
{ value: "template", label: "User input or template" },
|
|
3589
|
-
{ value: "user", label: "User input only" }
|
|
3590
|
-
],
|
|
3591
|
-
onChange: (value) => updateField(selectedField.name, { input: value === "user" ? "user" : "template" })
|
|
3592
|
-
}),
|
|
3593
|
-
!selectedField.required && /* @__PURE__ */ jsx14(Select, {
|
|
3594
|
-
label: "Include in Binding",
|
|
3595
|
-
value: selectedField.excluded ? "exclude" : "include",
|
|
3596
|
-
options: [
|
|
3597
|
-
{ value: "include", label: "Keep this field" },
|
|
3598
|
-
{ value: "exclude", label: "Exclude and hide it" }
|
|
3599
|
-
],
|
|
3600
|
-
onChange: (value) => updateField(selectedField.name, { excluded: value === "exclude" })
|
|
3601
|
-
}),
|
|
3602
|
-
context.options[selectedField.name]?.length ? /* @__PURE__ */ jsxs11("p", {
|
|
3603
|
-
className: "binding-muted",
|
|
3604
|
-
children: [
|
|
3605
|
-
"Sample choices:",
|
|
3606
|
-
" ",
|
|
3607
|
-
context.options[selectedField.name].map((option) => `${option.label} (${option.value})`).join(" · ")
|
|
3608
|
-
]
|
|
3609
|
-
}) : null,
|
|
3610
|
-
fieldIssues(selectedField.name, selectedField.label).filter((i) => i.startsWith(selectedField.label)).map((i) => /* @__PURE__ */ jsx14("p", {
|
|
3611
|
-
className: "binding-error",
|
|
3612
|
-
children: i
|
|
3613
|
-
}, i))
|
|
3614
|
-
]
|
|
3615
|
-
}),
|
|
3616
|
-
editing !== "customers" && /* @__PURE__ */ jsxs11(Fragment6, {
|
|
3617
|
-
children: [
|
|
3618
|
-
/* @__PURE__ */ jsx14(ApiMappings, {
|
|
3619
|
-
config,
|
|
3620
|
-
context,
|
|
3621
|
-
focus,
|
|
3622
|
-
defaultTarget: editing === "actions" ? SUBMIT : focus,
|
|
3623
|
-
onChange: change
|
|
3624
|
-
}),
|
|
3625
|
-
/* @__PURE__ */ jsx14(RequestPreview, {
|
|
3626
|
-
title: "Request preview (current window values)",
|
|
3627
|
-
requests: previewRequests(config, context, liveCtx, focus ? (c) => c.component === focus || c.params.some((p) => p.source === focus) : undefined)
|
|
3628
|
-
})
|
|
3629
|
-
]
|
|
3630
|
-
}),
|
|
3631
|
-
/* @__PURE__ */ jsx14(ButtonBar, {
|
|
3632
|
-
children: /* @__PURE__ */ jsx14(Button, {
|
|
3633
|
-
icon: "Check",
|
|
3634
|
-
onClick: () => setEditing(null),
|
|
3635
|
-
children: "Done"
|
|
3636
|
-
})
|
|
3637
|
-
})
|
|
3638
|
-
]
|
|
3639
|
-
})
|
|
3640
|
-
]
|
|
3641
|
-
}),
|
|
3642
|
-
/* @__PURE__ */ jsxs11("footer", {
|
|
3643
|
-
className: "binding-footer",
|
|
3644
|
-
children: [
|
|
3645
|
-
/* @__PURE__ */ jsx14("p", {
|
|
3646
|
-
className: "binding-muted",
|
|
3647
|
-
role: "status",
|
|
3648
|
-
children: applied ? "API binding applied in this browser only. Current Notes behavior is unchanged." : canApply(config, context, verified) ? "Simulated save and retrieval passed. Ready to apply." : gaps.length ? gaps[0] : verified ? "Resolve the remaining connections before applying." : "Try a save and retrieve the sample note before applying."
|
|
3649
|
-
}),
|
|
3650
|
-
/* @__PURE__ */ jsxs11(ButtonBar, {
|
|
3651
|
-
children: [
|
|
3652
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3653
|
-
icon: "Play",
|
|
3654
|
-
onClick: () => setSimulation(true),
|
|
3655
|
-
children: "Try It"
|
|
3656
|
-
}),
|
|
3657
|
-
/* @__PURE__ */ jsx14(Button, {
|
|
3658
|
-
variant: "primary",
|
|
3659
|
-
icon: "Check",
|
|
3660
|
-
disabled: !canApply(config, context, verified) || !!storageError,
|
|
3661
|
-
onClick: () => {
|
|
3662
|
-
try {
|
|
3663
|
-
localStorage.setItem(appliedKey(pageKey), JSON.stringify({ version: CONFIG_VERSION, config, appliedAt: new Date().toISOString() }));
|
|
3664
|
-
notifyApplied(pageKey, config);
|
|
3665
|
-
setApplied(true);
|
|
3666
|
-
} catch {
|
|
3667
|
-
setStorageError("Could not apply: browser storage is unavailable.");
|
|
3668
|
-
}
|
|
3669
|
-
},
|
|
3670
|
-
children: "Apply Binding"
|
|
3671
|
-
})
|
|
3672
|
-
]
|
|
3673
|
-
})
|
|
3674
|
-
]
|
|
3675
|
-
}),
|
|
3676
|
-
simulation && /* @__PURE__ */ jsx14(SimulationDialog, {
|
|
3677
|
-
config,
|
|
3678
|
-
context,
|
|
3679
|
-
onClose: () => setSimulation(false),
|
|
3680
|
-
onVerified: setVerified
|
|
3681
|
-
}, fingerprint(config))
|
|
3682
|
-
]
|
|
3683
|
-
});
|
|
3684
|
-
}
|
|
3685
|
-
var ApiBindingStudio = ApiBindingWorkspace;
|
|
3686
|
-
export {
|
|
3687
|
-
ApiBindingStudio,
|
|
3688
|
-
ApiBindingWorkspace,
|
|
3689
|
-
ApiMappings,
|
|
3690
|
-
AppGrid,
|
|
3691
|
-
BINDING_APPLIED,
|
|
3692
|
-
BINDING_CHANNEL,
|
|
3693
|
-
BINDING_CONTEXT,
|
|
3694
|
-
BINDING_READY,
|
|
3695
|
-
BINDING_SELECTION_UPDATE,
|
|
3696
|
-
Badge,
|
|
3697
|
-
Banner,
|
|
3698
|
-
BindApiButton,
|
|
3699
|
-
Button,
|
|
3700
|
-
ButtonBar,
|
|
3701
|
-
ButtonGroup,
|
|
3702
|
-
CANCEL,
|
|
3703
|
-
CONFIG_VERSION,
|
|
3704
|
-
CUSTOMER_PARAM,
|
|
3705
|
-
Checkbox,
|
|
3706
|
-
DetailCard,
|
|
3707
|
-
Dialog,
|
|
3708
|
-
FREE_TEXT_OPTION,
|
|
3709
|
-
FREE_TEXT_SOURCE,
|
|
3710
|
-
exports_Icons as Icons,
|
|
3711
|
-
Input,
|
|
3712
|
-
RequestPreview,
|
|
3713
|
-
SUBMIT,
|
|
3714
|
-
Select,
|
|
3715
|
-
SimulationDialog,
|
|
3716
|
-
TextArea,
|
|
3717
|
-
WorkspaceComponentsProvider,
|
|
3718
|
-
actionContext,
|
|
3719
|
-
actionFormDialogTemplateV1,
|
|
3720
|
-
alertDialogTemplateV1,
|
|
3721
|
-
apisOf,
|
|
3722
|
-
appliedKey,
|
|
3723
|
-
baseType,
|
|
3724
|
-
buildCatalogFromRegistry,
|
|
3725
|
-
buildRequest,
|
|
3726
|
-
buildSnapshot,
|
|
3727
|
-
callIssues,
|
|
3728
|
-
callsFor,
|
|
3729
|
-
canApply,
|
|
3730
|
-
changeCallTarget,
|
|
3731
|
-
changePurpose,
|
|
3732
|
-
coerce,
|
|
3733
|
-
coerceScalar,
|
|
3734
|
-
componentId,
|
|
3735
|
-
componentLabel,
|
|
3736
|
-
contextComponents,
|
|
3737
|
-
contextKey,
|
|
3738
|
-
createActionColumn,
|
|
3739
|
-
definitionFor,
|
|
3740
|
-
discoverButtons,
|
|
3741
|
-
discoverFormComponents,
|
|
3742
|
-
draftKey,
|
|
3743
|
-
droppedBy,
|
|
3744
|
-
endpointLabel,
|
|
3745
|
-
fieldId,
|
|
3746
|
-
fieldSummary,
|
|
3747
|
-
fingerprint,
|
|
3748
|
-
friendlyLabel,
|
|
3749
|
-
gridPaths,
|
|
3750
|
-
gridPropertyPaths,
|
|
3751
|
-
isButton,
|
|
3752
|
-
isButtonId,
|
|
3753
|
-
isNoteContract,
|
|
3754
|
-
isSimulated,
|
|
3755
|
-
issuesFor,
|
|
3756
|
-
methodsFor,
|
|
3757
|
-
migrateConfig,
|
|
3758
|
-
moveCall,
|
|
3759
|
-
newCall,
|
|
3760
|
-
newCallId,
|
|
3761
|
-
normalizeComponent,
|
|
3762
|
-
notifyApplied,
|
|
3763
|
-
notifySelectionUpdate,
|
|
3764
|
-
openBindingStudio,
|
|
3765
|
-
parseComponentId,
|
|
3766
|
-
populated,
|
|
3767
|
-
previewRequests,
|
|
3768
|
-
proposeConfig,
|
|
3769
|
-
readBindingContext,
|
|
3770
|
-
readSnapshot,
|
|
3771
|
-
reconcileCallParams,
|
|
3772
|
-
reconcileConfig,
|
|
3773
|
-
resolutionIssues,
|
|
3774
|
-
resolveParam,
|
|
3775
|
-
setParamSource,
|
|
3776
|
-
simulateSave,
|
|
3777
|
-
simulationErrors,
|
|
3778
|
-
simulationForm,
|
|
3779
|
-
simulatorGaps,
|
|
3780
|
-
snapshotForm,
|
|
3781
|
-
sourceComponents,
|
|
3782
|
-
storageKey,
|
|
3783
|
-
storeBindingContext,
|
|
3784
|
-
storeSnapshot,
|
|
3785
|
-
useAppliedBinding,
|
|
3786
|
-
useWorkspaceComponents,
|
|
3787
|
-
validBindingContext,
|
|
3788
|
-
validContext,
|
|
3789
|
-
windowIds
|
|
3790
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
catalogFromOpenApi2,
|
|
3
|
+
localStorageAdapter2,
|
|
4
|
+
memoryAdapter2,
|
|
5
|
+
toManifest2,
|
|
6
|
+
createEngine2,
|
|
7
|
+
validateRegistry2,
|
|
8
|
+
validateBindings2,
|
|
9
|
+
useBindingRuntime,
|
|
10
|
+
BindingProvider2
|
|
11
|
+
} from "./index-e9twc25y.js";
|
|
12
|
+
// src/framework/expr.ts
|
|
13
|
+
var PIPE = /^([A-Za-z_][\w-]*)\((.*)\)$/;
|
|
14
|
+
var cache = new Map;
|
|
15
|
+
function parseArg(raw) {
|
|
16
|
+
const t = raw.trim();
|
|
17
|
+
if (!t)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(t);
|
|
21
|
+
} catch {
|
|
22
|
+
return t;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function segments(path) {
|
|
26
|
+
return path.replace(/\[(\*|\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
27
|
+
}
|
|
28
|
+
function parse(expr) {
|
|
29
|
+
const hit = cache.get(expr);
|
|
30
|
+
if (hit)
|
|
31
|
+
return hit;
|
|
32
|
+
const [main, ...rest] = expr.split("|");
|
|
33
|
+
const segs = segments(main.trim());
|
|
34
|
+
if (!segs.length)
|
|
35
|
+
throw new Error(`Empty expression: "${expr}"`);
|
|
36
|
+
const width = segs[0] === "$item" || segs[0].startsWith("$") && !["$context", "$steps"].includes(segs[0]) ? 1 : 2;
|
|
37
|
+
const pipes = rest.map((p) => {
|
|
38
|
+
const m = PIPE.exec(p.trim());
|
|
39
|
+
if (!m)
|
|
40
|
+
throw new Error(`Bad transform "${p.trim()}" in "${expr}"`);
|
|
41
|
+
return { name: m[1], args: m[2] ? m[2].split(",").map(parseArg) : [] };
|
|
42
|
+
});
|
|
43
|
+
const parsed = { head: segs.slice(0, width), path: segs.slice(width), pipes };
|
|
44
|
+
cache.set(expr, parsed);
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
function walk(value, path) {
|
|
48
|
+
let v = value;
|
|
49
|
+
for (let i = 0;i < path.length; i++) {
|
|
50
|
+
if (v == null)
|
|
51
|
+
return;
|
|
52
|
+
if (path[i] === "*") {
|
|
53
|
+
if (!Array.isArray(v))
|
|
54
|
+
return;
|
|
55
|
+
const rest = path.slice(i + 1);
|
|
56
|
+
return v.map((x) => walk(x, rest));
|
|
57
|
+
}
|
|
58
|
+
v = v[path[i]];
|
|
59
|
+
}
|
|
60
|
+
return v;
|
|
61
|
+
}
|
|
62
|
+
function evaluate(expr, scope) {
|
|
63
|
+
const { head, path, pipes } = parse(expr);
|
|
64
|
+
let root;
|
|
65
|
+
if (head[0] === "$item")
|
|
66
|
+
root = scope.item;
|
|
67
|
+
else if (head[0] === "$context")
|
|
68
|
+
root = scope.context?.[head[1]];
|
|
69
|
+
else if (head[0] === "$steps")
|
|
70
|
+
root = scope.steps?.[head[1]];
|
|
71
|
+
else if (head[0].startsWith("$"))
|
|
72
|
+
root = scope.providers?.[head[0].slice(1)];
|
|
73
|
+
else
|
|
74
|
+
root = scope.slot(head[0], head[1]);
|
|
75
|
+
let value = walk(root, path);
|
|
76
|
+
for (const p of pipes) {
|
|
77
|
+
const fn = scope.transforms?.[p.name];
|
|
78
|
+
if (!fn)
|
|
79
|
+
throw new Error(`Unknown transform "${p.name}" in "${expr}"`);
|
|
80
|
+
value = fn(value, ...p.args);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
// src/framework/runner.ts
|
|
85
|
+
class BindingError extends Error {
|
|
86
|
+
detail;
|
|
87
|
+
constructor(message, detail) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.detail = detail;
|
|
90
|
+
this.name = "BindingError";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function setPointer(target, keys, value) {
|
|
94
|
+
let node = target;
|
|
95
|
+
keys.slice(0, -1).forEach((k) => {
|
|
96
|
+
node = node[k] ??= {};
|
|
97
|
+
});
|
|
98
|
+
node[keys[keys.length - 1]] = value;
|
|
99
|
+
}
|
|
100
|
+
var decode = (k) => k.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
101
|
+
function buildRequest(op, request, scope) {
|
|
102
|
+
const out = { method: op.method, path: op.path, query: {}, headers: {} };
|
|
103
|
+
let body;
|
|
104
|
+
for (const [pointer, expr] of Object.entries(request)) {
|
|
105
|
+
const value = evaluate(expr, scope);
|
|
106
|
+
if (value === undefined)
|
|
107
|
+
continue;
|
|
108
|
+
const [, where, ...rest] = pointer.split("/");
|
|
109
|
+
const keys = rest.map(decode);
|
|
110
|
+
if (where === "path")
|
|
111
|
+
out.path = out.path.replace(`{${keys[0]}}`, encodeURIComponent(String(value)));
|
|
112
|
+
else if (where === "query")
|
|
113
|
+
out.query[keys[0]] = String(value);
|
|
114
|
+
else if (where === "header")
|
|
115
|
+
out.headers[keys[0]] = String(value);
|
|
116
|
+
else if (where === "body") {
|
|
117
|
+
if (!keys.length)
|
|
118
|
+
out.body = value;
|
|
119
|
+
else
|
|
120
|
+
setPointer(body ??= {}, keys, value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (body)
|
|
124
|
+
out.body = { ...out.body, ...body };
|
|
125
|
+
const missing = op.parameters.filter((p) => p.required && !((p.pointer in request) && evaluate(request[p.pointer], scope) !== undefined));
|
|
126
|
+
if (missing.length)
|
|
127
|
+
throw new BindingError(`Missing required input: ${missing.map((p) => p.name).join(", ")}.`);
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
var requestKey = (r) => JSON.stringify([r.method, r.path, r.query, r.body ?? null]);
|
|
131
|
+
function select(response, selector) {
|
|
132
|
+
const path = selector.replace(/^\$\.?/, "").replace(/\[\*\]$/, "").split(".").filter(Boolean);
|
|
133
|
+
return path.reduce((v, k) => v == null ? undefined : v[k], response);
|
|
134
|
+
}
|
|
135
|
+
function mapItems(data, binding, scope) {
|
|
136
|
+
if (!binding.map || !Object.keys(binding.map).length)
|
|
137
|
+
return data;
|
|
138
|
+
const one = (item) => Object.fromEntries(Object.entries(binding.map).map(([k, e]) => [k, evaluate(e, { ...scope, item })]));
|
|
139
|
+
return Array.isArray(data) ? data.map(one) : data == null ? data : one(data);
|
|
140
|
+
}
|
|
141
|
+
async function runConsume(binding, op, scope, fetcher) {
|
|
142
|
+
const response = await fetcher(buildRequest(op, binding.request, scope), op);
|
|
143
|
+
return mapItems(select(response, binding.select), binding, scope);
|
|
144
|
+
}
|
|
145
|
+
async function runSteps(steps, operations, scope, fetcher) {
|
|
146
|
+
const outcomes = [];
|
|
147
|
+
const results = { ...scope.steps };
|
|
148
|
+
for (const step of steps) {
|
|
149
|
+
const op = operations.get(step.operation);
|
|
150
|
+
if (!op)
|
|
151
|
+
throw new BindingError(`Operation "${step.operation}" is not in the catalog.`);
|
|
152
|
+
const items = step.forEach ? evaluate(step.forEach, scope) : [scope.item];
|
|
153
|
+
const list = Array.isArray(items) ? items : [items];
|
|
154
|
+
const responses = [];
|
|
155
|
+
let failed = false;
|
|
156
|
+
for (const item of list) {
|
|
157
|
+
try {
|
|
158
|
+
responses.push(await fetcher(buildRequest(op, step.request, { ...scope, item, steps: results }), op));
|
|
159
|
+
} catch (error) {
|
|
160
|
+
failed = true;
|
|
161
|
+
outcomes.push({ step: step.id, ok: false, error });
|
|
162
|
+
if (step.onError === "stop")
|
|
163
|
+
return outcomes;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const response = step.forEach ? responses : responses[0];
|
|
167
|
+
results[step.id] = { response };
|
|
168
|
+
if (!failed)
|
|
169
|
+
outcomes.push({ step: step.id, ok: true, response });
|
|
170
|
+
}
|
|
171
|
+
return outcomes;
|
|
172
|
+
}
|
|
173
|
+
// src/runtime/BindingScreen.tsx
|
|
174
|
+
import * as React from "react";
|
|
175
|
+
|
|
176
|
+
// src/runtime/screenStore.ts
|
|
177
|
+
function createScreenStore() {
|
|
178
|
+
const values = new Map;
|
|
179
|
+
const listeners = new Set;
|
|
180
|
+
return {
|
|
181
|
+
get: (b, s) => values.get(`${b}.${s}`),
|
|
182
|
+
set(b, s, value) {
|
|
183
|
+
const k = `${b}.${s}`;
|
|
184
|
+
if (Object.is(values.get(k), value))
|
|
185
|
+
return;
|
|
186
|
+
values.set(k, value);
|
|
187
|
+
listeners.forEach((l) => l());
|
|
188
|
+
},
|
|
189
|
+
subscribe(l) {
|
|
190
|
+
listeners.add(l);
|
|
191
|
+
return () => {
|
|
192
|
+
listeners.delete(l);
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/runtime/BindingScreen.tsx
|
|
199
|
+
import { jsx } from "react/jsx-runtime";
|
|
200
|
+
var ScreenContext = React.createContext(null);
|
|
201
|
+
var ShownContext = React.createContext(true);
|
|
202
|
+
function useScreen() {
|
|
203
|
+
const s = React.useContext(ScreenContext);
|
|
204
|
+
if (!s)
|
|
205
|
+
throw new Error("Use binding hooks inside <BindingScreen id=…>.");
|
|
206
|
+
return s;
|
|
207
|
+
}
|
|
208
|
+
function BindingActive({ value, children }) {
|
|
209
|
+
const parent = React.useContext(ShownContext);
|
|
210
|
+
return /* @__PURE__ */ jsx(ShownContext.Provider, {
|
|
211
|
+
value: parent && value,
|
|
212
|
+
children
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
var NO_CONTEXT = {};
|
|
216
|
+
function BindingScreen({ id, context = NO_CONTEXT, children }) {
|
|
217
|
+
const [store] = React.useState(createScreenStore);
|
|
218
|
+
const shown = React.useContext(ShownContext);
|
|
219
|
+
const { loadSaved, mountScreen } = useBindingRuntime();
|
|
220
|
+
React.useEffect(() => {
|
|
221
|
+
loadSaved(id);
|
|
222
|
+
}, [loadSaved, id]);
|
|
223
|
+
React.useEffect(() => mountScreen(id, shown), [mountScreen, id, shown]);
|
|
224
|
+
const scope = React.useMemo(() => ({ id, store, context }), [id, store, context]);
|
|
225
|
+
return /* @__PURE__ */ jsx(ScreenContext.Provider, {
|
|
226
|
+
value: scope,
|
|
227
|
+
children
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// src/runtime/hooks.ts
|
|
231
|
+
import * as React2 from "react";
|
|
232
|
+
function scopeOf(runtime, screen, item) {
|
|
233
|
+
return { slot: screen.store.get, context: screen.context, providers: runtime.providers(), transforms: runtime.transforms, item };
|
|
234
|
+
}
|
|
235
|
+
function useProduce(bindId, slot, value) {
|
|
236
|
+
const { store } = useScreen();
|
|
237
|
+
React2.useEffect(() => {
|
|
238
|
+
store.set(bindId, slot, value);
|
|
239
|
+
}, [store, bindId, slot, value]);
|
|
240
|
+
}
|
|
241
|
+
var WAIT = "\x00wait:";
|
|
242
|
+
function useSlot(bindId, slot) {
|
|
243
|
+
const runtime = useBindingRuntime();
|
|
244
|
+
const screen = useScreen();
|
|
245
|
+
const binding = runtime.binding(screen.id, bindId, slot);
|
|
246
|
+
const [nonce, setNonce] = React2.useState(0);
|
|
247
|
+
const [state, setState] = React2.useState({ key: "", status: "loading" });
|
|
248
|
+
const snapshot = React2.useCallback(() => {
|
|
249
|
+
if (!binding)
|
|
250
|
+
return "";
|
|
251
|
+
try {
|
|
252
|
+
const scope = scopeOf(runtime, screen);
|
|
253
|
+
if (binding.type === "expression")
|
|
254
|
+
return JSON.stringify(evaluate(binding.value, scope) ?? null);
|
|
255
|
+
if (binding.type !== "operation")
|
|
256
|
+
return "";
|
|
257
|
+
const op = runtime.operations.get(binding.operation);
|
|
258
|
+
return op ? requestKey(buildRequest(op, binding.request, scope)) : WAIT + "operation missing";
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return WAIT + (e instanceof Error ? e.message : String(e));
|
|
261
|
+
}
|
|
262
|
+
}, [binding, runtime, screen]);
|
|
263
|
+
const key = React2.useSyncExternalStore(screen.store.subscribe, snapshot, snapshot);
|
|
264
|
+
React2.useEffect(() => {
|
|
265
|
+
if (!binding || binding.type !== "operation" || !key || key.startsWith(WAIT))
|
|
266
|
+
return;
|
|
267
|
+
const op = runtime.operations.get(binding.operation);
|
|
268
|
+
const scope = scopeOf(runtime, screen);
|
|
269
|
+
const request = buildRequest(op, binding.request, scope);
|
|
270
|
+
const k = requestKey(request);
|
|
271
|
+
let live = true;
|
|
272
|
+
setState((s) => ({ key, status: "loading", data: s.data }));
|
|
273
|
+
let p = nonce ? undefined : runtime.inflight.get(k);
|
|
274
|
+
if (!p) {
|
|
275
|
+
p = runtime.fetcher(request, op).finally(() => runtime.inflight.delete(k));
|
|
276
|
+
runtime.inflight.set(k, p);
|
|
277
|
+
}
|
|
278
|
+
p.then((res) => {
|
|
279
|
+
if (live)
|
|
280
|
+
setState({ key, status: "ready", data: mapItems(select(res, binding.select), binding, scope) });
|
|
281
|
+
}, (error) => {
|
|
282
|
+
if (live)
|
|
283
|
+
setState({ key, status: "error", error });
|
|
284
|
+
});
|
|
285
|
+
return () => {
|
|
286
|
+
live = false;
|
|
287
|
+
};
|
|
288
|
+
}, [binding, key, nonce, runtime, screen]);
|
|
289
|
+
const reload = React2.useCallback(() => setNonce((n) => n + 1), []);
|
|
290
|
+
if (!binding || binding.type === "steps" || binding.type === "none")
|
|
291
|
+
return { status: "unbound", data: undefined, error: undefined, reload };
|
|
292
|
+
if (binding.type === "expression")
|
|
293
|
+
return { status: "ready", data: key ? JSON.parse(key) : undefined, error: undefined, reload };
|
|
294
|
+
if (key.startsWith(WAIT))
|
|
295
|
+
return { status: "waiting", data: undefined, error: new BindingError(key.slice(WAIT.length)), reload };
|
|
296
|
+
return { status: state.key === key ? state.status : "loading", data: state.data, error: state.error, reload };
|
|
297
|
+
}
|
|
298
|
+
function useCell(bindId, slot) {
|
|
299
|
+
const runtime = useBindingRuntime();
|
|
300
|
+
const screen = useScreen();
|
|
301
|
+
const binding = runtime.binding(screen.id, bindId, slot);
|
|
302
|
+
return React2.useCallback((item) => binding?.type === "expression" ? evaluate(binding.value, scopeOf(runtime, screen, item)) : undefined, [binding, runtime, screen]);
|
|
303
|
+
}
|
|
304
|
+
function useTrigger(bindId, slot) {
|
|
305
|
+
const runtime = useBindingRuntime();
|
|
306
|
+
const screen = useScreen();
|
|
307
|
+
const binding = runtime.binding(screen.id, bindId, slot);
|
|
308
|
+
const [running, setRunning] = React2.useState(false);
|
|
309
|
+
const run = React2.useCallback(async (item) => {
|
|
310
|
+
if (binding?.type !== "steps")
|
|
311
|
+
return [];
|
|
312
|
+
setRunning(true);
|
|
313
|
+
try {
|
|
314
|
+
return await runSteps(binding.steps, runtime.operations, scopeOf(runtime, screen, item), runtime.fetcher);
|
|
315
|
+
} finally {
|
|
316
|
+
setRunning(false);
|
|
317
|
+
}
|
|
318
|
+
}, [binding, runtime, screen]);
|
|
319
|
+
return { bound: binding?.type === "steps" || binding?.type === "none", running, run };
|
|
320
|
+
}
|
|
321
|
+
export {
|
|
322
|
+
BindingActive,
|
|
323
|
+
BindingError,
|
|
324
|
+
BindingProvider2 as BindingProvider,
|
|
325
|
+
BindingScreen,
|
|
326
|
+
buildRequest,
|
|
327
|
+
catalogFromOpenApi2 as catalogFromOpenApi,
|
|
328
|
+
createEngine2 as createEngine,
|
|
329
|
+
evaluate,
|
|
330
|
+
localStorageAdapter2 as localStorageAdapter,
|
|
331
|
+
memoryAdapter2 as memoryAdapter,
|
|
332
|
+
runConsume,
|
|
333
|
+
runSteps,
|
|
334
|
+
select,
|
|
335
|
+
toManifest2 as toManifest,
|
|
336
|
+
useCell,
|
|
337
|
+
useProduce,
|
|
338
|
+
useSlot,
|
|
339
|
+
useTrigger,
|
|
340
|
+
validateBindings2 as validateBindings,
|
|
341
|
+
validateRegistry2 as validateRegistry
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
//# debugId=0D89B09671A8D81B64756E2164756E21
|
|
345
|
+
//# sourceMappingURL=index.js.map
|