nucleus-core-ts 0.10.136 → 0.10.137

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.136
1
+ 0.10.137
@@ -474,6 +474,13 @@ export function PendingTab({ pendingAction, decideAction, uploadSignatureAction,
474
474
  className: theme.pending.card.subtitle,
475
475
  children: item.step_name
476
476
  }) : null,
477
+ item.sent_back?.reason ? /*#__PURE__*/ _jsxs("p", {
478
+ className: theme.pending.card.decisionMeta,
479
+ children: [
480
+ typeof item.pass_no === 'number' && typeof item.max_passes === 'number' ? `${say.pendingRound(item.pass_no, item.max_passes)} · ` : null,
481
+ say.previousRoundReason(item.sent_back.reason)
482
+ ]
483
+ }) : null,
477
484
  renderRecord(item)
478
485
  ]
479
486
  }),
@@ -316,6 +316,7 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
316
316
  * a ref here, because the render needs it too — to tell this editor's save
317
317
  * from one left behind — and a ref restarted at 0 on each mount.
318
318
  */ /** The latest readiness question; any older answer is about another graph. */ const feasibilityTicketRef = useRef(0);
319
+ /** The pending automatic re-check after an edit; see `scheduleFeasibility`. */ const feasibilityTimerRef = useRef(null);
319
320
  /*
320
321
  * The id a NEW flow is saved under, minted at its first save and reused
321
322
  * until one succeeds. It is not written to `selectedFlowId` before then:
@@ -756,22 +757,40 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
756
757
  }
757
758
  });
758
759
  });
760
+ /*
761
+ * Re-ask once the author PAUSES, with the graph they paused on.
762
+ *
763
+ * The answer was dropped on every edit and came back only on "Şimdi kontrol
764
+ * et" or a save, so adding a third step left the card blank ("henüz
765
+ * karşılaştırılmadı") exactly when it should have turned red (found by the
766
+ * re-check). A request per keystroke is not the fix either; one request
767
+ * 700ms after the last edit is. The ticket still discards an answer that
768
+ * lands after a newer edit.
769
+ */ const scheduleFeasibility = useEffectEvent(()=>{
770
+ if (feasibilityTimerRef.current) clearTimeout(feasibilityTimerRef.current);
771
+ feasibilityTimerRef.current = setTimeout(()=>{
772
+ feasibilityTimerRef.current = null;
773
+ checkFeasibility();
774
+ }, 700);
775
+ });
776
+ useEffect(()=>()=>{
777
+ if (feasibilityTimerRef.current) clearTimeout(feasibilityTimerRef.current);
778
+ }, []);
759
779
  const handleModel2Change = useEffectEvent((next, notes)=>{
760
780
  store.setModel2Graph(next);
761
781
  if (notes.length > 0) store.setModel2Notes(notes);
762
782
  store.markEdited();
763
- /*
764
- * The readiness answer is about a graph that no longer exists the moment an
765
- * edit lands, so it is dropped rather than left on screen. Re-asking on
766
- * every keystroke would be a request per character; the author asks again
767
- * with the button, and a save always re-asks on the server anyway.
768
- */ dropFeasibility();
783
+ // The old answer is about a graph that no longer exists: drop it now,
784
+ // re-ask when the author pauses.
785
+ dropFeasibility();
786
+ scheduleFeasibility();
769
787
  });
770
788
  const handleMaxPassesChange = useEffectEvent((value)=>{
771
789
  store.setMaxPasses(value);
772
790
  store.markEdited();
773
791
  // The round limit is part of the question the card answered.
774
792
  dropFeasibility();
793
+ scheduleFeasibility();
775
794
  });
776
795
  // ─── Flow details ─────────────────────────────────────────────
777
796
  //
@@ -443,6 +443,8 @@ export type VerificationFlowLabels = {
443
443
  reviewStartedNote: string;
444
444
  /** On the reviewer's card: which round, and what Reject will do. */
445
445
  pendingRound: (pass: number, max: number) => string;
446
+ /** On a reviewer's card in a later round: why the previous round was rejected. */
447
+ previousRoundReason: (reason: string) => string;
446
448
  pendingRejectSendsBack: (stepLabel: string) => string;
447
449
  pendingRejectEndsReview: string;
448
450
  pendingLastRoundWarning: string;
@@ -235,7 +235,7 @@
235
235
  approvalModeAll: 'Everyone listed',
236
236
  approvalModeAllHint: 'Every person above must decide before the record moves on.',
237
237
  approvalModeAny: 'Any one of them',
238
- approvalModeAnyHint: 'The first decision closes the step, and the others are no longer asked.',
238
+ approvalModeAnyHint: 'The first person listed to finish closes the step, and the others are no longer asked. A role set to "every member" finishes only when all its members have signed.',
239
239
  outcomes: 'Where each answer sends it',
240
240
  onApproved: 'If approved',
241
241
  onApprovedNext: 'Go to the next step',
@@ -320,6 +320,7 @@
320
320
  startReviewFailed: 'The review could not be started.',
321
321
  reviewStartedNote: 'Sent for review.',
322
322
  pendingRound: (pass, max)=>`Round ${pass} (at most ${max})`,
323
+ previousRoundReason: (reason)=>`Rejected last round because: ${reason}`,
323
324
  pendingRejectSendsBack: (stepLabel)=>`Rejecting sends this back to ${stepLabel}.`,
324
325
  pendingRejectEndsReview: 'Rejecting ends this review.',
325
326
  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.',
@@ -431,7 +432,12 @@ export function recipientTypeLabels(say) {
431
432
  // one, the engine's sentence when it does not, and a generic line only when
432
433
  // the engine sent nothing at all.
433
434
  export function refusalSentence(say, code, detail, fallback, generic) {
434
- const own = code ? say.flowRefusalByCode[code] : undefined;
435
+ /*
436
+ * Publish refuses with the READINESS codes too (a chain needing more people
437
+ * than the install has). A host that worded those only for the readiness
438
+ * card got the English sentence in the publish banner — measured by the
439
+ * re-check on the Turkish panel.
440
+ */ const own = code ? say.flowRefusalByCode[code] ?? say.feasibilityByCode[code] : undefined;
435
441
  if (own) return own(detail ?? {});
436
442
  const decision = code ? say.decisionRefusalByCode[code] : undefined;
437
443
  if (decision) return decision;
@@ -251,7 +251,9 @@ export function StepCard({ step, graph, isFirst, isLast, roles, users, triggerLa
251
251
  step_node_id: step.node_id
252
252
  },
253
253
  actions: stepActions,
254
- triggers: STEP_TRIGGERS,
254
+ // "The author sent it back" can only happen on the author's own
255
+ // correction step; offered elsewhere it saved and never fired.
256
+ triggers: (step.kind ?? 'approval') === 'revision' ? STEP_TRIGGERS : STEP_TRIGGERS.filter((t)=>t !== 'on_resubmitted'),
255
257
  triggerLabels: triggerLabels,
256
258
  recipientTypeLabels: recipientTypeLabels,
257
259
  channelLabels: channelLabels,
@@ -412,7 +412,15 @@ export function updateActionAt(graph, index, patch) {
412
412
  /** The steps an approval on `nodeId` may skip to: strictly later ones. */ export function approveTargets(graph, nodeId) {
413
413
  const step = (graph.steps ?? []).find((s)=>s.node_id === nodeId);
414
414
  if (!step) return [];
415
- return (graph.steps ?? []).filter((s)=>s.step_order > step.step_order).sort(byOrder);
415
+ const later = (graph.steps ?? []).filter((s)=>s.step_order > step.step_order).sort(byOrder);
416
+ if ((step.kind ?? 'approval') !== 'revision') return later;
417
+ /*
418
+ * A correction step may not jump OVER a reviewer. Two correction steps at
419
+ * the front with different jumps gave each round a different entry, and a
420
+ * reviewer walked in round 1 but jumped over in round 2 kept a signature on
421
+ * the old text (found by the re-check). Mirrors `revision_skips_reviewers`.
422
+ */ const firstReviewer = later.find((s)=>(s.kind ?? 'approval') !== 'revision');
423
+ return firstReviewer ? later.filter((s)=>s.step_order <= firstReviewer.step_order) : later;
416
424
  }
417
425
  /**
418
426
  * The approval outcomes the step card may OFFER for `nodeId`.
@@ -547,10 +555,12 @@ export const MAX_PASSES_MAX = 20;
547
555
  * first (found in the audit). Telling the author the two buttons do the same
548
556
  * thing there is telling them a choice that matters does not.
549
557
  */ export function approvalModeHasNoEffect(step) {
550
- const slots = step.assignees ?? [];
551
- if (slots.length !== 1) return false;
552
- const only = slots[0];
553
- return !(only?.verifier_type === 'role' && only.role_quorum === 'all');
558
+ /*
559
+ * The engine reads "any one of them" as any one SLOT, and a slot of "every
560
+ * member" is finished only when every member has signed (0.10.135). So with
561
+ * ONE slot the two modes are the same rule whatever its quorum — the
562
+ * exception this carried described the engine before that fix.
563
+ */ return (step.assignees ?? []).length === 1;
554
564
  }
555
565
  /**
556
566
  * The part of a step patch a live flow still accepts: its wording.