import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../../../providers/programmer_day_data_provider.dart'; import '../../../providers/programmer_tasks_provider.dart'; import '../../../widgets/app_section_header.dart'; import 'monitor_model.dart'; final _dateFmt = DateFormat('EEE, MMM d'); /// Side panel that shows "who's helping whom" for a selected day, with a /// ← / → stepper that cannot advance past today. /// /// Uses a [ValueNotifier] initialized to [todayKeyProvider] so the /// displayed day is independent of the parent; the family provider is /// re-watched whenever the day changes. class HelpPanel extends ConsumerStatefulWidget { const HelpPanel({super.key, this.profileNames = const {}}); final Map profileNames; @override ConsumerState createState() => _HelpPanelState(); } class _HelpPanelState extends ConsumerState { late final ValueNotifier _day; @override void initState() { super.initState(); final today = ref.read(todayKeyProvider); _day = ValueNotifier(today); } @override void dispose() { _day.dispose(); super.dispose(); } void _stepDay(int delta) { final today = ref.read(todayKeyProvider); final next = _day.value.add(Duration(days: delta)); if (delta > 0 && !next.isBefore(today) && next != today) return; if (delta > 0 && next.isAfter(today)) return; setState(() { _day.value = next; }); } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final tt = Theme.of(context).textTheme; final today = ref.watch(todayKeyProvider); final tasksAsync = ref.watch(programmerTasksProvider); final dayDataAsync = ref.watch(programmerDayDataProvider(_day.value)); final isAtToday = !_day.value.isBefore(today); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const AppSectionHeader("Who's helping whom"), // Day stepper Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.chevron_left), onPressed: () => _stepDay(-1), tooltip: 'Previous day', ), Text( _dateFmt.format(_day.value), style: tt.labelMedium?.copyWith(color: cs.onSurface), ), IconButton( icon: Icon( Icons.chevron_right, color: isAtToday ? cs.onSurface.withValues(alpha: 0.3) : null, ), onPressed: isAtToday ? null : () => _stepDay(1), tooltip: 'Next day', ), ], ), const SizedBox(height: 8), // Help edges Expanded( child: Builder( builder: (context) { if (dayDataAsync.isLoading && !dayDataAsync.hasValue) { return const Center(child: CircularProgressIndicator()); } final dayData = dayDataAsync.valueOrNull; if (dayData == null) { return _emptyText( 'No data for this day.', cs: cs, tt: tt, ); } final tasks = tasksAsync.valueOrNull ?? []; final taskById = {for (final t in tasks) t.id: t}; final edges = buildHelpEdges( workLogs: dayData.workLogs, taskById: taskById, day: _day.value, ); if (edges.isEmpty) { return _emptyText( 'No help logged this day.', cs: cs, tt: tt, ); } return ListView.separated( itemCount: edges.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, i) { final e = edges[i]; final names = widget.profileNames; final helperName = names[e.helperId] ?? e.helperId; final assigneeName = names[e.assigneeId] ?? e.assigneeId; final timeStr = TimeOfDay.fromDateTime(e.at).format(context); return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Text( '$helperName → $assigneeName · ${e.taskTitle} · ${e.minutes}m · $timeStr', style: tt.bodySmall?.copyWith(color: cs.onSurface), ), ); }, ); }, ), ), ], ); } Widget _emptyText( String msg, { required ColorScheme cs, required TextTheme tt, }) { return Center( child: Text( msg, style: tt.bodySmall?.copyWith(color: cs.onSurfaceVariant), ), ); } }