fitur petugas

This commit is contained in:
Andreas Malvino
2025-06-22 09:25:58 +07:00
parent c4dd4fdfa2
commit 8284c93aa5
48 changed files with 8688 additions and 3436 deletions

View File

@ -8,17 +8,11 @@ import '../../../data/providers/aset_provider.dart';
class WargaSewaBinding extends Bindings {
@override
void dependencies() {
// Ensure NavigationService is registered and set to Sewa tab
if (Get.isRegistered<NavigationService>()) {
final navService = Get.find<NavigationService>();
navService.setNavIndex(1); // Set to Sewa tab
}
// Ensure AuthProvider is registered
if (!Get.isRegistered<AuthProvider>()) {
Get.put(AuthProvider(), permanent: true);
}
// Ensure AsetProvider is registered
if (!Get.isRegistered<AsetProvider>()) {
Get.put(AsetProvider(), permanent: true);

View File

@ -88,6 +88,19 @@ class SewaAsetController extends GetxController
void onReady() {
super.onReady();
debugPrint('🚀 SewaAsetController: onReady called');
// Set tab index from arguments (if any) after build
Future.delayed(Duration.zero, () {
final args = Get.arguments;
if (args != null && args is Map && args['tab'] != null) {
int initialTab =
args['tab'] is int
? args['tab']
: int.tryParse(args['tab'].toString()) ?? 0;
if (tabController.length > initialTab) {
tabController.index = initialTab;
}
}
});
}
@override

View File

@ -2,11 +2,14 @@ import 'package:get/get.dart';
import '../../../data/providers/auth_provider.dart';
import '../../../routes/app_routes.dart';
import '../../../services/navigation_service.dart';
import '../../../data/providers/aset_provider.dart';
import 'package:intl/intl.dart';
class WargaDashboardController extends GetxController {
// Dependency injection
final AuthProvider _authProvider = Get.find<AuthProvider>();
final NavigationService navigationService = Get.find<NavigationService>();
final AsetProvider _asetProvider = Get.find<AsetProvider>();
// User data
final userName = 'Pengguna Warga'.obs;
@ -28,6 +31,11 @@ class WargaDashboardController extends GetxController {
// Active penalties
final activePenalties = <Map<String, dynamic>>[].obs;
// Summary counts
final diterimaCount = 0.obs;
final tagihanAktifCount = 0.obs;
final dendaAktifCount = 0.obs;
@override
void onInit() {
super.onInit();
@ -36,6 +44,7 @@ class WargaDashboardController extends GetxController {
navigationService.setNavIndex(0);
// Load user data
fetchProfileFromWargaDesa();
_loadUserData();
// Load sample data
@ -46,6 +55,12 @@ class WargaDashboardController extends GetxController {
// Load unpaid rentals
loadUnpaidRentals();
// Debug count sewa_aset by status
_debugCountSewaAset();
// Load sewa aktif
loadActiveRentals();
}
Future<void> _loadUserData() async {
@ -112,7 +127,7 @@ class WargaDashboardController extends GetxController {
}
void refreshData() {
// Refresh data from repository
fetchProfileFromWargaDesa();
_loadSampleData();
loadDummyData();
}
@ -129,12 +144,17 @@ class WargaDashboardController extends GetxController {
// Already on Home tab
break;
case 1:
// Navigate to Sewa page
navigationService.toWargaSewa();
// Navigate to Sewa page, tab Aktif
toWargaSewaTabAktif();
break;
}
}
void toWargaSewaTabAktif() {
// Navigasi ke halaman warga sewa dan tab Aktif (index 3)
Get.toNamed(Routes.WARGA_SEWA, arguments: {'tab': 3});
}
void logout() async {
await _authProvider.signOut();
navigationService.toLogin();
@ -177,4 +197,137 @@ class WargaDashboardController extends GetxController {
print('Error loading unpaid rentals: $e');
}
}
Future<void> _debugCountSewaAset() async {
diterimaCount.value = await _asetProvider.countSewaAsetByStatus([
'DITERIMA',
]);
tagihanAktifCount.value = await _asetProvider.countSewaAsetByStatus([
'MENUNGGU PEMBAYARAN',
'PERIKSA PEMBAYARAN',
]);
dendaAktifCount.value = await _asetProvider.countSewaAsetByStatus([
'PEMBAYARAN DENDA',
'PERIKSA PEMBAYARAN DENDA',
]);
print('[DEBUG] Jumlah sewa diterima: ${diterimaCount.value}');
print('[DEBUG] Jumlah tagihan aktif: ${tagihanAktifCount.value}');
print('[DEBUG] Jumlah denda aktif: ${dendaAktifCount.value}');
}
Future<void> loadActiveRentals() async {
try {
activeRentals.clear();
final sewaAsetList = await _authProvider.getSewaAsetByStatus(['AKTIF']);
for (var sewaAset in sewaAsetList) {
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await _asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
assetName = asetData.nama;
imageUrl = asetData.imageUrl;
}
}
DateTime? waktuMulai;
DateTime? waktuSelesai;
String waktuSewa = '';
String tanggalSewa = '';
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
if (namaSatuanWaktu.toLowerCase() == 'jam') {
rentangWaktu = '$jamMulai - $jamSelesai';
} else if (namaSatuanWaktu.toLowerCase() == 'hari') {
final tanggalMulai = formatTanggalLengkap.format(waktuMulai);
final tanggalSelesai = formatTanggalLengkap.format(waktuSelesai);
rentangWaktu = '$tanggalMulai - $tanggalSelesai';
} else {
rentangWaktu = '$jamMulai - $jamSelesai';
}
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
final formatter = NumberFormat.currency(
locale: 'id',
symbol: 'Rp ',
decimalDigits: 0,
);
totalPrice = formatter.format(sewaAset['total']);
}
String duration = '-';
final tagihan = await _asetProvider.getTagihanSewa(sewaAset['id']);
if (tagihan != null) {
final durasiTagihan = tagihan['durasi'] ?? sewaAset['durasi'];
final satuanTagihan = tagihan['nama_satuan_waktu'] ?? namaSatuanWaktu;
duration = '${durasiTagihan ?? '-'} ${satuanTagihan ?? ''}';
} else {
duration = '${sewaAset['durasi'] ?? '-'} ${namaSatuanWaktu ?? ''}';
}
activeRentals.add({
'id': sewaAset['id'] ?? '',
'name': assetName,
'imageUrl': imageUrl ?? 'assets/images/gambar_pendukung.jpg',
'jumlahUnit': sewaAset['kuantitas'] ?? 0,
'waktuSewa': waktuSewa,
'duration': duration,
'status': sewaAset['status'] ?? 'AKTIF',
'totalPrice': totalPrice,
'tanggalSewa': tanggalSewa,
'jamMulai': jamMulai,
'jamSelesai': jamSelesai,
'rentangWaktu': rentangWaktu,
'namaSatuanWaktu': namaSatuanWaktu,
'waktuMulai': sewaAset['waktu_mulai'],
'waktuSelesai': sewaAset['waktu_selesai'],
'can_extend': sewaAset['can_extend'] == true,
});
}
} catch (e) {
print('Error loading active rentals: $e');
}
}
void toSewaAsetTabPaket() {
// Navigasi ke halaman sewa_aset tab Paket (index 1)
Get.toNamed(Routes.SEWA_ASET, arguments: {'tab': 1});
}
Future<void> fetchProfileFromWargaDesa() async {
try {
final user = _authProvider.currentUser;
if (user == null) return;
final userId = user.id;
final data =
await _authProvider.client
.from('warga_desa')
.select('nik, alamat, email, nama_lengkap, no_hp, avatar')
.eq('user_id', userId)
.maybeSingle();
if (data != null) {
userNik.value = data['nik']?.toString() ?? '';
userAddress.value = data['alamat']?.toString() ?? '';
userEmail.value = data['email']?.toString() ?? '';
userName.value = data['nama_lengkap']?.toString() ?? '';
userPhone.value = data['no_hp']?.toString() ?? '';
userAvatar.value = data['avatar']?.toString() ?? '';
}
} catch (e) {
print('Error fetching profile from warga_desa: $e');
}
}
}

View File

@ -12,10 +12,10 @@ class WargaSewaController extends GetxController
// Get navigation service
final NavigationService navigationService = Get.find<NavigationService>();
// Get auth provider for user data and sewa_aset queries
final AuthProvider authProvider = Get.find<AuthProvider>();
// Get aset provider for asset data
final AsetProvider asetProvider = Get.find<AsetProvider>();
@ -25,33 +25,35 @@ class WargaSewaController extends GetxController
final acceptedRentals = <Map<String, dynamic>>[].obs;
final completedRentals = <Map<String, dynamic>>[].obs;
final cancelledRentals = <Map<String, dynamic>>[].obs;
final returnedRentals = <Map<String, dynamic>>[].obs;
final activeRentals = <Map<String, dynamic>>[].obs;
// Loading states
final isLoading = false.obs;
final isLoadingPending = false.obs;
final isLoadingAccepted = false.obs;
final isLoadingCompleted = false.obs;
final isLoadingCancelled = false.obs;
final isLoadingReturned = false.obs;
final isLoadingActive = false.obs;
bool _tabSetFromArgument = false;
@override
void onInit() {
super.onInit();
// Ensure tab index is set to Sewa (1)
navigationService.setNavIndex(1);
// Initialize tab controller with 6 tabs
tabController = TabController(length: 6, vsync: this);
// Set initial tab and ensure tab view is updated
tabController.index = 0;
// Initialize tab controller with 7 tabs
tabController = TabController(length: 7, vsync: this);
// Load real rental data for all tabs
loadRentalsData();
loadPendingRentals();
loadAcceptedRentals();
loadActiveRentals();
loadCompletedRentals();
loadCancelledRentals();
loadReturnedRentals();
// Listen to tab changes to update state if needed
tabController.addListener(() {
@ -77,7 +79,9 @@ class WargaSewaController extends GetxController
}
break;
case 3: // Aktif
// Add Aktif tab logic when needed
if (activeRentals.isEmpty && !isLoadingActive.value) {
loadActiveRentals();
}
break;
case 4: // Selesai
if (completedRentals.isEmpty && !isLoadingCompleted.value) {
@ -89,6 +93,11 @@ class WargaSewaController extends GetxController
loadCancelledRentals();
}
break;
case 6: // Dikembalikan
if (returnedRentals.isEmpty && !isLoadingReturned.value) {
loadReturnedRentals();
}
break;
}
});
}
@ -96,9 +105,26 @@ class WargaSewaController extends GetxController
@override
void onReady() {
super.onReady();
// Ensure nav index is set to Sewa (1) when the controller is ready
// This helps maintain correct state during hot reload
navigationService.setNavIndex(1);
// Jalankan update nav index dan tab index setelah build selesai
Future.delayed(Duration.zero, () {
navigationService.setNavIndex(1);
final args = Get.arguments;
int initialTab = 0;
if (!_tabSetFromArgument &&
args != null &&
args is Map &&
args['tab'] != null) {
initialTab =
args['tab'] is int
? args['tab']
: int.tryParse(args['tab'].toString()) ?? 0;
if (tabController.length > initialTab) {
tabController.index = initialTab;
_tabSetFromArgument = true;
}
}
});
}
@override
@ -111,25 +137,25 @@ class WargaSewaController extends GetxController
Future<void> loadRentalsData() async {
try {
isLoading.value = true;
// Clear existing data
rentals.clear();
// Get sewa_aset data with status "MENUNGGU PEMBAYARAN" or "PEMBAYARAN DENDA"
final sewaAsetList = await authProvider.getSewaAsetByStatus([
'MENUNGGU PEMBAYARAN',
'PEMBAYARAN DENDA'
'PEMBAYARAN DENDA',
]);
debugPrint('Fetched ${sewaAsetList.length} sewa_aset records');
// Process each sewa_aset record
for (var sewaAset in sewaAsetList) {
// Get asset details if aset_id is available
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
@ -137,7 +163,7 @@ class WargaSewaController extends GetxController
imageUrl = asetData.imageUrl;
}
}
// Parse waktu mulai and waktu selesai
DateTime? waktuMulai;
DateTime? waktuSelesai;
@ -146,20 +172,21 @@ class WargaSewaController extends GetxController
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null && sewaAset['waktu_selesai'] != null) {
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
// Format for display
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
// Format based on satuan waktu
if (namaSatuanWaktu.toLowerCase() == 'jam') {
// For hours, show time range on same day
@ -173,12 +200,13 @@ class WargaSewaController extends GetxController
// Default format
rentangWaktu = '$jamMulai - $jamSelesai';
}
// Full time format for waktuSewa
waktuSewa = '${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
// Format price
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
@ -189,7 +217,7 @@ class WargaSewaController extends GetxController
);
totalPrice = formatter.format(sewaAset['total']);
}
// Add to rentals list
rentals.add({
'id': sewaAset['id'] ?? '',
@ -208,9 +236,10 @@ class WargaSewaController extends GetxController
'namaSatuanWaktu': namaSatuanWaktu,
'waktuMulai': sewaAset['waktu_mulai'],
'waktuSelesai': sewaAset['waktu_selesai'],
'updated_at': sewaAset['updated_at'],
});
}
debugPrint('Processed ${rentals.length} rental records');
} catch (e) {
debugPrint('Error loading rentals data: $e');
@ -245,28 +274,67 @@ class WargaSewaController extends GetxController
}
// Actions
void cancelRental(String id) {
Get.snackbar(
'Info',
'Pembatalan berhasil',
snackPosition: SnackPosition.BOTTOM,
void cancelRental(String id) async {
final confirmed = await Get.dialog<bool>(
AlertDialog(
title: const Text('Konfirmasi Pembatalan'),
content: const Text('Apakah Anda yakin ingin membatalkan pesanan ini?'),
actions: [
TextButton(
onPressed: () => Get.back(result: false),
child: const Text('Tidak'),
),
ElevatedButton(
onPressed: () => Get.back(result: true),
child: const Text('Ya, Batalkan'),
),
],
),
);
if (confirmed == true) {
try {
await asetProvider.client
.from('sewa_aset')
.update({'status': 'DIBATALKAN'})
.eq('id', id);
Get.snackbar(
'Berhasil',
'Pesanan berhasil dibatalkan',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
colorText: Colors.white,
);
// Refresh data
loadRentalsData();
loadPendingRentals();
loadAcceptedRentals();
loadActiveRentals();
loadCompletedRentals();
loadCancelledRentals();
loadReturnedRentals();
} catch (e) {
Get.snackbar(
'Gagal',
'Gagal membatalkan pesanan: $e',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
}
// Navigate to payment page with the selected rental data
void viewRentalDetail(Map<String, dynamic> rental) {
debugPrint('Navigating to payment page with rental ID: ${rental['id']}');
// Navigate to payment page with rental data
Get.toNamed(
Routes.PEMBAYARAN_SEWA,
arguments: {
'orderId': rental['id'],
'rentalData': rental,
},
arguments: {'orderId': rental['id'], 'rentalData': rental},
);
}
void payRental(String id) {
Get.snackbar(
'Info',
@ -274,27 +342,27 @@ class WargaSewaController extends GetxController
snackPosition: SnackPosition.BOTTOM,
);
}
// Load data for the Selesai tab (status: SELESAI)
Future<void> loadCompletedRentals() async {
try {
isLoadingCompleted.value = true;
// Clear existing data
completedRentals.clear();
// Get sewa_aset data with status "SELESAI"
final sewaAsetList = await authProvider.getSewaAsetByStatus(['SELESAI']);
debugPrint('Fetched ${sewaAsetList.length} completed sewa_aset records');
// Process each sewa_aset record
for (var sewaAset in sewaAsetList) {
// Get asset details if aset_id is available
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
@ -302,7 +370,7 @@ class WargaSewaController extends GetxController
imageUrl = asetData.imageUrl;
}
}
// Parse waktu mulai and waktu selesai
DateTime? waktuMulai;
DateTime? waktuSelesai;
@ -311,20 +379,21 @@ class WargaSewaController extends GetxController
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null && sewaAset['waktu_selesai'] != null) {
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
// Format for display
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
// Format based on satuan waktu
if (namaSatuanWaktu.toLowerCase() == 'jam') {
// For hours, show time range on same day
@ -338,12 +407,13 @@ class WargaSewaController extends GetxController
// Default format
rentangWaktu = '$jamMulai - $jamSelesai';
}
// Full time format for waktuSewa
waktuSewa = '${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
// Format price
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
@ -354,7 +424,7 @@ class WargaSewaController extends GetxController
);
totalPrice = formatter.format(sewaAset['total']);
}
// Add to completed rentals list
completedRentals.add({
'id': sewaAset['id'] ?? '',
@ -374,35 +444,39 @@ class WargaSewaController extends GetxController
'waktuSelesai': sewaAset['waktu_selesai'],
});
}
debugPrint('Processed ${completedRentals.length} completed rental records');
debugPrint(
'Processed ${completedRentals.length} completed rental records',
);
} catch (e) {
debugPrint('Error loading completed rentals data: $e');
} finally {
isLoadingCompleted.value = false;
}
}
// Load data for the Dibatalkan tab (status: DIBATALKAN)
Future<void> loadCancelledRentals() async {
try {
isLoadingCancelled.value = true;
// Clear existing data
cancelledRentals.clear();
// Get sewa_aset data with status "DIBATALKAN"
final sewaAsetList = await authProvider.getSewaAsetByStatus(['DIBATALKAN']);
final sewaAsetList = await authProvider.getSewaAsetByStatus([
'DIBATALKAN',
]);
debugPrint('Fetched ${sewaAsetList.length} cancelled sewa_aset records');
// Process each sewa_aset record
for (var sewaAset in sewaAsetList) {
// Get asset details if aset_id is available
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
@ -410,7 +484,7 @@ class WargaSewaController extends GetxController
imageUrl = asetData.imageUrl;
}
}
// Parse waktu mulai and waktu selesai
DateTime? waktuMulai;
DateTime? waktuSelesai;
@ -419,20 +493,21 @@ class WargaSewaController extends GetxController
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null && sewaAset['waktu_selesai'] != null) {
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
// Format for display
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
// Format based on satuan waktu
if (namaSatuanWaktu.toLowerCase() == 'jam') {
// For hours, show time range on same day
@ -446,12 +521,13 @@ class WargaSewaController extends GetxController
// Default format
rentangWaktu = '$jamMulai - $jamSelesai';
}
// Full time format for waktuSewa
waktuSewa = '${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
// Format price
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
@ -462,7 +538,7 @@ class WargaSewaController extends GetxController
);
totalPrice = formatter.format(sewaAset['total']);
}
// Add to cancelled rentals list
cancelledRentals.add({
'id': sewaAset['id'] ?? '',
@ -483,35 +559,40 @@ class WargaSewaController extends GetxController
'alasanPembatalan': sewaAset['alasan_pembatalan'] ?? '-',
});
}
debugPrint('Processed ${cancelledRentals.length} cancelled rental records');
debugPrint(
'Processed ${cancelledRentals.length} cancelled rental records',
);
} catch (e) {
debugPrint('Error loading cancelled rentals data: $e');
} finally {
isLoadingCancelled.value = false;
}
}
// Load data for the Pending tab (status: PERIKSA PEMBAYARAN)
Future<void> loadPendingRentals() async {
try {
isLoadingPending.value = true;
// Clear existing data
pendingRentals.clear();
// Get sewa_aset data with status "PERIKSA PEMBAYARAN"
final sewaAsetList = await authProvider.getSewaAsetByStatus(['PERIKSA PEMBAYARAN']);
// Get sewa_aset data with status 'PERIKSA PEMBAYARAN' dan 'PERIKSA PEMBAYARAN DENDA'
final sewaAsetList = await authProvider.getSewaAsetByStatus([
'PERIKSA PEMBAYARAN',
'PERIKSA PEMBAYARAN DENDA',
]);
debugPrint('Fetched ${sewaAsetList.length} pending sewa_aset records');
// Process each sewa_aset record
for (var sewaAset in sewaAsetList) {
// Get asset details if aset_id is available
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
@ -519,7 +600,7 @@ class WargaSewaController extends GetxController
imageUrl = asetData.imageUrl;
}
}
// Parse waktu mulai and waktu selesai
DateTime? waktuMulai;
DateTime? waktuSelesai;
@ -528,20 +609,21 @@ class WargaSewaController extends GetxController
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null && sewaAset['waktu_selesai'] != null) {
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
// Format for display
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
// Format based on satuan waktu
if (namaSatuanWaktu.toLowerCase() == 'jam') {
// For hours, show time range on same day
@ -555,12 +637,13 @@ class WargaSewaController extends GetxController
// Default format
rentangWaktu = '$jamMulai - $jamSelesai';
}
// Full time format for waktuSewa
waktuSewa = '${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
// Format price
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
@ -571,7 +654,7 @@ class WargaSewaController extends GetxController
);
totalPrice = formatter.format(sewaAset['total']);
}
// Add to pending rentals list
pendingRentals.add({
'id': sewaAset['id'] ?? '',
@ -591,7 +674,7 @@ class WargaSewaController extends GetxController
'waktuSelesai': sewaAset['waktu_selesai'],
});
}
debugPrint('Processed ${pendingRentals.length} pending rental records');
} catch (e) {
debugPrint('Error loading pending rentals data: $e');
@ -599,27 +682,27 @@ class WargaSewaController extends GetxController
isLoadingPending.value = false;
}
}
// Load data for the Diterima tab (status: DITERIMA)
Future<void> loadAcceptedRentals() async {
try {
isLoadingAccepted.value = true;
// Clear existing data
acceptedRentals.clear();
// Get sewa_aset data with status "DITERIMA"
final sewaAsetList = await authProvider.getSewaAsetByStatus(['DITERIMA']);
debugPrint('Fetched ${sewaAsetList.length} accepted sewa_aset records');
// Process each sewa_aset record
for (var sewaAset in sewaAsetList) {
// Get asset details if aset_id is available
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
@ -627,7 +710,7 @@ class WargaSewaController extends GetxController
imageUrl = asetData.imageUrl;
}
}
// Parse waktu mulai and waktu selesai
DateTime? waktuMulai;
DateTime? waktuSelesai;
@ -636,20 +719,21 @@ class WargaSewaController extends GetxController
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null && sewaAset['waktu_selesai'] != null) {
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
// Format for display
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
// Format based on satuan waktu
if (namaSatuanWaktu.toLowerCase() == 'jam') {
// For hours, show time range on same day
@ -663,12 +747,13 @@ class WargaSewaController extends GetxController
// Default format
rentangWaktu = '$jamMulai - $jamSelesai';
}
// Full time format for waktuSewa
waktuSewa = '${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
// Format price
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
@ -679,7 +764,7 @@ class WargaSewaController extends GetxController
);
totalPrice = formatter.format(sewaAset['total']);
}
// Add to accepted rentals list
acceptedRentals.add({
'id': sewaAset['id'] ?? '',
@ -699,7 +784,7 @@ class WargaSewaController extends GetxController
'waktuSelesai': sewaAset['waktu_selesai'],
});
}
debugPrint('Processed ${acceptedRentals.length} accepted rental records');
} catch (e) {
debugPrint('Error loading accepted rentals data: $e');
@ -707,4 +792,166 @@ class WargaSewaController extends GetxController
isLoadingAccepted.value = false;
}
}
Future<void> loadReturnedRentals() async {
try {
isLoadingReturned.value = true;
returnedRentals.clear();
final sewaAsetList = await authProvider.getSewaAsetByStatus([
'DIKEMBALIKAN',
]);
for (var sewaAset in sewaAsetList) {
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
assetName = asetData.nama;
imageUrl = asetData.imageUrl;
}
}
DateTime? waktuMulai;
DateTime? waktuSelesai;
String waktuSewa = '';
String tanggalSewa = '';
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
if (namaSatuanWaktu.toLowerCase() == 'jam') {
rentangWaktu = '$jamMulai - $jamSelesai';
} else if (namaSatuanWaktu.toLowerCase() == 'hari') {
final tanggalMulai = formatTanggalLengkap.format(waktuMulai);
final tanggalSelesai = formatTanggalLengkap.format(waktuSelesai);
rentangWaktu = '$tanggalMulai - $tanggalSelesai';
} else {
rentangWaktu = '$jamMulai - $jamSelesai';
}
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
final formatter = NumberFormat.currency(
locale: 'id',
symbol: 'Rp ',
decimalDigits: 0,
);
totalPrice = formatter.format(sewaAset['total']);
}
returnedRentals.add({
'id': sewaAset['id'] ?? '',
'name': assetName,
'imageUrl': imageUrl ?? 'assets/images/gambar_pendukung.jpg',
'jumlahUnit': sewaAset['kuantitas'] ?? 0,
'waktuSewa': waktuSewa,
'duration': '${sewaAset['durasi'] ?? 0} ${namaSatuanWaktu}',
'status': sewaAset['status'] ?? 'DIKEMBALIKAN',
'totalPrice': totalPrice,
'tanggalSewa': tanggalSewa,
'jamMulai': jamMulai,
'jamSelesai': jamSelesai,
'rentangWaktu': rentangWaktu,
'namaSatuanWaktu': namaSatuanWaktu,
'waktuMulai': sewaAset['waktu_mulai'],
'waktuSelesai': sewaAset['waktu_selesai'],
});
}
} catch (e) {
debugPrint('Error loading returned rentals data: $e');
} finally {
isLoadingReturned.value = false;
}
}
Future<void> loadActiveRentals() async {
try {
isLoadingActive.value = true;
activeRentals.clear();
final sewaAsetList = await authProvider.getSewaAsetByStatus(['AKTIF']);
for (var sewaAset in sewaAsetList) {
String assetName = 'Aset';
String? imageUrl;
String namaSatuanWaktu = sewaAset['nama_satuan_waktu'] ?? 'jam';
if (sewaAset['aset_id'] != null) {
final asetData = await asetProvider.getAsetById(sewaAset['aset_id']);
if (asetData != null) {
assetName = asetData.nama;
imageUrl = asetData.imageUrl;
}
}
DateTime? waktuMulai;
DateTime? waktuSelesai;
String waktuSewa = '';
String tanggalSewa = '';
String jamMulai = '';
String jamSelesai = '';
String rentangWaktu = '';
if (sewaAset['waktu_mulai'] != null &&
sewaAset['waktu_selesai'] != null) {
waktuMulai = DateTime.parse(sewaAset['waktu_mulai']);
waktuSelesai = DateTime.parse(sewaAset['waktu_selesai']);
final formatTanggal = DateFormat('dd-MM-yyyy');
final formatWaktu = DateFormat('HH:mm');
final formatTanggalLengkap = DateFormat('dd MMMM yyyy', 'id_ID');
tanggalSewa = formatTanggalLengkap.format(waktuMulai);
jamMulai = formatWaktu.format(waktuMulai);
jamSelesai = formatWaktu.format(waktuSelesai);
if (namaSatuanWaktu.toLowerCase() == 'jam') {
rentangWaktu = '$jamMulai - $jamSelesai';
} else if (namaSatuanWaktu.toLowerCase() == 'hari') {
final tanggalMulai = formatTanggalLengkap.format(waktuMulai);
final tanggalSelesai = formatTanggalLengkap.format(waktuSelesai);
rentangWaktu = '$tanggalMulai - $tanggalSelesai';
} else {
rentangWaktu = '$jamMulai - $jamSelesai';
}
waktuSewa =
'${formatTanggal.format(waktuMulai)} | ${formatWaktu.format(waktuMulai)} - '
'${formatTanggal.format(waktuSelesai)} | ${formatWaktu.format(waktuSelesai)}';
}
String totalPrice = 'Rp 0';
if (sewaAset['total'] != null) {
final formatter = NumberFormat.currency(
locale: 'id',
symbol: 'Rp ',
decimalDigits: 0,
);
totalPrice = formatter.format(sewaAset['total']);
}
activeRentals.add({
'id': sewaAset['id'] ?? '',
'name': assetName,
'imageUrl': imageUrl ?? 'assets/images/gambar_pendukung.jpg',
'jumlahUnit': sewaAset['kuantitas'] ?? 0,
'waktuSewa': waktuSewa,
'duration': '${sewaAset['durasi'] ?? 0} ${namaSatuanWaktu}',
'status': sewaAset['status'] ?? 'AKTIF',
'totalPrice': totalPrice,
'tanggalSewa': tanggalSewa,
'jamMulai': jamMulai,
'jamSelesai': jamSelesai,
'rentangWaktu': rentangWaktu,
'namaSatuanWaktu': namaSatuanWaktu,
'waktuMulai': sewaAset['waktu_mulai'],
'waktuSelesai': sewaAset['waktu_selesai'],
});
}
} catch (e) {
debugPrint('Error loading active rentals data: $e');
} finally {
isLoadingActive.value = false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -117,6 +117,7 @@ class SewaAsetView extends GetView<SewaAsetController> {
),
],
),
dividerColor: Colors.transparent,
labelColor: Colors.white,
unselectedLabelColor: const Color(
0xFF718093,

View File

@ -154,10 +154,10 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
'route': () => controller.navigateToRentals(),
},
{
'title': 'Bayar',
'icon': Icons.payment_outlined,
'title': 'Paket',
'icon': Icons.widgets_outlined,
'color': const Color(0xFF2196F3),
'route': () => Get.toNamed(Routes.PEMBAYARAN_SEWA),
'route': () => controller.toSewaAsetTabPaket(),
},
];
@ -218,32 +218,44 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
child: Column(
children: [
// Sewa Diterima
_buildActivityCard(
title: 'Sewa Diterima',
value: controller.activeRentals.length.toString(),
icon: Icons.check_circle_outline,
color: AppColors.success,
onTap: () => controller.navigateToRentals(),
Obx(
() => _buildActivityCard(
title: 'Sewa Diterima',
value: controller.diterimaCount.value.toString(),
icon: Icons.check_circle_outline,
color: AppColors.success,
onTap:
() =>
Get.toNamed(Routes.WARGA_SEWA, arguments: {'tab': 2}),
),
),
const SizedBox(height: 12),
// Tagihan Aktif
_buildActivityCard(
title: 'Tagihan Aktif',
value: controller.activeBills.length.toString(),
icon: Icons.receipt_long_outlined,
color: AppColors.warning,
onTap: () => Get.toNamed(Routes.PEMBAYARAN_SEWA),
Obx(
() => _buildActivityCard(
title: 'Tagihan Aktif',
value: controller.tagihanAktifCount.value.toString(),
icon: Icons.receipt_long_outlined,
color: AppColors.warning,
onTap:
() =>
Get.toNamed(Routes.WARGA_SEWA, arguments: {'tab': 0}),
),
),
const SizedBox(height: 12),
// Denda Aktif
_buildActivityCard(
title: 'Denda Aktif',
value: controller.activePenalties.length.toString(),
icon: Icons.warning_amber_outlined,
color: AppColors.error,
onTap: () => Get.toNamed(Routes.PEMBAYARAN_SEWA),
Obx(
() => _buildActivityCard(
title: 'Denda Aktif',
value: controller.dendaAktifCount.value.toString(),
icon: Icons.warning_amber_outlined,
color: AppColors.error,
onTap:
() =>
Get.toNamed(Routes.WARGA_SEWA, arguments: {'tab': 0}),
),
),
],
),
@ -357,7 +369,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Sewa Diterima',
'Sewa Aktif',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@ -498,31 +510,34 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
),
child: Row(
children: [
// Asset icon
// Asset icon/gambar
Container(
padding: const EdgeInsets.all(12),
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.7),
AppColors.primary,
],
),
borderRadius: BorderRadius.circular(14),
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 3),
),
],
color: AppColors.primary.withOpacity(0.08),
),
child: const Icon(
Icons.local_shipping,
color: Colors.white,
size: 24,
child: ClipRRect(
borderRadius: BorderRadius.circular(14),
child:
rental['imageUrl'] != null &&
rental['imageUrl'].toString().isNotEmpty
? Image.network(
rental['imageUrl'],
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) => Icon(
Icons.local_shipping,
color: AppColors.primary,
size: 28,
),
)
: Icon(
Icons.local_shipping,
color: AppColors.primary,
size: 28,
),
),
),
const SizedBox(width: 16),
@ -533,7 +548,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
rental['name'],
rental['name'] ?? '-',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
@ -542,7 +557,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
),
const SizedBox(height: 4),
Text(
rental['time'],
rental['waktuSewa'] ?? '',
style: TextStyle(
fontSize: 13,
color: AppColors.textSecondary,
@ -567,7 +582,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
),
),
child: Text(
rental['price'],
rental['totalPrice'] ?? 'Rp 0',
style: TextStyle(
color: AppColors.primary,
fontWeight: FontWeight.bold,
@ -591,14 +606,14 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
child: _buildInfoItem(
icon: Icons.timer_outlined,
title: 'Durasi',
value: rental['duration'],
value: rental['duration'] ?? '-',
),
),
Expanded(
child: _buildInfoItem(
icon: Icons.calendar_today_outlined,
title: 'Status',
value: 'Diterima',
value: rental['status'] ?? '-',
valueColor: AppColors.success,
),
),
@ -608,7 +623,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
const SizedBox(height: 16),
// Action buttons
if (rental['can_extend'])
if ((rental['can_extend'] ?? false) == true)
OutlinedButton.icon(
onPressed: () => controller.extendRental(rental['id']),
icon: const Icon(Icons.update, size: 18),

View File

@ -3,12 +3,16 @@ import 'package:get/get.dart';
import '../controllers/warga_dashboard_controller.dart';
import '../views/warga_layout.dart';
import '../../../theme/app_colors.dart';
import '../../../widgets/app_drawer.dart';
import '../../../services/navigation_service.dart';
class WargaProfileView extends GetView<WargaDashboardController> {
const WargaProfileView({super.key});
@override
Widget build(BuildContext context) {
final navigationService = Get.find<NavigationService>();
navigationService.setNavIndex(2);
return WargaLayout(
appBar: AppBar(
title: const Text('Profil Saya'),
@ -29,6 +33,14 @@ class WargaProfileView extends GetView<WargaDashboardController> {
),
],
),
drawer: AppDrawer(
onNavItemTapped: (index) {
// Handle navigation if needed
},
onLogout: () {
controller.logout();
},
),
backgroundColor: Colors.grey.shade100,
body: RefreshIndicator(
color: AppColors.primary,

File diff suppressed because it is too large Load Diff