kelola penyewa dan beberapa error fix

This commit is contained in:
Andreas Malvino
2025-07-09 16:01:10 +07:00
parent 0423c2fdf9
commit 47766bbdda
90 changed files with 2705 additions and 1555 deletions

View File

@ -49,7 +49,7 @@ class ListPetugasMitraController extends GetxController {
Get.snackbar(
'Sukses',
'Petugas mitra berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -62,7 +62,7 @@ class ListPetugasMitraController extends GetxController {
Get.snackbar(
'Sukses',
'Data petugas mitra berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -73,7 +73,7 @@ class ListPetugasMitraController extends GetxController {
Get.snackbar(
'Sukses',
'Petugas mitra berhasil dihapus',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -86,7 +86,7 @@ class ListPetugasMitraController extends GetxController {
Get.snackbar(
'Status Diperbarui',
'Status petugas mitra diubah menjadi ${!currentStatus ? 'Aktif' : 'Nonaktif'}',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}

View File

@ -122,7 +122,7 @@ class PetugasAsetController extends GetxController {
Get.snackbar(
'Error Memuat Data',
'Gagal mengambil data aset dari server. Silakan coba lagi nanti.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -258,7 +258,7 @@ class PetugasAsetController extends GetxController {
Get.snackbar(
'Gagal',
'Terjadi kesalahan saat menghapus aset',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -274,7 +274,7 @@ class PetugasAsetController extends GetxController {
Get.snackbar(
'Error',
'Gagal menghapus aset: ${e.toString()}',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);

View File

@ -66,7 +66,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Error',
'Gagal memuat data. Silakan coba lagi.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isLoading.value = false;
@ -92,7 +92,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Rekening Utama',
'Rekening ${account['bank_name']} telah dijadikan rekening utama',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -109,7 +109,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Rekening Ditambahkan',
'Rekening bank baru telah berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -125,7 +125,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Rekening Diperbarui',
'Informasi rekening bank telah berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -138,7 +138,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Tidak Dapat Menghapus',
'Rekening utama tidak dapat dihapus. Silakan atur rekening lain sebagai utama terlebih dahulu.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
return;
}
@ -148,7 +148,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Rekening Dihapus',
'Rekening bank telah berhasil dihapus',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -164,7 +164,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Status Diperbarui',
'Status mitra telah diubah menjadi ${partner['is_active'] ? 'Aktif' : 'Tidak Aktif'}',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -181,7 +181,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Mitra Ditambahkan',
'Mitra baru telah berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -197,7 +197,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Mitra Diperbarui',
'Informasi mitra telah berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -210,7 +210,7 @@ class PetugasBumdesCbpController extends GetxController {
Get.snackbar(
'Mitra Dihapus',
'Mitra telah berhasil dihapus',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}

View File

@ -1,12 +1,14 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../data/providers/auth_provider.dart';
import '../../../routes/app_routes.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:bumrent_app/app/data/providers/auth_provider.dart';
import 'package:bumrent_app/app/data/providers/aset_provider.dart';
import 'package:bumrent_app/app/data/providers/pesanan_provider.dart';
import 'package:bumrent_app/app/routes/app_routes.dart';
import '../../../services/sewa_service.dart';
import '../../../services/service_manager.dart';
import '../../../data/models/pembayaran_model.dart';
import '../../../services/pembayaran_service.dart';
import '../../../data/providers/aset_provider.dart';
import '../../../data/providers/pesanan_provider.dart';
class PetugasBumdesDashboardController extends GetxController {
AuthProvider? _authProvider;
@ -297,37 +299,56 @@ class PetugasBumdesDashboardController extends GetxController {
void logout() async {
try {
// Clear providers data
// Store login route for navigation
final loginRoute = Routes.LOGIN;
// Sign out from Supabase
if (_authProvider != null) {
// Sign out from Supabase
await _authProvider!.signOut();
// Clear auth provider data
_authProvider!.clearAuthData();
// Clear aset provider data
try {
final asetProvider = Get.find<AsetProvider>();
asetProvider.clearCache();
} catch (e) {
print('Error clearing AsetProvider: $e');
}
// Clear pesanan provider data
try {
final pesananProvider = Get.find<PesananProvider>();
pesananProvider.clearCache();
} catch (e) {
print('Error clearing PesananProvider: $e');
}
}
// Navigate to login screen
Get.offAllNamed(Routes.LOGIN);
// Navigate to login screen while context is still valid
Get.offAllNamed(loginRoute);
// Clear auth provider data if available
if (_authProvider != null) {
_authProvider!.clearAuthData();
}
// Clear provider caches
_clearProviderCaches();
// Clean up GetX controllers but keep navigation intact
Get.deleteAll(force: false);
} catch (e) {
print('Error during logout: $e');
// Still try to navigate to login even if sign out fails
Get.offAllNamed(Routes.LOGIN);
}
}
// Helper method to clear provider caches that need explicit clearing
void _clearProviderCaches() {
try {
// Clear AsetProvider cache
if (Get.isRegistered<AsetProvider>()) {
final asetProvider = Get.find<AsetProvider>();
asetProvider.clearCache();
}
} catch (e) {
print('Error clearing AsetProvider: $e');
}
try {
// Clear PesananProvider cache
if (Get.isRegistered<PesananProvider>()) {
final pesananProvider = Get.find<PesananProvider>();
pesananProvider.clearCache();
}
} catch (e) {
print('Error clearing PesananProvider: $e');
}
// Add other providers here that need explicit cache clearing
}
}

View File

@ -21,7 +21,7 @@ class PetugasDetailPenyewaController extends GetxController {
Get.snackbar(
'Error',
'Data penyewa tidak ditemukan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -46,7 +46,7 @@ class PetugasDetailPenyewaController extends GetxController {
Get.snackbar(
'Error',
'Gagal memuat data penyewa',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isLoading.value = false;
@ -203,14 +203,14 @@ class PetugasDetailPenyewaController extends GetxController {
Get.snackbar(
'Berhasil',
'Status penyewa berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} catch (e) {
print('Error updating penyewa status: $e');
Get.snackbar(
'Error',
'Gagal memperbarui status penyewa',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isLoading.value = false;

View File

@ -184,7 +184,7 @@ class PetugasLaporanController extends GetxController {
'Gagal membuat laporan: $e',
backgroundColor: Colors.red[100],
colorText: Colors.red[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isLoading.value = false;
@ -1641,7 +1641,7 @@ class PetugasLaporanController extends GetxController {
'Harap generate laporan terlebih dahulu',
backgroundColor: Colors.amber[100],
colorText: Colors.amber[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -1654,7 +1654,7 @@ class PetugasLaporanController extends GetxController {
'Harap generate laporan terlebih dahulu',
backgroundColor: Colors.amber[100],
colorText: Colors.amber[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
return;
}
@ -1687,7 +1687,7 @@ class PetugasLaporanController extends GetxController {
'Laporan disimpan di ${file.path}',
backgroundColor: Colors.green[100],
colorText: Colors.green[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 5),
);
@ -1701,7 +1701,7 @@ class PetugasLaporanController extends GetxController {
'Gagal menyimpan laporan: $e',
backgroundColor: Colors.red[100],
colorText: Colors.red[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}
@ -1714,7 +1714,7 @@ class PetugasLaporanController extends GetxController {
'Harap generate laporan terlebih dahulu',
backgroundColor: Colors.amber[100],
colorText: Colors.amber[900],
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
return;
}

View File

@ -79,7 +79,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Rekening utama berhasil diubah',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -93,7 +93,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Status mitra berhasil diubah',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -110,7 +110,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Rekening bank berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -124,7 +124,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Rekening bank berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -145,7 +145,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Rekening bank berhasil dihapus',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -155,7 +155,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Mitra berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -166,7 +166,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Mitra berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -177,7 +177,7 @@ class PetugasManajemenBumdesController extends GetxController {
Get.snackbar(
'Sukses',
'Mitra berhasil dihapus',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
}

View File

@ -87,7 +87,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal memuat data paket. Silakan coba lagi.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -320,7 +320,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Sukses',
'Paket baru berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);
@ -334,7 +334,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal menambahkan paket. Silakan coba lagi.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -373,7 +373,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Sukses',
'Paket berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);
@ -388,7 +388,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal memperbarui paket. Silakan coba lagi.',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -440,7 +440,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Gagal',
'Terjadi kesalahan saat menghapus paket',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
duration: const Duration(seconds: 3),
@ -462,7 +462,7 @@ class PetugasPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal menghapus paket: ${e.toString()}',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
duration: const Duration(seconds: 3),

View File

@ -6,6 +6,7 @@ class PetugasPenyewaController extends GetxController {
// Reactive variables
final isLoading = true.obs;
final isRefreshing = false.obs;
final penyewaList = <Map<String, dynamic>>[].obs;
final filteredPenyewaList = <Map<String, dynamic>>[].obs;
final filterStatus = 'all'.obs;
@ -30,6 +31,39 @@ class PetugasPenyewaController extends GetxController {
fetchPenyewaList();
}
// Method khusus untuk pull-to-refresh yang tidak menampilkan loading spinner penuh layar
Future<void> refreshPenyewaList() async {
try {
isRefreshing.value = true;
// Get all penyewa data without filtering
final data =
await _authProvider.client
.from('warga_desa')
.select(
'user_id, nama_lengkap, email, nik, no_hp, avatar, status, keterangan',
)
as List<dynamic>;
// Filter out rows where user_id is null
final filteredData = data.where((row) => row['user_id'] != null).toList();
// Get total sewa count for each user
final enrichedData = await _enrichWithSewaCount(filteredData);
penyewaList.value = enrichedData;
// Apply filters to update filteredPenyewaList
applyFilters();
// Tampilkan pesan sukses
} catch (e) {
print('Error refreshing penyewa list: $e');
} finally {
isRefreshing.value = false;
}
}
void changeTab(int index) {
currentTabIndex.value = index;
applyFilters();
@ -180,14 +214,14 @@ class PetugasPenyewaController extends GetxController {
Get.snackbar(
'Berhasil',
'Status penyewa berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} catch (e) {
print('Error updating penyewa status: $e');
Get.snackbar(
'Gagal',
'Terjadi kesalahan saat memperbarui status penyewa',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isLoading.value = false;

View File

@ -4,6 +4,7 @@ import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:bumrent_app/app/data/models/aset_model.dart';
import 'package:bumrent_app/app/data/providers/aset_provider.dart';
import 'package:bumrent_app/app/routes/app_routes.dart';
class PetugasTambahAsetController extends GetxController {
// Flag to check if in edit mode
@ -13,34 +14,38 @@ class PetugasTambahAsetController extends GetxController {
@override
Future<void> onInit() async {
super.onInit();
try {
// Handle edit mode and load data if needed
final args = Get.arguments;
debugPrint('[DEBUG] PetugasTambahAsetController initialized with args: $args');
debugPrint(
'[DEBUG] PetugasTambahAsetController initialized with args: $args',
);
if (args != null && args is Map<String, dynamic>) {
isEditing.value = args['isEditing'] ?? false;
debugPrint('[DEBUG] isEditing set to: ${isEditing.value}');
if (isEditing.value) {
// Get asset ID from arguments
final assetId = args['assetId']?.toString() ?? '';
debugPrint('[DEBUG] Edit mode: Loading asset with ID: $assetId');
if (assetId.isNotEmpty) {
// Store the asset ID and load asset data
this.assetId = assetId;
debugPrint('[DEBUG] Asset ID set to: $assetId');
// Load asset data and await completion
await _loadAssetData(assetId);
} else {
debugPrint('[ERROR] Edit mode but no assetId provided in arguments');
debugPrint(
'[ERROR] Edit mode but no assetId provided in arguments',
);
Get.snackbar(
'Error',
'ID Aset tidak ditemukan',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
// Optionally navigate back if in edit mode without an ID
Future.delayed(Duration.zero, () => Get.back());
@ -53,7 +58,9 @@ class PetugasTambahAsetController extends GetxController {
}
} else {
// Default values for new asset when no arguments are passed
debugPrint('[DEBUG] No arguments passed, defaulting to add new asset mode');
debugPrint(
'[DEBUG] No arguments passed, defaulting to add new asset mode',
);
quantityController.text = '1';
unitOfMeasureController.text = 'Unit';
}
@ -62,11 +69,11 @@ class PetugasTambahAsetController extends GetxController {
debugPrint('Stack trace: $stackTrace');
// Ensure loading is set to false even if there's an error
isLoading.value = false;
Get.snackbar(
'Error',
'Terjadi kesalahan saat memuat data',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
@ -85,21 +92,21 @@ class PetugasTambahAsetController extends GetxController {
try {
isLoading.value = true;
debugPrint('[DEBUG] Fetching asset data for ID: $assetId');
// Fetch asset data from Supabase
final aset = await _asetProvider.getAsetById(assetId);
if (aset == null) {
throw Exception('Aset tidak ditemukan');
}
debugPrint('[DEBUG] Successfully fetched asset data: ${aset.toJson()}');
// Populate form fields with the fetched data
nameController.text = aset.nama ?? '';
descriptionController.text = aset.deskripsi ?? '';
quantityController.text = (aset.kuantitas ?? 1).toString();
// Ensure the status matches one of the available options exactly
final status = aset.status?.toLowerCase() ?? 'tersedia';
if (status == 'tersedia') {
@ -110,18 +117,19 @@ class PetugasTambahAsetController extends GetxController {
// Default to 'Tersedia' if status is not recognized
selectedStatus.value = 'Tersedia';
}
// Handle time options and pricing
if (aset.satuanWaktuSewa != null && aset.satuanWaktuSewa!.isNotEmpty) {
// Reset time options
timeOptions.forEach((key, value) => value.value = false);
// Process each satuan waktu sewa
for (var sws in aset.satuanWaktuSewa) {
final satuan = sws['nama_satuan_waktu']?.toString().toLowerCase() ?? '';
final satuan =
sws['nama_satuan_waktu']?.toString().toLowerCase() ?? '';
final harga = sws['harga'] as int? ?? 0;
final maksimalWaktu = sws['maksimal_waktu'] as int? ?? 24;
if (satuan.contains('jam')) {
timeOptions['Per Jam']?.value = true;
pricePerHourController.text = harga.toString();
@ -133,19 +141,21 @@ class PetugasTambahAsetController extends GetxController {
}
}
}
// Clear existing images
selectedImages.clear();
networkImageUrls.clear();
// Get all image URLs from the model
final allImageUrls = aset.imageUrls.toList();
// If no imageUrls but has imageUrl, use that as fallback (backward compatibility)
if (allImageUrls.isEmpty && aset.imageUrl != null && aset.imageUrl!.isNotEmpty) {
if (allImageUrls.isEmpty &&
aset.imageUrl != null &&
aset.imageUrl!.isNotEmpty) {
allImageUrls.add(aset.imageUrl!);
}
// Add all images to the lists
for (final imageUrl in allImageUrls) {
if (imageUrl != null && imageUrl.isNotEmpty) {
@ -161,27 +171,30 @@ class PetugasTambahAsetController extends GetxController {
}
}
}
debugPrint('Total ${networkImageUrls.length} images loaded for asset $assetId');
debugPrint(
'Total ${networkImageUrls.length} images loaded for asset $assetId',
);
debugPrint('[DEBUG] Successfully loaded asset data for ID: $assetId');
} catch (e, stackTrace) {
debugPrint('[ERROR] Failed to load asset data: $e');
debugPrint('Stack trace: $stackTrace');
Get.snackbar(
'Error',
'Gagal memuat data aset: ${e.toString()}',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
// Optionally navigate back if there's an error
Future.delayed(const Duration(seconds: 2), () => Get.back());
} finally {
isLoading.value = false;
}
}
// Form controllers
final nameController = TextEditingController();
final descriptionController = TextEditingController();
@ -213,8 +226,6 @@ class PetugasTambahAsetController extends GetxController {
final isFormValid = false.obs;
final isSubmitting = false.obs;
@override
void onClose() {
// Dispose controllers
@ -255,11 +266,10 @@ class PetugasTambahAsetController extends GetxController {
if (!anySelected) {
timeOptions[option]?.value = true;
}
validateForm();
}
// Create a new asset in Supabase
Future<String?> _createAsset(
Map<String, dynamic> assetData,
@ -268,7 +278,7 @@ class PetugasTambahAsetController extends GetxController {
try {
// Create the asset in the 'aset' table
final response = await _asetProvider.createAset(assetData);
if (response == null || response['id'] == null) {
debugPrint('❌ Failed to create asset: No response or ID from server');
return null;
@ -285,7 +295,7 @@ class PetugasTambahAsetController extends GetxController {
harga: sws['harga'],
maksimalWaktu: sws['maksimal_waktu'],
);
if (!success) {
debugPrint('❌ Failed to add satuan waktu sewa: $sws');
}
@ -307,12 +317,12 @@ class PetugasTambahAsetController extends GetxController {
) async {
try {
debugPrint('\n🔄 Starting update for asset ID: $assetId');
// 1. Extract and remove foto_aset from assetData as it's not in the aset table
final fotoAsetUrl = assetData['foto_aset'];
assetData.remove('foto_aset');
debugPrint('📝 Asset data prepared for update (without foto_aset)');
// 2. Update the main asset data (without foto_aset)
debugPrint('🔄 Updating main asset data...');
final success = await _asetProvider.updateAset(assetId, assetData);
@ -326,7 +336,7 @@ class PetugasTambahAsetController extends GetxController {
debugPrint('\n🔄 Updating rental time units...');
// First, delete existing satuan waktu sewa
await _asetProvider.deleteSatuanWaktuSewaByAsetId(assetId);
// Then add the new ones
for (var sws in satuanWaktuSewa) {
debugPrint(' - Adding: ${sws['satuan_waktu']} (${sws['harga']} IDR)');
@ -346,21 +356,25 @@ class PetugasTambahAsetController extends GetxController {
...networkImageUrls,
...selectedImages.map((file) => file.path),
];
debugPrint('\n🖼️ Processing photos for asset $assetId');
debugPrint(' - Network URLs: ${networkImageUrls.length}');
debugPrint(' - Local files: ${selectedImages.length}');
debugPrint(' - Total unique photos: ${allImageUrls.toSet().length} (before deduplication)');
debugPrint(
' - Total unique photos: ${allImageUrls.toSet().length} (before deduplication)',
);
try {
// Use updateFotoAset which handles both uploading new photos and updating the database
final photoSuccess = await _asetProvider.updateFotoAset(
asetId: assetId,
fotoUrls: allImageUrls,
);
if (!photoSuccess) {
debugPrint('⚠️ Some photos might not have been updated for asset $assetId');
debugPrint(
'⚠️ Some photos might not have been updated for asset $assetId',
);
// We don't fail the whole update if photo update fails
// as the main asset data has been saved successfully
} else {
@ -377,7 +391,6 @@ class PetugasTambahAsetController extends GetxController {
debugPrint('\n✅ Asset update completed successfully for ID: $assetId');
return true;
} catch (e, stackTrace) {
debugPrint('❌ Error updating asset: $e');
debugPrint('Stack trace: $stackTrace');
@ -445,30 +458,30 @@ class PetugasTambahAsetController extends GetxController {
// Handle time options and pricing
final List<Map<String, dynamic>> satuanWaktuSewa = [];
if (timeOptions['Per Jam']?.value == true) {
final hargaPerJam = int.tryParse(pricePerHourController.text) ?? 0;
final maxJam = int.tryParse(maxHourController.text) ?? 24;
if (hargaPerJam <= 0) {
throw Exception('Harga per jam harus lebih dari 0');
}
satuanWaktuSewa.add({
'satuan_waktu': 'jam',
'harga': hargaPerJam,
'maksimal_waktu': maxJam,
});
}
if (timeOptions['Per Hari']?.value == true) {
final hargaPerHari = int.tryParse(pricePerDayController.text) ?? 0;
final maxHari = int.tryParse(maxDayController.text) ?? 30;
if (hargaPerHari <= 0) {
throw Exception('Harga per hari harus lebih dari 0');
}
satuanWaktuSewa.add({
'satuan_waktu': 'hari',
'harga': hargaPerHari,
@ -483,7 +496,7 @@ class PetugasTambahAsetController extends GetxController {
// Handle image uploads
List<String> imageUrls = [];
if (networkImageUrls.isNotEmpty) {
// Use existing network URLs
imageUrls = List.from(networkImageUrls);
@ -505,12 +518,12 @@ class PetugasTambahAsetController extends GetxController {
// Create or update the asset
bool success;
String? createdAssetId;
if (isEditing.value && (assetId?.isNotEmpty ?? false)) {
// Update existing asset
debugPrint('🔄 Updating asset with ID: $assetId');
success = await _updateAsset(assetId!, assetData, satuanWaktuSewa);
// Update all photos if we have any
if (success && imageUrls.isNotEmpty) {
await _asetProvider.updateFotoAset(
@ -523,7 +536,7 @@ class PetugasTambahAsetController extends GetxController {
debugPrint('🔄 Creating new asset');
createdAssetId = await _createAsset(assetData, satuanWaktuSewa);
success = createdAssetId != null;
// Add all photos for new asset
if (success && createdAssetId != null && imageUrls.isNotEmpty) {
await _asetProvider.updateFotoAset(
@ -537,16 +550,18 @@ class PetugasTambahAsetController extends GetxController {
// Show success message
Get.snackbar(
'Sukses',
isEditing.value ? 'Aset berhasil diperbarui' : 'Aset berhasil ditambahkan',
snackPosition: SnackPosition.BOTTOM,
isEditing.value
? 'Aset berhasil diperbarui'
: 'Aset berhasil ditambahkan',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
duration: const Duration(seconds: 3),
duration: const Duration(seconds: 2),
);
// Navigate back with success after a short delay
await Future.delayed(const Duration(seconds: 1));
Get.back(result: true);
// Navigate back with success result instead of using offNamed
await Future.delayed(const Duration(milliseconds: 500));
Get.offNamed(Routes.PETUGAS_ASET);
} else {
throw Exception('Gagal menyimpan aset');
}
@ -557,14 +572,13 @@ class PetugasTambahAsetController extends GetxController {
'Terjadi kesalahan: ${e.toString()}',
backgroundColor: Colors.red,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isSubmitting.value = false;
}
}
// Example method to upload images (to be implemented with your backend)
// Future<List<String>> _uploadImages(List<XFile> images) async {
// List<String> urls = [];
@ -593,7 +607,7 @@ class PetugasTambahAsetController extends GetxController {
Get.snackbar(
'Error',
'Gagal mengambil gambar dari kamera: $e',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -615,7 +629,7 @@ class PetugasTambahAsetController extends GetxController {
Get.snackbar(
'Error',
'Gagal memilih gambar dari galeri: $e',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -625,7 +639,9 @@ class PetugasTambahAsetController extends GetxController {
// For demonstration purposes: add sample image
void addSampleImage() {
// In a real app, this would open the image picker
selectedImages.add(XFile('assets/images/sample_asset_${selectedImages.length + 1}.jpg'));
selectedImages.add(
XFile('assets/images/sample_asset_${selectedImages.length + 1}.jpg'),
);
validateForm();
}
}

View File

@ -5,6 +5,7 @@ import 'package:bumrent_app/app/data/models/paket_model.dart';
import 'package:image_picker/image_picker.dart';
import 'package:bumrent_app/app/data/providers/aset_provider.dart';
import 'dart:io';
import 'package:bumrent_app/app/routes/app_routes.dart';
import 'package:uuid/uuid.dart';
class PetugasTambahPaketController extends GetxController {
@ -47,6 +48,9 @@ class PetugasTambahPaketController extends GetxController {
// New RxBool for editing
final isEditing = false.obs;
// New RxBool for viewing (read-only mode)
final isViewing = false.obs;
final timeOptions = {'Per Jam': true.obs, 'Per Hari': false.obs};
final pricePerHourController = TextEditingController();
final maxHourController = TextEditingController();
@ -64,11 +68,13 @@ class PetugasTambahPaketController extends GetxController {
void onInit() {
super.onInit();
// Ambil flag isEditing dari arguments
// Ambil flag isEditing dan isViewing dari arguments
isEditing.value =
Get.arguments != null && Get.arguments['isEditing'] == true;
isViewing.value =
Get.arguments != null && Get.arguments['isViewing'] == true;
if (isEditing.value) {
if (isEditing.value || isViewing.value) {
final paketArg = Get.arguments['paket'];
String? paketId;
if (paketArg != null) {
@ -230,7 +236,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Pilih aset dan masukkan jumlah',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -251,7 +257,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Jumlah harus lebih dari 0',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -264,7 +270,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Jumlah melebihi stok yang tersedia',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -286,7 +292,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Sukses',
'Item berhasil ditambahkan ke paket',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);
@ -300,7 +306,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Pilih aset dan masukkan jumlah',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -321,7 +327,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Jumlah harus lebih dari 0',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -349,7 +355,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Jumlah melebihi stok yang tersedia',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -362,7 +368,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Jumlah melebihi stok yang tersedia',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -385,7 +391,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Sukses',
'Item berhasil diperbarui',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);
@ -402,7 +408,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Dihapus',
'Item berhasil dihapus dari paket',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.orange,
colorText: Colors.white,
);
@ -538,13 +544,13 @@ class PetugasTambahPaketController extends GetxController {
}
// Sukses
Get.back();
Get.offNamed(Routes.PETUGAS_PAKET);
Get.snackbar(
'Berhasil',
'Paket berhasil diperbarui',
backgroundColor: Colors.green,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} else {
// --- ADD LOGIC ---
@ -611,13 +617,13 @@ class PetugasTambahPaketController extends GetxController {
}
}
// Sukses
Get.back();
Get.back(result: true);
Get.snackbar(
'Berhasil',
'Paket berhasil ditambahkan',
backgroundColor: Colors.green,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
}
} catch (e) {
@ -627,7 +633,7 @@ class PetugasTambahPaketController extends GetxController {
'Terjadi kesalahan: \\${e.toString()}',
backgroundColor: Colors.red,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
);
} finally {
isSubmitting.value = false;
@ -810,7 +816,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal mengambil gambar dari kamera: $e',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
@ -833,7 +839,7 @@ class PetugasTambahPaketController extends GetxController {
Get.snackbar(
'Error',
'Gagal memilih gambar dari galeri: $e',
snackPosition: SnackPosition.BOTTOM,
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);