From 626404c5e400edcd8322d5289521bdde3f23d29d Mon Sep 17 00:00:00 2001 From: flemming-it Date: Tue, 9 Jun 2026 09:18:55 +0200 Subject: [PATCH] feat(studio): wire inline approval driver methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StudioFlowRunDriver gains three new methods that satisfy the editor 0.21.0 FlowRunDriver interface: - pendingApprovalIdForStep(flowName, stepId) — picks the newest pending approval row matching this (flow, step) pair from HubService.pendingApprovals(). - approveApproval(approvalId, reviewer) — delegates to HubService.approve, same RPC the Approvals page uses. - rejectApproval(approvalId, reviewer, reason) — delegates to HubService.reject. End result: when a flow run pauses on system.approval@^0, the Run tab now renders a complete Approve / Reject form directly under the awaiting step. Operator no longer needs to switch tabs to make the decision; the standalone Approvals page stays available for non-running approvals and history. Studio bumped to 0.68.0; editor pin moves to 0.21.0. Signed-off-by: flemming-it --- lib/data/flow_run_driver.dart | 36 +++++++++++++++++++++++++++++++++++ lib/main.dart | 2 +- pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/data/flow_run_driver.dart b/lib/data/flow_run_driver.dart index 832f5e6..34af0b7 100644 --- a/lib/data/flow_run_driver.dart +++ b/lib/data/flow_run_driver.dart @@ -120,6 +120,42 @@ class StudioFlowRunDriver implements editor.FlowRunDriver { return null; } + @override + Future pendingApprovalIdForStep({ + required String flowName, + required String stepId, + }) async { + try { + final pending = await HubService.instance.pendingApprovals(); + // Match the newest approval for this (flow, step). The hub + // creates one approval row per step invocation; if multiple + // are pending we surface the most recent (operator can still + // navigate to the standalone Approvals page for the others). + final match = pending + .where((p) => p.flowName == flowName && p.stepId == stepId) + .toList() + ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); + return match.isEmpty ? null : match.first.id; + } catch (_) { + return null; + } + } + + @override + Future approveApproval({ + required String approvalId, + required String reviewer, + }) => + HubService.instance.approve(approvalId, reviewer); + + @override + Future rejectApproval({ + required String approvalId, + required String reviewer, + required String reason, + }) => + HubService.instance.reject(approvalId, reviewer, reason); + /// Studio's FlowOutput hierarchy is gRPC-shaped (one class /// per payload variant); the editor's is host-agnostic /// (text / json / bytes only). File outputs degrade to diff --git a/lib/main.dart b/lib/main.dart index 0973317..184b4f9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,7 @@ import 'widgets/widgets.dart'; /// Studio's own build version. Bump on every UI commit so the /// running app self-identifies — visible in the sidebar header /// and quick-glance proof that you're seeing the current build. -const String kStudioVersion = '0.67.0'; +const String kStudioVersion = '0.68.0'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/pubspec.lock b/pubspec.lock index 913973f..3c891f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -126,7 +126,7 @@ packages: path: "../fai_studio_flow_editor" relative: true source: path - version: "0.20.1" + version: "0.21.0" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6dad0ed..a59304b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: fai_studio description: "F∆I Studio — desktop GUI for the F∆I hub" publish_to: 'none' -version: 0.67.0 +version: 0.68.0 environment: sdk: ^3.11.0-200.1.beta