396 lines
11 KiB
Dart
396 lines
11 KiB
Dart
import 'package:get/get.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
import '../../../services/firestore_service.dart';
|
|
import '../../../services/realtime_database_service.dart';
|
|
import '../../../models/patient_model.dart';
|
|
import '../../../models/device_model.dart';
|
|
import '../../../models/room_model.dart';
|
|
import '../../../widgets/app_snackbar.dart';
|
|
|
|
class Patient {
|
|
final String id;
|
|
final String name;
|
|
final String room;
|
|
final String deviceId;
|
|
final String guardian;
|
|
final String email;
|
|
final String? userId;
|
|
final String status;
|
|
|
|
Patient({
|
|
required this.id,
|
|
required this.name,
|
|
required this.room,
|
|
required this.deviceId,
|
|
required this.guardian,
|
|
required this.email,
|
|
this.userId,
|
|
this.status = 'Aktif',
|
|
});
|
|
|
|
Map<String, dynamic> toFirestore() {
|
|
return {
|
|
'name_patient': name,
|
|
'name_guardian': guardian,
|
|
'device_id': deviceId,
|
|
'room_id': room,
|
|
'user_id': userId,
|
|
};
|
|
}
|
|
|
|
factory Patient.fromFirestore(DocumentSnapshot doc) {
|
|
final data = doc.data() as Map<String, dynamic>;
|
|
return Patient(
|
|
id: doc.id,
|
|
name: data['name_patient'] ?? '',
|
|
guardian: data['name_guardian'] ?? '',
|
|
deviceId: data['device_id'] ?? '',
|
|
room: data['room_id'] ?? '',
|
|
email: '',
|
|
userId: data['user_id'],
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProfileController extends GetxController {
|
|
final FirestoreService _firestoreService = FirestoreService();
|
|
final RealtimeDatabaseService _realtimeService = RealtimeDatabaseService();
|
|
|
|
final patients = <Patient>[].obs;
|
|
final isLoading = false.obs;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_listenToPatients();
|
|
}
|
|
|
|
void refreshPatients() {
|
|
_listenToPatients();
|
|
}
|
|
|
|
void _listenToPatients() {
|
|
_firestoreService.getPatientsStream().listen((patientModels) async {
|
|
final patientsWithEmail = <Patient>[];
|
|
|
|
for (var model in patientModels) {
|
|
try {
|
|
final email = await _getGuardianEmail(model.nameGuardian);
|
|
final roomName = await _getRoomName(model.roomId);
|
|
|
|
patientsWithEmail.add(
|
|
Patient(
|
|
id: model.id,
|
|
name: model.namePatient,
|
|
guardian: model.nameGuardian,
|
|
deviceId: model.deviceId,
|
|
room: roomName,
|
|
email: email,
|
|
userId: null,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
patients.value = patientsWithEmail;
|
|
}, onError: (error) => AppSnackbar.error('Gagal memuat data'));
|
|
}
|
|
|
|
Future<String> _getGuardianEmail(String guardianName) async {
|
|
try {
|
|
final userDoc = await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.where('role', isEqualTo: 'guardian')
|
|
.where('name', isEqualTo: guardianName)
|
|
.limit(1)
|
|
.get();
|
|
|
|
return userDoc.docs.isNotEmpty
|
|
? userDoc.docs.first.data()['email'] ?? ''
|
|
: '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
Future<String> _getRoomName(String roomId) async {
|
|
try {
|
|
final roomDoc = await FirebaseFirestore.instance
|
|
.collection('rooms')
|
|
.doc(roomId)
|
|
.get();
|
|
return roomDoc.data()?['room_name'] ?? roomId;
|
|
} catch (e) {
|
|
return roomId;
|
|
}
|
|
}
|
|
|
|
Future<void> addPatient({
|
|
required String patientName,
|
|
required String guardianName,
|
|
required String roomName,
|
|
required String deviceId,
|
|
required String email,
|
|
required String password,
|
|
}) async {
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
if (!await _validateNewPatient(email, deviceId)) return;
|
|
|
|
final roomId = _generateRoomId(roomName);
|
|
|
|
// PERBAIKAN: Tambahkan device_id saat create user
|
|
final userId = await _firestoreService.createUser(
|
|
email: email,
|
|
password: password,
|
|
name: guardianName,
|
|
role: 'guardian',
|
|
deviceId: deviceId, // ✅ TAMBAHKAN INI
|
|
);
|
|
|
|
if (userId == null) throw Exception('Gagal membuat user');
|
|
|
|
await _ensureRoomExists(roomId, roomName);
|
|
await _createDevice(deviceId, roomId);
|
|
await _firestoreService.createPatient(
|
|
PatientModel(
|
|
id: '',
|
|
namePatient: patientName,
|
|
nameGuardian: guardianName,
|
|
deviceId: deviceId,
|
|
roomId: roomId,
|
|
),
|
|
);
|
|
|
|
await _realtimeService.updateDeviceStatus(roomId, deviceId, 'connected');
|
|
await _createDeviceInRealtimeDB(roomId, deviceId);
|
|
|
|
Get.back();
|
|
AppSnackbar.success('Pasien berhasil ditambahkan');
|
|
} catch (e) {
|
|
AppSnackbar.error('Gagal menambahkan pasien: $e');
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
Future<bool> _validateNewPatient(String email, String deviceId) async {
|
|
if (await _firestoreService.userExists(email)) {
|
|
AppSnackbar.warning('Email sudah terdaftar');
|
|
return false;
|
|
}
|
|
|
|
if (await _firestoreService.deviceExists(deviceId)) {
|
|
AppSnackbar.warning('Device ID sudah terdaftar');
|
|
return false;
|
|
}
|
|
|
|
if (await _firestoreService.patientExistsByDevice(deviceId)) {
|
|
AppSnackbar.warning('Device sudah digunakan');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Future<void> updatePatient({
|
|
required String patientId,
|
|
required String patientName,
|
|
required String guardianName,
|
|
required String roomName,
|
|
required String deviceId,
|
|
required String email,
|
|
String? newPassword,
|
|
}) async {
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
final oldPatient = await _firestoreService.getPatientById(patientId);
|
|
if (oldPatient == null) throw Exception('Pasien tidak ditemukan');
|
|
|
|
final roomId = _generateRoomId(roomName);
|
|
|
|
if (deviceId != oldPatient.deviceId) {
|
|
if (await _firestoreService.deviceExists(deviceId)) {
|
|
AppSnackbar.warning('Device ID sudah terdaftar');
|
|
return;
|
|
}
|
|
}
|
|
|
|
await _firestoreService.updatePatient(
|
|
patientId,
|
|
PatientModel(
|
|
id: patientId,
|
|
namePatient: patientName,
|
|
nameGuardian: guardianName,
|
|
deviceId: deviceId,
|
|
roomId: roomId,
|
|
),
|
|
);
|
|
|
|
if (deviceId != oldPatient.deviceId || roomId != oldPatient.roomId) {
|
|
await _updateDeviceAndRoom(oldPatient, deviceId, roomId);
|
|
}
|
|
|
|
await _ensureRoomExists(roomId, roomName);
|
|
|
|
// PERBAIKAN: Update device_id di users collection jika device berubah
|
|
if (deviceId != oldPatient.deviceId) {
|
|
final userDoc = await _firestoreService.getUserByEmail(email);
|
|
if (userDoc != null) {
|
|
await _firestoreService.updateUser(
|
|
uid: userDoc.id,
|
|
name: guardianName,
|
|
email: email,
|
|
deviceId: deviceId, // ✅ UPDATE device_id
|
|
);
|
|
}
|
|
}
|
|
|
|
Get.back();
|
|
AppSnackbar.success('Data berhasil diperbarui');
|
|
} catch (e) {
|
|
AppSnackbar.error('Gagal memperbarui data: $e');
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
Future<void> _updateDeviceAndRoom(
|
|
PatientModel oldPatient,
|
|
String newDeviceId,
|
|
String newRoomId,
|
|
) async {
|
|
await _firestoreService.deleteDevice(oldPatient.deviceId);
|
|
await _deleteDeviceFromRealtimeDB(oldPatient.roomId, oldPatient.deviceId);
|
|
await _createDevice(newDeviceId, newRoomId);
|
|
await _createDeviceInRealtimeDB(newRoomId, newDeviceId);
|
|
}
|
|
|
|
// PERBAIKAN LENGKAP: Hapus dari semua collection + Authentication
|
|
Future<void> deletePatient(String patientId, {String? password}) async {
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
final patient = await _firestoreService.getPatientById(patientId);
|
|
if (patient == null) throw Exception('Pasien tidak ditemukan');
|
|
|
|
// 1. Hapus dari Realtime Database
|
|
await _deleteDeviceFromRealtimeDB(patient.roomId, patient.deviceId);
|
|
|
|
// 2. Hapus histories berdasarkan device_id
|
|
await _firestoreService.deleteHistoriesByDevice(patient.deviceId);
|
|
|
|
// 3. Hapus notifications berdasarkan device_id
|
|
await _firestoreService.deleteNotificationsByDevice(patient.deviceId);
|
|
|
|
// 4. Hapus schedules berdasarkan device_id
|
|
await _firestoreService.deleteSchedulesByDevice(patient.deviceId);
|
|
|
|
// 5. Hapus device dari Firestore
|
|
await _firestoreService.deleteDevice(patient.deviceId);
|
|
|
|
// 6. Cari user berdasarkan guardian name dan device_id
|
|
final userDoc = await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.where('role', isEqualTo: 'guardian')
|
|
.where('name', isEqualTo: patient.nameGuardian)
|
|
.where('device_id', isEqualTo: patient.deviceId)
|
|
.limit(1)
|
|
.get();
|
|
|
|
if (userDoc.docs.isNotEmpty) {
|
|
final userId = userDoc.docs.first.id;
|
|
final userEmail = userDoc.docs.first.data()['email'] as String;
|
|
|
|
// 7. Hapus dari collection users
|
|
await _firestoreService.deleteUser(userId);
|
|
|
|
// 8. Hapus dari Firebase Authentication
|
|
// CATATAN: Memerlukan password untuk delete dari Authentication
|
|
if (password != null && password.isNotEmpty) {
|
|
await _firestoreService.deleteUserFromAuth(userEmail, password);
|
|
} else {
|
|
// Jika tidak ada password, tampilkan warning
|
|
print(
|
|
'⚠️ User tidak terhapus dari Authentication karena password tidak tersedia',
|
|
);
|
|
AppSnackbar.warning(
|
|
'User terhapus dari database, tapi tidak dari Authentication. Hubungi admin untuk menghapus akun sepenuhnya.',
|
|
);
|
|
}
|
|
}
|
|
|
|
// 9. Hapus patient dari Firestore
|
|
await _firestoreService.deletePatient(patientId);
|
|
|
|
AppSnackbar.success('Pasien berhasil dihapus');
|
|
} catch (e) {
|
|
AppSnackbar.error('Gagal menghapus pasien: $e');
|
|
print('❌ Delete patient error: $e');
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
Future<void> _ensureRoomExists(String roomId, String roomName) async {
|
|
if (!await _firestoreService.roomExists(roomId)) {
|
|
await _firestoreService.createRoom(
|
|
RoomModel(
|
|
id: roomId,
|
|
roomName: roomName,
|
|
capacity: 10,
|
|
createdAt: DateTime.now(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _createDevice(String deviceId, String roomId) async {
|
|
await _firestoreService.createDevice(
|
|
DeviceModel(
|
|
idDevices: deviceId,
|
|
roomId: roomId,
|
|
devicesName: 'Infusion Device $deviceId',
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _createDeviceInRealtimeDB(String roomId, String deviceId) async {
|
|
final devicePath = 'realtime/rooms/$roomId/devices/$deviceId';
|
|
await FirebaseDatabase.instance.ref().child(devicePath).set({
|
|
'controlling': {
|
|
'servo_open': false,
|
|
'last_command': DateTime.now().toIso8601String(),
|
|
},
|
|
'monitoring': {
|
|
'device_status': 'connected',
|
|
'drop_rate': 0,
|
|
'last_update': DateTime.now().toIso8601String(),
|
|
},
|
|
'settings': {'servo_angle_open': 90, 'servo_angle_close': 0},
|
|
});
|
|
}
|
|
|
|
Future<void> _deleteDeviceFromRealtimeDB(
|
|
String roomId,
|
|
String deviceId,
|
|
) async {
|
|
final devicePath = 'realtime/rooms/$roomId/devices/$deviceId';
|
|
await FirebaseDatabase.instance.ref().child(devicePath).remove();
|
|
}
|
|
|
|
String _generateRoomId(String roomName) {
|
|
return roomName
|
|
.trim()
|
|
.toLowerCase()
|
|
.replaceAll(RegExp(r'[^a-z0-9]+'), '_')
|
|
.replaceAll(RegExp(r'_+'), '_')
|
|
.replaceAll(RegExp(r'^_|_$'), '');
|
|
}
|
|
}
|