219 lines
6.3 KiB
Dart
219 lines
6.3 KiB
Dart
import 'package:get/get.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'dart:async';
|
|
import '../../../services/firestore_service.dart';
|
|
import '../../../services/realtime_database_service.dart';
|
|
import '../../../models/patient_model.dart';
|
|
import '../../../models/room_model.dart';
|
|
import '../../../models/realtime_monitoring_model.dart';
|
|
import '../../../widgets/app_snackbar.dart';
|
|
|
|
class InfusMonitoring {
|
|
final String id;
|
|
final String patientName;
|
|
final String deviceId;
|
|
final String room;
|
|
final String roomId;
|
|
final String dropsPerMinute;
|
|
final DateTime lastUpdate;
|
|
final String deviceStatus;
|
|
|
|
InfusMonitoring({
|
|
required this.id,
|
|
required this.patientName,
|
|
required this.deviceId,
|
|
required this.room,
|
|
required this.roomId,
|
|
required this.dropsPerMinute,
|
|
required this.lastUpdate,
|
|
required this.deviceStatus,
|
|
});
|
|
}
|
|
|
|
class HomeController extends GetxController {
|
|
final searchController = TextEditingController();
|
|
final FirestoreService _firestoreService = FirestoreService();
|
|
final RealtimeDatabaseService _realtimeService = RealtimeDatabaseService();
|
|
|
|
final currentCarouselIndex = 0.obs;
|
|
final bannerImages = [
|
|
'assets/images/banner-slider1.png',
|
|
'assets/images/banner-slider2.png',
|
|
];
|
|
|
|
final monitoringList = <InfusMonitoring>[].obs;
|
|
final isLoading = true.obs;
|
|
|
|
StreamSubscription? _patientsSubscription;
|
|
StreamSubscription? _realtimeSubscription;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_initializeMonitoring();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
searchController.dispose();
|
|
_patientsSubscription?.cancel();
|
|
_realtimeSubscription?.cancel();
|
|
super.onClose();
|
|
}
|
|
|
|
void _initializeMonitoring() {
|
|
isLoading.value = true;
|
|
|
|
_patientsSubscription = _firestoreService.getPatientsStream().listen(
|
|
(patients) async {
|
|
_realtimeSubscription = _realtimeService.getAllDevicesStream().listen((
|
|
realtimeDevices,
|
|
) {
|
|
_combineMonitoringData(patients, realtimeDevices);
|
|
});
|
|
},
|
|
onError: (error) {
|
|
isLoading.value = false;
|
|
AppSnackbar.error('Gagal memuat data');
|
|
},
|
|
);
|
|
}
|
|
|
|
void _combineMonitoringData(
|
|
List<PatientModel> patients,
|
|
List<RealtimeMonitoringModel> realtimeDevices,
|
|
) async {
|
|
final List<InfusMonitoring> combinedData = [];
|
|
|
|
for (var patient in patients) {
|
|
final realtimeDevice = realtimeDevices.firstWhereOrNull(
|
|
(device) => device.deviceId == patient.deviceId,
|
|
);
|
|
|
|
final roomName = await _getRoomName(patient.roomId);
|
|
final dropRate = realtimeDevice?.dropRate ?? 0.0;
|
|
final lastUpdate = realtimeDevice?.lastUpdate ?? DateTime.now();
|
|
final deviceStatus = realtimeDevice?.deviceStatus ?? 'disconnected';
|
|
|
|
combinedData.add(
|
|
InfusMonitoring(
|
|
id: patient.id,
|
|
patientName: patient.namePatient,
|
|
deviceId: patient.deviceId,
|
|
room: roomName,
|
|
roomId: patient.roomId,
|
|
dropsPerMinute: '${dropRate.toStringAsFixed(0)} tpm',
|
|
lastUpdate: lastUpdate,
|
|
deviceStatus: deviceStatus,
|
|
),
|
|
);
|
|
}
|
|
|
|
monitoringList.value = combinedData;
|
|
isLoading.value = false;
|
|
}
|
|
|
|
Future<String> _getRoomName(String roomId) async {
|
|
try {
|
|
final rooms = await _firestoreService.getRoomsStream().first;
|
|
final room = rooms.firstWhereOrNull(
|
|
(r) =>
|
|
r.id == roomId || r.roomName.toLowerCase() == roomId.toLowerCase(),
|
|
);
|
|
return room?.roomName ?? roomId;
|
|
} catch (e) {
|
|
return roomId;
|
|
}
|
|
}
|
|
|
|
Future<void> controlInfusServo(
|
|
String deviceId,
|
|
String roomId,
|
|
bool open,
|
|
) async {
|
|
try {
|
|
final dbRoomId = roomId.toLowerCase().replaceAll(' ', '_');
|
|
await _realtimeService.controlServo(dbRoomId, deviceId, open);
|
|
AppSnackbar.success(open ? 'Infus dibuka' : 'Infus ditutup');
|
|
} catch (e) {
|
|
AppSnackbar.error('Gagal mengontrol infus');
|
|
}
|
|
}
|
|
|
|
void updateCarouselIndex(int index) {
|
|
currentCarouselIndex.value = index;
|
|
}
|
|
|
|
void showLogoutDialog(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
backgroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
title: const Text(
|
|
'Konfirmasi Logout',
|
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
|
),
|
|
content: const Text(
|
|
'Yakin ingin keluar?',
|
|
style: TextStyle(fontSize: 16),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: Colors.grey[600],
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 12,
|
|
horizontal: 20,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: const Text('Batal'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () async {
|
|
Navigator.of(context).pop();
|
|
await FirebaseAuth.instance.signOut();
|
|
Get.offAllNamed('/login');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 12,
|
|
horizontal: 20,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: const Text(
|
|
'Logout',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
String getLastUpdateText(DateTime lastUpdate) {
|
|
final difference = DateTime.now().difference(lastUpdate);
|
|
if (difference.inMinutes < 60) {
|
|
return '${difference.inMinutes}m ago';
|
|
} else if (difference.inHours < 24) {
|
|
return '${difference.inHours}h ago';
|
|
} else {
|
|
return '${difference.inDays}d ago';
|
|
}
|
|
}
|
|
}
|