nucleus-core-ts 0.10.140 → 0.10.142

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 (38) hide show
  1. package/dist/.build-ok +1 -1
  2. package/dist/dist/index.d.ts +1 -1
  3. package/dist/fe/components/VerificationFlowPage/components/PendingTab.d.ts +7 -2
  4. package/dist/fe/components/VerificationFlowPage/components/PendingTab.js +76 -23
  5. package/dist/fe/components/VerificationFlowPage/components/VerificationEntityList.d.ts +22 -2
  6. package/dist/fe/components/VerificationFlowPage/components/VerificationEntityList.js +127 -23
  7. package/dist/fe/components/VerificationFlowPage/components/VerificationFlowPage.d.ts +1 -1
  8. package/dist/fe/components/VerificationFlowPage/components/VerificationFlowPage.js +61 -7
  9. package/dist/fe/components/VerificationFlowPage/components/decisionPlan.d.ts +20 -0
  10. package/dist/fe/components/VerificationFlowPage/components/decisionPlan.js +39 -0
  11. package/dist/fe/components/VerificationFlowPage/components/recordLabels.d.ts +41 -0
  12. package/dist/fe/components/VerificationFlowPage/components/recordLabels.js +58 -0
  13. package/dist/fe/components/VerificationFlowPage/components/supersededCard.d.ts +14 -0
  14. package/dist/fe/components/VerificationFlowPage/components/supersededCard.js +17 -0
  15. package/dist/fe/components/VerificationFlowPage/components/useEntityLabels.d.ts +12 -0
  16. package/dist/fe/components/VerificationFlowPage/components/useEntityLabels.js +40 -0
  17. package/dist/fe/components/VerificationFlowPage/index.d.ts +1 -1
  18. package/dist/fe/components/VerificationFlowPage/labels.d.ts +60 -0
  19. package/dist/fe/components/VerificationFlowPage/labels.js +21 -2
  20. package/dist/fe/components/VerificationFlowPage/types/index.d.ts +40 -0
  21. package/dist/fe/index.d.ts +1 -1
  22. package/dist/index.js +6 -6
  23. package/dist/server.js +6 -6
  24. package/dist/src/Client/ApiCaller/types.d.ts +29 -0
  25. package/dist/src/Nucleus/routes/storage/cachePolicy.d.ts +37 -0
  26. package/dist/src/Nucleus/routes/storage/cdn.d.ts +21 -0
  27. package/dist/src/Nucleus/routes/storage/file-record.d.ts +12 -0
  28. package/dist/src/Nucleus/routes/storage/serveGuard.d.ts +60 -0
  29. package/dist/src/Services/Notification/index.d.ts +1 -0
  30. package/dist/src/Services/Notification/source.d.ts +22 -0
  31. package/dist/src/Services/Verification/cancelPhase.d.ts +76 -0
  32. package/dist/src/Services/Verification/index.d.ts +25 -4
  33. package/dist/src/Services/Verification/model2Outcome.d.ts +13 -0
  34. package/dist/src/Services/Verification/reviewSteps.d.ts +21 -0
  35. package/dist/src/Services/Verification/startPhase.d.ts +26 -0
  36. package/dist/src/Services/Verification/supersession.d.ts +63 -0
  37. package/dist/src/Services/Verification/types.d.ts +61 -2
  38. package/package.json +1 -1
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.10.140
1
+ 0.10.142
@@ -59,7 +59,7 @@ declare function parseCron2(expression: any): {
59
59
  };
60
60
  error?: undefined;
61
61
  };
62
- export function matchesCron(fields: any, when: any, timeZone: any): any;
62
+ export function matchesCron(fields: any, when2: any, timeZone: any): any;
63
63
  export function isDue(input: any): {
64
64
  due: boolean;
65
65
  error: string | undefined;
@@ -1,6 +1,6 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { VerificationFlowLabels } from '../labels';
3
- import type { VerificationDecideAction, VerificationPendingAction, VerificationSignatureUploadAction } from '../types';
3
+ import type { EntityLabel, VerificationDecideAction, VerificationPendingAction, VerificationSignatureUploadAction } from '../types';
4
4
  interface PendingTabProps {
5
5
  pendingAction: VerificationPendingAction;
6
6
  decideAction: VerificationDecideAction;
@@ -12,6 +12,11 @@ interface PendingTabProps {
12
12
  /** Names the record a decision is about. See `VerificationFlowPageProps`. */
13
13
  describeEntity?: (entityName: string, entityId: string) => string | undefined;
14
14
  entityHref?: (entityName: string, entityId: string) => string | undefined;
15
+ /** Looks the records up by name; see `VerificationFlowPageProps`. */
16
+ labelEntities?: (refs: {
17
+ entity_name: string;
18
+ entity_id: string;
19
+ }[]) => Promise<EntityLabel[]>;
15
20
  }
16
- export declare function PendingTab({ pendingAction, decideAction, uploadSignatureAction, onDecisionMade, labels, describeEntity, entityHref, }: PendingTabProps): ReactElement;
21
+ export declare function PendingTab({ pendingAction, decideAction, uploadSignatureAction, onDecisionMade, labels, describeEntity, entityHref, labelEntities, }: PendingTabProps): ReactElement;
17
22
  export {};
@@ -1,11 +1,14 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useEffectEvent, useRef, useState } from 'react';
4
4
  import { DEFAULT_VERIFICATION_FLOW_LABELS, refusalSentence } from '../labels';
5
5
  import { useVerificationFlowStore } from '../store';
6
6
  import { getActiveTheme } from '../theme/store';
7
7
  import { entityWord, isRevisionStep, planDecision, returnedSentence, showsLastRoundWarning, signatureFileType } from './decisionPlan';
8
- export function PendingTab({ pendingAction, decideAction, uploadSignatureAction, onDecisionMade, labels, describeEntity, entityHref }) {
8
+ import { hostRecordLabel, matchesSearch } from './recordLabels';
9
+ import { approvalOffered, supersededSentence } from './supersededCard';
10
+ import { useEntityLabels } from './useEntityLabels';
11
+ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction, onDecisionMade, labels, describeEntity, entityHref, labelEntities }) {
9
12
  /*
10
13
  * Local, not in the store: a chosen file must not survive cancelling out of
11
14
  * its card. Keyed by instance because it was one slot for the whole tab: a
@@ -52,6 +55,13 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
52
55
  ...labels
53
56
  };
54
57
  const store = useVerificationFlowStore();
58
+ /*
59
+ * What the reviewer typed to narrow the queue, and the names it is matched
60
+ * against. Measured in the audit: fourteen cards reading
61
+ * "Sayfa Versiyonları · 9f769e4b", no search, no order — the one about a
62
+ * given medicine was found by opening every "Open the record".
63
+ */ const [query, setQuery] = useState('');
64
+ const resolvedLabels = useEntityLabels(labelEntities, store.pendingItems);
55
65
  const loadPending = useEffectEvent(()=>{
56
66
  /*
57
67
  * Found in review: the spinner below replaces the whole list, so every
@@ -306,7 +316,8 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
306
316
  * `page_versions/9f769e4b-…` and nothing else, so a reviewer signed a leaflet
307
317
  * version they had no way to read. `describeEntity` lets the host supply the
308
318
  * name; the href, when there is a screen, lets them open it.
309
- */ const renderRecord = (item)=>/*#__PURE__*/ _jsxs("span", {
319
+ */ const nameOf = (item)=>hostRecordLabel(item, resolvedLabels, describeEntity) ?? `${entityWord(say, item.entity_name)}/${item.entity_id}`;
320
+ const renderRecord = (item)=>/*#__PURE__*/ _jsxs("span", {
310
321
  className: theme.pending.card.recordRow,
311
322
  children: [
312
323
  /*#__PURE__*/ _jsx("span", {
@@ -315,7 +326,7 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
315
326
  }),
316
327
  /*#__PURE__*/ _jsx("span", {
317
328
  className: theme.pending.card.recordValue,
318
- children: describeEntity?.(item.entity_name, item.entity_id) ?? `${entityWord(say, item.entity_name)}/${item.entity_id}`
329
+ children: nameOf(item)
319
330
  }),
320
331
  entityHref?.(item.entity_name, item.entity_id) ? /*#__PURE__*/ _jsx("a", {
321
332
  href: entityHref(item.entity_name, item.entity_id),
@@ -326,6 +337,18 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
326
337
  }) : null
327
338
  ]
328
339
  });
340
+ /*
341
+ * A record the host says was replaced by a newer one: said on the card,
342
+ * before anything is pressed, because the engine will refuse its approval.
343
+ * See `supersededCard`.
344
+ */ const renderSuperseded = (item)=>{
345
+ const text = supersededSentence(say, item);
346
+ return text ? /*#__PURE__*/ _jsx("p", {
347
+ className: theme.pending.card.lastRoundWarning,
348
+ role: "note",
349
+ children: text
350
+ }) : null;
351
+ };
329
352
  const renderSignaturePicker = (item, onPick)=>item.require_signature ? /*#__PURE__*/ _jsxs("label", {
330
353
  className: "flex flex-col gap-1 text-xs",
331
354
  children: [
@@ -385,7 +408,8 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
385
408
  className: theme.pending.card.decisionMeta,
386
409
  children: returned
387
410
  }) : null,
388
- renderRecord(item)
411
+ renderRecord(item),
412
+ renderSuperseded(item)
389
413
  ]
390
414
  }),
391
415
  confirming ? /*#__PURE__*/ _jsxs("div", {
@@ -426,17 +450,21 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
426
450
  }) : /*#__PURE__*/ _jsxs("div", {
427
451
  className: theme.pending.card.actions,
428
452
  children: [
429
- renderSignaturePicker(item, ()=>enterCard(item)),
430
- /*#__PURE__*/ _jsx("button", {
431
- type: "button",
432
- onClick: ()=>{
433
- enterCard(item);
434
- handleDecide(item, 'approved');
435
- },
436
- "aria-describedby": `resubmit-hint-${item.instance_id}`,
437
- className: theme.pending.approveButton,
438
- children: firstRound ? say.submitForReview : say.resubmit
439
- }),
453
+ approvalOffered(item) ? /*#__PURE__*/ _jsxs(_Fragment, {
454
+ children: [
455
+ renderSignaturePicker(item, ()=>enterCard(item)),
456
+ /*#__PURE__*/ _jsx("button", {
457
+ type: "button",
458
+ onClick: ()=>{
459
+ enterCard(item);
460
+ handleDecide(item, 'approved');
461
+ },
462
+ "aria-describedby": `resubmit-hint-${item.instance_id}`,
463
+ className: theme.pending.approveButton,
464
+ children: firstRound ? say.submitForReview : say.resubmit
465
+ })
466
+ ]
467
+ }) : null,
440
468
  /*#__PURE__*/ _jsx("button", {
441
469
  type: "button",
442
470
  id: `withdraw-${item.instance_id}`,
@@ -450,11 +478,11 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
450
478
  className: theme.pending.cancelButton,
451
479
  children: say.withdrawSubmission
452
480
  }),
453
- /*#__PURE__*/ _jsx("p", {
481
+ approvalOffered(item) ? /*#__PURE__*/ _jsx("p", {
454
482
  id: `resubmit-hint-${item.instance_id}`,
455
483
  className: theme.pending.card.decisionMeta,
456
484
  children: firstRound ? say.submitForReviewHint : say.resubmitHint
457
- })
485
+ }) : null
458
486
  ]
459
487
  })
460
488
  ]
@@ -481,7 +509,8 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
481
509
  say.previousRoundReason(item.sent_back.reason)
482
510
  ]
483
511
  }) : null,
484
- renderRecord(item)
512
+ renderRecord(item),
513
+ renderSuperseded(item)
485
514
  ]
486
515
  }),
487
516
  store.decidingId === item.instance_id ? /*#__PURE__*/ _jsxs("div", {
@@ -499,7 +528,7 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
499
528
  className: theme.pending.card.lastRoundWarning,
500
529
  children: say.pendingLastRoundWarning
501
530
  }) : null,
502
- renderSignaturePicker(item),
531
+ approvalOffered(item) ? renderSignaturePicker(item) : null,
503
532
  /*#__PURE__*/ _jsx("input", {
504
533
  type: "text",
505
534
  placeholder: say.reasonPlaceholder,
@@ -508,12 +537,12 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
508
537
  onChange: (e)=>store.setDecisionReason(e.target.value),
509
538
  className: theme.pending.input
510
539
  }),
511
- /*#__PURE__*/ _jsx("button", {
540
+ approvalOffered(item) ? /*#__PURE__*/ _jsx("button", {
512
541
  type: "button",
513
542
  onClick: ()=>handleDecide(item, 'approved'),
514
543
  className: theme.pending.approveButton,
515
544
  children: say.approve
516
- }),
545
+ }) : null,
517
546
  /*#__PURE__*/ _jsx("button", {
518
547
  type: "button",
519
548
  onClick: ()=>handleDecide(item, 'rejected'),
@@ -544,6 +573,17 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
544
573
  })
545
574
  ]
546
575
  });
576
+ /*
577
+ * The card being acted on stays whatever is typed: hiding it mid-decision
578
+ * would also hide the refusal written under it.
579
+ */ const shown = store.pendingItems.filter((item)=>item.instance_id === store.decidingId || matchesSearch([
580
+ nameOf(item),
581
+ entityWord(say, item.entity_name),
582
+ item.entity_id,
583
+ item.flow_name,
584
+ item.step_name
585
+ ], query));
586
+ const searching = query.trim() !== '';
547
587
  return /*#__PURE__*/ _jsxs("div", {
548
588
  className: theme.pending.container,
549
589
  children: [
@@ -551,7 +591,20 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
551
591
  className: theme.pending.title,
552
592
  children: say.pendingApprovals(store.pendingItems.length)
553
593
  }),
554
- store.pendingItems.map((item)=>/*#__PURE__*/ _jsxs("div", {
594
+ store.pendingItems.length > 1 ? /*#__PURE__*/ _jsx("input", {
595
+ type: "search",
596
+ value: query,
597
+ onChange: (e)=>setQuery(e.target.value),
598
+ placeholder: say.pendingSearchLabel,
599
+ "aria-label": say.pendingSearchLabel,
600
+ className: theme.pending.input
601
+ }) : null,
602
+ searching ? /*#__PURE__*/ _jsx("p", {
603
+ className: theme.pending.card.decisionMeta,
604
+ role: "status",
605
+ children: shown.length === 0 ? say.pendingSearchNoMatch(query.trim()) : say.pendingSearchShowing(shown.length, store.pendingItems.length)
606
+ }) : null,
607
+ shown.map((item)=>/*#__PURE__*/ _jsxs("div", {
555
608
  className: theme.pending.card.container,
556
609
  children: [
557
610
  isRevisionStep(item) ? renderRevisionCard(item) : renderReviewCard(item),
@@ -1,5 +1,5 @@
1
1
  import type { VerificationFlowLabels } from '../labels';
2
- import type { VerificationBulkStatusItem, VerificationCancelAction, VerificationEntityStatusesAction, VerificationStartForEntityAction } from '../types';
2
+ import type { EntityLabel, VerificationBulkStatusItem, VerificationCancelAction, VerificationEntityStatusesAction, VerificationStartForEntityAction } from '../types';
3
3
  export type VerificationEntityListTheme = {
4
4
  container: string;
5
5
  header: string;
@@ -26,6 +26,15 @@ export type VerificationEntityListTheme = {
26
26
  progressSection: string;
27
27
  progressBar: string;
28
28
  progressFill: string;
29
+ /**
30
+ * The fill of a chain that stopped. It fell through to `bg-white/20`, which
31
+ * on the light track (`bg-gray-200`) is invisible: a chain cancelled at 3/3
32
+ * showed an empty bar beside "3/3".
33
+ */
34
+ progressCancelled: string;
35
+ /** The search box above the rows, and the line saying what it found. */
36
+ searchInput: string;
37
+ searchNote: string;
29
38
  progressLabel: string;
30
39
  /** "Round 2 (at most 3)" under the bar, which otherwise reads backwards. */
31
40
  roundLabel: string;
@@ -68,5 +77,16 @@ export type VerificationEntityListProps = {
68
77
  * its first review still starts from the record's own screen.
69
78
  */
70
79
  startForEntityAction?: VerificationStartForEntityAction;
80
+ /**
81
+ * The same naming hooks the queue has. Rows printed the table's word and a
82
+ * full uuid, so an operator looking for one medicine's review read every
83
+ * row's id. See `VerificationFlowPageProps`.
84
+ */
85
+ describeEntity?: (entityName: string, entityId: string) => string | undefined;
86
+ entityHref?: (entityName: string, entityId: string) => string | undefined;
87
+ labelEntities?: (refs: {
88
+ entity_name: string;
89
+ entity_id: string;
90
+ }[]) => Promise<EntityLabel[]>;
71
91
  };
72
- export declare function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode, onEntityClick, pageSize, labels, cancelAction, startForEntityAction, }: VerificationEntityListProps): import("react/jsx-runtime").JSX.Element;
92
+ export declare function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode, onEntityClick, pageSize, labels, cancelAction, startForEntityAction, describeEntity, entityHref, labelEntities, }: VerificationEntityListProps): import("react/jsx-runtime").JSX.Element;
@@ -1,8 +1,10 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useEffectEvent, useRef, useState } from 'react';
4
4
  import { DEFAULT_VERIFICATION_FLOW_LABELS, refusalSentence } from '../labels';
5
- import { chainRoundLabel, chainStatusWord, entityWord, listStatusLabel, rowForStartNote } from './decisionPlan';
5
+ import { chainProgress, chainRoundLabel, chainStatusWord, entityWord, listStatusLabel, rowForStartNote } from './decisionPlan';
6
+ import { hostRecordLabel, matchesSearch } from './recordLabels';
7
+ import { useEntityLabels } from './useEntityLabels';
6
8
  const darkTheme = {
7
9
  container: 'space-y-4',
8
10
  header: 'space-y-1',
@@ -28,6 +30,9 @@ const darkTheme = {
28
30
  progressSection: 'flex items-center gap-2.5 shrink-0 w-[140px]',
29
31
  progressBar: 'flex-1 h-1.5 rounded-full bg-white/8 overflow-hidden',
30
32
  progressFill: 'h-full rounded-full transition-all duration-500',
33
+ progressCancelled: 'bg-white/20',
34
+ searchInput: 'w-full rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-xs text-white/80 outline-none placeholder:text-white/30 focus:border-white/20',
35
+ searchNote: 'text-[11px] leading-relaxed text-white/45',
31
36
  progressLabel: 'text-[10px] text-white/40 font-medium whitespace-nowrap w-[45px] text-right',
32
37
  roundLabel: 'text-[10px] text-white/45 whitespace-nowrap',
33
38
  metaSection: 'flex items-center gap-4 shrink-0',
@@ -67,6 +72,9 @@ const lightTheme = {
67
72
  progressSection: 'flex items-center gap-2.5 shrink-0 w-[140px]',
68
73
  progressBar: 'flex-1 h-1.5 rounded-full bg-gray-200 overflow-hidden',
69
74
  progressFill: 'h-full rounded-full transition-all duration-500',
75
+ progressCancelled: 'bg-gray-400',
76
+ searchInput: 'w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs text-gray-800 outline-none placeholder:text-gray-400 focus:border-gray-400',
77
+ searchNote: 'text-[11px] leading-relaxed text-gray-500',
70
78
  progressLabel: 'text-[10px] text-gray-500 font-medium whitespace-nowrap w-[45px] text-right',
71
79
  roundLabel: 'text-[10px] text-gray-500 whitespace-nowrap',
72
80
  metaSection: 'flex items-center gap-4 shrink-0',
@@ -88,7 +96,7 @@ const lightTheme = {
88
96
  'rejected',
89
97
  'cancelled'
90
98
  ];
91
- export function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode = 'dark', onEntityClick, pageSize = 20, labels, cancelAction, startForEntityAction }) {
99
+ export function VerificationEntityList({ entityName, entityStatusesAction, theme: themeOverrides, themeMode = 'dark', onEntityClick, pageSize = 20, labels, cancelAction, startForEntityAction, describeEntity, entityHref, labelEntities }) {
92
100
  const baseTheme = themeMode === 'light' ? lightTheme : darkTheme;
93
101
  const t = {
94
102
  ...baseTheme,
@@ -107,6 +115,15 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
107
115
  // Which row is being sent back into review, and what it was told.
108
116
  const [startingId, setStartingId] = useState(null);
109
117
  const [startNote, setStartNote] = useState(null);
118
+ // What the reader typed to narrow the rows on screen; see `shownItems`.
119
+ const [query, setQuery] = useState('');
120
+ /*
121
+ * The list could not be READ. Measured in the audit: a reviewer opening this
122
+ * tab got `403 operator privileges required`, the cached state went to
123
+ * `data: null`, and the list read that as "0 records" and "No verification
124
+ * records found" while dozens of chains existed. `forbidden` is the server
125
+ * refusing this reader, which is said differently from a failure.
126
+ */ const [loadFailure, setLoadFailure] = useState(null);
110
127
  /*
111
128
  * Counts list reloads, so an answer can tell whether the list it was asked
112
129
  * about is still the one on screen. See `startAgain`.
@@ -181,6 +198,7 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
181
198
  * under a row through tab switches and page turns, long after the row it
182
199
  * described had changed state.
183
200
  */ setStartNote(null);
201
+ setLoadFailure(null);
184
202
  loadGeneration.current += 1;
185
203
  entityStatusesAction.start({
186
204
  payload: {
@@ -188,6 +206,20 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
188
206
  status: status === 'all' ? undefined : status,
189
207
  page: page || 1,
190
208
  limit: pageSize
209
+ },
210
+ onAfterHandle: (res)=>{
211
+ const r = res;
212
+ if (r?.success === false) setLoadFailure({
213
+ forbidden: false,
214
+ message: r.message
215
+ });
216
+ },
217
+ onErrorHandle: (err, code)=>{
218
+ const e = err;
219
+ setLoadFailure({
220
+ forbidden: code === 403,
221
+ message: e?.message
222
+ });
191
223
  }
192
224
  });
193
225
  });
@@ -243,6 +275,26 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
243
275
  const items = responseData?.data?.items || [];
244
276
  const total = responseData?.data?.total || 0;
245
277
  const totalPages = Math.ceil(total / pageSize) || 1;
278
+ // Whose chains these are; see `BulkStatusResult.scope`. Absent on an older engine.
279
+ const scope = responseData?.data?.scope;
280
+ const resolvedLabels = useEntityLabels(labelEntities, items);
281
+ /*
282
+ * The rows that match what was typed, by the host's name for the record,
283
+ * the entity word, the id, the flow, the step and the status word. Only the
284
+ * page on screen: the server pages the list and only the host knows the
285
+ * names, so a search across pages would be a claim this list cannot back —
286
+ * the line under the box says so when there are more pages. A row whose
287
+ * cancel is open stays, so the question and its answer are not hidden
288
+ * mid-decision.
289
+ */ const searching = query.trim() !== '';
290
+ const shownItems = searching ? items.filter((item)=>item.instance_id === cancellingId || matchesSearch([
291
+ hostRecordLabel(item, resolvedLabels, describeEntity),
292
+ entityWord(say, item.entity_name),
293
+ item.entity_id,
294
+ item.flow_name,
295
+ item.current_step_name,
296
+ chainStatusWord(say, item)
297
+ ], query)) : items;
246
298
  const getStatusBadgeClass = (item)=>{
247
299
  if (item.status === 'active' && item.current_step_kind === 'revision') return t.statusRevision;
248
300
  switch(item.status){
@@ -267,7 +319,8 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
267
319
  case 'active':
268
320
  return 'bg-gradient-to-r from-amber-400 to-cyan-400';
269
321
  default:
270
- return 'bg-white/20';
322
+ // Cancelled, withdrawn, or a status this list does not know.
323
+ return t.progressCancelled;
271
324
  }
272
325
  };
273
326
  /*
@@ -279,14 +332,10 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
279
332
  * region that is always mounted (so it is announced), shown on the record's
280
333
  * row when that row is on this page (`rowForStartNote`) and above the list
281
334
  * when it is not.
282
- */ const noteRowId = isPending ? null : rowForStartNote(items, startNote);
335
+ */ // The rows actually drawn: a note for a row the search hid is shown above.
336
+ const noteRowId = isPending ? null : rowForStartNote(shownItems, startNote);
283
337
  const noteShownAbove = startNote !== null && !isPending && noteRowId === null;
284
338
  const noteClass = themeMode === 'light' ? 'text-[10px] leading-relaxed text-gray-500' : 'text-[10px] leading-relaxed text-white/45';
285
- const calculateProgress = (item)=>{
286
- if (item.status === 'completed') return 100;
287
- if (item.total_steps === 0) return 0;
288
- return Math.round((item.current_step_order - 1) / item.total_steps * 100);
289
- };
290
339
  return /*#__PURE__*/ _jsxs("div", {
291
340
  className: t.container,
292
341
  children: [
@@ -297,9 +346,9 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
297
346
  className: t.title,
298
347
  children: say.verificationStatusTitle
299
348
  }),
300
- /*#__PURE__*/ _jsx("p", {
349
+ loadFailure ? null : /*#__PURE__*/ _jsx("p", {
301
350
  className: t.description,
302
- children: say.recordCount(total, entityWord(say, entityName))
351
+ children: scope === 'started_by_me' ? say.recordCountStartedByMe(total, entityWord(say, entityName)) : say.recordCount(total, entityWord(say, entityName))
303
352
  })
304
353
  ]
305
354
  }),
@@ -312,6 +361,27 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
312
361
  children: listStatusLabel(say, tab)
313
362
  }, tab))
314
363
  }),
364
+ !loadFailure && (items.length > 1 || searching) ? /*#__PURE__*/ _jsxs("div", {
365
+ className: "flex flex-col gap-1",
366
+ children: [
367
+ /*#__PURE__*/ _jsx("input", {
368
+ type: "search",
369
+ value: query,
370
+ onChange: (e)=>setQuery(e.target.value),
371
+ placeholder: say.statusesSearchLabel,
372
+ "aria-label": say.statusesSearchLabel,
373
+ className: t.searchInput
374
+ }),
375
+ searching ? /*#__PURE__*/ _jsxs("p", {
376
+ className: t.searchNote,
377
+ role: "status",
378
+ children: [
379
+ shownItems.length === 0 ? say.statusesSearchNoMatch(query.trim()) : say.statusesSearchShowing(shownItems.length, items.length),
380
+ totalPages > 1 ? ` ${say.statusesSearchPageOnly(currentPage, totalPages)}` : null
381
+ ]
382
+ }) : null
383
+ ]
384
+ }) : null,
315
385
  /*#__PURE__*/ _jsx("p", {
316
386
  role: "status",
317
387
  className: noteShownAbove ? noteClass : 'sr-only',
@@ -322,6 +392,33 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
322
392
  children: /*#__PURE__*/ _jsx("div", {
323
393
  className: `w-6 h-6 rounded-full border-2 animate-spin ${themeMode === 'light' ? 'border-gray-200 border-t-blue-500' : 'border-white/10 border-t-cyan-400'}`
324
394
  })
395
+ }) : loadFailure ? /*#__PURE__*/ _jsxs("div", {
396
+ className: t.emptyState,
397
+ role: "alert",
398
+ children: [
399
+ /*#__PURE__*/ _jsx("p", {
400
+ className: t.emptyText,
401
+ children: loadFailure.forbidden ? say.statusesForbidden : say.statusesLoadFailedTitle
402
+ }),
403
+ loadFailure.forbidden ? null : /*#__PURE__*/ _jsxs(_Fragment, {
404
+ children: [
405
+ /*#__PURE__*/ _jsx("p", {
406
+ className: t.emptyText,
407
+ children: say.statusesLoadFailedBody
408
+ }),
409
+ loadFailure.message ? /*#__PURE__*/ _jsx("p", {
410
+ className: t.entityId,
411
+ children: loadFailure.message
412
+ }) : null,
413
+ /*#__PURE__*/ _jsx("button", {
414
+ type: "button",
415
+ className: t.pageButton,
416
+ onClick: ()=>reloadCurrent(),
417
+ children: say.retry
418
+ })
419
+ ]
420
+ })
421
+ ]
325
422
  }) : items.length === 0 ? /*#__PURE__*/ _jsxs("div", {
326
423
  className: t.emptyState,
327
424
  children: [
@@ -345,14 +442,16 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
345
442
  }),
346
443
  /*#__PURE__*/ _jsx("p", {
347
444
  className: t.emptyText,
348
- children: activeTab === 'all' ? say.noRecords : say.noRecordsForStatus(listStatusLabel(say, activeTab))
445
+ children: scope === 'started_by_me' ? activeTab === 'all' ? say.noRecordsStartedByMe : say.noRecordsStartedByMeForStatus(listStatusLabel(say, activeTab)) : activeTab === 'all' ? say.noRecords : say.noRecordsForStatus(listStatusLabel(say, activeTab))
349
446
  })
350
447
  ]
351
448
  }) : /*#__PURE__*/ _jsx("div", {
352
449
  className: t.list,
353
- children: items.map((item)=>{
354
- const progress = calculateProgress(item);
450
+ children: shownItems.map((item)=>{
451
+ // In review steps; see `chainProgress`.
452
+ const progress = chainProgress(item);
355
453
  const round = chainRoundLabel(say, item);
454
+ const href = entityHref?.(item.entity_name, item.entity_id);
356
455
  return /*#__PURE__*/ _jsxs("article", {
357
456
  className: `${t.listItem} ${onEntityClick ? t.listItemHover : ''} ${onEntityClick ? 'cursor-pointer' : ''}`,
358
457
  onClick: ()=>onEntityClick?.(item),
@@ -367,7 +466,7 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
367
466
  children: [
368
467
  /*#__PURE__*/ _jsx("p", {
369
468
  className: t.entityName,
370
- children: entityWord(say, item.entity_name)
469
+ children: hostRecordLabel(item, resolvedLabels, describeEntity) ?? entityWord(say, item.entity_name)
371
470
  }),
372
471
  /*#__PURE__*/ _jsx("p", {
373
472
  className: t.entityId,
@@ -377,6 +476,15 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
377
476
  className: t.flowName,
378
477
  children: item.flow_name
379
478
  }),
479
+ href ? /*#__PURE__*/ _jsx("a", {
480
+ href: href,
481
+ target: "_blank",
482
+ rel: "noopener noreferrer",
483
+ onClick: (e)=>e.stopPropagation(),
484
+ onKeyDown: (e)=>e.stopPropagation(),
485
+ className: themeMode === 'light' ? 'text-[11px] font-medium text-blue-600 hover:underline' : 'text-[11px] font-medium text-cyan-300 hover:underline',
486
+ children: say.openRecord
487
+ }) : null,
380
488
  noteRowId === item.instance_id && startNote ? /*#__PURE__*/ _jsx("p", {
381
489
  "aria-hidden": "true",
382
490
  className: noteClass,
@@ -395,17 +503,13 @@ export function VerificationEntityList({ entityName, entityStatusesAction, theme
395
503
  children: /*#__PURE__*/ _jsx("div", {
396
504
  className: `${t.progressFill} ${getProgressColor(item.status)}`,
397
505
  style: {
398
- width: `${progress}%`
506
+ width: `${progress.percent}%`
399
507
  }
400
508
  })
401
509
  }),
402
- /*#__PURE__*/ _jsxs("span", {
510
+ /*#__PURE__*/ _jsx("span", {
403
511
  className: t.progressLabel,
404
- children: [
405
- item.current_step_order,
406
- "/",
407
- item.total_steps
408
- ]
512
+ children: progress.label
409
513
  })
410
514
  ]
411
515
  }),
@@ -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, startForEntityAction, authoringModel, flowFeasibilityAction, manageRolesHref, }: 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, labelEntities, canManageFlows, }: VerificationFlowPageProps): ReactElement;