495 lines
16 KiB
Dart
495 lines
16 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'settings_page.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
|
|
class JadwalPage extends StatefulWidget {
|
|
const JadwalPage({super.key});
|
|
|
|
@override
|
|
State<JadwalPage> createState() => _JadwalPageState();
|
|
}
|
|
|
|
class _JadwalPageState extends State<JadwalPage> {
|
|
final tanggalController = TextEditingController();
|
|
final jamController = TextEditingController();
|
|
final dbRef = FirebaseDatabase.instance.ref("jadwal");
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
dbRef.onValue.listen((event) {
|
|
final data = event.snapshot.value;
|
|
|
|
if (data != null) {
|
|
Map map = data as Map;
|
|
|
|
List<Map<String, String>> temp = [];
|
|
|
|
map.forEach((key, value) {
|
|
temp.add({
|
|
"key": key,
|
|
"waktu": value["waktu"] ?? "",
|
|
"jam": value["jam"] ?? "",
|
|
"tanggal": value["tanggal"] ?? "",
|
|
"status": value["status"] ?? "idle",
|
|
});
|
|
});
|
|
|
|
setState(() {
|
|
jadwalList = temp;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
DateTime? startDate;
|
|
DateTime? endDate;
|
|
|
|
String? selectedWaktu;
|
|
|
|
List<Map<String, String>> jadwalList = [];
|
|
|
|
int? editIndex;
|
|
|
|
// 🔥 PILIH TANGGAL
|
|
Future<void> pilihTanggal() async {
|
|
final result = await showDialog<String>(
|
|
context: context,
|
|
builder: (_) => AlertDialog(
|
|
title: const Text("Pilih Mode"),
|
|
content: const Text("Pilih 1 hari atau rentang tanggal"),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, "single"),
|
|
child: const Text("1 Hari"),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, "range"),
|
|
child: const Text("Rentang"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
if (result == "single") {
|
|
DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now(),
|
|
firstDate: DateTime(2020),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
|
|
if (picked != null) {
|
|
setState(() {
|
|
startDate = picked;
|
|
endDate = null;
|
|
tanggalController.text =
|
|
"${picked.day}-${picked.month}-${picked.year}";
|
|
});
|
|
}
|
|
}
|
|
|
|
if (result == "range") {
|
|
DateTimeRange? picked = await showDateRangePicker(
|
|
context: context,
|
|
firstDate: DateTime(2020),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
|
|
if (picked != null) {
|
|
setState(() {
|
|
startDate = picked.start;
|
|
endDate = picked.end;
|
|
|
|
tanggalController.text =
|
|
"${picked.start.day}-${picked.start.month}-${picked.start.year} s/d ${picked.end.day}-${picked.end.month}-${picked.end.year}";
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<String?> kirimKeFirebase() async {
|
|
if (startDate == null) return null;
|
|
|
|
DatabaseReference newRef = dbRef.push();
|
|
|
|
String tanggal;
|
|
|
|
// 🔥 kalau range
|
|
if (endDate != null) {
|
|
tanggal =
|
|
"${startDate!.day}-${startDate!.month}-${startDate!.year} s/d ${endDate!.day}-${endDate!.month}-${endDate!.year}";
|
|
}
|
|
// 🔥 kalau single
|
|
else {
|
|
tanggal =
|
|
"${startDate!.day}-${startDate!.month}-${startDate!.year}";
|
|
}
|
|
|
|
await newRef.set({
|
|
"tanggal": tanggal,
|
|
"jam": jamController.text,
|
|
"waktu": selectedWaktu,
|
|
});
|
|
|
|
return newRef.key;
|
|
}
|
|
|
|
// 🔥 PILIH JAM
|
|
Future<void> pilihJam() async {
|
|
TimeOfDay? picked = await showTimePicker(
|
|
context: context,
|
|
initialTime: TimeOfDay.now(),
|
|
);
|
|
|
|
if (picked != null) {
|
|
setState(() {
|
|
jamController.text =
|
|
"${picked.hour.toString().padLeft(2, '0')}:${picked.minute.toString().padLeft(2, '0')} WIB";
|
|
});
|
|
}
|
|
}
|
|
|
|
// 🔥 SIMPAN DATA
|
|
void simpanData() async {
|
|
if (selectedWaktu == null ||
|
|
jamController.text.isEmpty ||
|
|
tanggalController.text.isEmpty) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text("Isi semua data dulu")),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// 🔥 MODE EDIT
|
|
if (editIndex != null) {
|
|
String key = jadwalList[editIndex!]["key"] ?? "";
|
|
|
|
if (key.isNotEmpty) {
|
|
await dbRef.child(key).update({
|
|
"tanggal": tanggalController.text,
|
|
"jam": jamController.text,
|
|
"waktu": selectedWaktu,
|
|
"status": "idle", // 🔥 TAMBAHAN
|
|
});
|
|
}
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text("Jadwal berhasil diupdate")),
|
|
);
|
|
}
|
|
|
|
// 🔥 MODE TAMBAH
|
|
else {
|
|
DatabaseReference newRef = dbRef.push();
|
|
|
|
await newRef.set({
|
|
"tanggal": tanggalController.text,
|
|
"jam": jamController.text,
|
|
"waktu": selectedWaktu,
|
|
"status": "idle", // reset ulang
|
|
});
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text("Jadwal berhasil ditambah")),
|
|
);
|
|
}
|
|
|
|
// reset form
|
|
selectedWaktu = null;
|
|
jamController.clear();
|
|
tanggalController.clear();
|
|
}
|
|
|
|
// 🔥 EDIT DATA
|
|
void editData(int index) {
|
|
setState(() {
|
|
selectedWaktu = jadwalList[index]["waktu"];
|
|
jamController.text = jadwalList[index]["jam"]!;
|
|
tanggalController.text = jadwalList[index]["tanggal"]!;
|
|
editIndex = index;
|
|
});
|
|
}
|
|
|
|
// 🔥 DELETE DATA
|
|
void deleteData(int index) async {
|
|
String key = jadwalList[index]["key"] ?? "";
|
|
|
|
if (key.isNotEmpty) {
|
|
await dbRef.child(key).remove();
|
|
}
|
|
|
|
setState(() {
|
|
jadwalList.removeAt(index);
|
|
});
|
|
}
|
|
|
|
Color getStatusColor(String? status) {
|
|
switch (status) {
|
|
case "running":
|
|
return Colors.orange;
|
|
case "done":
|
|
return Colors.green;
|
|
default:
|
|
return Colors.grey;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
tanggalController.dispose();
|
|
jamController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFE6E7A3),
|
|
|
|
body: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
|
|
// 🔶 HEADER
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
|
|
child: Row(
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () => Navigator.pop(context),
|
|
child: const CircleAvatar(
|
|
backgroundColor: Colors.white,
|
|
child: Icon(Icons.arrow_back),
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
const Expanded(
|
|
child: Text(
|
|
"Atur Jadwal Pakan",
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.settings),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) => SettingsPage()),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 🔻 CONTENT
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
|
|
// 🔶 FORM INPUT
|
|
Container(
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[200],
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
|
|
const Text("Tanggal/Hari"),
|
|
const SizedBox(height: 10),
|
|
|
|
TextField(
|
|
controller: tanggalController,
|
|
readOnly: true,
|
|
onTap: pilihTanggal,
|
|
decoration: InputDecoration(
|
|
hintText: "Pilih tanggal",
|
|
suffixIcon:
|
|
const Icon(Icons.calendar_today),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
const Text("Jam"),
|
|
const SizedBox(height: 10),
|
|
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: DropdownButtonFormField<String>(
|
|
value: selectedWaktu,
|
|
hint: const Text("Pagi / Sore"),
|
|
items: ["Pagi", "Sore"].map((e) {
|
|
return DropdownMenuItem(
|
|
value: e,
|
|
child: Text(e),
|
|
);
|
|
}).toList(),
|
|
onChanged: (value) {
|
|
setState(() {
|
|
selectedWaktu = value;
|
|
});
|
|
},
|
|
decoration: InputDecoration(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
border: OutlineInputBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 10),
|
|
|
|
Expanded(
|
|
child: TextField(
|
|
controller: jamController,
|
|
readOnly: true,
|
|
onTap: pilihJam,
|
|
decoration: InputDecoration(
|
|
hintText: "Pilih Jam",
|
|
suffixIcon:
|
|
const Icon(Icons.access_time),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
border: OutlineInputBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
ElevatedButton(
|
|
onPressed: simpanData,
|
|
child: Text(editIndex != null
|
|
? "Update"
|
|
: "Simpan"),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// 🔥 LIST HASIL
|
|
if (jadwalList.isNotEmpty)
|
|
Column(
|
|
children: List.generate(jadwalList.length, (index) {
|
|
final data = jadwalList[index];
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 15),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.green.withOpacity(0.3),
|
|
blurRadius: 8,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
Text(data["waktu"]!,
|
|
style: const TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold)),
|
|
const SizedBox(height: 5),
|
|
Text("Jam ${data["jam"]}"),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
data["tanggal"]!,
|
|
style: const TextStyle(
|
|
fontSize: 10,
|
|
color: Colors.grey),
|
|
),
|
|
const SizedBox(height: 5),
|
|
|
|
// 🔥 STATUS
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: getStatusColor(data["status"]),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: SizedBox(
|
|
width: 220,
|
|
child: Text(
|
|
data["status"] ?? "idle",
|
|
softWrap: true,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
Row(
|
|
children: [
|
|
|
|
// EDIT
|
|
IconButton(
|
|
icon: const Icon(Icons.edit),
|
|
onPressed: () =>
|
|
editData(index),
|
|
),
|
|
|
|
// DELETE
|
|
IconButton(
|
|
icon: const Icon(Icons.delete,
|
|
color: Colors.red),
|
|
onPressed: () =>
|
|
deleteData(index),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |