import 'package:flutter/material.dart'; import '../../../models/profile.model.dart'; import '../../../models/programmer_task.model.dart'; import '../../../providers/programmer_day_data_provider.dart'; import '../../../widgets/app_section_header.dart'; import '../widgets/programmer_task_card.dart'; typedef TaskSectionGroups = ({ List running, List paused, List notStarted, List completed, List cancelled, }); /// Groups [tasks] into dashboard buckets for [userId], using [runStates] to /// distinguish running from paused from not-started. Only tasks assigned to /// [userId] are included. TaskSectionGroups groupTasksForDashboard( List tasks, Map runStates, String userId, ) { final running = []; final paused = []; final notStarted = []; final completed = []; final cancelled = []; for (final task in tasks) { if (task.assigneeId != userId) continue; final state = runStates[task.id]?.state; if (state == ProgrammerRunState.running) { running.add(task); } else if (state == ProgrammerRunState.paused) { paused.add(task); } else if (state == ProgrammerRunState.notStarted) { notStarted.add(task); } else if (task.status == ProgrammerTaskStatus.completed) { completed.add(task); } else if (task.status == ProgrammerTaskStatus.cancelled) { cancelled.add(task); } } completed.sort( (a, b) => (b.completedAt ?? DateTime(0)).compareTo(a.completedAt ?? DateTime(0)), ); cancelled.sort( (a, b) => (b.cancelledAt ?? DateTime(0)).compareTo(a.cancelledAt ?? DateTime(0)), ); return ( running: running, paused: paused, notStarted: notStarted, completed: completed, cancelled: cancelled, ); } /// Renders the five task groups as collapsible / always-visible sections. /// /// - In progress, Paused, Not started: always visible when non-empty. /// - Completed, Cancelled: start collapsed; tap header to expand; shows first /// 10 + "Show all (N)" when N > 10; tap "Show all" to see all. /// - Empty sections produce no header. class TaskSections extends StatefulWidget { const TaskSections({ super.key, required this.groups, required this.profileById, required this.readOnly, }); final TaskSectionGroups groups; final Map profileById; final bool readOnly; @override State createState() => _TaskSectionsState(); } class _TaskSectionsState extends State { bool _completedExpanded = false; bool _completedShowAll = false; bool _cancelledExpanded = false; bool _cancelledShowAll = false; @override Widget build(BuildContext context) { final g = widget.groups; final isEmpty = g.running.isEmpty && g.paused.isEmpty && g.notStarted.isEmpty && g.completed.isEmpty && g.cancelled.isEmpty; if (isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center(child: Text('No tasks assigned to you')), ); } return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ..._alwaysSection('In progress', g.running), ..._alwaysSection('Paused', g.paused), ..._alwaysSection('Not started', g.notStarted), ..._collapsibleSection( 'Completed', g.completed, _completedExpanded, _completedShowAll, (v) => setState(() => _completedExpanded = v), (v) => setState(() => _completedShowAll = v), ), ..._collapsibleSection( 'Cancelled', g.cancelled, _cancelledExpanded, _cancelledShowAll, (v) => setState(() => _cancelledExpanded = v), (v) => setState(() => _cancelledShowAll = v), ), ], ); } List _alwaysSection(String name, List tasks) { if (tasks.isEmpty) return const []; return [ AppSectionHeader(name, trailing: Text('${tasks.length}')), ...tasks.map(_taskCard), const SizedBox(height: 8), ]; } List _collapsibleSection( String name, List tasks, bool expanded, bool showAll, void Function(bool) onToggle, void Function(bool) onShowAll, ) { if (tasks.isEmpty) return const []; List visible; if (!expanded) { visible = const []; } else if (showAll || tasks.length <= 10) { visible = tasks; } else { visible = tasks.take(10).toList(); } return [ GestureDetector( onTap: () => onToggle(!expanded), behavior: HitTestBehavior.opaque, child: AppSectionHeader(name, trailing: Text('${tasks.length}')), ), if (expanded) ...[ ...visible.map(_taskCard), if (tasks.length > 10 && !showAll) Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () => onShowAll(true), child: Text('Show all (${tasks.length})'), ), ), const SizedBox(height: 8), ], ]; } Widget _taskCard(ProgrammerTask t) => Padding( padding: const EdgeInsets.only(bottom: 10), child: ProgrammerTaskCard( task: t, assignee: t.assigneeId != null ? widget.profileById[t.assigneeId!] : null, isMine: true, readOnly: widget.readOnly, ), ); }