import 'package:flutter/material.dart'; import '../../../models/programmer_day_sheet.model.dart'; import '../../../utils/programmer_day_rows.dart'; /// Pure helper: if the sheet is approved with a snapshot, returns the snapshot /// rows; otherwise returns the live rows. List rowsForSheet( ProgrammerDaySheet sheet, List liveRows, ) { if (sheet.status == DaySheetStatus.approved && sheet.approvedSnapshot != null) { return sheet.approvedSnapshot!.rows; } return liveRows; } // --------------------------------------------------------------------------- // showDisapproveDialog // --------------------------------------------------------------------------- /// Shows an AlertDialog collecting disapproval remarks and optionally /// toggling which rows are flagged. /// /// Returns the non-empty remarks string on confirm, or `null` on cancel. Future showDisapproveDialog( BuildContext context, { required List flaggedRows, }) { return showDialog( context: context, barrierDismissible: false, builder: (ctx) => _DisapproveDialog(flaggedRows: flaggedRows), ); } class _DisapproveDialog extends StatefulWidget { const _DisapproveDialog({required this.flaggedRows}); final List flaggedRows; @override State<_DisapproveDialog> createState() => _DisapproveDialogState(); } class _DisapproveDialogState extends State<_DisapproveDialog> { final _remarksCtrl = TextEditingController(); String? _error; late Set _selected; @override void initState() { super.initState(); _selected = {for (final r in widget.flaggedRows) r.taskId}; } @override void dispose() { _remarksCtrl.dispose(); super.dispose(); } void _submit() { final remarks = _remarksCtrl.text.trim(); if (remarks.isEmpty) { setState(() => _error = 'Remarks are required'); return; } Navigator.of(context).pop(remarks); } @override Widget build(BuildContext context) { final tt = Theme.of(context).textTheme; final cs = Theme.of(context).colorScheme; return AlertDialog( title: const Text('Disapprove'), content: SizedBox( width: 360, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: _remarksCtrl, maxLines: 3, decoration: InputDecoration( labelText: 'Remarks', hintText: 'Explain what needs to change', errorText: _error, border: const OutlineInputBorder(), ), onChanged: (_) { if (_error != null) setState(() => _error = null); }, ), if (widget.flaggedRows.isNotEmpty) ...[ const SizedBox(height: 16), Text( 'Flagged tasks', style: tt.labelMedium?.copyWith( color: cs.onSurfaceVariant, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), for (final row in widget.flaggedRows) CheckboxListTile( dense: true, contentPadding: EdgeInsets.zero, title: Text(row.title, style: tt.bodySmall), value: _selected.contains(row.taskId), onChanged: (v) { setState(() { if (v == true) { _selected.add(row.taskId); } else { _selected.remove(row.taskId); } }); }, ), ], ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(null), child: const Text('Cancel'), ), FilledButton( onPressed: _submit, child: const Text('Disapprove'), ), ], ); } } // --------------------------------------------------------------------------- // showJustifyDialog // --------------------------------------------------------------------------- /// Shows an AlertDialog for the programmer to write a justification before /// resubmitting. /// /// Returns the non-empty justification string on confirm, or `null` on cancel. Future showJustifyDialog(BuildContext context) { return showDialog( context: context, barrierDismissible: false, builder: (ctx) => const _JustifyDialog(), ); } class _JustifyDialog extends StatefulWidget { const _JustifyDialog(); @override State<_JustifyDialog> createState() => _JustifyDialogState(); } class _JustifyDialogState extends State<_JustifyDialog> { final _bodyCtrl = TextEditingController(); String? _error; @override void dispose() { _bodyCtrl.dispose(); super.dispose(); } void _submit() { final body = _bodyCtrl.text.trim(); if (body.isEmpty) { setState(() => _error = 'Please write a justification'); return; } Navigator.of(context).pop(body); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Justify & Resubmit'), content: SizedBox( width: 360, child: TextField( controller: _bodyCtrl, maxLines: 4, decoration: InputDecoration( labelText: 'Justification', hintText: 'Explain what changed or why the sheet is correct', errorText: _error, border: const OutlineInputBorder(), ), onChanged: (_) { if (_error != null) setState(() => _error = null); }, ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(null), child: const Text('Cancel'), ), FilledButton( onPressed: _submit, child: const Text('Submit'), ), ], ); } }