141 lines
4.7 KiB
Dart
141 lines
4.7 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:mobile_monitoring/core/utils/cycle_phase_utils.dart';
|
|
|
|
/// Service untuk mengelola siklus tanam dengan fase pertumbuhan otomatis
|
|
class PlantingCyclesService {
|
|
static const String collectionName = 'planting_cycles';
|
|
|
|
CollectionReference<Map<String, dynamic>> get _collection =>
|
|
FirebaseFirestore.instance.collection(collectionName);
|
|
|
|
/// Stream siklus tanam yang sedang aktif (per user dengan fallback)
|
|
Stream<Map<String, dynamic>?> getActiveCycleStream({required String userId}) {
|
|
return _collection.snapshots().map((snapshot) {
|
|
if (snapshot.docs.isEmpty) return null;
|
|
|
|
if (userId.isNotEmpty) {
|
|
final matches = snapshot.docs.where((d) => d.id == userId).toList();
|
|
if (matches.isNotEmpty && matches.first.data().isNotEmpty) {
|
|
return {...matches.first.data(), 'doc_id': matches.first.id};
|
|
}
|
|
}
|
|
|
|
// Fallback ke dokumen siklus tanam pertama di database
|
|
final firstDoc = snapshot.docs.first;
|
|
return {...firstDoc.data(), 'doc_id': firstDoc.id};
|
|
}).handleError((error) {
|
|
debugPrint('[PlantingCyclesService] Stream error: $error');
|
|
return null;
|
|
});
|
|
}
|
|
|
|
/// Tambah siklus tanam baru (gunakan userId sebagai doc ID)
|
|
Future<String> addCycle({
|
|
required String userId,
|
|
required DateTime startDate,
|
|
}) async {
|
|
if (userId.isEmpty) throw Exception('User tidak terautentikasi');
|
|
|
|
await _collection.doc(userId).set({
|
|
'user_id': userId,
|
|
'start_date': Timestamp.fromDate(startDate),
|
|
'days_since_planting': 0,
|
|
'growth_phase': 'Semai',
|
|
'created_at': FieldValue.serverTimestamp(),
|
|
'updated_at': FieldValue.serverTimestamp(),
|
|
});
|
|
|
|
return userId;
|
|
}
|
|
|
|
/// Update hari dan fase tanam berdasarkan start_date
|
|
Future<void> updateCycleProgress({
|
|
required String userId,
|
|
required DateTime startDate,
|
|
}) async {
|
|
final now = DateTime.now();
|
|
final daysSincePlanting = now.difference(startDate).inDays;
|
|
final growthPhase = CyclePhaseUtils.calculateCyclePhase(daysSincePlanting);
|
|
|
|
await _collection.doc(userId).set({
|
|
'days_since_planting': daysSincePlanting,
|
|
'growth_phase': growthPhase,
|
|
'updated_at': FieldValue.serverTimestamp(),
|
|
}, SetOptions(merge: true));
|
|
}
|
|
|
|
/// Hitung hari total siklus untuk panen
|
|
Future<int> calculateCycleDaysBeforeDelete({required String userId}) async {
|
|
if (userId.isEmpty) throw Exception('User tidak terautentikasi');
|
|
|
|
final doc = await _collection.doc(userId).get();
|
|
if (!doc.exists || doc.data() == null) {
|
|
throw Exception('Siklus tanam aktif tidak ditemukan');
|
|
}
|
|
|
|
final data = doc.data()!;
|
|
final startValue = data['start_date'];
|
|
DateTime? startDate;
|
|
|
|
if (startValue is Timestamp) {
|
|
startDate = startValue.toDate();
|
|
} else if (startValue is DateTime) {
|
|
startDate = startValue;
|
|
}
|
|
|
|
final daysSincePlanting = startDate == null
|
|
? (data['days_since_planting'] as num?)?.toInt() ?? 0
|
|
: DateTime.now().difference(startDate).inDays;
|
|
|
|
return daysSincePlanting;
|
|
}
|
|
|
|
/// Hapus siklus tanam (gunakan userId)
|
|
Future<void> deleteCycle({required String userId}) async {
|
|
await _collection.doc(userId).delete();
|
|
}
|
|
|
|
/// Get detail siklus berdasarkan userId dengan update otomatis
|
|
Future<Map<String, dynamic>?> getCycleByUserId({
|
|
required String userId,
|
|
}) async {
|
|
final doc = await _collection.doc(userId).get();
|
|
if (!doc.exists || doc.data() == null) return null;
|
|
|
|
final data = doc.data()!;
|
|
final startDate = (data['start_date'] as Timestamp).toDate();
|
|
|
|
// Update progress sebelum return
|
|
await updateCycleProgress(userId: userId, startDate: startDate);
|
|
|
|
// Get data terbaru
|
|
final updatedDoc = await _collection.doc(userId).get();
|
|
if (!updatedDoc.exists) return null;
|
|
return {...updatedDoc.data()!, 'doc_id': updatedDoc.id};
|
|
}
|
|
|
|
/// Get siklus aktif dengan info fase terkini
|
|
Future<Map<String, dynamic>?> getActiveCycle({required String userId}) async {
|
|
if (userId.isEmpty) throw Exception('User tidak terautentikasi');
|
|
|
|
final doc = await _collection.doc(userId).get();
|
|
if (!doc.exists || doc.data() == null) return null;
|
|
|
|
final data = doc.data()!;
|
|
final startDate = (data['start_date'] as Timestamp).toDate();
|
|
|
|
// Hitung days_since_planting dan growth_phase terkini
|
|
final now = DateTime.now();
|
|
final daysSincePlanting = now.difference(startDate).inDays;
|
|
final growthPhase = CyclePhaseUtils.calculateCyclePhase(daysSincePlanting);
|
|
|
|
return {
|
|
...data,
|
|
'doc_id': doc.id,
|
|
'days_since_planting': daysSincePlanting,
|
|
'growth_phase': growthPhase,
|
|
};
|
|
}
|
|
}
|