386 lines
11 KiB
Dart
386 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
class HistoryScreen extends StatefulWidget {
|
|
const HistoryScreen({super.key});
|
|
|
|
@override
|
|
State<HistoryScreen> createState() => _HistoryScreenState();
|
|
}
|
|
|
|
class _HistoryScreenState extends State<HistoryScreen> {
|
|
|
|
late String selectedYear;
|
|
late String selectedMonth;
|
|
|
|
final List<String> months = [
|
|
"Januari","Februari","Maret","April","Mei","Juni",
|
|
"Juli","Agustus","September","Oktober","November","Desember"
|
|
];
|
|
|
|
late List<String> years;
|
|
|
|
Future<void> deleteOldData() async {
|
|
|
|
final snapshot = await FirebaseFirestore.instance
|
|
.collection("watering_history")
|
|
.get();
|
|
|
|
final now = DateTime.now();
|
|
|
|
for (var doc in snapshot.docs) {
|
|
|
|
final data = doc.data();
|
|
final dateString = data["date"];
|
|
|
|
final parsedDate = DateFormat("dd-MM-yyyy").parse(dateString);
|
|
|
|
final difference = now.difference(parsedDate).inDays;
|
|
|
|
if (difference >= 7) {
|
|
await FirebaseFirestore.instance
|
|
.collection("watering_history")
|
|
.doc(doc.id)
|
|
.delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
final now = DateTime.now();
|
|
|
|
selectedYear = now.year.toString();
|
|
selectedMonth = months[now.month - 1];
|
|
|
|
years = List.generate(
|
|
5,
|
|
(index) => (now.year - 2 + index).toString(),
|
|
);
|
|
|
|
deleteOldData(); // hapus data lama
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.grey[200],
|
|
body: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
|
|
/// ================= HEADER =================
|
|
Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: _buildHeader(),
|
|
),
|
|
|
|
/// ================= LIST =================
|
|
Expanded(
|
|
child: StreamBuilder<QuerySnapshot>(
|
|
stream: FirebaseFirestore.instance
|
|
.collection("watering_history")
|
|
.orderBy("timestamp", descending: true)
|
|
.snapshots(),
|
|
builder: (context, snapshot) {
|
|
|
|
if (!snapshot.hasData) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
final docs = snapshot.data!.docs;
|
|
|
|
if (docs.isEmpty) {
|
|
return const Center(child: Text("Belum ada riwayat"));
|
|
}
|
|
|
|
/// FILTER BULAN & TAHUN
|
|
final filteredDocs = docs.where((doc) {
|
|
|
|
final data = doc.data() as Map<String, dynamic>;
|
|
final dateString = data["date"] ?? "";
|
|
|
|
if (dateString.isEmpty) return false;
|
|
|
|
final parsedDate =
|
|
DateFormat("dd-MM-yyyy").parse(dateString);
|
|
|
|
final yearMatch =
|
|
parsedDate.year.toString() == selectedYear;
|
|
|
|
final monthMatch =
|
|
months[parsedDate.month - 1] == selectedMonth;
|
|
|
|
return yearMatch && monthMatch;
|
|
|
|
}).toList()
|
|
|
|
// SORT TERBARU KE ATAS
|
|
..sort((a, b) {
|
|
|
|
final dateA = DateFormat("dd-MM-yyyy")
|
|
.parse((a.data() as Map<String, dynamic>)["date"]);
|
|
|
|
final dateB = DateFormat("dd-MM-yyyy")
|
|
.parse((b.data() as Map<String, dynamic>)["date"]);
|
|
|
|
return dateB.compareTo(dateA);
|
|
});
|
|
|
|
if (filteredDocs.isEmpty) {
|
|
return const Center(
|
|
child: Text("Tidak ada data di bulan ini"));
|
|
}
|
|
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
itemCount: filteredDocs.length,
|
|
itemBuilder: (context, index) {
|
|
|
|
final data = filteredDocs[index]
|
|
.data() as Map<String, dynamic>;
|
|
|
|
final date = data["date"] ?? "";
|
|
final startTime = data["startTime"] ?? "-";
|
|
final endTime = data["endTime"] ?? "-";
|
|
final status = data["status"] ?? "unknown";
|
|
final type = data["type"] ?? "-";
|
|
|
|
final parsedDate =
|
|
DateFormat("dd-MM-yyyy").parse(date);
|
|
|
|
final formattedDay =
|
|
DateFormat("EEEE, dd MMMM yyyy", "id_ID")
|
|
.format(parsedDate);
|
|
|
|
return HistoryCard(
|
|
day: formattedDay,
|
|
startTime: startTime,
|
|
endTime: endTime ?? "-",
|
|
type: type,
|
|
status: status,
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// ================= HEADER WIDGET =================
|
|
Widget _buildHeader() {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF2ECC71),
|
|
borderRadius: BorderRadius.circular(18),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Riwayat Penyiraman",
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
Row(
|
|
children: [
|
|
|
|
DropdownButton<String>(
|
|
value: selectedYear,
|
|
underline: const SizedBox(),
|
|
dropdownColor: Colors.white,
|
|
items: years.map((year) {
|
|
return DropdownMenuItem(
|
|
value: year,
|
|
child: Text(year),
|
|
);
|
|
}).toList(),
|
|
onChanged: (value) {
|
|
setState(() {
|
|
selectedYear = value!;
|
|
});
|
|
},
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(
|
|
children: months.map((month) {
|
|
|
|
final isActive = month == selectedMonth;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
selectedMonth = month;
|
|
});
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 14, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: isActive
|
|
? Colors.black
|
|
: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
month,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: isActive
|
|
? Colors.white
|
|
: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// ================= CARD =================
|
|
class HistoryCard extends StatelessWidget {
|
|
final String day;
|
|
final String startTime;
|
|
final String endTime;
|
|
final String type;
|
|
final String status;
|
|
|
|
const HistoryCard({
|
|
super.key,
|
|
required this.day,
|
|
required this.startTime,
|
|
required this.endTime,
|
|
required this.type,
|
|
required this.status,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(15),
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
Text(
|
|
day,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
Row(
|
|
children: [
|
|
|
|
/// START
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: Colors.orange[100],
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Text("⏰ "),
|
|
Text(startTime,
|
|
style: const TextStyle(fontSize: 11)),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
/// END
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: status == "done"
|
|
? Colors.green[100]
|
|
: Colors.red[100],
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Text(status == "done" ? "✅ " : "❌ "),
|
|
Text(
|
|
endTime,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: status == "done"
|
|
? Colors.green
|
|
: Colors.red,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
/// TYPE
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
type,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |