diff --git a/assets/icons/home-icon.svg b/assets/icons/home-icon.svg new file mode 100644 index 0000000..88cb579 --- /dev/null +++ b/assets/icons/home-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/inventory-icon.svg b/assets/icons/inventory-icon.svg new file mode 100644 index 0000000..5afad1f --- /dev/null +++ b/assets/icons/inventory-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/jadwal-icon.svg b/assets/icons/jadwal-icon.svg new file mode 100644 index 0000000..ccc9f22 --- /dev/null +++ b/assets/icons/jadwal-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/notif-icon.svg b/assets/icons/notif-icon.svg new file mode 100644 index 0000000..5e5ea6b --- /dev/null +++ b/assets/icons/notif-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/app/modules/dashboard/views/petugas_desa_dashboard_view.dart b/lib/app/modules/dashboard/views/petugas_desa_dashboard_view.dart index fc04a85..590dffc 100644 --- a/lib/app/modules/dashboard/views/petugas_desa_dashboard_view.dart +++ b/lib/app/modules/dashboard/views/petugas_desa_dashboard_view.dart @@ -1,219 +1,526 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:penyaluran_app/app/modules/dashboard/controllers/dashboard_controller.dart'; +import 'package:google_fonts/google_fonts.dart'; class PetugasDesaDashboardView extends GetView { const PetugasDesaDashboardView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Dashboard Petugas Desa'), - actions: [ - IconButton( - icon: const Icon(Icons.logout), - onPressed: controller.logout, - tooltip: 'Logout', - ), - ], + final TextTheme textTheme = + GoogleFonts.dmSansTextTheme(Theme.of(context).textTheme); + + return Theme( + data: Theme.of(context).copyWith( + textTheme: textTheme, ), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Obx(() { - if (controller.isLoading.value) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Greeting - Text( - 'Halo, ${controller.roleData.value?['namaLengkap'] ?? controller.user?.email ?? 'Petugas'}!', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 5), - Text( - 'Selamat datang di Dashboard Petugas Desa ${controller.roleData.value?['Desa'] ?? ''}', - style: const TextStyle( - fontSize: 16, - color: Colors.grey, - ), - ), - const SizedBox(height: 30), - - // Petugas Data - if (controller.roleData.value != null) ...[ - const Text( - 'Data Petugas', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 10), - Card( - elevation: 2, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildInfoRow( - 'NIP', controller.roleData.value?['NIP'] ?? '-'), - _buildInfoRow('Nama', - controller.roleData.value?['namaLengkap'] ?? '-'), - _buildInfoRow('Jabatan', - controller.roleData.value?['jabatan'] ?? '-'), - _buildInfoRow('Desa', - controller.roleData.value?['Desa'] ?? '-'), - _buildInfoRow('No. HP', - controller.roleData.value?['noHP'] ?? '-'), - _buildInfoRow('Email', - controller.roleData.value?['email'] ?? '-'), - ], - ), - ), - ), - ] else ...[ - const Center( - child: Text( - 'Data petugas belum tersedia', - style: TextStyle( - fontSize: 16, - color: Colors.grey, - ), - ), - ), - ], - - const SizedBox(height: 30), - - // Menu - const Text( - 'Menu', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 10), - _buildMenuCard( - 'Penyaluran Bantuan', - 'Kelola penyaluran bantuan', - Icons.local_shipping, - Colors.blue, - () { - // Navigasi ke halaman penyaluran bantuan - }, - ), - const SizedBox(height: 10), - _buildMenuCard( - 'Pengaduan', - 'Kelola pengaduan warga', - Icons.report_problem, - Colors.orange, - () { - // Navigasi ke halaman pengaduan - }, - ), - const SizedBox(height: 10), - _buildMenuCard( - 'Laporan', - 'Lihat laporan penyaluran', - Icons.assessment, - Colors.green, - () { - // Navigasi ke halaman laporan - }, - ), - ], - ); - }), - ), - ), - ); - } - - Widget _buildInfoRow(String label, String value) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 120, - child: Text( - label, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - Expanded( - child: Text(value), - ), - ], - ), - ); - } - - Widget _buildMenuCard(String title, String subtitle, IconData icon, - Color color, VoidCallback onTap) { - return Card( - elevation: 2, - child: InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Row( + child: Scaffold( + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(8.0), + DrawerHeader( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF2E5077), Color(0xFF5882B1)], + transform: GradientRotation(96.93 * 3.14159 / 180), + ), ), - child: Icon( - icon, - color: color, - size: 30, - ), - ), - const SizedBox(width: 16), - Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + const CircleAvatar( + radius: 30, + backgroundColor: Colors.white, + child: Icon( + Icons.person, + size: 40, + color: Color(0xFF2E5077), ), ), - const SizedBox(height: 4), + const SizedBox(height: 10), Text( - subtitle, - style: TextStyle( + 'Petugas Desa', + style: textTheme.titleLarge?.copyWith( + color: Colors.white, + fontSize: 18, + ), + ), + Text( + controller.user?.email ?? 'email@example.com', + style: textTheme.bodyMedium?.copyWith( + color: Colors.white70, fontSize: 14, - color: Colors.grey[600], ), ), ], ), ), - const Icon(Icons.arrow_forward_ios, size: 16), + ListTile( + leading: const Icon(Icons.home), + title: const Text('Beranda'), + onTap: () { + Get.back(); + }, + ), + ListTile( + leading: const Icon(Icons.calendar_today), + title: const Text('Jadwal'), + onTap: () { + Get.back(); + }, + ), + ListTile( + leading: const Icon(Icons.notifications), + title: const Text('Notifikasi'), + onTap: () { + Get.back(); + }, + ), + ListTile( + leading: const Icon(Icons.inventory), + title: const Text('Inventaris'), + onTap: () { + Get.back(); + }, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.logout), + title: const Text('Logout'), + onTap: controller.logout, + ), ], ), ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Obx(() { + if (controller.isLoading.value) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header dengan greeting + _buildGreetingHeader(textTheme), + const SizedBox(height: 20), + + // Jadwal penyaluran hari ini + _buildScheduleCard( + textTheme, + title: 'Jadwal Penyaluran Hari ini', + location: 'Kantor Kepala Desa (Beras)', + dateTime: '15 April 2023, 13:00 - 14:00', + isToday: true, + ), + const SizedBox(height: 10), + + // Jadwal penyaluran mendatang + _buildScheduleCard( + textTheme, + title: 'Jadwal Penyaluran Mendatang', + location: 'Balai Desa A (Sembako)', + dateTime: '17 April 2023, 13:00 - 14:00', + isToday: false, + ), + const SizedBox(height: 20), + + // Statistik penyaluran + _buildStatisticsRow(textTheme), + const SizedBox(height: 20), + + // Progress penyaluran + _buildProgressSection(textTheme), + const SizedBox(height: 20), + + // Daftar penerima + _buildRecipientsList(textTheme), + ], + ); + }), + ), + ), + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: 0, + type: BottomNavigationBarType.fixed, + selectedLabelStyle: textTheme.bodySmall, + unselectedLabelStyle: textTheme.bodySmall, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Beranda', + ), + BottomNavigationBarItem( + icon: Icon(Icons.calendar_today), + label: 'Jadwal', + ), + BottomNavigationBarItem( + icon: Icon(Icons.notifications), + label: 'Notifikasi', + ), + BottomNavigationBarItem( + icon: Icon(Icons.inventory), + label: 'Inventaris', + ), + ], + ), + ), + ); + } + + Widget _buildGreetingHeader(TextTheme textTheme) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 1), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Selamat Datang, Ahmad!', + style: textTheme.headlineSmall?.copyWith( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5), + Text( + 'Kamu Login Sebagai Petugas Desa Jatihurip.', + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + color: Colors.grey[600], + ), + ), + ], + ), + ); + } + + Widget _buildScheduleCard( + TextTheme textTheme, { + required String title, + required String location, + required String dateTime, + bool isToday = true, + }) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF2E5077), Color(0xFF5882B1)], + transform: GradientRotation(96.93 * 3.14159 / 180), + ), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + color: Colors.white.withOpacity(0.8), + ), + ), + const SizedBox(height: 8), + Text( + location, + style: textTheme.titleLarge?.copyWith( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 4), + Text( + dateTime, + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + color: Colors.white, + ), + ), + ], + ), + ); + } + + Widget _buildStatisticsRow(TextTheme textTheme) { + return Row( + children: [ + Expanded( + child: _buildStatCard('Penerima', '3', 'Konfirmasi', textTheme), + ), + const SizedBox(width: 10), + Expanded( + child: _buildStatCard('Pengajuan', '1', 'Pengajuan', textTheme), + ), + const SizedBox(width: 10), + Expanded( + child: _buildStatCard('Pengaduan', '1', 'Pengaduan', textTheme), + ), + ], + ); + } + + Widget _buildStatCard( + String title, String count, String subtitle, TextTheme textTheme) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF2E5077), Color(0xFF5882B1)], + transform: GradientRotation(96.93 * 3.14159 / 180), + ), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + count, + style: textTheme.headlineSmall?.copyWith( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: textTheme.bodySmall?.copyWith( + fontSize: 12, + color: Colors.white, + ), + ), + ], + ), + ); + } + + Widget _buildProgressSection(TextTheme textTheme) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Progress Penyaluran', + style: textTheme.titleMedium?.copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 10, + backgroundColor: Colors.grey[300], + valueColor: const AlwaysStoppedAnimation(Color(0xFF2E5077)), + ), + ), + const SizedBox(height: 10), + Text( + '70% Selesai', + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + fontWeight: FontWeight.bold, + color: const Color(0xFF2E5077), + ), + ), + const SizedBox(height: 10), + _buildProgressDetailRow('Telah Menerima', 70, textTheme), + _buildProgressDetailRow('Dijedwalkan', 20, textTheme), + _buildProgressDetailRow('Belum Dijadwalkan', 10, textTheme), + const SizedBox(height: 5), + Text( + 'Total : 100 Penerima, Telah Disalurkan : 70 Penerima, Belum Disalurkan : 30', + style: textTheme.bodySmall?.copyWith( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ); + } + + Widget _buildProgressDetailRow(String label, int value, TextTheme textTheme) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + children: [ + Expanded( + flex: 3, + child: Text( + label, + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + Expanded( + flex: 7, + child: Stack( + children: [ + Container( + height: 8, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(4), + ), + ), + Container( + height: 8, + width: (MediaQuery.of(Get.context!).size.width - 32) * + 0.7 * + (value / 100), + decoration: BoxDecoration( + color: const Color(0xFF2E5077), + borderRadius: BorderRadius.circular(4), + ), + ), + ], + ), + ), + const SizedBox(width: 10), + Text( + '$value', + style: textTheme.bodyMedium?.copyWith( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ); + } + + Widget _buildRecipientsList(TextTheme textTheme) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Daftar Penerima', + style: textTheme.titleMedium?.copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + TextButton( + onPressed: () {}, + child: Row( + children: [ + Text( + 'Lihat Semua', + style: textTheme.bodyMedium?.copyWith( + color: const Color(0xFF2E5077), + ), + ), + Icon( + Icons.chevron_right, + size: 16, + color: const Color(0xFF2E5077), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 10), + _buildRecipientItem( + 'Siti Rahayu', '3201020107030010', 'Selesai', textTheme), + _buildRecipientItem( + 'Siti Rahayu', '3201020107030010', 'Selesai', textTheme), + _buildRecipientItem( + 'Siti Rahayu', '3201020107030010', 'Selesai', textTheme), + ], + ); + } + + Widget _buildRecipientItem( + String name, String nik, String status, TextTheme textTheme) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 1), + ), + ], + ), + child: ListTile( + title: Text( + name, + style: textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text( + 'NIK: $nik', + style: textTheme.bodyMedium, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.green.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + status, + style: textTheme.bodySmall?.copyWith( + color: Colors.green, + fontSize: 12, + ), + ), + ), + IconButton( + icon: const Icon( + Icons.chevron_right, + color: Colors.grey, + ), + onPressed: () {}, + ), + ], + ), ), ); } diff --git a/pubspec.lock b/pubspec.lock index b2c63d4..c364ca4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -168,6 +168,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.7.2" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 + url: "https://pub.dev" + source: hosted + version: "6.2.1" gotrue: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4ff1123..5d35d38 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: # Untuk tampilan loading flutter_spinkit: ^5.2.0 + google_fonts: ^6.2.1 dev_dependencies: flutter_test: @@ -72,9 +73,9 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/icons/ + - assets/images/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images