132 lines
3.8 KiB
Dart
132 lines
3.8 KiB
Dart
import 'dart:async';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:fl_chart/fl_chart.dart';
|
|
|
|
class DashboardController extends GetxController {
|
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
|
|
var totalPendapatan = 0.0.obs;
|
|
var totalPendapatanCOD = 0.0.obs;
|
|
var totalPendapatanNonCOD = 0.0.obs;
|
|
|
|
var totalPaket = 0.obs;
|
|
var totalPaketCOD = 0.obs;
|
|
var totalPaketNonCOD = 0.obs;
|
|
|
|
var isLoading = false.obs;
|
|
var grafikPengiriman = <FlSpot>[].obs;
|
|
|
|
StreamSubscription? _listenerRealtime;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_listenRealtimeData();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_listenerRealtime?.cancel();
|
|
super.onClose();
|
|
}
|
|
|
|
/// 🔁 Listener realtime dashboard & grafik
|
|
void _listenRealtimeData() {
|
|
final user = _auth.currentUser;
|
|
if (user == null) return;
|
|
final userId = user.uid;
|
|
|
|
// Pastikan urut berdasarkan waktu dibuat
|
|
_listenerRealtime = _firestore
|
|
.collection('resis')
|
|
.where('store_id', isEqualTo: userId)
|
|
.orderBy('created_at', descending: false)
|
|
.snapshots()
|
|
.listen((snapshot) {
|
|
_updateDashboard(snapshot.docs);
|
|
}, onError: (e) {
|
|
print("🔥 Error listen data: $e");
|
|
});
|
|
}
|
|
|
|
/// 🔹 Hitung ulang total dan grafik setiap ada perubahan data
|
|
void _updateDashboard(List<QueryDocumentSnapshot<Map<String, dynamic>>> docs) {
|
|
final now = DateTime.now();
|
|
final cutoff = now.subtract(const Duration(days: 6));
|
|
|
|
double total = 0;
|
|
double totalCod = 0;
|
|
double totalNonCod = 0;
|
|
int paket = 0;
|
|
int paketCod = 0;
|
|
int paketNonCod = 0;
|
|
Map<String, int> pengirimanPerTanggal = {};
|
|
|
|
for (var doc in docs) {
|
|
final data = doc.data();
|
|
if (data['created_at'] == null) continue;
|
|
|
|
final createdAt = (data['created_at'] as Timestamp).toDate().toLocal();
|
|
final tanggalKey = DateFormat('yyyy-MM-dd').format(createdAt);
|
|
|
|
// Total untuk hari ini
|
|
if (createdAt.year == now.year &&
|
|
createdAt.month == now.month &&
|
|
createdAt.day == now.day) {
|
|
final totalTransaksi = double.tryParse(data['total'].toString()) ?? 0;
|
|
final pembayaran = data['pembayaran']?.toString().toUpperCase() ?? '';
|
|
|
|
total += totalTransaksi;
|
|
paket++;
|
|
|
|
if (pembayaran == 'COD') {
|
|
paketCod++;
|
|
totalCod += totalTransaksi;
|
|
} else {
|
|
paketNonCod++;
|
|
totalNonCod += totalTransaksi;
|
|
}
|
|
}
|
|
|
|
// Total pengiriman 7 hari terakhir
|
|
if (!(createdAt.isBefore(cutoff) || createdAt.isAfter(now))) {
|
|
pengirimanPerTanggal.update(tanggalKey, (v) => v + 1,
|
|
ifAbsent: () => 1);
|
|
}
|
|
}
|
|
|
|
// 🔸 Update data observabel
|
|
totalPendapatan.value = total;
|
|
totalPendapatanCOD.value = totalCod;
|
|
totalPendapatanNonCOD.value = totalNonCod;
|
|
totalPaket.value = paket;
|
|
totalPaketCOD.value = paketCod;
|
|
totalPaketNonCOD.value = paketNonCod;
|
|
|
|
// 🔹 Buat data untuk 7 hari grafik
|
|
final List<FlSpot> newSpots = [];
|
|
for (int i = 0; i < 7; i++) {
|
|
final date = cutoff.add(Duration(days: i));
|
|
final key = DateFormat('yyyy-MM-dd').format(date);
|
|
final jumlah = pengirimanPerTanggal[key] ?? 0;
|
|
newSpots.add(FlSpot(i.toDouble(), jumlah.toDouble()));
|
|
}
|
|
|
|
// 🔸 Assign dan refresh supaya UI update langsung
|
|
grafikPengiriman.assignAll(newSpots);
|
|
grafikPengiriman.refresh();
|
|
|
|
print("📊 Dashboard & grafik diperbarui realtime (${DateTime.now()})");
|
|
}
|
|
|
|
String formatRupiah(double value) {
|
|
final format =
|
|
NumberFormat.currency(locale: 'id_ID', symbol: 'Rp ', decimalDigits: 0);
|
|
return format.format(value);
|
|
}
|
|
}
|