feat(studio): Approvals page gains History tab
Pending tab keeps the existing card layout for actionable approvals. New History tab shows decided rows (approved / rejected / expired) as compact one-liners, color-coded by status with reviewer + decided-at metadata. Click expands to the full record (prompt, payload preview, reason, ids). Closes the visibility gap from the recent fix: rejected approvals were correctly written to the hash-chained audit log but invisible to operators on the Approvals page itself. HubService gains `listApprovalsRecords(statuses:)` returning the decided-side fields the Studio wire previously dropped. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
d82738e17e
commit
d9aabfd00a
2 changed files with 448 additions and 53 deletions
|
|
@ -461,6 +461,42 @@ class HubService {
|
|||
.toList();
|
||||
}
|
||||
|
||||
/// List approvals filtered by status. Empty [statuses] returns
|
||||
/// every row (pending + decided + expired). Returned records
|
||||
/// carry the decided-side fields so the History tab can render
|
||||
/// "rejected by alice@studio at … because …".
|
||||
Future<List<ApprovalRecord>> listApprovalsRecords({
|
||||
List<String> statuses = const [],
|
||||
int limit = 200,
|
||||
}) async {
|
||||
final entries = await _client.listApprovals(
|
||||
statuses: statuses,
|
||||
limit: limit,
|
||||
);
|
||||
return entries
|
||||
.map(
|
||||
(e) => ApprovalRecord(
|
||||
id: e.approvalId,
|
||||
flowName: e.flowName,
|
||||
stepId: e.stepId,
|
||||
prompt: e.prompt,
|
||||
payloadPreview:
|
||||
e.payloadPreview.isEmpty ? null : e.payloadPreview,
|
||||
createdAt: DateTime.tryParse(e.createdAt) ?? DateTime.now(),
|
||||
expiresAt: e.expiresAt.isEmpty
|
||||
? null
|
||||
: DateTime.tryParse(e.expiresAt),
|
||||
status: e.status,
|
||||
decidedAt: e.decidedAt.isEmpty
|
||||
? null
|
||||
: DateTime.tryParse(e.decidedAt),
|
||||
decidedBy: e.decidedBy,
|
||||
reason: e.reason,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> approve(String id, String reviewer) =>
|
||||
_client.approve(approvalId: id, reviewer: reviewer);
|
||||
|
||||
|
|
@ -695,6 +731,41 @@ class PendingApproval {
|
|||
});
|
||||
}
|
||||
|
||||
/// Full approval record including decided-side fields. Used by
|
||||
/// the History tab so operators can review what they (or the
|
||||
/// system) decided.
|
||||
class ApprovalRecord {
|
||||
final String id;
|
||||
final String flowName;
|
||||
final String stepId;
|
||||
final String prompt;
|
||||
final String? payloadPreview;
|
||||
final DateTime createdAt;
|
||||
final DateTime? expiresAt;
|
||||
/// One of: pending / approved / rejected / expired.
|
||||
final String status;
|
||||
final DateTime? decidedAt;
|
||||
/// Reviewer handle (e.g. "alice@studio") or "system" for
|
||||
/// auto-expired approvals. Empty until decided.
|
||||
final String decidedBy;
|
||||
/// Reject reason — empty for approve / pending / expired.
|
||||
final String reason;
|
||||
|
||||
const ApprovalRecord({
|
||||
required this.id,
|
||||
required this.flowName,
|
||||
required this.stepId,
|
||||
required this.prompt,
|
||||
required this.payloadPreview,
|
||||
required this.createdAt,
|
||||
required this.expiresAt,
|
||||
required this.status,
|
||||
required this.decidedAt,
|
||||
required this.decidedBy,
|
||||
required this.reason,
|
||||
});
|
||||
}
|
||||
|
||||
class SystemAiStatus {
|
||||
final bool enabled;
|
||||
final String provider;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue