fix(approvals,l10n,theme): usertest findings — localized prompts, no-data confirm, .chain log path, honest wording, AA contrast
Some checks failed
Security / Security check (push) Failing after 2s
Some checks failed
Security / Security check (push) Failing after 2s
- approvals: empty/legacy hub prompts render the localized fallback; approving without show: data asks for conscious confirmation first - chain_log: write to ~/.chain/logs/studio-errors.log (was .fai), one-time best-effort migration of the legacy file + rotation sibling - l10n: 'manipulationssicher' -> 'manipulationserkennend', neutral WORM-1 blurb, doctor pill 'Integritätskette v1', federation hint says the CA authenticates the first connect (DE+EN) - theme: muted text token now >=4.5:1 on canvas, cards and elevated dark surfaces (was 3.7:1 on cards) Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
0fda2600ad
commit
bb606a8b23
10 changed files with 167 additions and 26 deletions
|
|
@ -10,6 +10,24 @@ import '../theme/tokens.dart';
|
|||
import '../widgets/widgets.dart';
|
||||
import 'welcome.dart' show showFaiDoc;
|
||||
|
||||
/// The fixed English sentence pre-0.21 hubs baked into stored
|
||||
/// approvals when the flow gave no `prompt:`. Newer hubs store the
|
||||
/// empty prompt verbatim.
|
||||
const _legacyHubPromptDefault =
|
||||
'Please review and approve this step before continuing.';
|
||||
|
||||
/// Reviewer-facing prompt with fallbacks: an empty prompt (the flow
|
||||
/// gave none) renders the localized default, and the legacy English
|
||||
/// default from old hub rows is mapped onto the same localized
|
||||
/// default so it stops showing English inside a German UI.
|
||||
String displayApprovalPrompt(AppLocalizations l, String prompt) {
|
||||
final trimmed = prompt.trim();
|
||||
if (trimmed.isEmpty || trimmed == _legacyHubPromptDefault) {
|
||||
return l.approvalsRequestFallback;
|
||||
}
|
||||
return prompt;
|
||||
}
|
||||
|
||||
class ApprovalsPage extends StatefulWidget {
|
||||
const ApprovalsPage({super.key});
|
||||
|
||||
|
|
@ -68,6 +86,29 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
|
||||
Future<void> _approve(ApprovalRecord a) async {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
// No `show:` data attached → never approve on a reflex. Calmly
|
||||
// explain and ask for a conscious confirmation first.
|
||||
if (a.payloadPreview == null) {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text(l.approvalsNoDataConfirmTitle),
|
||||
content: Text(l.approvalsNoDataConfirmBody),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
child: Text(l.buttonCancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: Text(l.approvalsNoDataConfirmAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true) return;
|
||||
}
|
||||
if (!mounted) return;
|
||||
try {
|
||||
await HubService.instance.approve(a.id, _reviewer);
|
||||
_toast(l.approvalsApprovedToast(a.flowName, a.stepId));
|
||||
|
|
@ -77,6 +118,7 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _reject(ApprovalRecord a) async {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final reason = await _promptReason(context);
|
||||
|
|
@ -558,9 +600,7 @@ class _ApprovalCard extends StatelessWidget {
|
|||
// step left the prompt empty, so the card is never reduced to
|
||||
// a cryptic flow id.
|
||||
Text(
|
||||
approval.prompt.trim().isEmpty
|
||||
? l.approvalsRequestFallback
|
||||
: approval.prompt,
|
||||
displayApprovalPrompt(l, approval.prompt),
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
|
|
@ -850,7 +890,13 @@ class _HistoryDialog extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SelectableText(record.prompt, style: theme.textTheme.bodyMedium),
|
||||
SelectableText(
|
||||
displayApprovalPrompt(
|
||||
AppLocalizations.of(context)!,
|
||||
record.prompt,
|
||||
),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
if (record.payloadPreview != null) ...[
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -3252,7 +3252,7 @@ const List<TodayStoryData> _kFallbackTodayStories = <TodayStoryData>[
|
|||
badgeEn: 'AUDIT',
|
||||
badgeDe: 'AUDIT',
|
||||
titleEn: 'Tamper-evident hash chain — built in',
|
||||
titleDe: 'Manipulationssicher per Hash-Kette — eingebaut',
|
||||
titleDe: 'Manipulation erkennbar per Hash-Kette — eingebaut',
|
||||
bodyEn:
|
||||
'Every flow run, every install, every approval lands in ~/.chain/audit/ as a hash-chained event log. Any later edit invalidates the chain. CRA-ready out of the box — no compliance product to buy on top.',
|
||||
bodyDe:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue