feat(studio): approval payload — explain absence, cap height, accurate label
Some checks failed
Security / Security check (push) Failing after 1s

The approval card only rendered the payload section when a preview
existed, so a step with no 'show:' showed nothing and the reviewer
could not tell why. Now always show the section: a present payload
scrolls inside a height-capped, copyable box; an absent one shows an
explanatory hint (the flow's approval step chooses what to surface via
its 'show:' field). Relabel 'data to be released' -> 'data to review'
(the payload is review context, accurate to its source).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-06-19 02:29:20 +02:00
parent 8a40e8e315
commit 97f3cb8560
6 changed files with 75 additions and 23 deletions

View file

@ -1014,7 +1014,8 @@
"approvalsPillApproved": "freigegeben", "approvalsPillApproved": "freigegeben",
"approvalsPillRejected": "abgelehnt", "approvalsPillRejected": "abgelehnt",
"approvalsPillExpired": "abgelaufen", "approvalsPillExpired": "abgelaufen",
"approvalsPayloadPreview": "DIESE DATEN WERDEN FREIGEGEBEN", "approvalsPayloadPreview": "ZU PRÜFENDE DATEN",
"approvalsNoPayload": "Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setze es, um die Daten hinter dieser Entscheidung sichtbar zu machen.",
"approvalsRequestFallback": "Freigabe für diesen Schritt erforderlich", "approvalsRequestFallback": "Freigabe für diesen Schritt erforderlich",
"approvalsFlowStepMeta": "Flow: {flow} · Schritt: {step}", "approvalsFlowStepMeta": "Flow: {flow} · Schritt: {step}",
"approvalsApproveButton": "Freigeben", "approvalsApproveButton": "Freigeben",

View file

@ -1027,7 +1027,8 @@
"approvalsPillApproved": "approved", "approvalsPillApproved": "approved",
"approvalsPillRejected": "rejected", "approvalsPillRejected": "rejected",
"approvalsPillExpired": "expired", "approvalsPillExpired": "expired",
"approvalsPayloadPreview": "DATA TO BE RELEASED", "approvalsPayloadPreview": "DATA TO REVIEW",
"approvalsNoPayload": "No data was attached for review. The flow's approval step chooses what to show via its \"show:\" field — set it to surface the data behind this decision.",
"approvalsRequestFallback": "Approval required for this step", "approvalsRequestFallback": "Approval required for this step",
"approvalsFlowStepMeta": "Flow: {flow} · Step: {step}", "approvalsFlowStepMeta": "Flow: {flow} · Step: {step}",
"@approvalsFlowStepMeta": { "@approvalsFlowStepMeta": {

View file

@ -3065,9 +3065,15 @@ abstract class AppLocalizations {
/// No description provided for @approvalsPayloadPreview. /// No description provided for @approvalsPayloadPreview.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'DATA TO BE RELEASED'** /// **'DATA TO REVIEW'**
String get approvalsPayloadPreview; String get approvalsPayloadPreview;
/// No description provided for @approvalsNoPayload.
///
/// In en, this message translates to:
/// **'No data was attached for review. The flow\'s approval step chooses what to show via its \"show:\" field — set it to surface the data behind this decision.'**
String get approvalsNoPayload;
/// No description provided for @approvalsRequestFallback. /// No description provided for @approvalsRequestFallback.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View file

@ -1767,7 +1767,11 @@ class AppLocalizationsDe extends AppLocalizations {
String get approvalsPillExpired => 'abgelaufen'; String get approvalsPillExpired => 'abgelaufen';
@override @override
String get approvalsPayloadPreview => 'DIESE DATEN WERDEN FREIGEGEBEN'; String get approvalsPayloadPreview => 'ZU PRÜFENDE DATEN';
@override
String get approvalsNoPayload =>
'Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setze es, um die Daten hinter dieser Entscheidung sichtbar zu machen.';
@override @override
String get approvalsRequestFallback => String get approvalsRequestFallback =>

View file

@ -1777,7 +1777,11 @@ class AppLocalizationsEn extends AppLocalizations {
String get approvalsPillExpired => 'expired'; String get approvalsPillExpired => 'expired';
@override @override
String get approvalsPayloadPreview => 'DATA TO BE RELEASED'; String get approvalsPayloadPreview => 'DATA TO REVIEW';
@override
String get approvalsNoPayload =>
'No data was attached for review. The flow\'s approval step chooses what to show via its \"show:\" field — set it to surface the data behind this decision.';
@override @override
String get approvalsRequestFallback => 'Approval required for this step'; String get approvalsRequestFallback => 'Approval required for this step';

View file

@ -585,7 +585,6 @@ class _ApprovalCard extends StatelessWidget {
), ),
], ],
), ),
if (approval.payloadPreview != null) ...[
const SizedBox(height: ChainSpace.md), const SizedBox(height: ChainSpace.md),
Padding( Padding(
padding: const EdgeInsets.only(bottom: 4), padding: const EdgeInsets.only(bottom: 4),
@ -598,14 +597,20 @@ class _ApprovalCard extends StatelessWidget {
), ),
), ),
), ),
if (approval.payloadPreview != null)
Container( Container(
width: double.infinity, width: double.infinity,
// Cap the height so a large payload scrolls inside the
// card instead of stretching it off-screen; still fully
// readable + copyable (SelectableText).
constraints: const BoxConstraints(maxHeight: 280),
padding: const EdgeInsets.all(ChainSpace.md), padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh, color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(ChainRadius.sm), borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant), border: Border.all(color: theme.colorScheme.outlineVariant),
), ),
child: SingleChildScrollView(
child: SelectableText( child: SelectableText(
_prettyPreview(approval.payloadPreview!), _prettyPreview(approval.payloadPreview!),
style: ChainTheme.mono( style: ChainTheme.mono(
@ -614,7 +619,38 @@ class _ApprovalCard extends StatelessWidget {
), ),
), ),
), ),
)
else
// No `show:` on the approval step explain where the data
// would come from instead of rendering nothing.
Container(
width: double.infinity,
padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.info_outline,
size: 14,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
l.approvalsNoPayload,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
], ],
),
),
const SizedBox(height: ChainSpace.lg), const SizedBox(height: ChainSpace.lg),
Row( Row(
children: [ children: [