nucleus-core-ts 0.10.129 → 0.10.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/.build-ok +1 -1
  2. package/dist/fe/components/VerificationFlowPage/components/PendingTab.js +12 -0
  3. package/dist/fe/components/VerificationFlowPage/components/VerificationEntityList.d.ts +14 -2
  4. package/dist/fe/components/VerificationFlowPage/components/VerificationEntityList.js +64 -1
  5. package/dist/fe/components/VerificationFlowPage/components/VerificationFlowPage.d.ts +1 -1
  6. package/dist/fe/components/VerificationFlowPage/components/VerificationFlowPage.js +254 -4
  7. package/dist/fe/components/VerificationFlowPage/index.d.ts +7 -0
  8. package/dist/fe/components/VerificationFlowPage/index.js +6 -0
  9. package/dist/fe/components/VerificationFlowPage/labels.d.ts +158 -0
  10. package/dist/fe/components/VerificationFlowPage/labels.js +134 -1
  11. package/dist/fe/components/VerificationFlowPage/model2/ActionRows.d.ts +37 -0
  12. package/dist/fe/components/VerificationFlowPage/model2/ActionRows.js +721 -0
  13. package/dist/fe/components/VerificationFlowPage/model2/AssigneeRows.d.ts +61 -0
  14. package/dist/fe/components/VerificationFlowPage/model2/AssigneeRows.js +324 -0
  15. package/dist/fe/components/VerificationFlowPage/model2/FlowDiagram.d.ts +13 -0
  16. package/dist/fe/components/VerificationFlowPage/model2/FlowDiagram.js +637 -0
  17. package/dist/fe/components/VerificationFlowPage/model2/OutcomeRows.d.ts +13 -0
  18. package/dist/fe/components/VerificationFlowPage/model2/OutcomeRows.js +261 -0
  19. package/dist/fe/components/VerificationFlowPage/model2/ReadinessCard.d.ts +30 -0
  20. package/dist/fe/components/VerificationFlowPage/model2/ReadinessCard.js +310 -0
  21. package/dist/fe/components/VerificationFlowPage/model2/StepCard.d.ts +37 -0
  22. package/dist/fe/components/VerificationFlowPage/model2/StepCard.js +273 -0
  23. package/dist/fe/components/VerificationFlowPage/model2/StepListEditor.d.ts +44 -0
  24. package/dist/fe/components/VerificationFlowPage/model2/StepListEditor.js +344 -0
  25. package/dist/fe/components/VerificationFlowPage/model2/editorGraph.d.ts +76 -0
  26. package/dist/fe/components/VerificationFlowPage/model2/editorGraph.js +385 -0
  27. package/dist/fe/components/VerificationFlowPage/model2/index.d.ts +9 -0
  28. package/dist/fe/components/VerificationFlowPage/model2/index.js +9 -0
  29. package/dist/fe/components/VerificationFlowPage/model2/theme.d.ts +156 -0
  30. package/dist/fe/components/VerificationFlowPage/model2/theme.js +312 -0
  31. package/dist/fe/components/VerificationFlowPage/store/index.d.ts +18 -0
  32. package/dist/fe/components/VerificationFlowPage/store/index.js +54 -0
  33. package/dist/fe/components/VerificationFlowPage/theme/index.d.ts +11 -0
  34. package/dist/fe/components/VerificationFlowPage/theme/index.js +6 -2
  35. package/dist/fe/components/VerificationFlowPage/types/index.d.ts +62 -1
  36. package/dist/fe/index.d.ts +2 -2
  37. package/dist/fe/index.js +1 -1
  38. package/dist/index.js +3 -3
  39. package/dist/server.js +3 -3
  40. package/dist/src/Client/ApiCaller/types.d.ts +23 -0
  41. package/dist/src/Services/Verification/model2Validate.d.ts +1 -1
  42. package/dist/src/Services/Verification/types.d.ts +50 -1
  43. package/package.json +1 -1
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.10.129
1
+ 0.10.131
@@ -281,6 +281,18 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
281
281
  store.decidingId === item.instance_id ? /*#__PURE__*/ _jsxs("div", {
282
282
  className: theme.pending.card.actions,
283
283
  children: [
284
+ typeof item.pass_no === 'number' && typeof item.max_passes === 'number' ? /*#__PURE__*/ _jsxs("p", {
285
+ className: theme.pending.card.decisionMeta,
286
+ children: [
287
+ item.max_passes > 1 ? say.pendingRound(item.pass_no, item.max_passes) : null,
288
+ item.max_passes > 1 ? ' · ' : null,
289
+ item.rejection_sends_back_to ? say.pendingRejectSendsBack(say.stepOption(item.rejection_sends_back_to.step_order, item.rejection_sends_back_to.step_name)) : say.pendingRejectEndsReview
290
+ ]
291
+ }) : null,
292
+ item.is_last_pass === true && (item.max_passes ?? 1) > 1 ? /*#__PURE__*/ _jsx("p", {
293
+ className: theme.pending.card.lastRoundWarning,
294
+ children: say.pendingLastRoundWarning
295
+ }) : null,
284
296
  item.require_signature ? /*#__PURE__*/ _jsxs("label", {
285
297
  className: "flex flex-col gap-1 text-xs",
286
298
  children: [
@@ -1,5 +1,5 @@
1
1
  import type { VerificationFlowLabels } from '../labels';
2
- import type { VerificationBulkStatusItem, VerificationCancelAction, VerificationEntityStatusesAction } from '../types';
2
+ import type { VerificationBulkStatusItem, VerificationCancelAction, VerificationEntityStatusesAction, VerificationStartForEntityAction } from '../types';
3
3
  export type VerificationEntityListTheme = {
4
4
  container: string;
5
5
  header: string;
@@ -52,5 +52,17 @@ export type VerificationEntityListProps = {
52
52
  * now the API had one and the product offered none.
53
53
  */
54
54
  cancelAction?: VerificationCancelAction;
55
+ /**
56
+ * Puts a record back into review.
57
+ *
58
+ * It was declared on the page's props, passed in by the panel, and never
59
+ * destructured anywhere — so on a flow with `trigger_on: 'manual'` there was
60
+ * no way from the product to start a review at all. This list is the one
61
+ * screen that knows which chains have FINISHED, so a rejected record that has
62
+ * since been corrected gets its next review here. A record that has never
63
+ * been reviewed does not appear in this list (it is built from instances), so
64
+ * its first review still starts from the record's own screen.
65
+ */
66
+ startForEntityAction?: VerificationStartForEntityAction;
55
67
  };
56
- export declare function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode, onEntityClick, pageSize, labels, cancelAction, }: VerificationEntityListProps): import("react/jsx-runtime").JSX.Element;
68
+ export declare function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode, onEntityClick, pageSize, labels, cancelAction, startForEntityAction, }: VerificationEntityListProps): import("react/jsx-runtime").JSX.Element;
@@ -97,7 +97,7 @@ const lightTheme = {
97
97
  };
98
98
  return map[status] ?? status;
99
99
  }
100
- export function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode = 'dark', onEntityClick, pageSize = 20, labels, cancelAction }) {
100
+ export function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode = 'dark', onEntityClick, pageSize = 20, labels, cancelAction, startForEntityAction }) {
101
101
  const baseTheme = themeMode === 'light' ? lightTheme : darkTheme;
102
102
  const t = {
103
103
  ...baseTheme,
@@ -113,6 +113,50 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
113
113
  const [cancellingId, setCancellingId] = useState(null);
114
114
  const [cancelReason, setCancelReason] = useState('');
115
115
  const [cancelNote, setCancelNote] = useState(null);
116
+ // Which row is being sent back into review, and what it was told.
117
+ const [startingId, setStartingId] = useState(null);
118
+ const [startNote, setStartNote] = useState(null);
119
+ const startAgain = useEffectEvent((item)=>{
120
+ if (!startForEntityAction) return;
121
+ setStartingId(item.instance_id);
122
+ setStartNote(null);
123
+ startForEntityAction.start({
124
+ payload: {
125
+ entity_name: item.entity_name,
126
+ entity_id: item.entity_id
127
+ },
128
+ onAfterHandle: (res)=>{
129
+ setStartingId(null);
130
+ const r = res;
131
+ /*
132
+ * The engine has plenty of legitimate reasons to refuse — no live flow
133
+ * for this entity, the first step resolves to nobody, a chain is
134
+ * already running — and each one is a sentence. Shown on the row rather
135
+ * than swallowed: a button that does nothing and says nothing is how
136
+ * "send to review does not work" became a support conversation.
137
+ */ if (r.success === false) {
138
+ setStartNote({
139
+ id: item.instance_id,
140
+ text: r.message || say.startReviewFailed
141
+ });
142
+ return;
143
+ }
144
+ setStartNote({
145
+ id: item.instance_id,
146
+ text: r.message || say.reviewStartedNote
147
+ });
148
+ loadStatuses(activeTab === 'all' ? undefined : activeTab, currentPage);
149
+ },
150
+ onErrorHandle: (err)=>{
151
+ setStartingId(null);
152
+ const e = err;
153
+ setStartNote({
154
+ id: item.instance_id,
155
+ text: e?.message || say.startReviewFailed
156
+ });
157
+ }
158
+ });
159
+ });
116
160
  const loadStatuses = useEffectEvent((status, page)=>{
117
161
  entityStatusesAction.start({
118
162
  payload: {
@@ -332,6 +376,25 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
332
376
  className: `${t.statusBadge} ${getStatusBadgeClass(item.status)}`,
333
377
  children: statusLabel(say, item.status)
334
378
  }),
379
+ startForEntityAction && item.status !== 'active' ? /*#__PURE__*/ _jsxs("div", {
380
+ className: "shrink-0 flex flex-col items-end gap-1",
381
+ children: [
382
+ /*#__PURE__*/ _jsx("button", {
383
+ type: "button",
384
+ disabled: startingId === item.instance_id,
385
+ onClick: (e)=>{
386
+ e.stopPropagation();
387
+ startAgain(item);
388
+ },
389
+ className: themeMode === 'light' ? 'rounded-lg border border-gray-200 bg-white px-2.5 py-1 text-[11px] font-medium text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed' : 'rounded-lg border border-white/[0.08] bg-white/[0.04] px-2.5 py-1 text-[11px] font-medium text-white/60 hover:bg-white/[0.1] disabled:opacity-40 disabled:cursor-not-allowed',
390
+ children: startingId === item.instance_id ? say.startingReview : say.startNewReview
391
+ }),
392
+ startNote?.id === item.instance_id ? /*#__PURE__*/ _jsx("p", {
393
+ className: themeMode === 'light' ? 'max-w-[18rem] text-right text-[10px] leading-relaxed text-gray-500' : 'max-w-[18rem] text-right text-[10px] leading-relaxed text-white/45',
394
+ children: startNote.text
395
+ }) : null
396
+ ]
397
+ }) : null,
335
398
  cancelAction && item.status === 'active' ? cancellingId === item.instance_id ? // No handlers on this wrapper: the row above is a button
336
399
  // when `onEntityClick` is set, so each INTERACTIVE child
337
400
  // stops the bubble itself rather than a bare div doing it
@@ -1,4 +1,4 @@
1
1
  import '@xyflow/react/dist/style.css';
2
2
  import type { ReactElement } from 'react';
3
3
  import type { VerificationFlowPageProps } from '../types';
4
- export declare function VerificationFlowPage({ entityName, title, subtitle, flowListAction, flowGetAction, flowSaveAction, flowPublishAction, flowUnpublishAction, flowDeleteAction, pendingAction, decideAction, uploadSignatureAction, listUsersAction, listRolesAction, entityStatusesAction, onFlowSelected, onDecisionMade, onEntityClick, onBack, showPending, showEntityStatuses, className, themeMode, labels, describeEntity, entityHref, availableChannels, initialTab, cancelAction, }: VerificationFlowPageProps): ReactElement;
4
+ export declare function VerificationFlowPage({ entityName, title, subtitle, flowListAction, flowGetAction, flowSaveAction, flowPublishAction, flowUnpublishAction, flowDeleteAction, pendingAction, decideAction, uploadSignatureAction, listUsersAction, listRolesAction, entityStatusesAction, onFlowSelected, onDecisionMade, onEntityClick, onBack, showPending, showEntityStatuses, className, themeMode, labels, describeEntity, entityHref, availableChannels, initialTab, cancelAction, startForEntityAction, authoringModel, flowFeasibilityAction, manageRolesHref, }: VerificationFlowPageProps): ReactElement;
@@ -5,7 +5,10 @@ import '@xyflow/react/dist/style.css';
5
5
  import { useEffect, useEffectEvent, useRef, useState } from 'react';
6
6
  import { cn } from '../../../utils/cn';
7
7
  import { newId } from '../../../utils/newId';
8
- import { DEFAULT_VERIFICATION_FLOW_LABELS, setActiveLabels } from '../labels';
8
+ import { DEFAULT_VERIFICATION_FLOW_LABELS, notificationChannelLabels, notificationTriggerLabels, recipientTypeLabels, setActiveLabels } from '../labels';
9
+ import { emptyModel2Graph } from '../model2/editorGraph';
10
+ import { StepListEditor } from '../model2/StepListEditor';
11
+ import { getModel2Theme } from '../model2/theme';
9
12
  import { assignStepOrders } from '../stepOrder';
10
13
  import { useVerificationFlowStore } from '../store';
11
14
  import { getVerificationFlowTheme } from '../theme';
@@ -281,7 +284,7 @@ function buildSavePayload(flowId, entityName, flowName, flowDescription, trigger
281
284
  };
282
285
  }
283
286
  // ─── Main Component ───────────────────────────────────────────────
284
- export function VerificationFlowPage({ entityName, title, subtitle, flowListAction, flowGetAction, flowSaveAction, flowPublishAction, flowUnpublishAction, flowDeleteAction, pendingAction, decideAction, uploadSignatureAction, listUsersAction, listRolesAction, entityStatusesAction, onFlowSelected, onDecisionMade, onEntityClick, onBack, showPending = true, showEntityStatuses = false, className, themeMode = 'dark', labels, describeEntity, entityHref, availableChannels, initialTab, cancelAction }) {
287
+ export function VerificationFlowPage({ entityName, title, subtitle, flowListAction, flowGetAction, flowSaveAction, flowPublishAction, flowUnpublishAction, flowDeleteAction, pendingAction, decideAction, uploadSignatureAction, listUsersAction, listRolesAction, entityStatusesAction, onFlowSelected, onDecisionMade, onEntityClick, onBack, showPending = true, showEntityStatuses = false, className, themeMode = 'dark', labels, describeEntity, entityHref, availableChannels, initialTab, cancelAction, startForEntityAction, authoringModel = 1, flowFeasibilityAction, manageRolesHref }) {
285
288
  const theme = getVerificationFlowTheme(themeMode);
286
289
  setActiveTheme(theme);
287
290
  const say = {
@@ -304,6 +307,15 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
304
307
  const [showCreateForm, setShowCreateForm] = useState(false);
305
308
  const reactFlowWrapper = useRef(null);
306
309
  const reactFlowInstance = useRef(null);
310
+ /*
311
+ * Which editor is on screen — NOT which one the host prefers.
312
+ *
313
+ * An existing flow always opens in the editor it was written in, decided by
314
+ * the `model_version` the server sends back, because opening a drawn flow in
315
+ * the list editor would show an empty chain for a flow that has three steps.
316
+ * `authoringModel` only decides what the CREATE button makes.
317
+ */ const [isModel2, setIsModel2] = useState(false);
318
+ const m2Theme = getModel2Theme(themeMode);
307
319
  const resolvedTitle = title || say.pageTitle(entityName);
308
320
  const resolvedSubtitle = subtitle || entityName;
309
321
  // ─── Node/Edge Change Handlers ────────────────────────────────
@@ -400,6 +412,54 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
400
412
  }
401
413
  const name = flowName || say.defaultFlowName(entityName);
402
414
  store.setIsSaving(true);
415
+ /*
416
+ * An authored chain sends a DIFFERENT body, and `graph` goes out empty
417
+ * beside it. The server reads `model2` only when `model_version` is 2, and
418
+ * `graph` only when it is not — sending both filled would leave the
419
+ * question of which one is the flow to whichever reader ran last.
420
+ */ if (isModel2) {
421
+ flowSaveAction.start({
422
+ payload: {
423
+ flow_id: flowId,
424
+ entity_name: entityName,
425
+ name,
426
+ description: flowDescription || undefined,
427
+ trigger_on: flowTriggerOn,
428
+ is_draft: true,
429
+ model_version: 2,
430
+ max_passes: store.maxPasses,
431
+ model2: store.model2Graph,
432
+ graph: {
433
+ steps: [],
434
+ edges: [],
435
+ verifier_configs: [],
436
+ notification_rules: [],
437
+ notification_recipients: [],
438
+ notification_channels: []
439
+ }
440
+ },
441
+ onAfterHandle: (res)=>{
442
+ const resObj = res;
443
+ store.setIsSaving(false);
444
+ if (resObj.success === false) {
445
+ // Refused. The chain on screen was NOT written, so the editor stays
446
+ // dirty — clearing it here would tell the author their work is saved.
447
+ store.setError(resObj.message || say.saveRefused);
448
+ return;
449
+ }
450
+ store.setError(null);
451
+ if (resObj.flow_id) store.setSelectedFlowId(resObj.flow_id);
452
+ store.setIsDirty(false);
453
+ loadFlows();
454
+ },
455
+ onErrorHandle: (err)=>{
456
+ store.setIsSaving(false);
457
+ const e = err;
458
+ store.setError(e?.message || say.saveRefused);
459
+ }
460
+ });
461
+ return;
462
+ }
403
463
  const viewport = reactFlowInstance.current ? reactFlowInstance.current.getViewport() : undefined;
404
464
  const payload = buildSavePayload(flowId, entityName, name, flowDescription, flowTriggerOn, true, viewport, nodes, edges);
405
465
  flowSaveAction.start({
@@ -492,6 +552,56 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
492
552
  }
493
553
  });
494
554
  });
555
+ // ─── The authored chain ───────────────────────────────────────
556
+ /*
557
+ * Asked of the graph on screen, not of the stored flow.
558
+ *
559
+ * The moment the answer is worth anything is while the author is still
560
+ * editing. An endpoint that only answered for saved flows would mean saving a
561
+ * flow you already suspect is broken in order to find out whether it is.
562
+ */ const checkFeasibility = useEffectEvent(()=>{
563
+ if (!flowFeasibilityAction) return;
564
+ if ((store.model2Graph.steps ?? []).length === 0) {
565
+ store.setFeasibility(null);
566
+ return;
567
+ }
568
+ store.setFeasibilityLoading(true);
569
+ store.setFeasibilityError(null);
570
+ flowFeasibilityAction.start({
571
+ payload: {
572
+ entity_name: entityName,
573
+ max_passes: store.maxPasses,
574
+ model2: store.model2Graph
575
+ },
576
+ onAfterHandle: (res)=>{
577
+ store.setFeasibilityLoading(false);
578
+ const data = res?.data;
579
+ store.setFeasibility(data ?? null);
580
+ },
581
+ onErrorHandle: (err)=>{
582
+ store.setFeasibilityLoading(false);
583
+ const e = err;
584
+ /*
585
+ * The check failing is not the same as the flow being fine. Reported as
586
+ * its own line rather than left as a stale green card — a readiness
587
+ * panel that keeps showing the last good answer after it stopped being
588
+ * able to ask is worse than one that says it could not ask.
589
+ */ store.setFeasibilityError(e?.message || say.readinessSaveRefusal);
590
+ store.setFeasibility(null);
591
+ }
592
+ });
593
+ });
594
+ const handleModel2Change = useEffectEvent((next, notes)=>{
595
+ store.setModel2Graph(next);
596
+ if (notes.length > 0) store.setModel2Notes(notes);
597
+ store.setIsDirty(true);
598
+ /*
599
+ * The readiness answer is about a graph that no longer exists the moment an
600
+ * edit lands, so it is dropped rather than left on screen. Re-asking on
601
+ * every keystroke would be a request per character; the author asks again
602
+ * with the button, and a save always re-asks on the server anyway.
603
+ */ store.setFeasibility(null);
604
+ });
495
605
  // ─── View Mode Handlers ──────────────────────────────────────
496
606
  const openDesigner = useEffectEvent((flowId)=>{
497
607
  store.setSelectedFlowId(flowId);
@@ -508,6 +618,11 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
508
618
  store.setSelectedFlowId(null);
509
619
  setNodes([]);
510
620
  setEdges([]);
621
+ setIsModel2(authoringModel === 2);
622
+ store.setModel2Graph(emptyModel2Graph());
623
+ store.clearModel2Notes();
624
+ store.setFeasibility(null);
625
+ store.setMaxPasses(3);
511
626
  store.setIsDirty(true);
512
627
  setShowCreateForm(false);
513
628
  setViewMode('designer');
@@ -554,6 +669,24 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
554
669
  setFlowDescription(flow.description || '');
555
670
  setFlowTriggerOn(flow.trigger_on || 'create');
556
671
  }
672
+ /*
673
+ * The STORED model decides which editor opens, not the host's
674
+ * preference. A model-2 flow read into the drawn editor would render an
675
+ * empty canvas for a chain that has three steps, and the author's next
676
+ * save would write that emptiness back.
677
+ */ const model2 = resObj.data?.model2;
678
+ if (model2) {
679
+ setIsModel2(true);
680
+ store.setModel2Graph(model2);
681
+ store.setMaxPasses(Number(flow?.max_passes ?? 3));
682
+ store.setFeasibility(null);
683
+ store.clearModel2Notes();
684
+ setNodes([]);
685
+ setEdges([]);
686
+ store.setLoadingGraph(false);
687
+ return;
688
+ }
689
+ setIsModel2(false);
557
690
  const graph = resObj.data?.graph;
558
691
  if (graph?.steps && graph.steps.length > 0) {
559
692
  store.setGraphData(graph);
@@ -643,6 +776,19 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
643
776
  }, [
644
777
  store.selectedFlowId
645
778
  ]);
779
+ /*
780
+ * Roles and people are loaded when the AUTHORED editor opens, not on mount.
781
+ *
782
+ * On mount would mean two directory requests on every visit to a screen most
783
+ * people open to look at the pending queue, and the pickers those lists feed
784
+ * do not exist unless the list editor is on screen. Loaded once per opening
785
+ * rather than per step card — see `loadDirectories`.
786
+ */ useEffect(()=>{
787
+ if (viewMode === 'designer' && isModel2) loadDirectories();
788
+ }, [
789
+ viewMode,
790
+ isModel2
791
+ ]);
646
792
  // ─── Derived State ────────────────────────────────────────────
647
793
  const selectedFlow = store.flows.find((f)=>f.id === store.selectedFlowId);
648
794
  const selectedNode = store.selectedNodeId ? nodes.find((n)=>n.id === store.selectedNodeId) || null : null;
@@ -675,6 +821,73 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
675
821
  delete: say.triggerOnDelete,
676
822
  manual: say.triggerOnManual
677
823
  };
824
+ /*
825
+ * The enum → label maps the authored editor needs, built from the same
826
+ * functions the drawn editor's panel uses. Two maps would be two spellings of
827
+ * `on_step_reached` on two screens of one product.
828
+ */ const notifTriggerLabels = notificationTriggerLabels(say);
829
+ const recipientLabels = recipientTypeLabels(say);
830
+ const channelLabelMap = notificationChannelLabels(say);
831
+ /*
832
+ * Roles and people, once, for the whole authored editor.
833
+ *
834
+ * The drawn editor's properties panel fetches its own and caches them in
835
+ * module scope, because it is mounted and unmounted as nodes are selected.
836
+ * The list editor shows every step at once, so a fetch per step card would be
837
+ * one request per step — and every assignee picker would be a separately
838
+ * paginated list of the same twenty people.
839
+ *
840
+ * A high limit rather than pagination: these pickers name reviewers, and a
841
+ * role that is on page two of the list is a role an author cannot choose.
842
+ * When an install outgrows this the readiness card is what catches it — a
843
+ * slot pointing at nothing is blocking there.
844
+ */ const [roleOptions, setRoleOptions] = useState([]);
845
+ const [userOptions, setUserOptions] = useState([]);
846
+ const loadDirectories = useEffectEvent(()=>{
847
+ if (listRolesAction) {
848
+ listRolesAction.start({
849
+ payload: {
850
+ page: 1,
851
+ limit: 200
852
+ },
853
+ onAfterHandle: (res)=>{
854
+ const dataObj = res?.data;
855
+ const items = dataObj?.items || dataObj?.data || [];
856
+ setRoleOptions(items.map((r)=>({
857
+ id: r.id,
858
+ name: r.name
859
+ })));
860
+ },
861
+ onErrorHandle: ()=>{
862
+ setRoleOptions([]);
863
+ }
864
+ });
865
+ }
866
+ if (listUsersAction) {
867
+ listUsersAction.start({
868
+ payload: {
869
+ page: 1,
870
+ limit: 200
871
+ },
872
+ onAfterHandle: (res)=>{
873
+ const dataObj = res?.data;
874
+ const items = dataObj?.items || dataObj?.data || [];
875
+ /*
876
+ * A person with no display name still has to be pickable, and by
877
+ * something a human recognises. The e-mail is that; the id is not —
878
+ * a reviewer chosen by uuid is a reviewer nobody can verify was the
879
+ * right one.
880
+ */ setUserOptions(items.map((u)=>({
881
+ id: u.id,
882
+ name: u.name || u.email || u.id
883
+ })));
884
+ },
885
+ onErrorHandle: ()=>{
886
+ setUserOptions([]);
887
+ }
888
+ });
889
+ }
890
+ });
678
891
  // ─── Render ───────────────────────────────────────────────────
679
892
  return /*#__PURE__*/ _jsxs("div", {
680
893
  className: cn(theme.layout.root, className),
@@ -1111,7 +1324,37 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
1111
1324
  })
1112
1325
  ]
1113
1326
  }),
1114
- viewMode === 'designer' && store.activeTab === 'flow' && /*#__PURE__*/ _jsxs("div", {
1327
+ viewMode === 'designer' && store.activeTab === 'flow' && isModel2 && /*#__PURE__*/ _jsx(StepListEditor, {
1328
+ graph: store.model2Graph,
1329
+ maxPasses: store.maxPasses,
1330
+ notes: store.model2Notes,
1331
+ roles: roleOptions,
1332
+ users: userOptions,
1333
+ triggerLabels: notifTriggerLabels,
1334
+ recipientTypeLabels: recipientLabels,
1335
+ channelLabels: channelLabelMap,
1336
+ availableChannels: availableChannels,
1337
+ feasibility: store.feasibility,
1338
+ isCheckingFeasibility: store.isCheckingFeasibility,
1339
+ feasibilityError: store.feasibilityError,
1340
+ manageRolesHref: manageRolesHref,
1341
+ diagramOpen: store.diagramOpen,
1342
+ say: say,
1343
+ theme: m2Theme,
1344
+ themeMode: themeMode,
1345
+ disabled: false,
1346
+ newStepId: newId,
1347
+ onGraphChange: handleModel2Change,
1348
+ onMaxPassesChange: (v)=>{
1349
+ store.setMaxPasses(v);
1350
+ store.setIsDirty(true);
1351
+ },
1352
+ onDismissNotes: store.clearModel2Notes,
1353
+ onToggleDiagram: ()=>store.setDiagramOpen(!store.diagramOpen),
1354
+ onRecheckFeasibility: checkFeasibility,
1355
+ confirmRemoveStep: (step)=>typeof window === 'undefined' ? true : window.confirm(say.removeStepConfirm(step.name || say.stepNumber(step.step_order)))
1356
+ }),
1357
+ viewMode === 'designer' && store.activeTab === 'flow' && !isModel2 && /*#__PURE__*/ _jsxs("div", {
1115
1358
  className: theme.layout.main,
1116
1359
  children: [
1117
1360
  /*#__PURE__*/ _jsx("aside", {
@@ -1308,7 +1551,14 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
1308
1551
  onEntityClick: onEntityClick,
1309
1552
  themeMode: themeMode,
1310
1553
  labels: say,
1311
- cancelAction: cancelAction
1554
+ cancelAction: cancelAction,
1555
+ /*
1556
+ * Declared on these props, passed in by the panel, and destructured
1557
+ * NOWHERE until now — so on a flow with `trigger_on: 'manual'` the
1558
+ * product had no way to put a record into review at all. The list
1559
+ * knows which chains have finished, so a corrected record gets its
1560
+ * next review from the row it is already on.
1561
+ */ startForEntityAction: startForEntityAction
1312
1562
  })
1313
1563
  })
1314
1564
  ]
@@ -6,6 +6,13 @@ export { VerificationEntityList, type VerificationEntityListProps, type Verifica
6
6
  export { VerificationFlowPage } from './components/VerificationFlowPage';
7
7
  export { VerificationStatusCard, type VerificationStatusCardProps, type VerificationStatusCardTheme, } from './components/VerificationStatusCard';
8
8
  export { DEFAULT_VERIFICATION_FLOW_LABELS, type VerificationFlowLabels, } from './labels';
9
+ /**
10
+ * The authored-chain editor. Exported whole, because a host that wants a
11
+ * different arrangement of the same blocks — the readiness card beside the
12
+ * list, say, or the diagram on its own screen — can only have it if the pieces
13
+ * are reachable.
14
+ */
15
+ export * from './model2';
9
16
  export { useVerificationFlowStore } from './store';
10
17
  export type { VerificationFlowPageTheme } from './theme';
11
18
  export { extendVerificationFlowPageTheme, getVerificationFlowTheme, verificationFlowPageLightTheme, verificationFlowPageTheme, } from './theme';
@@ -6,5 +6,11 @@ export { VerificationEntityList } from './components/VerificationEntityList';
6
6
  export { VerificationFlowPage } from './components/VerificationFlowPage';
7
7
  export { VerificationStatusCard } from './components/VerificationStatusCard';
8
8
  export { DEFAULT_VERIFICATION_FLOW_LABELS } from './labels';
9
+ /**
10
+ * The authored-chain editor. Exported whole, because a host that wants a
11
+ * different arrangement of the same blocks — the readiness card beside the
12
+ * list, say, or the diagram on its own screen — can only have it if the pieces
13
+ * are reachable.
14
+ */ export * from './model2';
9
15
  export { useVerificationFlowStore } from './store';
10
16
  export { extendVerificationFlowPageTheme, getVerificationFlowTheme, verificationFlowPageLightTheme, verificationFlowPageTheme } from './theme';