315 lines
14 KiB
Dart
315 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:seedina/provider/rtdb_handler.dart';
|
|
import 'package:seedina/services/chartdata_service.dart';
|
|
import 'package:seedina/services/spreadsheet_service.dart';
|
|
import 'package:seedina/utils/rewidgets/contentbox/for_charts/chart_box.dart';
|
|
import 'package:seedina/utils/rewidgets/global/myappbar.dart';
|
|
import 'package:seedina/utils/rewidgets/graph/dailysum_card.dart';
|
|
import 'package:seedina/utils/style/gcolor.dart';
|
|
|
|
class MainCharts extends StatefulWidget {
|
|
const MainCharts({super.key});
|
|
|
|
@override
|
|
State<MainCharts> createState() => _MainChartState();
|
|
}
|
|
|
|
class _MainChartState extends State<MainCharts> {
|
|
final ChartsDataService _chartsService = ChartsDataService();
|
|
final SpreadsheetService _spreadsheetService = SpreadsheetService();
|
|
|
|
bool _isLoading = true;
|
|
String _errorMessage = '';
|
|
DateTime _selectedDate = DateTime.now();
|
|
|
|
List<num> _ecData = List.filled(8, 0.0);
|
|
List<num> _tdsData = List.filled(8, 0.0);
|
|
List<num> _tinggiAirData = List.filled(8, 0.0);
|
|
List<num> _suhuAirData = List.filled(8, 0.0);
|
|
List<num> _suhuLingData = List.filled(8, 0.0);
|
|
List<num> _humiLingData = List.filled(8, 0.0);
|
|
Map<String, dynamic>? _dailySummaryData;
|
|
|
|
final List<String> _intervalKeys = const [
|
|
"00:00-03:00", "03:00-06:00", "06:00-09:00", "09:00-12:00",
|
|
"12:00-15:00", "15:00-18:00", "18:00-21:00", "21:00-24:00"
|
|
];
|
|
final String _laporanHarianCollectionName = "laporanHarianAeroponic_U10325P1";
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadDataForSelectedDate();
|
|
}
|
|
|
|
bool _isToday(DateTime date) {
|
|
final now = DateTime.now();
|
|
return date.year == now.year && date.month == now.month && date.day == now.day;
|
|
}
|
|
|
|
Future<void> _loadDataForSelectedDate() async {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_isLoading = true;
|
|
_errorMessage = '';
|
|
_dailySummaryData = null;
|
|
_ecData = List.filled(8, 0.0);
|
|
_tdsData = List.filled(8, 0.0);
|
|
_tinggiAirData = List.filled(8, 0.0);
|
|
_suhuAirData = List.filled(8, 0.0);
|
|
_suhuLingData = List.filled(8, 0.0);
|
|
_humiLingData = List.filled(8, 0.0);
|
|
});
|
|
|
|
try {
|
|
final handlingProvider = Provider.of<HandlingProvider>(context, listen: false);
|
|
final String? seedKey = handlingProvider.currentUserSeedKey;
|
|
|
|
final fetchedIntervalData = await _chartsService.fetchChartIntervalData(
|
|
dateToFetch: _selectedDate,
|
|
seedKey: seedKey,
|
|
laporanHarianCollectionName: _laporanHarianCollectionName,
|
|
);
|
|
|
|
_ecData = fetchedIntervalData['ec'] ?? List.filled(8, 0.0);
|
|
_tdsData = fetchedIntervalData['tds'] ?? List.filled(8, 0.0);
|
|
_tinggiAirData = fetchedIntervalData['tinggiAir'] ?? List.filled(8, 0.0);
|
|
_suhuAirData = fetchedIntervalData['suhuAir'] ?? List.filled(8, 0.0);
|
|
_suhuLingData = fetchedIntervalData['suhuLing'] ?? List.filled(8, 0.0);
|
|
_humiLingData = fetchedIntervalData['humiLing'] ?? List.filled(8, 0.0);
|
|
|
|
if (!_isToday(_selectedDate)) {
|
|
_dailySummaryData = await _chartsService.fetchDailySummary(
|
|
dateToFetch: _selectedDate,
|
|
laporanHarianCollectionName: _laporanHarianCollectionName,
|
|
);
|
|
}
|
|
|
|
bool hasAnyValidData = _ecData.any((d) => d != 0.0) ||
|
|
_tdsData.any((d) => d != 0.0) ||
|
|
_tinggiAirData.any((d) => d != 0.0) ||
|
|
_suhuAirData.any((d) => d != 0.0) ||
|
|
_suhuLingData.any((d) => d != 0.0) ||
|
|
_humiLingData.any((d) => d != 0.0);
|
|
|
|
if (!hasAnyValidData && (_dailySummaryData == null || _dailySummaryData!.isEmpty)) {
|
|
_errorMessage = _isToday(_selectedDate)
|
|
? 'Data untuk hari ini belum tersedia.'
|
|
: 'Tidak ada laporan untuk tanggal ${DateFormat('dd MMMM yyyy', 'id_ID').format(_selectedDate)}. Pastikan ESP32 menyimpan laporan dengan ID "YYYY-MM-DD".';
|
|
}
|
|
|
|
} catch (e) {
|
|
print('UI Error di _loadDataForSelectedDate: $e');
|
|
_errorMessage = 'Gagal memuat data: ${e.toString().length > 70 ? e.toString().substring(0,70)+"..." : e.toString()}';
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _selectDate(BuildContext context) async {
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: _selectedDate,
|
|
firstDate: DateTime(2022),
|
|
lastDate: DateTime.now(),
|
|
locale: const Locale('id', 'ID'),
|
|
);
|
|
if (picked != null && picked != _selectedDate) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_selectedDate = picked;
|
|
});
|
|
_loadDataForSelectedDate();
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _generateAndOpenSpreadsheet() async {
|
|
if (_isLoading) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Harap tunggu, data sedang dimuat.')),
|
|
);
|
|
return;
|
|
}
|
|
bool hasAnyValidData = _ecData.any((d) => d != 0.0) ||
|
|
_tdsData.any((d) => d != 0.0) ||
|
|
_tinggiAirData.any((d) => d != 0.0) ||
|
|
_suhuAirData.any((d) => d != 0.0) ||
|
|
_suhuLingData.any((d) => d != 0.0) ||
|
|
_humiLingData.any((d) => d != 0.0);
|
|
|
|
if (!hasAnyValidData && (_dailySummaryData == null || _dailySummaryData!.isEmpty)) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(_errorMessage.isNotEmpty && _errorMessage.startsWith("Tidak ada laporan") ? _errorMessage : 'Tidak ada data valid untuk dibuat spreadsheet.')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final rows = _spreadsheetService.prepareCsvRows(
|
|
intervalKeys: _intervalKeys,
|
|
ecData: _ecData,
|
|
tdsData: _tdsData,
|
|
tinggiAirData: _tinggiAirData,
|
|
suhuAirData: _suhuAirData,
|
|
suhuLingData: _suhuLingData,
|
|
humiLingData: _humiLingData,
|
|
dailySummary: _dailySummaryData,
|
|
selectedDate: _selectedDate,
|
|
);
|
|
|
|
await _spreadsheetService.createAndOpenCsvFile(
|
|
csvRows: rows,
|
|
selectedDate: _selectedDate,
|
|
context: context,
|
|
fieldDelimiter: ';', // Menggunakan titik koma (;) sebagai delimiter
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: GColors.myHijau,
|
|
appBar: CustomAppBar(
|
|
title: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Grafik Data Sensor',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w900,
|
|
fontFamily: 'Quicksand',
|
|
color: GColors.myKuning),
|
|
),
|
|
Text(
|
|
DateFormat('EEEE, dd MMMM yyyy', 'id_ID').format(_selectedDate),
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: GColors.myKuning.withOpacity(0.85)),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: Icon(Icons.calendar_today, color: GColors.myKuning, size: 22),
|
|
onPressed: _isLoading ? null : () => _selectDate(context),
|
|
tooltip: 'Pilih Tanggal',
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.table_chart_outlined, color: GColors.myKuning, size: 24),
|
|
onPressed: _isLoading ? null : _generateAndOpenSpreadsheet,
|
|
tooltip: 'Buka Data sebagai Spreadsheet (CSV)',
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.refresh, color: GColors.myKuning, size: 24),
|
|
onPressed: _isLoading ? null : _loadDataForSelectedDate,
|
|
tooltip: 'Muat Ulang Data',
|
|
),
|
|
],
|
|
showBackButton: true,
|
|
),
|
|
body: _isLoading
|
|
? Center(child: CircularProgressIndicator(color: GColors.myKuning))
|
|
: _errorMessage.isNotEmpty
|
|
? Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20.0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.error_outline, color: Colors.red.shade300, size: 40),
|
|
const SizedBox(height: 15),
|
|
Text(
|
|
_errorMessage,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(color: Colors.red.shade300, fontSize: 15),
|
|
),
|
|
const SizedBox(height: 20),
|
|
ElevatedButton.icon(
|
|
icon: const Icon(Icons.refresh, size: 18),
|
|
label: const Text('Coba Lagi'),
|
|
onPressed: _loadDataForSelectedDate,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: GColors.myKuning,
|
|
foregroundColor: GColors.myBiru,
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10)
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
)
|
|
: Column(
|
|
children: [
|
|
if (_dailySummaryData != null && _dailySummaryData!.isNotEmpty)
|
|
DailySummaryCard(summaryData: _dailySummaryData!, date: _selectedDate),
|
|
Expanded(
|
|
child: Container(
|
|
width: MediaQuery.of(context).size.width,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: (_dailySummaryData != null && _dailySummaryData!.isNotEmpty)
|
|
? const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16))
|
|
: BorderRadius.circular(16),
|
|
),
|
|
child: SingleChildScrollView(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
children: [
|
|
if (_isToday(_selectedDate) && (_dailySummaryData == null || _dailySummaryData!.isEmpty))
|
|
const SizedBox(height: 8),
|
|
BarGraphBox(
|
|
title: 'Grafik Data TDS Air (ppm)', maxDataGraph: 2000,
|
|
dataPertama: _tdsData[0], dataKedua: _tdsData[1], dataKetiga: _tdsData[2], dataKeempat: _tdsData[3],
|
|
dataKelima: _tdsData[4], dataKeenam: _tdsData[5], dataKetujuh: _tdsData[6], dataKedelapan: _tdsData[7],
|
|
graphColor: GColors.tdsGraphColor,
|
|
description: "Rata-rata TDS Air (ppm) per 3 jam (${DateFormat('dd MMM yy', 'id_ID').format(_selectedDate)})",
|
|
),
|
|
BarGraphBox(
|
|
title: 'Grafik Data Tinggi Air (cm)', maxDataGraph: 50,
|
|
dataPertama: _tinggiAirData[0], dataKedua: _tinggiAirData[1], dataKetiga: _tinggiAirData[2], dataKeempat: _tinggiAirData[3],
|
|
dataKelima: _tinggiAirData[4], dataKeenam: _tinggiAirData[5], dataKetujuh: _tinggiAirData[6], dataKedelapan: _tinggiAirData[7],
|
|
graphColor: GColors.waterHeightGraphColor,
|
|
description: "Rata-rata Tinggi Air (cm) per 3 jam (${DateFormat('dd MMM yy', 'id_ID').format(_selectedDate)})",
|
|
),
|
|
BarGraphBox(
|
|
title: 'Grafik Data Suhu Air (°C)', maxDataGraph: 40,
|
|
dataPertama: _suhuAirData[0], dataKedua: _suhuAirData[1], dataKetiga: _suhuAirData[2], dataKeempat: _suhuAirData[3],
|
|
dataKelima: _suhuAirData[4], dataKeenam: _suhuAirData[5], dataKetujuh: _suhuAirData[6], dataKedelapan: _suhuAirData[7],
|
|
graphColor: GColors.waterTempGraphColor,
|
|
description: "Rata-rata Suhu Air (°C) per 3 jam (${DateFormat('dd MMM yy', 'id_ID').format(_selectedDate)})",
|
|
),
|
|
BarGraphBox(
|
|
title: 'Grafik Data Suhu Lingk. (°C)', maxDataGraph: 50,
|
|
dataPertama: _suhuLingData[0], dataKedua: _suhuLingData[1], dataKetiga: _suhuLingData[2], dataKeempat: _suhuLingData[3],
|
|
dataKelima: _suhuLingData[4], dataKeenam: _suhuLingData[5], dataKetujuh: _suhuLingData[6], dataKedelapan: _suhuLingData[7],
|
|
graphColor: GColors.tempGraphColor,
|
|
description: "Rata-rata Suhu Lingk. (°C) per 3 jam (${DateFormat('dd MMM yy', 'id_ID').format(_selectedDate)})",
|
|
),
|
|
BarGraphBox(
|
|
title: 'Grafik Data Kelembaban Udara (%)', maxDataGraph: 100,
|
|
dataPertama: _humiLingData[0], dataKedua: _humiLingData[1], dataKetiga: _humiLingData[2], dataKeempat: _humiLingData[3],
|
|
dataKelima: _humiLingData[4], dataKeenam: _humiLingData[5], dataKetujuh: _humiLingData[6], dataKedelapan: _humiLingData[7],
|
|
graphColor: GColors.humiGraphColor,
|
|
description: "Rata-rata Kelembaban Udara (%) per 3 jam (${DateFormat('dd MMM yy', 'id_ID').format(_selectedDate)})",
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |