diff --git a/src/screens/Lead.jsx b/src/screens/Lead.jsx index 6ae9e59..0b7a069 100644 --- a/src/screens/Lead.jsx +++ b/src/screens/Lead.jsx @@ -319,6 +319,25 @@ export default function Lead() { // anyway; showing them a row of buttons that all 403 reads as a broken app // rather than as a control. const actions = actionsFor(roles, stage?.uid) + + // THE ONE THING THIS USER MUST DO, promoted to the header. "What do I do + // now" is the first question an operator asks of a lead, and it was answered + // only by scrolling past the metrics and the stage rail to the Next step + // panel. When this user has an action, it belongs top-right, where the eye + // goes for the next move; when they do not, the passive status stays there + // instead. `role: 'do'` is the committing step — never a re-run or a + // recovery lever, which must not be dressed as the thing to do. + const acceptedAlready = Boolean(row.acceptance_ref) + const primaryAction = actions.find( + (a) => a.role === 'do' && !(acceptedAlready && a.uid === 'zk-act-accept'), + ) || null + const goToAction = () => { + if (!primaryAction) return + setOpen(primaryAction.uid) + // Let the form mount, then bring the working surface into view. + requestAnimationFrame(() => + document.getElementById('lead-next')?.scrollIntoView({ behavior: 'smooth', block: 'center' })) + } const fieldCount = groups.reduce((n, [, shown]) => n + shown.length, 0) // Built from the SAME rows the full file renders, so the two can never @@ -406,7 +425,18 @@ export default function Lead() { {/* WHOSE MOVE IT IS. The stage name is in the rail below; this says who holds the lead right now, which is the question the header is really being asked. The customer tone reuses the teal cust palette. */} - {holds ? ( + {primaryAction ? ( + /* YOUR MOVE. When this user owns the next step, the header carries it + as the prominent thing — amber, because it is waiting on a person — + and pressing it opens the form below and scrolls to it. */ + + ) : holds ? (