feat(programmer-tasks): loading states, confirmation & success dialogs

Day sheet dialogs (disapprove, justify): moved the async controller call
inside each dialog so loading state is owned there — button shows a
spinner and is disabled while the RPC is in flight; errors display inline
instead of via a snackbar after dialog close.

Detail screen (approve): added _approvingSheet bool; Approve button shows
CircularProgressIndicator while in flight. On success shows an AlertDialog
"Sheet Approved" with a check icon instead of silently updating state.

Approvals tab (bulk approve): upgraded post-approve feedback from a
snackbar to a success AlertDialog showing the count of approved sheets
and any skipped ones.

Day sheets tab (submit): added confirmation AlertDialog before the
existing submit flow so programmers don't accidentally submit mid-day.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-09-28 22:31:50 +08:00
parent 2946945ce3
commit 88848138d9
4 changed files with 157 additions and 58 deletions
@@ -87,12 +87,27 @@ class _ApprovalsTabState extends ConsumerState<ApprovalsTab> {
_approving = false;
});
final buf = StringBuffer('Approved ${result.approvedIds.length}');
if (result.skippedIds.isNotEmpty) {
buf.write(', ${result.skippedIds.length} already reviewed');
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(buf.toString())));
final count = result.approvedIds.length;
final skipped = result.skippedIds.length;
await showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.check_circle_outline, size: 48),
title: Text('Approved $count ${count == 1 ? 'Sheet' : 'Sheets'}'),
content: skipped > 0
? Text(
'$skipped ${skipped == 1 ? 'sheet was' : 'sheets were'} '
'already reviewed and skipped.',
)
: null,
actions: [
FilledButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('Done'),
),
],
),
);
} on DaySheetActionException catch (e) {
if (!mounted) return;
setState(() => _approving = false);