nucleus-core-ts 0.10.130 → 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.
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.10.130
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, 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, }: VerificationFlowPageProps): ReactElement;
@@ -284,7 +284,7 @@ function buildSavePayload(flowId, entityName, flowName, flowDescription, trigger
284
284
  };
285
285
  }
286
286
  // ─── Main Component ───────────────────────────────────────────────
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, authoringModel = 1, flowFeasibilityAction, manageRolesHref }) {
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 }) {
288
288
  const theme = getVerificationFlowTheme(themeMode);
289
289
  setActiveTheme(theme);
290
290
  const say = {
@@ -1551,7 +1551,14 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
1551
1551
  onEntityClick: onEntityClick,
1552
1552
  themeMode: themeMode,
1553
1553
  labels: say,
1554
- 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
1555
1562
  })
1556
1563
  })
1557
1564
  ]
@@ -409,6 +409,15 @@ export type VerificationFlowLabels = {
409
409
  /** In the diagram's text alternative: the chain finishing approved / rejected. */
410
410
  diagramFinishesApproved: string;
411
411
  diagramFinishesRejected: string;
412
+ startNewReview: string;
413
+ startingReview: string;
414
+ startReviewFailed: string;
415
+ reviewStartedNote: string;
416
+ /** On the reviewer's card: which round, and what Reject will do. */
417
+ pendingRound: (pass: number, max: number) => string;
418
+ pendingRejectSendsBack: (stepLabel: string) => string;
419
+ pendingRejectEndsReview: string;
420
+ pendingLastRoundWarning: string;
412
421
  locale: string;
413
422
  };
414
423
  export declare const DEFAULT_VERIFICATION_FLOW_LABELS: VerificationFlowLabels;
@@ -307,6 +307,14 @@
307
307
  diagramSendsBackTo: (stepLabel)=>`if rejected, goes back to ${stepLabel}`,
308
308
  diagramFinishesApproved: 'then the record is approved',
309
309
  diagramFinishesRejected: 'if rejected, the record is rejected and the review ends',
310
+ startNewReview: 'Review again',
311
+ startingReview: 'Starting…',
312
+ startReviewFailed: 'The review could not be started.',
313
+ reviewStartedNote: 'Sent for review.',
314
+ pendingRound: (pass, max)=>`Round ${pass} of ${max}`,
315
+ pendingRejectSendsBack: (stepLabel)=>`Rejecting sends this back to ${stepLabel}.`,
316
+ pendingRejectEndsReview: 'Rejecting ends this review.',
317
+ pendingLastRoundWarning: 'This is the last round this flow allows, so rejecting now ends the review for good — the record will not go back to its author again.',
310
318
  locale: 'en-US'
311
319
  };
312
320
  // ─── Enum → label maps ────────────────────────────────────────────
@@ -165,6 +165,17 @@ export type VerificationFlowPageTheme = {
165
165
  recordLabel: string;
166
166
  recordValue: string;
167
167
  recordLink: string;
168
+ /**
169
+ * The round and what Reject will do, on the card, before the press.
170
+ * Set as a plain fact beside the buttons.
171
+ */
172
+ decisionMeta: string;
173
+ /**
174
+ * The last allowed round. Louder than `decisionMeta`, because pressing
175
+ * Reject here ends a regulated review for good rather than sending the
176
+ * record back to be corrected.
177
+ */
178
+ lastRoundWarning: string;
168
179
  };
169
180
  input: string;
170
181
  approveButton: string;
@@ -160,7 +160,9 @@ export const verificationFlowPageTheme = {
160
160
  recordRow: 'mt-3 flex flex-wrap items-baseline gap-x-2 gap-y-1',
161
161
  recordLabel: 'text-[10px] uppercase tracking-wide text-white/30',
162
162
  recordValue: 'text-xs text-white/70',
163
- recordLink: 'text-xs font-medium text-blue-400 underline underline-offset-2'
163
+ recordLink: 'text-xs font-medium text-blue-400 underline underline-offset-2',
164
+ decisionMeta: 'text-[11px] text-white/45 leading-relaxed',
165
+ lastRoundWarning: 'text-[11px] leading-relaxed text-amber-300/90 rounded-lg border border-amber-500/25 bg-amber-500/[0.07] px-2.5 py-1.5'
164
166
  },
165
167
  input: 'rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-1.5 text-[11px] text-white/80 w-40 outline-none focus:border-white/20',
166
168
  approveButton: 'rounded-lg bg-emerald-500/20 border border-emerald-500/30 px-3 py-1.5 text-[11px] font-medium text-emerald-400 hover:bg-emerald-500/30 transition-all',
@@ -379,7 +381,9 @@ export const verificationFlowPageLightTheme = {
379
381
  recordRow: 'mt-3 flex flex-wrap items-baseline gap-x-2 gap-y-1',
380
382
  recordLabel: 'text-[10px] uppercase tracking-wide text-gray-400',
381
383
  recordValue: 'text-xs text-gray-700',
382
- recordLink: 'text-xs font-medium text-blue-600 underline underline-offset-2'
384
+ recordLink: 'text-xs font-medium text-blue-600 underline underline-offset-2',
385
+ decisionMeta: 'text-[11px] text-gray-500 leading-relaxed',
386
+ lastRoundWarning: 'text-[11px] leading-relaxed text-amber-800 rounded-lg border border-amber-300 bg-amber-50 px-2.5 py-1.5'
383
387
  },
384
388
  input: 'rounded-lg border border-gray-200 bg-gray-50 px-3 py-1.5 text-[11px] text-gray-800 w-40 outline-none focus:border-gray-400',
385
389
  approveButton: 'rounded-lg bg-emerald-50 border border-emerald-200 px-3 py-1.5 text-[11px] font-medium text-emerald-700 hover:bg-emerald-100 transition-all',
@@ -208,6 +208,21 @@ export type PendingItem = {
208
208
  step_name?: string;
209
209
  require_signature: boolean;
210
210
  created_at: string;
211
+ /**
212
+ * The round, and what a rejection would DO — so the card can say it beside
213
+ * the button instead of in the answer that comes back after it is pressed.
214
+ *
215
+ * Optional because a host on an older backend sends neither, and a card that
216
+ * crashes on a missing field is worse than one that says nothing about
217
+ * rounds. Absent reads as "one round", which is what model 1 is.
218
+ */
219
+ pass_no?: number;
220
+ max_passes?: number;
221
+ is_last_pass?: boolean;
222
+ rejection_sends_back_to?: {
223
+ step_order: number;
224
+ step_name?: string;
225
+ };
211
226
  };
212
227
  export type VerificationFlowPageThemeOverrides = {
213
228
  layout?: Record<string, string>;