kelola penyewa dan beberapa error fix
This commit is contained in:
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -476,7 +476,7 @@ class ListPetugasMitraView extends GetView<ListPetugasMitraController> {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Harap isi semua data',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
@ -600,7 +600,7 @@ class ListPetugasMitraView extends GetView<ListPetugasMitraController> {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Harap isi semua data',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
@ -203,7 +203,7 @@ class ListTagihanPeriodeView extends GetView<ListTagihanPeriodeController> {
|
||||
backgroundColor: Colors.orange.withOpacity(0.1),
|
||||
colorText: Colors.orange.shade800,
|
||||
duration: const Duration(seconds: 3),
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
margin: const EdgeInsets.all(8),
|
||||
);
|
||||
},
|
||||
|
@ -81,11 +81,15 @@ class _PetugasAsetViewState extends State<PetugasAsetView>
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
// Navigate to PetugasTambahAsetView in add mode
|
||||
// Navigate to PetugasTambahAsetView in add mode and refresh data when returning
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_ASET,
|
||||
arguments: {'isEditing': false, 'assetData': null},
|
||||
);
|
||||
)?.then((_) {
|
||||
// Refresh data when returning from tambah_aset page
|
||||
debugPrint('Returning from tambah aset page, refreshing data...');
|
||||
controller.loadAsetData();
|
||||
});
|
||||
},
|
||||
backgroundColor: AppColorsPetugas.babyBlueBright,
|
||||
icon: Icon(Icons.add, color: AppColorsPetugas.blueGrotto),
|
||||
@ -456,7 +460,7 @@ class _PetugasAsetViewState extends State<PetugasAsetView>
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'ID Aset tidak valid',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -467,7 +471,13 @@ class _PetugasAsetViewState extends State<PetugasAsetView>
|
||||
'isEditing': true,
|
||||
'assetId': assetId,
|
||||
},
|
||||
);
|
||||
)?.then((_) {
|
||||
// Refresh data when returning from edit page
|
||||
debugPrint(
|
||||
'Returning from edit aset page, refreshing data...',
|
||||
);
|
||||
controller.loadAsetData();
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
@ -670,235 +680,16 @@ class _PetugasAsetViewState extends State<PetugasAsetView>
|
||||
}
|
||||
}
|
||||
|
||||
void _showAddEditAssetDialog(
|
||||
BuildContext context, {
|
||||
Map<String, dynamic>? aset,
|
||||
}) {
|
||||
final isEditing = aset != null;
|
||||
|
||||
// In a real app, this would have proper form handling with controllers
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlue,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
isEditing ? Icons.edit : Icons.add,
|
||||
color: AppColorsPetugas.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
isEditing ? 'Edit Aset' : 'Tambah Aset Baru',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColorsPetugas.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Silakan lengkapi form di bawah ini',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColorsPetugas.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Mock form - In a real app this would have actual form fields
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlueBright,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColorsPetugas.babyBlue),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Form pengelolaan aset akan ditampilkan di sini dengan field untuk:',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildMockFormField('Nama Aset', 'Contoh: Meja Rapat'),
|
||||
_buildMockFormField('Kategori', 'Pilih kategori aset'),
|
||||
_buildMockFormField(
|
||||
'Harga',
|
||||
'Masukkan harga per unit/periode',
|
||||
),
|
||||
_buildMockFormField(
|
||||
'Satuan',
|
||||
'Contoh: per hari, per bulan',
|
||||
),
|
||||
_buildMockFormField('Stok', 'Jumlah unit tersedia'),
|
||||
_buildMockFormField(
|
||||
'Deskripsi',
|
||||
'Keterangan lengkap aset',
|
||||
),
|
||||
_buildMockToggle(
|
||||
'Status Ketersediaan',
|
||||
isEditing && aset?['tersedia'] == true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Action buttons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
'Batal',
|
||||
style: TextStyle(
|
||||
color: AppColorsPetugas.textSecondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
// In a real app, we would save the form data
|
||||
Get.snackbar(
|
||||
isEditing ? 'Aset Diperbarui' : 'Aset Ditambahkan',
|
||||
isEditing
|
||||
? 'Aset berhasil diperbarui'
|
||||
: 'Aset baru berhasil ditambahkan',
|
||||
backgroundColor: AppColorsPetugas.success,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 10,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(isEditing ? 'Simpan' : 'Tambah'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMockFormField(String label, String hint) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColorsPetugas.babyBlue),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
hint,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColorsPetugas.textLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: AppColorsPetugas.textSecondary,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMockToggle(String label, bool value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: (_) {},
|
||||
activeColor: AppColorsPetugas.blueGrotto,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
void _showAddEditAssetDialog(BuildContext context) {
|
||||
// Navigate to PetugasTambahAsetView in add mode and refresh data when returning
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_ASET,
|
||||
arguments: {'isEditing': false, 'assetData': null},
|
||||
)?.then((_) {
|
||||
// Refresh data when returning from tambah_aset page
|
||||
debugPrint('Returning from tambah aset page, refreshing data...');
|
||||
controller.loadAsetData();
|
||||
});
|
||||
}
|
||||
|
||||
void _showDeleteConfirmation(
|
||||
|
@ -1023,7 +1023,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status sewa telah diubah menjadi SELESAI',
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -1054,7 +1054,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status sewa telah diubah menjadi DIKEMBALIKAN',
|
||||
backgroundColor: Colors.teal,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -1127,7 +1127,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status sewa telah diubah menjadi DIBATALKAN',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -1381,7 +1381,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status pengajuan diubah menjadi Periksa Pembayaran',
|
||||
backgroundColor: Colors.amber.shade700,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
break;
|
||||
|
||||
@ -1394,7 +1394,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Pengajuan sewa aset telah disetujui',
|
||||
backgroundColor: Colors.green.shade600,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
break;
|
||||
|
||||
@ -1407,7 +1407,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Permintaan pembayaran denda telah dikirim',
|
||||
backgroundColor: Colors.deepOrange,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
break;
|
||||
|
||||
@ -1420,7 +1420,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status pengajuan diubah menjadi Periksa Denda',
|
||||
backgroundColor: Colors.red.shade600,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
break;
|
||||
|
||||
@ -1433,7 +1433,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Aset telah dikembalikan dan sewa telah selesai',
|
||||
backgroundColor: Colors.green.shade600,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
break;
|
||||
|
||||
@ -1476,7 +1476,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status sewa telah diubah menjadi DIBATALKAN',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -1595,7 +1595,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Status sewa diubah menjadi Dikembalikan',
|
||||
backgroundColor: Colors.teal,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1628,7 +1628,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Permintaan pembayaran denda telah dikirim',
|
||||
backgroundColor: Colors.deepOrange,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1661,7 +1661,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Sewa aset telah selesai',
|
||||
backgroundColor: Colors.purple,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
};
|
||||
} else if (status == 'Dikembalikan') {
|
||||
@ -1709,7 +1709,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Sewa aset telah selesai',
|
||||
backgroundColor: Colors.purple,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.task_alt_outlined),
|
||||
@ -1817,7 +1817,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Bukti sewa dalam format PDF sedang diunduh',
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1837,7 +1837,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Bukti sewa dalam format JPG sedang diunduh',
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1857,7 +1857,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Data sewa dalam format XLSX sedang diunduh',
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1963,7 +1963,8 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
nominalController.text = sewa.totalTagihan.toString();
|
||||
nominalController.text =
|
||||
sewa.totalTagihan.toInt().toString();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.payments_outlined,
|
||||
@ -2456,7 +2457,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Nominal denda harus diisi',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -2501,7 +2502,7 @@ class _PetugasDetailSewaViewState extends State<PetugasDetailSewaView> {
|
||||
'Permintaan pembayaran denda telah dikirim',
|
||||
backgroundColor: Colors.deepOrange,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.check_circle),
|
||||
|
@ -567,7 +567,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur tambah rekening bank sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -639,7 +639,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur edit rekening bank sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -689,7 +689,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur hapus rekening bank sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -765,7 +765,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur tambah mitra sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -845,7 +845,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur edit mitra sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -895,7 +895,7 @@ class PetugasManajemenBumdesView
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Fitur hapus mitra sedang dalam pengembangan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
|
@ -55,11 +55,17 @@ class PetugasPaketView extends GetView<PetugasPaketController> {
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed:
|
||||
() => Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_PAKET,
|
||||
arguments: {'isEditing': false},
|
||||
),
|
||||
onPressed: () async {
|
||||
final result = await Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_PAKET,
|
||||
arguments: {'isEditing': false},
|
||||
);
|
||||
|
||||
// Refresh the package list if a package was added
|
||||
if (result == true) {
|
||||
controller.loadPaketData();
|
||||
}
|
||||
},
|
||||
label: Text(
|
||||
'Tambah Paket',
|
||||
style: TextStyle(
|
||||
@ -142,11 +148,17 @@ class PetugasPaketView extends GetView<PetugasPaketController> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed:
|
||||
() => Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_PAKET,
|
||||
arguments: {'isEditing': false},
|
||||
),
|
||||
onPressed: () async {
|
||||
final result = await Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_PAKET,
|
||||
arguments: {'isEditing': false},
|
||||
);
|
||||
|
||||
// Refresh the package list if a package was added
|
||||
if (result == true) {
|
||||
controller.loadPaketData();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Tambah Paket'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -376,6 +388,21 @@ class PetugasPaketView extends GetView<PetugasPaketController> {
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final result = await Get.toNamed(
|
||||
Routes.PETUGAS_TAMBAH_PAKET,
|
||||
arguments: {
|
||||
'isEditing': false,
|
||||
'isViewing': true,
|
||||
'paket': paket,
|
||||
},
|
||||
);
|
||||
|
||||
// Refresh the package list if data was modified
|
||||
if (result == true) {
|
||||
controller.loadPaketData();
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
// Paket image or icon
|
||||
|
@ -111,6 +111,27 @@ class _PetugasPenyewaViewState extends State<PetugasPenyewaView>
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
// Tambahkan indikator refresh
|
||||
Obx(
|
||||
() =>
|
||||
controller.isRefreshing.value
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
drawer: PetugasSideNavbar(controller: dashboardController),
|
||||
drawerEdgeDragWidth: 60,
|
||||
@ -148,7 +169,13 @@ class _PetugasPenyewaViewState extends State<PetugasPenyewaView>
|
||||
}
|
||||
|
||||
if (controller.filteredPenyewaList.isEmpty) {
|
||||
return _buildEmptyState();
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await controller.refreshPenyewaList();
|
||||
},
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
child: _buildEmptyStateWithScrollView(),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildPenyewaList();
|
||||
@ -282,208 +309,234 @@ class _PetugasPenyewaViewState extends State<PetugasPenyewaView>
|
||||
);
|
||||
}
|
||||
|
||||
// Widget untuk menampilkan empty state dengan ScrollView untuk mendukung RefreshIndicator
|
||||
Widget _buildEmptyStateWithScrollView() {
|
||||
return ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(Get.context!).size.height * 0.7,
|
||||
child: _buildEmptyState(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPenyewaList() {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: controller.filteredPenyewaList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final penyewa = controller.filteredPenyewaList[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: InkWell(
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await controller.refreshPenyewaList();
|
||||
},
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: controller.filteredPenyewaList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final penyewa = controller.filteredPenyewaList[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header with avatar and badge
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlueLight.withOpacity(0.2),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header with avatar and badge
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlueLight.withOpacity(
|
||||
0.2,
|
||||
),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// Avatar with border
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.white,
|
||||
width: 2,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: AppColorsPetugas.babyBlueLight,
|
||||
backgroundImage:
|
||||
penyewa['avatar'] != null &&
|
||||
penyewa['avatar']
|
||||
.toString()
|
||||
.isNotEmpty
|
||||
? NetworkImage(penyewa['avatar'])
|
||||
: null,
|
||||
child:
|
||||
penyewa['avatar'] == null ||
|
||||
penyewa['avatar'].toString().isEmpty
|
||||
? const Icon(
|
||||
Icons.person,
|
||||
color: Colors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Name and email
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
penyewa['nama_lengkap'] ??
|
||||
'Nama tidak tersedia',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColorsPetugas.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.email_outlined,
|
||||
size: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
penyewa['email'] ??
|
||||
'Email tidak tersedia',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildStatusBadge(penyewa['status']),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// Avatar with border
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: AppColorsPetugas.babyBlueLight,
|
||||
backgroundImage:
|
||||
penyewa['avatar'] != null &&
|
||||
penyewa['avatar']
|
||||
.toString()
|
||||
.isNotEmpty
|
||||
? NetworkImage(penyewa['avatar'])
|
||||
: null,
|
||||
child:
|
||||
penyewa['avatar'] == null ||
|
||||
penyewa['avatar'].toString().isEmpty
|
||||
? const Icon(
|
||||
Icons.person,
|
||||
color: Colors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Name and email
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
penyewa['nama_lengkap'] ??
|
||||
'Nama tidak tersedia',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColorsPetugas.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.email_outlined,
|
||||
size: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
penyewa['email'] ??
|
||||
'Email tidak tersedia',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildStatusBadge(penyewa['status']),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Content section
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Show additional info only for Aktif and Ditangguhkan tabs
|
||||
if (controller.currentTabIndex.value != 0) ...[
|
||||
Row(
|
||||
children: [
|
||||
_buildInfoChip(
|
||||
Icons.credit_card_outlined,
|
||||
'NIK: ${penyewa['nik'] ?? 'Tidak tersedia'}',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildInfoChip(
|
||||
Icons.phone_outlined,
|
||||
penyewa['no_hp'] ?? 'No. HP tidak tersedia',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildInfoTile(
|
||||
'Total Sewa',
|
||||
penyewa['total_sewa']?.toString() ?? '0',
|
||||
Icons.shopping_bag_outlined,
|
||||
AppColorsPetugas.blueGrotto,
|
||||
),
|
||||
if (controller.currentTabIndex.value == 1 ||
|
||||
controller.currentTabIndex.value == 2)
|
||||
_buildActionChip(
|
||||
label: 'Lihat Detail',
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
icon: Icons.visibility,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_DETAIL_PENYEWA,
|
||||
arguments: {
|
||||
'userId': penyewa['user_id'],
|
||||
},
|
||||
);
|
||||
},
|
||||
// Content section
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Show additional info only for Aktif and Ditangguhkan tabs
|
||||
if (controller.currentTabIndex.value != 0) ...[
|
||||
Row(
|
||||
children: [
|
||||
_buildInfoChip(
|
||||
Icons.credit_card_outlined,
|
||||
'NIK: ${penyewa['nik'] ?? 'Tidak tersedia'}',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
_buildInfoChip(
|
||||
Icons.phone_outlined,
|
||||
penyewa['no_hp'] ?? 'No. HP tidak tersedia',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildInfoTile(
|
||||
'Total Sewa',
|
||||
penyewa['total_sewa']?.toString() ?? '0',
|
||||
Icons.shopping_bag_outlined,
|
||||
AppColorsPetugas.blueGrotto,
|
||||
),
|
||||
if (controller.currentTabIndex.value == 1 ||
|
||||
controller.currentTabIndex.value == 2)
|
||||
_buildActionChip(
|
||||
label: 'Lihat Detail',
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
icon: Icons.visibility,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_DETAIL_PENYEWA,
|
||||
arguments: {
|
||||
'userId': penyewa['user_id'],
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
// Add "Detail" button for Verifikasi tab
|
||||
if (controller.currentTabIndex.value == 0) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildActionChip(
|
||||
label: 'Lihat Detail & Verifikasi',
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
icon: Icons.visibility,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_DETAIL_PENYEWA,
|
||||
arguments: {
|
||||
'userId': penyewa['user_id'],
|
||||
},
|
||||
);
|
||||
},
|
||||
// Add "Detail" button for Verifikasi tab
|
||||
if (controller.currentTabIndex.value == 0) ...[
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildActionChip(
|
||||
label: 'Lihat Detail & Verifikasi',
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
icon: Icons.visibility,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
Routes.PETUGAS_DETAIL_PENYEWA,
|
||||
arguments: {
|
||||
'userId': penyewa['user_id'],
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,9 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
appBar: AppBar(
|
||||
title: Obx(
|
||||
() => Text(
|
||||
controller.isEditing.value ? 'Edit Paket' : 'Tambah Paket',
|
||||
controller.isViewing.value
|
||||
? 'Detail Paket'
|
||||
: (controller.isEditing.value ? 'Edit Paket' : 'Tambah Paket'),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
@ -168,15 +170,16 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
'Item Paket',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showAddItemDialog(),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Tambah Item'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.babyBlueLight,
|
||||
foregroundColor: AppColorsPetugas.blueGrotto,
|
||||
if (!controller.isViewing.value)
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _showAddItemDialog(),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Tambah Item'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.babyBlueLight,
|
||||
foregroundColor: AppColorsPetugas.blueGrotto,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -208,26 +211,33 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
Text('Stok tersedia: ${item['stok']}'),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => _showEditItemDialog(index),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
),
|
||||
onPressed:
|
||||
() => controller.removeItem(index),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing:
|
||||
controller.isViewing.value
|
||||
? null
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed:
|
||||
() =>
|
||||
_showEditItemDialog(index),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
),
|
||||
onPressed:
|
||||
() => controller.removeItem(
|
||||
index,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -526,6 +536,9 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
String? prefixText,
|
||||
IconData? prefixIcon,
|
||||
}) {
|
||||
final petugasController =
|
||||
this.controller; // Reference to PetugasTambahPaketController
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -539,7 +552,7 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
color: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
),
|
||||
if (isRequired) ...[
|
||||
if (isRequired && !petugasController.isViewing.value) ...[
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'*',
|
||||
@ -553,43 +566,55 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: controller,
|
||||
maxLines: maxLines,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: TextStyle(color: AppColorsPetugas.textLight),
|
||||
filled: true,
|
||||
fillColor: AppColorsPetugas.babyBlueBright,
|
||||
prefixText: prefixText,
|
||||
prefixIcon:
|
||||
prefixIcon != null
|
||||
? Icon(
|
||||
prefixIcon,
|
||||
size: 20,
|
||||
color: AppColorsPetugas.textSecondary,
|
||||
)
|
||||
: null,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
Obx(
|
||||
() => TextFormField(
|
||||
controller: controller,
|
||||
maxLines: maxLines,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
readOnly: petugasController.isViewing.value,
|
||||
style: TextStyle(
|
||||
color:
|
||||
petugasController.isViewing.value
|
||||
? AppColorsPetugas.textSecondary
|
||||
: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
width: 1.5,
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: TextStyle(color: AppColorsPetugas.textLight),
|
||||
filled: true,
|
||||
fillColor:
|
||||
petugasController.isViewing.value
|
||||
? AppColorsPetugas.babyBlueLight
|
||||
: AppColorsPetugas.babyBlueBright,
|
||||
prefixText: prefixText,
|
||||
prefixIcon:
|
||||
prefixIcon != null
|
||||
? Icon(
|
||||
prefixIcon,
|
||||
size: 20,
|
||||
color: AppColorsPetugas.textSecondary,
|
||||
)
|
||||
: null,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -646,7 +671,10 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
vertical: 12,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColorsPetugas.babyBlueBright,
|
||||
fillColor:
|
||||
controller.isViewing.value
|
||||
? AppColorsPetugas.babyBlueLight
|
||||
: AppColorsPetugas.babyBlueBright,
|
||||
),
|
||||
items:
|
||||
options.map((option) {
|
||||
@ -661,9 +689,12 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) onChanged(value);
|
||||
},
|
||||
onChanged:
|
||||
controller.isViewing.value
|
||||
? null // Disable in view-only mode
|
||||
: (value) {
|
||||
if (value != null) onChanged(value);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
@ -717,41 +748,42 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
// Add button
|
||||
GestureDetector(
|
||||
onTap: _showImageSourceOptions,
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlueBright,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: AppColorsPetugas.babyBlue,
|
||||
width: 1,
|
||||
style: BorderStyle.solid,
|
||||
if (!controller.isViewing.value)
|
||||
GestureDetector(
|
||||
onTap: _showImageSourceOptions,
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColorsPetugas.babyBlueBright,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: AppColorsPetugas.babyBlue,
|
||||
width: 1,
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Tambah Foto',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Tambah Foto',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColorsPetugas.blueGrotto,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Image previews
|
||||
...List<Widget>.generate(controller.selectedImages.length, (
|
||||
index,
|
||||
@ -811,21 +843,24 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
Positioned(
|
||||
top: 4,
|
||||
right: 4,
|
||||
child: InkWell(
|
||||
onTap: () => controller.removeImage(index),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
child:
|
||||
!controller.isViewing.value
|
||||
? InkWell(
|
||||
onTap: () => controller.removeImage(index),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -949,67 +984,97 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => Get.back(),
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
label: const Text('Batal'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColorsPetugas.textSecondary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
side: BorderSide(color: AppColorsPetugas.divider),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Obx(() {
|
||||
// In view-only mode, just show a back button
|
||||
if (controller.isViewing.value) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => Get.back(),
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
label: const Text('Kembali'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
final isValid = controller.isFormValid.value;
|
||||
final isSubmitting = controller.isSubmitting.value;
|
||||
return ElevatedButton.icon(
|
||||
onPressed:
|
||||
controller.isFormChanged.value && !isSubmitting
|
||||
? controller.savePaket
|
||||
: null,
|
||||
icon:
|
||||
);
|
||||
}
|
||||
|
||||
// For edit/add mode, show cancel and save buttons
|
||||
return Row(
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => Get.back(),
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
label: const Text('Batal'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColorsPetugas.textSecondary,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
side: BorderSide(color: AppColorsPetugas.divider),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
final isValid = controller.isFormValid.value;
|
||||
final isSubmitting = controller.isSubmitting.value;
|
||||
return ElevatedButton.icon(
|
||||
onPressed:
|
||||
controller.isFormChanged.value && !isSubmitting
|
||||
? controller.savePaket
|
||||
: null,
|
||||
icon:
|
||||
isSubmitting
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.save),
|
||||
label: Text(
|
||||
isSubmitting
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.save),
|
||||
label: Text(
|
||||
isSubmitting
|
||||
? 'Menyimpan...'
|
||||
: (controller.isEditing.value
|
||||
? 'Simpan Paket'
|
||||
: 'Tambah Paket'),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
? 'Menyimpan...'
|
||||
: (controller.isEditing.value
|
||||
? 'Simpan Paket'
|
||||
: 'Tambah Paket'),
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorsPetugas.blueGrotto,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
disabledBackgroundColor: AppColorsPetugas.textLight,
|
||||
),
|
||||
disabledBackgroundColor: AppColorsPetugas.textLight,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1035,7 +1100,10 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
() => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => controller.toggleTimeOption(option),
|
||||
onTap:
|
||||
controller.isViewing.value
|
||||
? null
|
||||
: () => controller.toggleTimeOption(option),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@ -1173,12 +1241,22 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
controller: priceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
readOnly: controller.isViewing.value,
|
||||
style: TextStyle(
|
||||
color:
|
||||
controller.isViewing.value
|
||||
? AppColorsPetugas.textSecondary
|
||||
: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Masukkan harga',
|
||||
hintStyle: TextStyle(color: AppColorsPetugas.textLight),
|
||||
prefixText: 'Rp ',
|
||||
filled: true,
|
||||
fillColor: AppColorsPetugas.babyBlueBright,
|
||||
fillColor:
|
||||
controller.isViewing.value
|
||||
? AppColorsPetugas.babyBlueLight
|
||||
: AppColorsPetugas.babyBlueBright,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
@ -1210,11 +1288,21 @@ class PetugasTambahPaketView extends GetView<PetugasTambahPaketController> {
|
||||
controller: maxController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
readOnly: controller.isViewing.value,
|
||||
style: TextStyle(
|
||||
color:
|
||||
controller.isViewing.value
|
||||
? AppColorsPetugas.textSecondary
|
||||
: AppColorsPetugas.textPrimary,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Opsional',
|
||||
hintStyle: TextStyle(color: AppColorsPetugas.textLight),
|
||||
filled: true,
|
||||
fillColor: AppColorsPetugas.babyBlueBright,
|
||||
fillColor:
|
||||
controller.isViewing.value
|
||||
? AppColorsPetugas.babyBlueLight
|
||||
: AppColorsPetugas.babyBlueBright,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
|
Reference in New Issue
Block a user