mobile_team/lib/screens/tabs/reports_tab.dart
2026-04-16 15:06:41 +05:30

214 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import 'package:open_filex/open_filex.dart';
import '../../models/project_model.dart';
import '../../models/user_model.dart';
import '../../services/report_service.dart';
import '../../services/task_log_service.dart';
import '../../services/project_service.dart';
import '../../services/auth_service.dart';
class ReportsTab extends StatefulWidget {
const ReportsTab({super.key});
@override
State<ReportsTab> createState() => _ReportsTabState();
}
class _ReportsTabState extends State<ReportsTab> {
DateTime _startDate = DateTime.now().subtract(const Duration(days: 30));
DateTime _endDate = DateTime.now().add(const Duration(hours: 23, minutes: 59));
String? _selectedProjectId;
String? _selectedUserId;
bool _isGenerating = false;
List<ProjectModel> _projects = [];
List<UserModel> _users = [];
@override
void initState() {
super.initState();
_loadFilters();
}
void _loadFilters() async {
final projects = await context.read<ProjectService>().getProjects().first;
final users = await context.read<AuthService>().getUsers().first;
if (mounted) {
setState(() {
_projects = projects;
_users = users;
});
}
}
Future<void> _generateReport(String type) async {
if (_isGenerating) return;
setState(() => _isGenerating = true);
try {
final logs = await context.read<TaskLogService>().getTaskLogs(
from: _startDate,
to: _endDate,
projectId: _selectedProjectId,
userId: _selectedUserId,
).first;
if (logs.isEmpty) {
if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("No data found for selected filters.")));
setState(() => _isGenerating = false);
return;
}
final reportService = context.read<ReportService>();
dynamic file;
if (type == 'pdf') {
file = await reportService.generatePDF(logs, _startDate, _endDate);
} else {
file = await reportService.generateExcel(logs);
}
if (file != null && mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Report generated! Opening..."), backgroundColor: Colors.green));
await OpenFilex.open(file.path);
}
} catch (e) {
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Report generation failed: $e")));
} finally {
if (mounted) setState(() => _isGenerating = false);
}
}
@override
Widget build(BuildContext context) {
final DateFormat formatter = DateFormat('EEE, dd MMM yyyy');
return Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Card(
color: Colors.indigo,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
child: Padding(
padding: EdgeInsets.all(24),
child: Column(
children: [
Icon(Icons.bar_chart, color: Colors.white, size: 48),
SizedBox(height: 16),
Text(
"Generate Analytics Report",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18),
),
SizedBox(height: 8),
Text(
"Select filters below to download your team work log report.",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white70, fontSize: 14),
),
],
),
),
),
const SizedBox(height: 32),
_buildFilterRow("Date Range", "${formatter.format(_startDate)} - ${formatter.format(_endDate)}", Icons.date_range, _pickDateRange),
_buildDropdownFilter<String?>(
"Project",
_selectedProjectId,
_projects.map((p) => DropdownMenuItem(value: p.id, child: Text(p.name))).toList(),
(v) => setState(() => _selectedProjectId = v),
Icons.folder_outlined,
),
_buildDropdownFilter<String?>(
"Team Member",
_selectedUserId,
_users.map((u) => DropdownMenuItem(value: u.uid, child: Text(u.name))).toList(),
(v) => setState(() => _selectedUserId = v),
Icons.person_outline,
),
const SizedBox(height: 48),
_isGenerating
? const Center(child: CircularProgressIndicator())
: Column(
children: [
ElevatedButton.icon(
onPressed: () => _generateReport('pdf'),
icon: const Icon(Icons.picture_as_pdf),
label: const Text("DOWNLOAD PDF"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.shade700,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 55),
),
),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: () => _generateReport('excel'),
icon: const Icon(Icons.table_chart),
label: const Text("DOWNLOAD EXCEL"),
style: OutlinedButton.styleFrom(
minimumSize: const Size(double.infinity, 55),
side: const BorderSide(color: Colors.green, width: 2),
foregroundColor: Colors.green.shade800,
),
),
],
),
],
),
),
);
}
Widget _buildFilterRow(String label, String value, IconData icon, VoidCallback onTap) {
return ListTile(
contentPadding: EdgeInsets.zero,
leading: CircleAvatar(backgroundColor: Colors.indigo.shade50, child: Icon(icon, color: Colors.indigo, size: 20)),
title: Text(label, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14)),
subtitle: Text(value),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
);
}
Widget _buildDropdownFilter<T>(String label, T value, List<DropdownMenuItem<T>> items, ValueChanged<T> onChanged, IconData icon) {
return ListTile(
contentPadding: EdgeInsets.zero,
leading: CircleAvatar(backgroundColor: Colors.indigo.shade50, child: Icon(icon, color: Colors.indigo, size: 20)),
title: Text(label, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14)),
subtitle: DropdownButton<T>(
value: value,
isExpanded: true,
underline: const SizedBox(),
hint: const Text("All"),
items: [
DropdownMenuItem<T>(value: null, child: const Text("All")),
...items,
],
onChanged: (v) {
if (v != null || true) onChanged(v as T);
},
),
);
}
void _pickDateRange() async {
final range = await showDateRangePicker(
context: context,
firstDate: DateTime(2024),
lastDate: DateTime.now(),
initialDateRange: DateTimeRange(start: _startDate, end: _endDate),
);
if (range != null) {
setState(() {
_startDate = range.start;
_endDate = DateTime(range.end.year, range.end.month, range.end.day, 23, 59, 59);
});
}
}
}