@almadar/std 3.0.4 → 3.0.5
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.
|
@@ -302,6 +302,8 @@ interface StdModalParams {
|
|
|
302
302
|
/** If provided, adds a save transition: open → closed with these effects */
|
|
303
303
|
saveEvent?: string;
|
|
304
304
|
saveEffects?: unknown[];
|
|
305
|
+
/** Event to emit after save succeeds. Browse traits listen for this instead of raw SAVE. */
|
|
306
|
+
emitOnSave?: string;
|
|
305
307
|
/** When false, INIT renders nothing to main (used inside molecules). Default true. */
|
|
306
308
|
standalone?: boolean;
|
|
307
309
|
pageName?: string;
|
|
@@ -342,6 +344,8 @@ interface StdConfirmationParams {
|
|
|
342
344
|
confirmEvent?: string;
|
|
343
345
|
/** Additional effects to run on confirm (e.g., persist delete) */
|
|
344
346
|
confirmEffects?: unknown[];
|
|
347
|
+
/** Event to emit after confirm succeeds. Browse traits listen for this. */
|
|
348
|
+
emitOnConfirm?: string;
|
|
345
349
|
/** When false, INIT renders nothing to main (used inside molecules). Default true. */
|
|
346
350
|
standalone?: boolean;
|
|
347
351
|
pageName?: string;
|
|
@@ -102,10 +102,12 @@ function buildTrait(c) {
|
|
|
102
102
|
return needsId ? { key: e, name: e, payload: [{ name: "id", type: "string", required: true }] } : { key: e, name: e };
|
|
103
103
|
})
|
|
104
104
|
];
|
|
105
|
+
const listensDecl = c.refreshEvents.length > 0 ? c.refreshEvents.map((evt) => ({ event: evt, triggers: evt })) : void 0;
|
|
105
106
|
return {
|
|
106
107
|
name: c.traitName,
|
|
107
108
|
linkedEntity: entityName,
|
|
108
109
|
category: "interaction",
|
|
110
|
+
...listensDecl ? { listens: listensDecl } : {},
|
|
109
111
|
stateMachine: {
|
|
110
112
|
states: [{ name: "browsing", isInitial: true }],
|
|
111
113
|
events,
|
|
@@ -220,6 +222,7 @@ function resolve2(params) {
|
|
|
220
222
|
openEffects: params.openEffects ?? [],
|
|
221
223
|
saveEvent: params.saveEvent ?? null,
|
|
222
224
|
saveEffects: params.saveEffects ?? [],
|
|
225
|
+
emitOnSave: params.emitOnSave ?? null,
|
|
223
226
|
standalone: params.standalone ?? true,
|
|
224
227
|
pageName: params.pageName ?? `${entityName}ModalPage`,
|
|
225
228
|
pagePath: params.pagePath ?? `/${p.toLowerCase()}/modal`,
|
|
@@ -274,13 +277,19 @@ function buildTrait2(c) {
|
|
|
274
277
|
from: "open",
|
|
275
278
|
to: "closed",
|
|
276
279
|
event: c.saveEvent,
|
|
277
|
-
effects: [
|
|
280
|
+
effects: [
|
|
281
|
+
...c.saveEffects,
|
|
282
|
+
["render-ui", "modal", null],
|
|
283
|
+
// Emit after persist succeeds so browse traits can fetch fresh data
|
|
284
|
+
...c.emitOnSave ? [["emit", c.emitOnSave]] : []
|
|
285
|
+
]
|
|
278
286
|
});
|
|
279
287
|
}
|
|
280
288
|
return {
|
|
281
289
|
name: c.traitName,
|
|
282
290
|
linkedEntity: c.entityName,
|
|
283
291
|
category: "interaction",
|
|
292
|
+
...c.emitOnSave ? { emits: [{ event: c.emitOnSave }] } : {},
|
|
284
293
|
stateMachine: {
|
|
285
294
|
states: [{ name: "closed", isInitial: true }, { name: "open" }],
|
|
286
295
|
events,
|
|
@@ -304,176 +313,9 @@ function stdModal(params) {
|
|
|
304
313
|
const c = resolve2(params);
|
|
305
314
|
return makeOrbital(`${c.entityName}Orbital`, buildEntity2(c), [buildTrait2(c)], [buildPage2(c)]);
|
|
306
315
|
}
|
|
307
|
-
function resolve3(params) {
|
|
308
|
-
const { entityName } = params;
|
|
309
|
-
const fields = ensureIdField(params.fields);
|
|
310
|
-
const nonIdFields = fields.filter((f) => f.name !== "id");
|
|
311
|
-
const p = plural(entityName);
|
|
312
|
-
return {
|
|
313
|
-
entityName,
|
|
314
|
-
fields,
|
|
315
|
-
displayField: nonIdFields[0]?.name ?? "id",
|
|
316
|
-
persistence: params.persistence ?? "runtime",
|
|
317
|
-
traitName: params.traitName ?? `${entityName}Confirmation`,
|
|
318
|
-
pluralName: p,
|
|
319
|
-
confirmTitle: params.confirmTitle ?? "Confirm Action",
|
|
320
|
-
confirmMessage: params.confirmMessage ?? "Are you sure?",
|
|
321
|
-
confirmLabel: params.confirmLabel ?? "Confirm",
|
|
322
|
-
cancelLabel: params.cancelLabel ?? "Cancel",
|
|
323
|
-
headerIcon: params.headerIcon ?? "alert-triangle",
|
|
324
|
-
requestEvent: params.requestEvent ?? "REQUEST",
|
|
325
|
-
confirmEvent: params.confirmEvent ?? "CONFIRM",
|
|
326
|
-
confirmEffects: params.confirmEffects ?? [],
|
|
327
|
-
standalone: params.standalone ?? true,
|
|
328
|
-
pageName: params.pageName ?? `${entityName}ConfirmPage`,
|
|
329
|
-
pagePath: params.pagePath ?? `/${p.toLowerCase()}/confirm`,
|
|
330
|
-
isInitial: params.isInitial ?? false
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
function buildEntity3(c) {
|
|
334
|
-
return makeEntity({ name: c.entityName, fields: c.fields, persistence: c.persistence });
|
|
335
|
-
}
|
|
336
|
-
function buildTrait3(c) {
|
|
337
|
-
const { entityName, displayField, pluralName, confirmTitle, confirmMessage, confirmLabel, cancelLabel, headerIcon } = c;
|
|
338
|
-
return {
|
|
339
|
-
name: c.traitName,
|
|
340
|
-
linkedEntity: entityName,
|
|
341
|
-
category: "interaction",
|
|
342
|
-
stateMachine: {
|
|
343
|
-
states: [
|
|
344
|
-
{ name: "idle", isInitial: true },
|
|
345
|
-
{ name: "confirming" }
|
|
346
|
-
],
|
|
347
|
-
events: [
|
|
348
|
-
{ key: "INIT", name: "Initialize" },
|
|
349
|
-
{ key: c.requestEvent, name: "Request Confirmation", payload: [{ name: "id", type: "string", required: true }] },
|
|
350
|
-
{ key: c.confirmEvent, name: "Confirm", payload: [{ name: "id", type: "string", required: true }] },
|
|
351
|
-
{ key: "CANCEL", name: "Cancel" },
|
|
352
|
-
{ key: "CLOSE", name: "Close" }
|
|
353
|
-
],
|
|
354
|
-
transitions: [
|
|
355
|
-
// INIT: idle -> idle
|
|
356
|
-
{
|
|
357
|
-
from: "idle",
|
|
358
|
-
to: "idle",
|
|
359
|
-
event: "INIT",
|
|
360
|
-
effects: c.standalone ? [["fetch", entityName], ["render-ui", "main", {
|
|
361
|
-
type: "stack",
|
|
362
|
-
direction: "vertical",
|
|
363
|
-
gap: "lg",
|
|
364
|
-
children: [
|
|
365
|
-
{ type: "stack", direction: "horizontal", gap: "md", align: "center", children: [
|
|
366
|
-
{ type: "icon", name: headerIcon, size: "lg" },
|
|
367
|
-
{ type: "typography", content: pluralName, variant: "h2" }
|
|
368
|
-
] },
|
|
369
|
-
{ type: "divider" },
|
|
370
|
-
{
|
|
371
|
-
type: "data-grid",
|
|
372
|
-
entity: entityName,
|
|
373
|
-
emptyIcon: "inbox",
|
|
374
|
-
emptyTitle: `No ${pluralName.toLowerCase()} yet`,
|
|
375
|
-
emptyDescription: "Items will appear here.",
|
|
376
|
-
itemActions: [{ label: confirmTitle, event: c.requestEvent, variant: "danger" }],
|
|
377
|
-
children: [{ type: "stack", direction: "vertical", gap: "sm", children: [
|
|
378
|
-
{ type: "typography", variant: "h4", content: `@entity.${displayField}` }
|
|
379
|
-
] }]
|
|
380
|
-
}
|
|
381
|
-
]
|
|
382
|
-
}]] : [["fetch", entityName]]
|
|
383
|
-
},
|
|
384
|
-
// REQUEST: idle -> confirming
|
|
385
|
-
{
|
|
386
|
-
from: "idle",
|
|
387
|
-
to: "confirming",
|
|
388
|
-
event: c.requestEvent,
|
|
389
|
-
effects: [
|
|
390
|
-
["render-ui", "modal", {
|
|
391
|
-
type: "stack",
|
|
392
|
-
direction: "vertical",
|
|
393
|
-
gap: "md",
|
|
394
|
-
children: [
|
|
395
|
-
{
|
|
396
|
-
type: "stack",
|
|
397
|
-
direction: "horizontal",
|
|
398
|
-
gap: "sm",
|
|
399
|
-
align: "center",
|
|
400
|
-
children: [
|
|
401
|
-
{ type: "icon", name: headerIcon, size: "md" },
|
|
402
|
-
{ type: "typography", content: confirmTitle, variant: "h3" }
|
|
403
|
-
]
|
|
404
|
-
},
|
|
405
|
-
{ type: "divider" },
|
|
406
|
-
{ type: "typography", content: confirmMessage, variant: "body" },
|
|
407
|
-
{
|
|
408
|
-
type: "stack",
|
|
409
|
-
direction: "horizontal",
|
|
410
|
-
gap: "sm",
|
|
411
|
-
justify: "end",
|
|
412
|
-
children: [
|
|
413
|
-
{ type: "button", label: cancelLabel, event: "CANCEL", variant: "ghost" },
|
|
414
|
-
{ type: "button", label: confirmLabel, event: c.confirmEvent, variant: "danger", icon: "check" }
|
|
415
|
-
]
|
|
416
|
-
}
|
|
417
|
-
]
|
|
418
|
-
}]
|
|
419
|
-
]
|
|
420
|
-
},
|
|
421
|
-
// CONFIRM: confirming -> idle (run injected effects, dismiss modal)
|
|
422
|
-
{
|
|
423
|
-
from: "confirming",
|
|
424
|
-
to: "idle",
|
|
425
|
-
event: c.confirmEvent,
|
|
426
|
-
effects: [
|
|
427
|
-
...c.confirmEffects,
|
|
428
|
-
["render-ui", "modal", null]
|
|
429
|
-
]
|
|
430
|
-
},
|
|
431
|
-
// CANCEL: confirming -> idle (dismiss modal)
|
|
432
|
-
{
|
|
433
|
-
from: "confirming",
|
|
434
|
-
to: "idle",
|
|
435
|
-
event: "CANCEL",
|
|
436
|
-
effects: [
|
|
437
|
-
["render-ui", "modal", null]
|
|
438
|
-
]
|
|
439
|
-
},
|
|
440
|
-
// CLOSE: confirming -> idle (dismiss modal)
|
|
441
|
-
{
|
|
442
|
-
from: "confirming",
|
|
443
|
-
to: "idle",
|
|
444
|
-
event: "CLOSE",
|
|
445
|
-
effects: [
|
|
446
|
-
["render-ui", "modal", null]
|
|
447
|
-
]
|
|
448
|
-
}
|
|
449
|
-
]
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
function buildPage3(c) {
|
|
454
|
-
return makePage({ name: c.pageName, path: c.pagePath, traitName: c.traitName, isInitial: c.isInitial });
|
|
455
|
-
}
|
|
456
|
-
function stdConfirmationEntity(params) {
|
|
457
|
-
return buildEntity3(resolve3(params));
|
|
458
|
-
}
|
|
459
|
-
function stdConfirmationTrait(params) {
|
|
460
|
-
return buildTrait3(resolve3(params));
|
|
461
|
-
}
|
|
462
|
-
function stdConfirmationPage(params) {
|
|
463
|
-
return buildPage3(resolve3(params));
|
|
464
|
-
}
|
|
465
|
-
function stdConfirmation(params) {
|
|
466
|
-
const c = resolve3(params);
|
|
467
|
-
return makeOrbital(
|
|
468
|
-
`${c.entityName}Orbital`,
|
|
469
|
-
buildEntity3(c),
|
|
470
|
-
[buildTrait3(c)],
|
|
471
|
-
[buildPage3(c)]
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
316
|
|
|
475
317
|
// behaviors/functions/std-list.ts
|
|
476
|
-
function
|
|
318
|
+
function resolve3(params) {
|
|
477
319
|
const { entityName } = params;
|
|
478
320
|
const fields = ensureIdField(params.fields);
|
|
479
321
|
const nonIdFields = fields.filter((f) => f.name !== "id");
|
|
@@ -544,14 +386,14 @@ function detailContent(detailFields, closeEvent) {
|
|
|
544
386
|
};
|
|
545
387
|
}
|
|
546
388
|
function stdListEntity(params) {
|
|
547
|
-
const c =
|
|
389
|
+
const c = resolve3(params);
|
|
548
390
|
return makeEntity({ name: c.entityName, fields: c.fields, persistence: c.persistence, collection: c.collection });
|
|
549
391
|
}
|
|
550
392
|
function stdListTrait(params) {
|
|
551
393
|
return extractTrait(stdList(params));
|
|
552
394
|
}
|
|
553
395
|
function stdListPage(params) {
|
|
554
|
-
const c =
|
|
396
|
+
const c = resolve3(params);
|
|
555
397
|
return {
|
|
556
398
|
name: c.pageName,
|
|
557
399
|
path: c.pagePath,
|
|
@@ -566,7 +408,7 @@ function stdListPage(params) {
|
|
|
566
408
|
};
|
|
567
409
|
}
|
|
568
410
|
function stdList(params) {
|
|
569
|
-
const c =
|
|
411
|
+
const c = resolve3(params);
|
|
570
412
|
const { entityName, fields, formFields, detailFields } = c;
|
|
571
413
|
const browseTrait = extractTrait(stdBrowse({
|
|
572
414
|
entityName,
|
|
@@ -583,7 +425,7 @@ function stdList(params) {
|
|
|
583
425
|
{ label: "Edit", event: "EDIT" },
|
|
584
426
|
{ label: "Delete", event: "DELETE", variant: "danger" }
|
|
585
427
|
],
|
|
586
|
-
refreshEvents: ["
|
|
428
|
+
refreshEvents: ["ENTITY_SAVED"]
|
|
587
429
|
}));
|
|
588
430
|
const createTrait = extractTrait(stdModal({
|
|
589
431
|
standalone: false,
|
|
@@ -597,7 +439,8 @@ function stdList(params) {
|
|
|
597
439
|
closeEvent: "CLOSE",
|
|
598
440
|
openEffects: [["fetch", entityName]],
|
|
599
441
|
saveEvent: "SAVE",
|
|
600
|
-
saveEffects: [["persist", "create", entityName, "@payload.data"], ["fetch", entityName]]
|
|
442
|
+
saveEffects: [["persist", "create", entityName, "@payload.data"], ["fetch", entityName]],
|
|
443
|
+
emitOnSave: "ENTITY_SAVED"
|
|
601
444
|
}));
|
|
602
445
|
const editTrait = extractTrait(stdModal({
|
|
603
446
|
standalone: false,
|
|
@@ -612,7 +455,8 @@ function stdList(params) {
|
|
|
612
455
|
closeEvent: "CLOSE",
|
|
613
456
|
openEffects: [["fetch", entityName, "@payload.id"]],
|
|
614
457
|
saveEvent: "SAVE",
|
|
615
|
-
saveEffects: [["persist", "update", entityName, "@payload.data"], ["fetch", entityName]]
|
|
458
|
+
saveEffects: [["persist", "update", entityName, "@payload.data"], ["fetch", entityName]],
|
|
459
|
+
emitOnSave: "ENTITY_SAVED"
|
|
616
460
|
}));
|
|
617
461
|
const viewTrait = extractTrait(stdModal({
|
|
618
462
|
standalone: false,
|
|
@@ -627,19 +471,44 @@ function stdList(params) {
|
|
|
627
471
|
closeEvent: "CLOSE",
|
|
628
472
|
openEffects: [["fetch", entityName, "@payload.id"]]
|
|
629
473
|
}));
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
474
|
+
const sm = browseTrait.stateMachine;
|
|
475
|
+
sm.states.push({ name: "deleting" });
|
|
476
|
+
const existingKeys = new Set(sm.events.map((e) => e.key));
|
|
477
|
+
if (!existingKeys.has("CONFIRM_DELETE")) sm.events.push({ key: "CONFIRM_DELETE", name: "Confirm Delete" });
|
|
478
|
+
if (!existingKeys.has("CANCEL")) sm.events.push({ key: "CANCEL", name: "Cancel" });
|
|
479
|
+
if (!existingKeys.has("CLOSE")) sm.events.push({ key: "CLOSE", name: "Close" });
|
|
480
|
+
sm.transitions.push(
|
|
481
|
+
// DELETE: browsing → deleting (fetch entity by ID, show confirmation modal)
|
|
482
|
+
{ from: "browsing", to: "deleting", event: "DELETE", effects: [
|
|
483
|
+
["fetch", entityName, "@payload.id"],
|
|
484
|
+
["render-ui", "modal", {
|
|
485
|
+
type: "stack",
|
|
486
|
+
direction: "vertical",
|
|
487
|
+
gap: "md",
|
|
488
|
+
children: [
|
|
489
|
+
{ type: "stack", direction: "horizontal", gap: "sm", children: [
|
|
490
|
+
{ type: "icon", name: "trash-2", size: "md" },
|
|
491
|
+
{ type: "typography", content: `Delete ${entityName}`, variant: "h3" }
|
|
492
|
+
] },
|
|
493
|
+
{ type: "divider" },
|
|
494
|
+
{ type: "typography", content: c.deleteMessage, variant: "body" },
|
|
495
|
+
{ type: "stack", direction: "horizontal", gap: "sm", justify: "end", children: [
|
|
496
|
+
{ type: "button", label: "Cancel", event: "CANCEL", variant: "ghost" },
|
|
497
|
+
{ type: "button", label: "Delete", event: "CONFIRM_DELETE", variant: "danger", icon: "trash" }
|
|
498
|
+
] }
|
|
499
|
+
]
|
|
500
|
+
}]
|
|
501
|
+
] },
|
|
502
|
+
// CONFIRM_DELETE: deleting → browsing (persist delete using selected entity's ID)
|
|
503
|
+
{ from: "deleting", to: "browsing", event: "CONFIRM_DELETE", effects: [
|
|
504
|
+
["persist", "delete", entityName, "@entity.id"],
|
|
505
|
+
["render-ui", "modal", null],
|
|
506
|
+
["fetch", entityName]
|
|
507
|
+
] },
|
|
508
|
+
// CANCEL/CLOSE from deleting
|
|
509
|
+
{ from: "deleting", to: "browsing", event: "CANCEL", effects: [["render-ui", "modal", null]] },
|
|
510
|
+
{ from: "deleting", to: "browsing", event: "CLOSE", effects: [["render-ui", "modal", null]] }
|
|
511
|
+
);
|
|
643
512
|
const entity = makeEntity({ name: entityName, fields, persistence: c.persistence, collection: c.collection });
|
|
644
513
|
const page = {
|
|
645
514
|
name: c.pageName,
|
|
@@ -649,18 +518,17 @@ function stdList(params) {
|
|
|
649
518
|
{ ref: browseTrait.name },
|
|
650
519
|
{ ref: createTrait.name },
|
|
651
520
|
{ ref: editTrait.name },
|
|
652
|
-
{ ref: viewTrait.name }
|
|
653
|
-
{ ref: deleteTrait.name }
|
|
521
|
+
{ ref: viewTrait.name }
|
|
654
522
|
]
|
|
655
523
|
};
|
|
656
524
|
return {
|
|
657
525
|
name: `${entityName}Orbital`,
|
|
658
526
|
entity,
|
|
659
|
-
traits: [browseTrait, createTrait, editTrait, viewTrait
|
|
527
|
+
traits: [browseTrait, createTrait, editTrait, viewTrait],
|
|
660
528
|
pages: [page]
|
|
661
529
|
};
|
|
662
530
|
}
|
|
663
|
-
function
|
|
531
|
+
function resolve4(params) {
|
|
664
532
|
const { entityName } = params;
|
|
665
533
|
const fields = ensureIdField(params.fields);
|
|
666
534
|
const nonIdFields = fields.filter((f) => f.name !== "id");
|
|
@@ -794,14 +662,14 @@ function buildCartTrait(c) {
|
|
|
794
662
|
};
|
|
795
663
|
}
|
|
796
664
|
function stdCartEntity(params) {
|
|
797
|
-
const c =
|
|
665
|
+
const c = resolve4(params);
|
|
798
666
|
return makeEntity({ name: c.entityName, fields: c.fields, persistence: c.persistence, collection: c.collection });
|
|
799
667
|
}
|
|
800
668
|
function stdCartTrait(params) {
|
|
801
|
-
return buildCartTrait(
|
|
669
|
+
return buildCartTrait(resolve4(params));
|
|
802
670
|
}
|
|
803
671
|
function stdCartPage(params) {
|
|
804
|
-
const c =
|
|
672
|
+
const c = resolve4(params);
|
|
805
673
|
return {
|
|
806
674
|
name: c.pageName,
|
|
807
675
|
path: c.pagePath,
|
|
@@ -810,7 +678,7 @@ function stdCartPage(params) {
|
|
|
810
678
|
};
|
|
811
679
|
}
|
|
812
680
|
function stdCart(params) {
|
|
813
|
-
const c =
|
|
681
|
+
const c = resolve4(params);
|
|
814
682
|
const { entityName, fields, formFields } = c;
|
|
815
683
|
const cartTrait = buildCartTrait(c);
|
|
816
684
|
const addTrait = extractTrait(stdModal({
|
|
@@ -853,7 +721,7 @@ function stdCart(params) {
|
|
|
853
721
|
pages: [page]
|
|
854
722
|
};
|
|
855
723
|
}
|
|
856
|
-
function
|
|
724
|
+
function resolve5(params) {
|
|
857
725
|
const { entityName } = params;
|
|
858
726
|
const fields = ensureIdField(params.fields);
|
|
859
727
|
const nonIdFields = fields.filter((f) => f.name !== "id");
|
|
@@ -879,14 +747,14 @@ function resolve6(params) {
|
|
|
879
747
|
};
|
|
880
748
|
}
|
|
881
749
|
function stdDetailEntity(params) {
|
|
882
|
-
const c =
|
|
750
|
+
const c = resolve5(params);
|
|
883
751
|
return makeEntity({ name: c.entityName, fields: c.fields, persistence: c.persistence, collection: c.collection });
|
|
884
752
|
}
|
|
885
753
|
function stdDetailTrait(params) {
|
|
886
754
|
return extractTrait(stdDetail(params));
|
|
887
755
|
}
|
|
888
756
|
function stdDetailPage(params) {
|
|
889
|
-
const c =
|
|
757
|
+
const c = resolve5(params);
|
|
890
758
|
return {
|
|
891
759
|
name: c.pageName,
|
|
892
760
|
path: c.pagePath,
|
|
@@ -895,7 +763,7 @@ function stdDetailPage(params) {
|
|
|
895
763
|
};
|
|
896
764
|
}
|
|
897
765
|
function stdDetail(params) {
|
|
898
|
-
const c =
|
|
766
|
+
const c = resolve5(params);
|
|
899
767
|
const { entityName, fields, formFields, detailFields } = c;
|
|
900
768
|
const browseTrait = extractTrait(stdBrowse({
|
|
901
769
|
entityName,
|
|
@@ -987,6 +855,184 @@ function stdDetail(params) {
|
|
|
987
855
|
pages: [page]
|
|
988
856
|
};
|
|
989
857
|
}
|
|
858
|
+
function resolve6(params) {
|
|
859
|
+
const { entityName } = params;
|
|
860
|
+
const fields = ensureIdField(params.fields);
|
|
861
|
+
const nonIdFields = fields.filter((f) => f.name !== "id");
|
|
862
|
+
const p = plural(entityName);
|
|
863
|
+
return {
|
|
864
|
+
entityName,
|
|
865
|
+
fields,
|
|
866
|
+
displayField: nonIdFields[0]?.name ?? "id",
|
|
867
|
+
persistence: params.persistence ?? "runtime",
|
|
868
|
+
traitName: params.traitName ?? `${entityName}Confirmation`,
|
|
869
|
+
pluralName: p,
|
|
870
|
+
confirmTitle: params.confirmTitle ?? "Confirm Action",
|
|
871
|
+
confirmMessage: params.confirmMessage ?? "Are you sure?",
|
|
872
|
+
confirmLabel: params.confirmLabel ?? "Confirm",
|
|
873
|
+
cancelLabel: params.cancelLabel ?? "Cancel",
|
|
874
|
+
headerIcon: params.headerIcon ?? "alert-triangle",
|
|
875
|
+
requestEvent: params.requestEvent ?? "REQUEST",
|
|
876
|
+
confirmEvent: params.confirmEvent ?? "CONFIRM",
|
|
877
|
+
confirmEffects: params.confirmEffects ?? [],
|
|
878
|
+
emitOnConfirm: params.emitOnConfirm ?? null,
|
|
879
|
+
standalone: params.standalone ?? true,
|
|
880
|
+
pageName: params.pageName ?? `${entityName}ConfirmPage`,
|
|
881
|
+
pagePath: params.pagePath ?? `/${p.toLowerCase()}/confirm`,
|
|
882
|
+
isInitial: params.isInitial ?? false
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function buildEntity3(c) {
|
|
886
|
+
const fields = [
|
|
887
|
+
...c.fields.filter((f) => f.name !== "pendingId"),
|
|
888
|
+
{ name: "pendingId", type: "string", default: "" }
|
|
889
|
+
];
|
|
890
|
+
return makeEntity({ name: c.entityName, fields, persistence: c.persistence });
|
|
891
|
+
}
|
|
892
|
+
function buildTrait3(c) {
|
|
893
|
+
const { entityName, displayField, pluralName, confirmTitle, confirmMessage, confirmLabel, cancelLabel, headerIcon } = c;
|
|
894
|
+
return {
|
|
895
|
+
name: c.traitName,
|
|
896
|
+
linkedEntity: entityName,
|
|
897
|
+
category: "interaction",
|
|
898
|
+
...c.emitOnConfirm ? { emits: [{ event: c.emitOnConfirm }] } : {},
|
|
899
|
+
stateMachine: {
|
|
900
|
+
states: [
|
|
901
|
+
{ name: "idle", isInitial: true },
|
|
902
|
+
{ name: "confirming" }
|
|
903
|
+
],
|
|
904
|
+
events: [
|
|
905
|
+
{ key: "INIT", name: "Initialize" },
|
|
906
|
+
{ key: c.requestEvent, name: "Request Confirmation", payload: [{ name: "id", type: "string", required: true }] },
|
|
907
|
+
{ key: c.confirmEvent, name: "Confirm" },
|
|
908
|
+
{ key: "CANCEL", name: "Cancel" },
|
|
909
|
+
{ key: "CLOSE", name: "Close" }
|
|
910
|
+
],
|
|
911
|
+
transitions: [
|
|
912
|
+
// INIT: idle -> idle
|
|
913
|
+
{
|
|
914
|
+
from: "idle",
|
|
915
|
+
to: "idle",
|
|
916
|
+
event: "INIT",
|
|
917
|
+
effects: c.standalone ? [["fetch", entityName], ["render-ui", "main", {
|
|
918
|
+
type: "stack",
|
|
919
|
+
direction: "vertical",
|
|
920
|
+
gap: "lg",
|
|
921
|
+
children: [
|
|
922
|
+
{ type: "stack", direction: "horizontal", gap: "md", align: "center", children: [
|
|
923
|
+
{ type: "icon", name: headerIcon, size: "lg" },
|
|
924
|
+
{ type: "typography", content: pluralName, variant: "h2" }
|
|
925
|
+
] },
|
|
926
|
+
{ type: "divider" },
|
|
927
|
+
{
|
|
928
|
+
type: "data-grid",
|
|
929
|
+
entity: entityName,
|
|
930
|
+
emptyIcon: "inbox",
|
|
931
|
+
emptyTitle: `No ${pluralName.toLowerCase()} yet`,
|
|
932
|
+
emptyDescription: "Items will appear here.",
|
|
933
|
+
itemActions: [{ label: confirmTitle, event: c.requestEvent, variant: "danger" }],
|
|
934
|
+
children: [{ type: "stack", direction: "vertical", gap: "sm", children: [
|
|
935
|
+
{ type: "typography", variant: "h4", content: `@entity.${displayField}` }
|
|
936
|
+
] }]
|
|
937
|
+
}
|
|
938
|
+
]
|
|
939
|
+
}]] : [["fetch", entityName]]
|
|
940
|
+
},
|
|
941
|
+
// REQUEST: idle -> confirming (fetch entity by ID so server has context)
|
|
942
|
+
{
|
|
943
|
+
from: "idle",
|
|
944
|
+
to: "confirming",
|
|
945
|
+
event: c.requestEvent,
|
|
946
|
+
effects: [
|
|
947
|
+
["set", "@entity.pendingId", "@payload.id"],
|
|
948
|
+
["fetch", entityName, "@payload.id"],
|
|
949
|
+
["render-ui", "modal", {
|
|
950
|
+
type: "stack",
|
|
951
|
+
direction: "vertical",
|
|
952
|
+
gap: "md",
|
|
953
|
+
children: [
|
|
954
|
+
{
|
|
955
|
+
type: "stack",
|
|
956
|
+
direction: "horizontal",
|
|
957
|
+
gap: "sm",
|
|
958
|
+
align: "center",
|
|
959
|
+
children: [
|
|
960
|
+
{ type: "icon", name: headerIcon, size: "md" },
|
|
961
|
+
{ type: "typography", content: confirmTitle, variant: "h3" }
|
|
962
|
+
]
|
|
963
|
+
},
|
|
964
|
+
{ type: "divider" },
|
|
965
|
+
{ type: "typography", content: confirmMessage, variant: "body" },
|
|
966
|
+
{
|
|
967
|
+
type: "stack",
|
|
968
|
+
direction: "horizontal",
|
|
969
|
+
gap: "sm",
|
|
970
|
+
justify: "end",
|
|
971
|
+
children: [
|
|
972
|
+
{ type: "button", label: cancelLabel, event: "CANCEL", variant: "ghost" },
|
|
973
|
+
{ type: "button", label: confirmLabel, event: c.confirmEvent, variant: "danger", icon: "check" }
|
|
974
|
+
]
|
|
975
|
+
}
|
|
976
|
+
]
|
|
977
|
+
}]
|
|
978
|
+
]
|
|
979
|
+
},
|
|
980
|
+
// CONFIRM: confirming -> idle (run injected effects, dismiss modal)
|
|
981
|
+
{
|
|
982
|
+
from: "confirming",
|
|
983
|
+
to: "idle",
|
|
984
|
+
event: c.confirmEvent,
|
|
985
|
+
effects: [
|
|
986
|
+
...c.confirmEffects,
|
|
987
|
+
["render-ui", "modal", null],
|
|
988
|
+
...c.emitOnConfirm ? [["emit", c.emitOnConfirm]] : []
|
|
989
|
+
]
|
|
990
|
+
},
|
|
991
|
+
// CANCEL: confirming -> idle (dismiss modal)
|
|
992
|
+
{
|
|
993
|
+
from: "confirming",
|
|
994
|
+
to: "idle",
|
|
995
|
+
event: "CANCEL",
|
|
996
|
+
effects: [
|
|
997
|
+
["render-ui", "modal", null]
|
|
998
|
+
]
|
|
999
|
+
},
|
|
1000
|
+
// CLOSE: confirming -> idle (dismiss modal)
|
|
1001
|
+
{
|
|
1002
|
+
from: "confirming",
|
|
1003
|
+
to: "idle",
|
|
1004
|
+
event: "CLOSE",
|
|
1005
|
+
effects: [
|
|
1006
|
+
["render-ui", "modal", null]
|
|
1007
|
+
]
|
|
1008
|
+
}
|
|
1009
|
+
]
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
function buildPage3(c) {
|
|
1014
|
+
return makePage({ name: c.pageName, path: c.pagePath, traitName: c.traitName, isInitial: c.isInitial });
|
|
1015
|
+
}
|
|
1016
|
+
function stdConfirmationEntity(params) {
|
|
1017
|
+
return buildEntity3(resolve6(params));
|
|
1018
|
+
}
|
|
1019
|
+
function stdConfirmationTrait(params) {
|
|
1020
|
+
return buildTrait3(resolve6(params));
|
|
1021
|
+
}
|
|
1022
|
+
function stdConfirmationPage(params) {
|
|
1023
|
+
return buildPage3(resolve6(params));
|
|
1024
|
+
}
|
|
1025
|
+
function stdConfirmation(params) {
|
|
1026
|
+
const c = resolve6(params);
|
|
1027
|
+
return makeOrbital(
|
|
1028
|
+
`${c.entityName}Orbital`,
|
|
1029
|
+
buildEntity3(c),
|
|
1030
|
+
[buildTrait3(c)],
|
|
1031
|
+
[buildPage3(c)]
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// behaviors/functions/std-inventory.ts
|
|
990
1036
|
function resolve7(params) {
|
|
991
1037
|
const { entityName } = params;
|
|
992
1038
|
const fields = ensureIdField(params.fields);
|