import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../models/programmer_day_sheet.model.dart'; import '../../../providers/profile_provider.dart'; import '../../../providers/programmer_day_data_provider.dart'; import '../../../providers/programmer_day_sheets_provider.dart'; import '../../../theme/app_status_colors.dart'; import '../../../utils/app_time.dart'; import '../../../utils/programmer_day_rows.dart'; import '../../../utils/programmer_task_ledger.dart'; import '../../../widgets/app_state_view.dart'; import '../../../widgets/app_status_summary.dart'; import '../../../widgets/status_pill.dart'; import '../report/accomplishment_report_dialog.dart'; /// Programmer-facing tab for managing personal day sheets. /// /// Sections: /// 1. **Today** — live work rows, total duration, sheet status and a /// "Submit day" button (enabled when the sheet is missing/draft and there /// is at least one logged row). /// 2. **Needs your attention** — disapproved sheets waiting for justification. /// 3. **History** — filterable list of past sheets /// (All / Pending / Disapproved / Approved); tapping a card opens the /// [DaySheetDetailScreen]. class DaySheetsTab extends ConsumerStatefulWidget { const DaySheetsTab({super.key}); @override ConsumerState createState() => _DaySheetsTabState(); } class _DaySheetsTabState extends ConsumerState { /// Null → show all history; or a [DaySheetStatus] constant to filter. String? _historyFilter; bool _submitting = false; // --------------------------------------------------------------------------- // Actions // --------------------------------------------------------------------------- Future _submit(DateTime today) async { setState(() => _submitting = true); try { await ref.read(daySheetsControllerProvider).submit(today); if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text('Day submitted'))); } on DaySheetActionException catch (e) { if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(e.message))); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(e.toString()))); } finally { if (mounted) setState(() => _submitting = false); } } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- StatusKind _kindFor(String status) => switch (status) { DaySheetStatus.approved => StatusKind.success, DaySheetStatus.pending => StatusKind.info, DaySheetStatus.disapproved => StatusKind.warning, _ => StatusKind.neutral, }; // --------------------------------------------------------------------------- // Build // --------------------------------------------------------------------------- @override Widget build(BuildContext context) { final userId = ref.watch(currentUserIdProvider); if (userId == null) { return const Center(child: CircularProgressIndicator()); } final today = ref.watch(todayKeyProvider); final dayDataAsync = ref.watch(programmerDayDataProvider(today)); final mySheetsAsync = ref.watch(myDaySheetsProvider); final profiles = ref.watch(profilesProvider).valueOrNull ?? const []; final profileNames = {for (final p in profiles) p.id: p.fullName}; final mySheets = mySheetsAsync.valueOrNull ?? const []; // Today's sheet (if any) — compare UTC date keys. final todaySheet = mySheets.where((s) => s.workDate == today).firstOrNull; // Build live rows for today. final todayRows = dayDataAsync.when( data: (data) => buildDayRows( userId: userId, day: today, tasks: data.tasks, logsByTask: data.logsByTask, workLogs: data.workLogs, projectNames: const {}, profileNames: profileNames, now: AppTime.now(), ), loading: () => const [], error: (_, _) => const [], ); final todayTotal = Duration(seconds: totalSeconds(todayRows)); final todayStatus = todaySheet?.status ?? DaySheetStatus.draft; final canSubmit = (todaySheet == null || todayStatus == DaySheetStatus.draft) && todayRows.isNotEmpty; // Attention: disapproved sheets. final disapproved = mySheets.where((s) => s.status == DaySheetStatus.disapproved).toList(); // History: past sheets (exclude today), optionally filtered by status. final history = mySheets.where((s) { if (s.workDate == today) return false; if (_historyFilter == null) return true; return s.status == _historyFilter; }).toList(); // Counts for the history filter chips. final historyAll = mySheets.where((s) => s.workDate != today).toList(); final countPending = historyAll.where((s) => s.status == DaySheetStatus.pending).length; final countDisapproved = historyAll.where((s) => s.status == DaySheetStatus.disapproved).length; final countApproved = historyAll.where((s) => s.status == DaySheetStatus.approved).length; return ListView( padding: const EdgeInsets.all(16), children: [ // ── Report action ────────────────────────────────────────────────── Align( alignment: Alignment.centerRight, child: IconButton( icon: const Icon(Icons.description_outlined), tooltip: 'Accomplishment Report', onPressed: () => showAccomplishmentReportDialog(context, ref), ), ), // ── Today card ───────────────────────────────────────────────────── _TodayCard( total: todayTotal, status: todayStatus, statusKind: _kindFor(todayStatus), canSubmit: canSubmit, submitting: _submitting, onSubmit: () => _submit(today), onTap: todaySheet != null ? () => context.pushNamed( 'daySheetDetail', pathParameters: {'id': todaySheet.id}, ) : null, ), const SizedBox(height: 20), // ── Needs your attention ────────────────────────────────────────── if (disapproved.isNotEmpty) ...[ Text( 'Needs your attention', style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 8), for (final sheet in disapproved) _SheetHistoryTile( sheet: sheet, statusKind: _kindFor(sheet.status), onTap: () => context.pushNamed( 'daySheetDetail', pathParameters: {'id': sheet.id}, ), ), const SizedBox(height: 16), ], // ── History ──────────────────────────────────────────────────────── Text('History', style: Theme.of(context).textTheme.titleSmall), const SizedBox(height: 8), AppStatusSummaryRow( items: [ AppStatusSummaryItem( label: 'All', count: historyAll.length, kind: StatusKind.neutral, selected: _historyFilter == null, onTap: () => setState(() => _historyFilter = null), ), AppStatusSummaryItem( label: 'Pending', count: countPending, kind: StatusKind.info, selected: _historyFilter == DaySheetStatus.pending, onTap: () => setState(() => _historyFilter = DaySheetStatus.pending), ), AppStatusSummaryItem( label: 'Disapproved', count: countDisapproved, kind: StatusKind.warning, selected: _historyFilter == DaySheetStatus.disapproved, onTap: () => setState(() => _historyFilter = DaySheetStatus.disapproved), ), AppStatusSummaryItem( label: 'Approved', count: countApproved, kind: StatusKind.success, selected: _historyFilter == DaySheetStatus.approved, onTap: () => setState(() => _historyFilter = DaySheetStatus.approved), ), ], ), const SizedBox(height: 8), if (history.isEmpty) const AppEmptyView( icon: Icons.history, title: 'No sheets yet', subtitle: 'Submit your first day sheet above.', ) else for (final sheet in history) _SheetHistoryTile( sheet: sheet, statusKind: _kindFor(sheet.status), onTap: () => context.pushNamed( 'daySheetDetail', pathParameters: {'id': sheet.id}, ), ), ], ); } } // --------------------------------------------------------------------------- // Today card // --------------------------------------------------------------------------- class _TodayCard extends StatelessWidget { const _TodayCard({ required this.total, required this.status, required this.statusKind, required this.canSubmit, required this.submitting, required this.onSubmit, this.onTap, }); final Duration total; final String status; final StatusKind statusKind; final bool canSubmit; final bool submitting; final VoidCallback onSubmit; final VoidCallback? onTap; @override Widget build(BuildContext context) { final tt = Theme.of(context).textTheme; final cs = Theme.of(context).colorScheme; return Card( child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text('Today', style: tt.titleMedium), ), StatusPill.kind( statusKind, label: DaySheetStatus.label(status), ), ], ), const SizedBox(height: 8), Text( formatDurationHm(total), style: tt.headlineSmall?.copyWith( fontWeight: FontWeight.w700, color: cs.primary, ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: FilledButton.tonal( onPressed: canSubmit && !submitting ? onSubmit : null, child: submitting ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Submit day'), ), ), ], ), ), ), ); } } // --------------------------------------------------------------------------- // History tile // --------------------------------------------------------------------------- class _SheetHistoryTile extends StatelessWidget { const _SheetHistoryTile({ required this.sheet, required this.statusKind, required this.onTap, }); final ProgrammerDaySheet sheet; final StatusKind statusKind; final VoidCallback onTap; @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( title: Text(AppTime.formatDate(sheet.workDate)), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ StatusPill.kind( statusKind, label: DaySheetStatus.label(sheet.status), ), const SizedBox(width: 4), const Icon(Icons.chevron_right), ], ), onTap: onTap, ), ); } }