feat(studio): operator QoL — flow runnability, audit grouping, batch approvals, welcome celebration (v0.42.0)

Four UX threads stitched into one commit. Each pulls Studio
toward Stefan's "zero-learning-curve" goal — the feedback
that earned its own memory entry.

1. Flow-Runnability-Indikator
   ─────────────────────────
   The Flows tab now fetches `listFlows` and `listModules`
   in parallel. Each card compares the flow's
   `requiredCapabilities` against the installed-modules'
   capability set; rows with missing modules show a "Needs:
   text.extract@^0" red pill row beneath the path and have
   their Run button greyed out + tooltip
   "Install the missing modules first." Operators stop
   hitting Run → cryptic hub error → frustration.

2. Welcome-Checklist Celebration
   ─────────────────────────────
   Once all four checklist signals flip to done, an
   `_AllDoneCelebration` card replaces the bare
   "All four steps complete" + Hide button. Three concrete
   next-threads with action buttons: "Read the audit log",
   "Set up the daily Today story" (opens the Flows / Today
   doc inline via `_DocReaderSheet`), and "Build your own
   module" (opens the architecture doc). Operator who just
   got set up sees what to do next instead of an empty
   "what now?" feeling.

3. Audit-Page Time-Bucket Headers + Flow-Run Detail
   ────────────────────────────────────────────────
   The flat event list grows tiny "TODAY / YESTERDAY /
   EARLIER THIS WEEK / OLDER" section headers — bucket is
   computed in the operator's local timezone so an event at
   23:55 yesterday in Berlin doesn't end up in "today"
   because UTC happened to spill into a new day.
   Plus: the event-detail dialog gains a "View flow run"
   action when the picked event has a `flow_execution`. It
   opens a drill-down that lists every event in the
   already-fetched 100-event window sharing the same
   execution id, sorted ascending — the operator reads the
   run from step.started top to flow.completed bottom.

4. Approvals-Batch-Aktionen
   ────────────────────────
   Each pending approval card grows a checkbox. When ≥1
   selected, a floating action bar appears at the bottom
   with "N selected · Select all · Clear · Reject all ·
   Approve all". The parent loops sequentially through the
   per-record SDK calls so a partial failure produces
   "X done, Y failed" instead of a confusing all-or-nothing
   rollback. Reject prompts for a reason once and applies
   to the whole picked set.

13 new ARB keys cover the strings the four features
needed. Studio's tests stay green.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-09 11:24:57 +02:00
parent f90d8cc7a7
commit 2002486828
13 changed files with 1215 additions and 64 deletions

View file

@ -1880,6 +1880,174 @@ abstract class AppLocalizations {
/// **'Could not read \"{path}\": {error}'**
String flowsFileReadFailed(String path, String error);
/// No description provided for @flowsMissingModulesLabel.
///
/// In en, this message translates to:
/// **'Needs:'**
String get flowsMissingModulesLabel;
/// No description provided for @flowsRunDisabledTooltip.
///
/// In en, this message translates to:
/// **'Install the missing modules first.'**
String get flowsRunDisabledTooltip;
/// No description provided for @welcomeChecklistAllSetTitle.
///
/// In en, this message translates to:
/// **'You\'re set up.'**
String get welcomeChecklistAllSetTitle;
/// No description provided for @welcomeChecklistAllSetBody.
///
/// In en, this message translates to:
/// **'Three threads to pull on next:'**
String get welcomeChecklistAllSetBody;
/// No description provided for @welcomeChecklistNextAuditTitle.
///
/// In en, this message translates to:
/// **'Read the audit log'**
String get welcomeChecklistNextAuditTitle;
/// No description provided for @welcomeChecklistNextAuditBody.
///
/// In en, this message translates to:
/// **'Every install, flow run, and approval lives in the hash-chained log. Open the Audit tab to see what your hub has been up to.'**
String get welcomeChecklistNextAuditBody;
/// No description provided for @welcomeChecklistNextAuditButton.
///
/// In en, this message translates to:
/// **'Open Audit'**
String get welcomeChecklistNextAuditButton;
/// No description provided for @welcomeChecklistNextTodayTitle.
///
/// In en, this message translates to:
/// **'Set up the daily Today story'**
String get welcomeChecklistNextTodayTitle;
/// No description provided for @welcomeChecklistNextTodayBody.
///
/// In en, this message translates to:
/// **'Run tools/today/propose.sh on a cron and the editorial card on the store auto-fills with operator-curated narratives.'**
String get welcomeChecklistNextTodayBody;
/// No description provided for @welcomeChecklistNextTodayButton.
///
/// In en, this message translates to:
/// **'Open Today docs'**
String get welcomeChecklistNextTodayButton;
/// No description provided for @welcomeChecklistNextModuleTitle.
///
/// In en, this message translates to:
/// **'Build your own module'**
String get welcomeChecklistNextModuleTitle;
/// No description provided for @welcomeChecklistNextModuleBody.
///
/// In en, this message translates to:
/// **'Generate a Rust+WASM scaffold with `fai new module <name>`. The module-sdk handles the WIT plumbing; you write a typed invoke.'**
String get welcomeChecklistNextModuleBody;
/// No description provided for @welcomeChecklistNextModuleButton.
///
/// In en, this message translates to:
/// **'Read the docs'**
String get welcomeChecklistNextModuleButton;
/// No description provided for @auditGroupToday.
///
/// In en, this message translates to:
/// **'TODAY'**
String get auditGroupToday;
/// No description provided for @auditGroupYesterday.
///
/// In en, this message translates to:
/// **'YESTERDAY'**
String get auditGroupYesterday;
/// No description provided for @auditGroupThisWeek.
///
/// In en, this message translates to:
/// **'EARLIER THIS WEEK'**
String get auditGroupThisWeek;
/// No description provided for @auditGroupOlder.
///
/// In en, this message translates to:
/// **'OLDER'**
String get auditGroupOlder;
/// No description provided for @auditEventViewFlowRun.
///
/// In en, this message translates to:
/// **'View flow run'**
String get auditEventViewFlowRun;
/// No description provided for @auditFlowRunDialogTitle.
///
/// In en, this message translates to:
/// **'Flow run · {flow}'**
String auditFlowRunDialogTitle(String flow);
/// No description provided for @auditFlowRunDialogSubtitle.
///
/// In en, this message translates to:
/// **'{n} {n, plural, =1{event} other{events}} for execution {execution}'**
String auditFlowRunDialogSubtitle(int n, String execution);
/// No description provided for @approvalsSelectAll.
///
/// In en, this message translates to:
/// **'Select all'**
String get approvalsSelectAll;
/// No description provided for @approvalsClearSelection.
///
/// In en, this message translates to:
/// **'Clear'**
String get approvalsClearSelection;
/// No description provided for @approvalsBatchSelected.
///
/// In en, this message translates to:
/// **'{n} selected'**
String approvalsBatchSelected(int n);
/// No description provided for @approvalsBatchApprove.
///
/// In en, this message translates to:
/// **'Approve all'**
String get approvalsBatchApprove;
/// No description provided for @approvalsBatchReject.
///
/// In en, this message translates to:
/// **'Reject all'**
String get approvalsBatchReject;
/// No description provided for @approvalsBatchApproveDoneToast.
///
/// In en, this message translates to:
/// **'{n} approvals approved.'**
String approvalsBatchApproveDoneToast(int n);
/// No description provided for @approvalsBatchRejectDoneToast.
///
/// In en, this message translates to:
/// **'{n} approvals rejected.'**
String approvalsBatchRejectDoneToast(int n);
/// No description provided for @approvalsBatchPartialFailure.
///
/// In en, this message translates to:
/// **'{ok} done, {failed} failed.'**
String approvalsBatchPartialFailure(int ok, int failed);
/// No description provided for @flowsRunButton.
///
/// In en, this message translates to: