Perbarui model Pengaduan dengan menambahkan getter isUang untuk memeriksa jenis bantuan. Modifikasi tampilan dan controller di modul donatur dan petugas desa untuk meningkatkan pengalaman pengguna, termasuk penggantian ikon dan penyesuaian format tampilan jumlah bantuan. Hapus kode yang tidak diperlukan untuk menjaga kebersihan kode.

This commit is contained in:
Khafidh Fuadi
2025-04-10 14:25:41 +07:00
parent 3f78514175
commit ca6c28f3d6
40 changed files with 3103 additions and 2270 deletions

View File

@ -10,6 +10,12 @@ class CalendarViewWidget extends StatelessWidget {
final JadwalPenyaluranController controller;
final CalendarController _calendarController = CalendarController();
// Tambahkan variabel untuk status filter
final RxBool _showAllSchedules = true.obs;
// Tambahkan variabel untuk mode tampilan kalender
final Rx<CalendarView> _calendarView = CalendarView.month.obs;
CalendarViewWidget({
super.key,
required this.controller,
@ -47,20 +53,135 @@ class CalendarViewWidget extends StatelessWidget {
topRight: Radius.circular(16),
),
),
child: Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.calendar_month_rounded,
color: Colors.white,
size: 28,
// Title pada baris pertama
Row(
children: [
const Icon(
Icons.calendar_month_rounded,
color: Colors.white,
size: 24,
),
const SizedBox(width: 12),
Text(
'Kalender Penyaluran',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
const SizedBox(width: 12),
Text(
'Kalender Penyaluran',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
const SizedBox(height: 12),
// Tombol filter pada baris kedua
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// Tombol mode tampilan
Obx(() => Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Tooltip(
message: _calendarView.value == CalendarView.month
? 'Beralih ke tampilan agenda'
: 'Beralih ke tampilan kalender',
child: InkWell(
onTap: () {
_calendarView.value =
_calendarView.value == CalendarView.month
? CalendarView.schedule
: CalendarView.month;
// Update calendar controller dengan tampilan baru
_calendarController.view = _calendarView.value;
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_calendarView.value == CalendarView.month
? Icons.view_agenda
: Icons.calendar_month,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4),
Text(
_calendarView.value == CalendarView.month
? 'Agenda'
: 'Bulan',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
)),
const SizedBox(width: 8),
// Tombol filter
Obx(() => Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Tooltip(
message: _showAllSchedules.value
? 'Tampilkan hanya jadwal bulan ini'
: 'Tampilkan semua jadwal',
child: InkWell(
onTap: () {
_showAllSchedules.value =
!_showAllSchedules.value;
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_showAllSchedules.value
? Icons.filter_list
: Icons.filter_alt,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4),
Text(
_showAllSchedules.value
? 'Semua'
: 'Bulan Ini',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
)),
],
),
],
),
@ -71,7 +192,7 @@ class CalendarViewWidget extends StatelessWidget {
height: MediaQuery.of(context).size.height * 0.65,
child: Obx(() {
return SfCalendar(
view: CalendarView.month,
view: _calendarView.value,
controller: _calendarController,
initialSelectedDate: DateTime.now(),
initialDisplayDate: DateTime.now(),
@ -119,6 +240,42 @@ class CalendarViewWidget extends StatelessWidget {
),
),
),
scheduleViewSettings: ScheduleViewSettings(
appointmentItemHeight: 70,
monthHeaderSettings: MonthHeaderSettings(
height: 50,
backgroundColor: AppTheme.primaryColor,
monthTextStyle: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
weekHeaderSettings: WeekHeaderSettings(
height: 40,
textAlign: TextAlign.center,
backgroundColor: Colors.grey.shade100,
weekTextStyle: TextStyle(
color: AppTheme.primaryColor,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
dayHeaderSettings: DayHeaderSettings(
dayFormat: 'EEEE',
width: 70,
dayTextStyle: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: AppTheme.primaryColor,
),
dateTextStyle: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppTheme.primaryColor,
),
),
),
cellBorderColor: Colors.grey.withOpacity(0.2),
todayHighlightColor: AppTheme.primaryColor,
selectionDecoration: BoxDecoration(
@ -291,6 +448,7 @@ class CalendarViewWidget extends StatelessWidget {
...controller.jadwalTerlaksana,
];
// Tambahkan filter berdasarkan _showAllSchedules
DateTime now = DateTime.now();
DateTime firstDayOfMonth = DateTime(now.year, now.month, 1);
DateTime lastDayOfMonth = DateTime(now.year, now.month + 1, 0);
@ -300,9 +458,14 @@ class CalendarViewWidget extends StatelessWidget {
DateTime jadwalDate =
FormatHelper.toLocalDateTime(jadwal.tanggalPenyaluran!);
if (jadwalDate
.isAfter(firstDayOfMonth.subtract(const Duration(days: 1))) &&
jadwalDate.isBefore(lastDayOfMonth.add(const Duration(days: 1)))) {
// Filter berdasarkan bulan saat ini jika _showAllSchedules.value = false
bool shouldShow = _showAllSchedules.value ||
(jadwalDate.isAfter(
firstDayOfMonth.subtract(const Duration(days: 1))) &&
jadwalDate
.isBefore(lastDayOfMonth.add(const Duration(days: 1))));
if (shouldShow) {
Color appointmentColor;
// Periksa status jadwal menggunakan switch-case untuk konsistensi

View File

@ -1,55 +1,223 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class GreetingHeader extends StatelessWidget {
final String name;
final String role;
final String? desa;
final String? nip;
final String? profileImageUrl;
const GreetingHeader({
super.key,
required this.name,
required this.role,
this.desa,
this.nip,
this.profileImageUrl,
});
String _getGreeting() {
final hour = DateTime.now().hour;
if (hour < 12) {
return 'Selamat Pagi';
} else if (hour < 15) {
return 'Selamat Siang';
} else if (hour < 19) {
return 'Selamat Sore';
} else {
return 'Selamat Malam';
}
}
String _getCurrentDate() {
final now = DateTime.now();
return DateFormat('EEEE, d MMMM yyyy', 'id_ID').format(now);
}
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
Colors.blue.shade800,
Colors.blue.shade600,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withAlpha(26), // 0.1 * 255 ≈ 26
color: Colors.blue.withOpacity(0.3),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 1),
blurRadius: 15,
offset: const Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Selamat Datang, $name!',
style: textTheme.headlineSmall?.copyWith(
fontSize: 24,
fontWeight: FontWeight.bold,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getGreeting(),
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
const SizedBox(height: 8),
Text(
name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(width: 12),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
image: profileImageUrl != null && profileImageUrl!.isNotEmpty
? DecorationImage(
image: NetworkImage(profileImageUrl!),
fit: BoxFit.cover,
)
: null,
),
child: profileImageUrl == null || profileImageUrl!.isEmpty
? const Icon(
Icons.person,
size: 30,
color: Colors.white,
)
: null,
),
],
),
const SizedBox(height: 5),
Text(
desa != null && desa!.isNotEmpty
? 'Kamu Login Sebagai $role $desa.'
: 'Kamu Login Sebagai $role.',
style: textTheme.bodyMedium?.copyWith(
fontSize: 14,
color: Colors.grey[600],
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.location_on,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Flexible(
child: Text(
desa != null && desa!.isNotEmpty
? '$role - Desa $desa'
: role,
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
if (nip != null && nip!.isNotEmpty)
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.badge,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Text(
'NIP: $nip',
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
),
],
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.calendar_today,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Text(
_getCurrentDate(),
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
),
],
),
),
],
),
],
),