479 lines
15 KiB
Dart
479 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
import 'package:sijentik/component/app_theme.dart';
|
|
import 'history_page.dart';
|
|
import 'add_report.dart';
|
|
import 'reports_page.dart';
|
|
import 'profile_page.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
class DashboardPage extends StatefulWidget {
|
|
final Map<String, dynamic> user;
|
|
|
|
const DashboardPage({super.key, required this.user});
|
|
|
|
@override
|
|
State<DashboardPage> createState() => _DashboardPageState();
|
|
}
|
|
|
|
class _DashboardPageState extends State<DashboardPage> {
|
|
|
|
String address = '';
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
loadUserData();
|
|
}
|
|
|
|
Future<void> loadUserData() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
setState(() {
|
|
address = prefs.getString('address') ?? '';
|
|
});
|
|
}
|
|
|
|
// Data statistik dummy
|
|
final Map<String, dynamic> stats = const {
|
|
'totalReports': 24,
|
|
'approved': 18,
|
|
'pending': 4,
|
|
'rejected': 2,
|
|
'percentage': 75,
|
|
};
|
|
|
|
// Data laporan terbaru dummy
|
|
final List<Map<String, dynamic>> recentReports = const [
|
|
{
|
|
'id': 'LP001',
|
|
'address': 'Jl. Merdeka No. 12',
|
|
'date': '12 Mar 2024',
|
|
'status': 'Disetujui',
|
|
'statusColor': Colors.green,
|
|
},
|
|
{
|
|
'id': 'LP002',
|
|
'address': 'Jl. Sudirman No. 45',
|
|
'date': '11 Mar 2024',
|
|
'status': 'Menunggu',
|
|
'statusColor': Colors.orange,
|
|
},
|
|
{
|
|
'id': 'LP003',
|
|
'address': 'Jl. Gatot Subroto No. 8',
|
|
'date': '10 Mar 2024',
|
|
'status': 'Disetujui',
|
|
'statusColor': Colors.green,
|
|
},
|
|
];
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// ===== HEADER SELAMAT DATANG =====
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF206E97),
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Selamat Datang,',
|
|
style: TextStyle(fontSize: 16, color: Colors.white70),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
widget.user['name'] ?? 'Kader',
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
Text(
|
|
'Wilayah: ${widget.user['address']}',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.white.withOpacity(0.9),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
'Total Laporan: ${stats['totalReports']}',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.white.withOpacity(0.9),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 20),
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.medical_services_outlined,
|
|
size: 40,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 25),
|
|
|
|
// ===== STATISTIK CARD =====
|
|
const Text(
|
|
'Statistik Laporan',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 15),
|
|
|
|
GridView.count(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
crossAxisCount: 2,
|
|
crossAxisSpacing: 15,
|
|
mainAxisSpacing: 15,
|
|
childAspectRatio: 1.2,
|
|
children: [
|
|
_buildStatCard(
|
|
title: 'Total Laporan',
|
|
value: '${stats['totalReports']}',
|
|
icon: Icons.assignment_outlined,
|
|
color: const Color(0xFF206E97),
|
|
),
|
|
_buildStatCard(
|
|
title: 'Disetujui',
|
|
value: '${stats['approved']}',
|
|
icon: Icons.check_circle_outline,
|
|
color: Colors.green,
|
|
),
|
|
_buildStatCard(
|
|
title: 'Menunggu',
|
|
value: '${stats['pending']}',
|
|
icon: Icons.access_time_outlined,
|
|
color: Colors.orange,
|
|
),
|
|
_buildStatCard(
|
|
title: 'Ditolak',
|
|
value: '${stats['rejected']}',
|
|
icon: Icons.cancel_outlined,
|
|
color: Colors.red,
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 25),
|
|
|
|
// ===== PERSENTASE DISETUJUI =====
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
SizedBox(
|
|
width: 80,
|
|
height: 80,
|
|
child: CircularProgressIndicator(
|
|
value: stats['percentage'] / 100,
|
|
strokeWidth: 8,
|
|
backgroundColor: Colors.grey[200],
|
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
|
Color(0xFF206E97),
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
'${stats['percentage']}%',
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(width: 20),
|
|
const Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Persentase Disetujui',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
Text(
|
|
'75% laporan Anda telah disetujui petugas. Pertahankan!',
|
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 25),
|
|
|
|
// ===== LAPORAN TERBARU =====
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text(
|
|
'Laporan Terbaru',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
// Navigasi ke halaman riwayat
|
|
// Navigator.push(context, MaterialPageRoute(builder: (context) => HistoryPage()));
|
|
},
|
|
child: const Text(
|
|
'Lihat Semua',
|
|
style: TextStyle(color: Color(0xFF206E97)),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: recentReports.map((report) {
|
|
return Column(
|
|
children: [
|
|
ListTile(
|
|
leading: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF206E97).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: const Icon(
|
|
Icons.home_outlined,
|
|
color: Color(0xFF206E97),
|
|
size: 20,
|
|
),
|
|
),
|
|
title: Text(
|
|
report['address'],
|
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
|
),
|
|
subtitle: Text(
|
|
'ID: ${report['id']} • ${report['date']}',
|
|
style: const TextStyle(fontSize: 12),
|
|
),
|
|
trailing: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: report['statusColor'].withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
report['status'],
|
|
style: TextStyle(
|
|
color: report['statusColor'],
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
onTap: () {
|
|
// Navigasi ke detail laporan
|
|
},
|
|
),
|
|
if (report != recentReports.last)
|
|
const Divider(height: 1, indent: 16, endIndent: 16),
|
|
],
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 25),
|
|
|
|
const SizedBox(height: 40),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatCard({
|
|
required String title,
|
|
required String value,
|
|
required IconData icon,
|
|
required Color color,
|
|
}) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(icon, color: color, size: 20),
|
|
),
|
|
const Spacer(),
|
|
Text(
|
|
value,
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 13,
|
|
color: Colors.grey,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildQuickActionCard({
|
|
required IconData icon,
|
|
required String title,
|
|
required String subtitle,
|
|
required Color color,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(icon, color: color, size: 24),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
subtitle,
|
|
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|