import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../providers/task_provider.dart'; import '../models/task_model.dart'; class TasksScreen extends ConsumerWidget { const TasksScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final tasksState = ref.watch(tasksProvider); return Scaffold( appBar: AppBar( title: const Text('Задачи'), actions: [ IconButton( icon: const Icon(Icons.filter_list), onPressed: () => _showFilterDialog(context), ), IconButton( icon: const Icon(Icons.search), onPressed: () {}, ), ], ), body: tasksState.when( data: (tasks) { if (tasks.isEmpty) { return _buildEmptyState(context); } // Группируем по статусам final todoTasks = tasks.where((t) => t.status == TaskStatus.todo).toList(); final inProgressTasks = tasks.where((t) => t.status == TaskStatus.inProgress).toList(); final doneTasks = tasks.where((t) => t.status == TaskStatus.done).toList(); return ListView( padding: const EdgeInsets.all(16), children: [ if (todoTasks.isNotEmpty) ...[ _buildSectionHeader('В планах', todoTasks.length, Colors.blue), ...todoTasks.map((task) => _TaskCard(task: task)), const SizedBox(height: 16), ], if (inProgressTasks.isNotEmpty) ...[ _buildSectionHeader( 'В работе', inProgressTasks.length, Colors.orange), ...inProgressTasks.map((task) => _TaskCard(task: task)), const SizedBox(height: 16), ], if (doneTasks.isNotEmpty) ...[ _buildSectionHeader('Готово', doneTasks.length, Colors.green), ...doneTasks.map((task) => _TaskCard(task: task)), ], ], ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 48, color: Colors.red), const SizedBox(height: 16), Text('Ошибка: $error'), const SizedBox(height: 16), ElevatedButton( onPressed: () => ref.refresh(tasksProvider), child: const Text('Повторить'), ), ], ), ), ), floatingActionButton: FloatingActionButton.extended( onPressed: () async { final result = await context .push('/home/tasks/new', extra: {'projectId': 'default'}); if (result == true) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('✅ Задача создана')), ); } }, icon: const Icon(Icons.add), label: const Text('Новая задача'), ), ); } Widget _buildEmptyState(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.task_alt, size: 100, color: Colors.grey, ), const SizedBox(height: 24), const Text( 'Нет задач', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Создайте первую задачу', style: TextStyle(color: Colors.grey), ), const SizedBox(height: 32), ElevatedButton.icon( onPressed: () => context .push('/home/tasks/new', extra: {'projectId': 'default'}), icon: const Icon(Icons.add), label: const Text('Создать задачу'), ), ], ), ); } Widget _buildSectionHeader(String title, int count, Color color) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ Container( width: 4, height: 20, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: color.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Text( '$count', style: TextStyle(color: color, fontWeight: FontWeight.bold), ), ), ], ), ); } void _showFilterDialog(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Фильтр задач'), content: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.all_inclusive), title: const Text('Все задачи'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.pending, color: Colors.blue), title: const Text('В планах'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.hourglass_empty, color: Colors.orange), title: const Text('В работе'), onTap: () => Navigator.pop(context), ), ListTile( leading: const Icon(Icons.check_circle, color: Colors.green), title: const Text('Готово'), onTap: () => Navigator.pop(context), ), ], ), ), ); } } class _TaskCard extends ConsumerWidget { final TaskModel task; const _TaskCard({required this.task}); @override Widget build(BuildContext context, WidgetRef ref) { return Card( margin: const EdgeInsets.only(bottom: 12), child: ListTile( leading: CircleAvatar( backgroundColor: _getPriorityColor(task.priority), child: Icon( _getPriorityIcon(task.priority), color: Colors.white, size: 20, ), ), title: Text( task.title, style: TextStyle( decoration: task.status == TaskStatus.done ? TextDecoration.lineThrough : null, color: task.status == TaskStatus.done ? Colors.grey : null, ), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (task.description != null && task.description!.isNotEmpty) ...[ const SizedBox(height: 4), Text( task.description!, maxLines: 2, overflow: TextOverflow.ellipsis, ), ], const SizedBox(height: 4), Row( children: [ if (task.dueDate != null) ...[ Icon( task.isOverdue ? Icons.warning : Icons.calendar_today, size: 12, color: task.isOverdue ? Colors.red : Colors.grey, ), const SizedBox(width: 4), Text( '${task.dueDate!.day}.${task.dueDate!.month}', style: TextStyle( fontSize: 12, color: task.isOverdue ? Colors.red : Colors.grey, ), ), const SizedBox(width: 12), ], if (task.estimatedHours > 0) ...[ const Icon(Icons.timer, size: 12, color: Colors.grey), const SizedBox(width: 4), Text( '${task.estimatedHours}ч', style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ], ), ], ), trailing: PopupMenuButton( onSelected: (value) => _handleMenuAction(context, ref, value), itemBuilder: (context) => [ const PopupMenuItem(value: 'edit', child: Text('Редактировать')), const PopupMenuItem(value: 'duplicate', child: Text('Дублировать')), const PopupMenuItem(value: 'delete', child: Text('Удалить')), ], ), isThreeLine: true, onTap: () => context.push('/home/tasks/${task.id}', extra: { 'task': task, 'projectId': task.projectId, }), ), ); } Color _getPriorityColor(TaskPriority priority) { switch (priority) { case TaskPriority.urgent: return Colors.red; case TaskPriority.high: return Colors.orange; case TaskPriority.medium: return Colors.blue; case TaskPriority.low: return Colors.green; } } IconData _getPriorityIcon(TaskPriority priority) { switch (priority) { case TaskPriority.urgent: return Icons.fire_engine; case TaskPriority.high: return Icons.arrow_upward; case TaskPriority.medium: return Icons.remove; case TaskPriority.low: return Icons.arrow_downward; } } void _handleMenuAction(BuildContext context, WidgetRef ref, String action) { switch (action) { case 'edit': context.push('/home/tasks/${task.id}', extra: { 'task': task, 'projectId': task.projectId, }); break; case 'delete': _confirmDelete(context, ref); break; } } void _confirmDelete(BuildContext context, WidgetRef ref) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Удалить задачу?'), content: const Text('Это действие нельзя отменить'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Отмена'), ), TextButton( onPressed: () { ref.read(tasksProvider.notifier).deleteTask(task.id); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('✅ Задача удалена')), ); }, style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Удалить'), ), ], ), ); } }