This commit is contained in:
kleponijo 2026-05-25 16:20:14 +07:00
parent 6289cc7629
commit 5d3d5e7ee6
4 changed files with 160 additions and 160 deletions

View File

@ -1,6 +1,14 @@
manifest.json,1768956954743,5edb32bebce763b2b7e55774ae4c5c4354a44df248387cc3c174e8710e85aaa1 manifest.json,1768956954743,5edb32bebce763b2b7e55774ae4c5c4354a44df248387cc3c174e8710e85aaa1
flutter.js,1767911992319,970356511bee637e57348b9e01b3218a3d1d6c4add478fdc306033b4b3e21ef4 flutter.js,1767911992319,970356511bee637e57348b9e01b3218a3d1d6c4add478fdc306033b4b3e21ef4
favicon.png,1768956954741,19880e004e8357a51f6a64f5d1fdc8828a6432fe9c781f8acb6b730eced2cd13 favicon.png,1768956954741,19880e004e8357a51f6a64f5d1fdc8828a6432fe9c781f8acb6b730eced2cd13
splash/img/light-4x.png,1779442242118,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578
splash/img/light-3x.png,1779442242118,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
splash/img/light-2x.png,1779442242116,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
splash/img/light-1x.png,1779442242116,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
splash/img/dark-4x.png,1779442242116,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578
splash/img/dark-3x.png,1779442242115,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
splash/img/dark-2x.png,1779442242115,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
splash/img/dark-1x.png,1779442242114,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
icons/Icon-maskable-512.png,1768956954742,ea58e5c49bc4dcdc5db0a9fe2f25b2a3152f85ef9094f0d020de0aa318451a1d icons/Icon-maskable-512.png,1768956954742,ea58e5c49bc4dcdc5db0a9fe2f25b2a3152f85ef9094f0d020de0aa318451a1d
icons/Icon-maskable-192.png,1768956954742,f7ac56197638aec92f195c5344d15395c2cbf8a5ae74e846c5b4156b64f05e38 icons/Icon-maskable-192.png,1768956954742,f7ac56197638aec92f195c5344d15395c2cbf8a5ae74e846c5b4156b64f05e38
icons/Icon-512.png,1768956954742,1de493563deb283e6ad70f0d5a89cfd61e37343df3b919c9862c5a41699ac5bc icons/Icon-512.png,1768956954742,1de493563deb283e6ad70f0d5a89cfd61e37343df3b919c9862c5a41699ac5bc
@ -17,28 +25,20 @@ canvaskit/canvaskit.js,1767911992193,777116800268972381a4832500bb0aafd96ab254607
canvaskit/chromium/canvaskit.wasm,1767911992256,06f70f0703803c7329be30338b79530564ddc1ab2c3789d608431697a9a30ba2 canvaskit/chromium/canvaskit.wasm,1767911992256,06f70f0703803c7329be30338b79530564ddc1ab2c3789d608431697a9a30ba2
canvaskit/chromium/canvaskit.js.symbols,1767911992241,4a8d181b0622d7a4b4254deb591c43f48dd89623c621dec6f84d0a0b2b8f77a7 canvaskit/chromium/canvaskit.js.symbols,1767911992241,4a8d181b0622d7a4b4254deb591c43f48dd89623c621dec6f84d0a0b2b8f77a7
canvaskit/chromium/canvaskit.js,1767911992241,e0f423bfd63202fb53a4dd5cffe330143bfb9eabf5e4e8198bb605442a49d3e7 canvaskit/chromium/canvaskit.js,1767911992241,e0f423bfd63202fb53a4dd5cffe330143bfb9eabf5e4e8198bb605442a49d3e7
version.json,1779493407408,f7f931727903f67c4b2ec2c38d4c5c4e2ec1be0c03071b8bff0c27f5be3b9e6e
index.html,1779493279226,3ffbcfc0c9d82b851ce84d2b1bfc85cea0a34b077bdad955e7cf52643a3cf434
flutter_bootstrap.js,1779493279204,ac46b098b9b222a709fa5ec23d1808db44a01d9b73d454c9a583d07a29cea348
flutter_service_worker.js,1779493415284,df198e5b7cc03c86b760a3ec2fe6f78f064d02cf545c4fb4b4a6a4ae142c3020
splash/img/light-1x.png,1779442242116,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
splash/img/dark-1x.png,1779442242114,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
assets/AssetManifest.bin.json,1779493407814,e23e10111498cc3b8b40e6549dcde13cffa6f45dc78e88194f4a7e284bb68294
assets/FontManifest.json,1779493407814,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
assets/AssetManifest.bin,1779493407814,fe7319a65158e186661b7edfcc1c2b6c0a6bc6823b923a13e1e932cd66407fa2
assets/shaders/stretch_effect.frag,1779493407940,586ff23669838dd33070077916f0b971a8800f67093ae8d6eed47e6cea50926f
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779493409456,85140ea453cd3e02bd93650fe8add6a724ae8c0ac73d0c491085b301e7bd21d2
assets/shaders/ink_sparkle.frag,1779493407944,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406
assets/images/windy.png,1776157890991,b9d34ed004fff60a4fea365c23508823943f3f0793375f34ca9202069d9c9955 assets/images/windy.png,1776157890991,b9d34ed004fff60a4fea365c23508823943f3f0793375f34ca9202069d9c9955
assets/images/atmosfer.png,1776157890990,c3a9ddfa5676b5f0e1cf833f73c0f09c1dfb9d2e202906c1afcdd7a50dcf2506
assets/fonts/MaterialIcons-Regular.otf,1779493409477,1c7547588fe9c202b730590aab269e1bae9988240caeba41da28091251a87dbe
assets/images/google-icon-logo.png,1776157890990,333cfafcd5c87738a4098359e18e02ff5848b2b1b819ec8aff29424c3f82d32b
splash/img/light-2x.png,1779442242116,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
splash/img/dark-2x.png,1779442242115,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
splash/img/light-3x.png,1779442242118,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
splash/img/dark-3x.png,1779442242115,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
splash/img/light-4x.png,1779442242118,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578
assets/images/logo_klimatologi.png,1777103040742,e042ff556e356926fcd5ff92555519a569a71316ab2f7074f86f4ea4a215f325 assets/images/logo_klimatologi.png,1777103040742,e042ff556e356926fcd5ff92555519a569a71316ab2f7074f86f4ea4a215f325
splash/img/dark-4x.png,1779442242116,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578 assets/images/google-icon-logo.png,1776157890990,333cfafcd5c87738a4098359e18e02ff5848b2b1b819ec8aff29424c3f82d32b
assets/NOTICES,1779493407816,62963000bb4f8b466d5b97db77a0f9563f047723e134974be6bf170da0f821b2 assets/images/atmosfer.png,1776157890990,c3a9ddfa5676b5f0e1cf833f73c0f09c1dfb9d2e202906c1afcdd7a50dcf2506
main.dart.js,1779493365556,91f95baa75b37e85885a3fc9b0fc953ed2245277abde8ca4682b0707131a2056 version.json,1779597551680,f7f931727903f67c4b2ec2c38d4c5c4e2ec1be0c03071b8bff0c27f5be3b9e6e
index.html,1779597409382,3ffbcfc0c9d82b851ce84d2b1bfc85cea0a34b077bdad955e7cf52643a3cf434
flutter_service_worker.js,1779597560727,6a808567675f0224b1af9ad8047f11ef5a7d664aa42a4805536cda3a2f3ef4d4
assets/FontManifest.json,1779597552223,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
assets/AssetManifest.bin.json,1779597552223,e23e10111498cc3b8b40e6549dcde13cffa6f45dc78e88194f4a7e284bb68294
flutter_bootstrap.js,1779597409344,5477d2c4d175fd91e3398d585c6f47786ab282fa386db0d7a1962e6111c4db7f
assets/AssetManifest.bin,1779597552222,fe7319a65158e186661b7edfcc1c2b6c0a6bc6823b923a13e1e932cd66407fa2
assets/shaders/stretch_effect.frag,1779597552364,586ff23669838dd33070077916f0b971a8800f67093ae8d6eed47e6cea50926f
assets/shaders/ink_sparkle.frag,1779597552364,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779597553861,85140ea453cd3e02bd93650fe8add6a724ae8c0ac73d0c491085b301e7bd21d2
assets/fonts/MaterialIcons-Regular.otf,1779597553863,1c7547588fe9c202b730590aab269e1bae9988240caeba41da28091251a87dbe
assets/NOTICES,1779597552226,62963000bb4f8b466d5b97db77a0f9563f047723e134974be6bf170da0f821b2
main.dart.js,1779597496677,fce3d453f3b298a9672bb06cd23598463329fddd17aff0a00e85491492561209

View File

@ -6,6 +6,7 @@ import '../../../blocs/notification_bloc/notification_bloc.dart';
import '../../monitoring/wind_speed/blocs/wind_speed_bloc.dart'; import '../../monitoring/wind_speed/blocs/wind_speed_bloc.dart';
import '../../monitoring/evaporasi/blocs/evaporasi_bloc.dart'; import '../../monitoring/evaporasi/blocs/evaporasi_bloc.dart';
import '../../monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart'; import '../../monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart';
import '../../auth/views/welcome_screen.dart';
import '../widgets/notification_panel.dart'; import '../widgets/notification_panel.dart';
import '../widgets/sensor_grid.dart'; import '../widgets/sensor_grid.dart';
import '../widgets/dashboard_charts.dart'; import '../widgets/dashboard_charts.dart';
@ -76,104 +77,115 @@ class _HomeScreenState extends State<HomeScreen>
)..add(WatchAtmosphericConditionsStarted()), )..add(WatchAtmosphericConditionsStarted()),
), ),
], ],
child: GestureDetector( child: BlocListener<AuthenticationBloc, AuthenticationState>(
onTap: () { listener: (context, state) {
if (_showNotifications) _toggleNotifications(); if (state.status == AuthenticationStatus.unauthenticated) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const WelcomeScreen()),
(route) => false,
);
}
}, },
child: Scaffold( child: GestureDetector(
drawer: const MainDrawer(), onTap: () {
appBar: AppBar( if (_showNotifications) _toggleNotifications();
backgroundColor: Colors.white, },
elevation: 0.5, child: Scaffold(
centerTitle: false, drawer: const MainDrawer(),
leading: Builder( appBar: AppBar(
builder: (context) => IconButton( backgroundColor: Colors.white,
icon: const Icon(Icons.menu, color: Colors.black), elevation: 0.5,
onPressed: () => Scaffold.of(context).openDrawer(), centerTitle: false,
leading: Builder(
builder: (context) => IconButton(
icon: const Icon(Icons.menu, color: Colors.black),
onPressed: () => Scaffold.of(context).openDrawer(),
),
), ),
), title: Image.asset(
title: Image.asset( 'images/logo_klimatologi.png',
'images/logo_klimatologi.png', height: 90,
height: 90, fit: BoxFit.contain,
fit: BoxFit.contain, ),
), actions: [
actions: [ // Icon lonceng dengan badge
// Icon lonceng dengan badge BlocBuilder<NotificationBloc, NotificationState>(
BlocBuilder<NotificationBloc, NotificationState>( buildWhen: (prev, cur) =>
buildWhen: (prev, cur) => prev.unreadCount != cur.unreadCount ||
prev.unreadCount != cur.unreadCount || prev.hasActiveAlerts != cur.hasActiveAlerts,
prev.hasActiveAlerts != cur.hasActiveAlerts, builder: (context, state) {
builder: (context, state) { return Stack(
return Stack( children: [
children: [ IconButton(
IconButton( icon: Icon(
icon: Icon( _showNotifications
_showNotifications ? Icons.notifications
? Icons.notifications : Icons.notifications_none,
: Icons.notifications_none, color: Colors.black,
color: Colors.black, ),
onPressed: _toggleNotifications,
), ),
onPressed: _toggleNotifications, if (state.unreadCount > 0)
), Positioned(
if (state.unreadCount > 0) right: 6,
Positioned( top: 6,
right: 6, child: Container(
top: 6, padding: const EdgeInsets.all(3),
child: Container( decoration: const BoxDecoration(
padding: const EdgeInsets.all(3), color: Colors.red,
decoration: const BoxDecoration( shape: BoxShape.circle,
color: Colors.red, ),
shape: BoxShape.circle, constraints: const BoxConstraints(
), minWidth: 16,
constraints: const BoxConstraints( minHeight: 16,
minWidth: 16, ),
minHeight: 16, child: Text(
), state.unreadCount > 9
child: Text( ? '9+'
state.unreadCount > 9 : '${state.unreadCount}',
? '9+' style: const TextStyle(
: '${state.unreadCount}', color: Colors.white,
style: const TextStyle( fontSize: 9,
color: Colors.white, fontWeight: FontWeight.bold,
fontSize: 9, ),
fontWeight: FontWeight.bold, textAlign: TextAlign.center,
), ),
textAlign: TextAlign.center,
), ),
), ),
), ],
], );
); },
}, ),
), IconButton(
IconButton( icon: const Icon(Icons.logout, color: Colors.black),
icon: const Icon(Icons.logout, color: Colors.black), onPressed: () => context
onPressed: () => context .read<AuthenticationBloc>()
.read<AuthenticationBloc>() .add(AuthenticationLogoutRequested()),
.add(AuthenticationLogoutRequested()), ),
), const SizedBox(width: 8),
const SizedBox(width: 8), ],
], ),
), body: Stack(
body: Stack( children: [
children: [ // Konten utama
// Konten utama const _HomeBody(),
const _HomeBody(), // Dropdown notification panel
// Dropdown notification panel if (_showNotifications)
if (_showNotifications) Positioned(
Positioned( top: 0,
top: 0, left: 0,
left: 0, right: 0,
right: 0, child: FadeTransition(
child: FadeTransition( opacity: _fadeAnim,
opacity: _fadeAnim, child: GestureDetector(
child: GestureDetector( onTap: () {}, // cegah tap di panel menutup panel
onTap: () {}, // cegah tap di panel menutup panel child: const NotificationPanel(),
child: const NotificationPanel(), ),
), ),
), ),
), ],
], ),
), ),
), ),
), ),

View File

@ -474,13 +474,6 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
Icons.water, Icons.water,
Colors.blue, Colors.blue,
), ),
_miniCard(
cardWidth,
'Acuan Air Pagi',
'${(state.currentData?.acuanPagi ?? 0.0).toStringAsFixed(1)} cm',
Icons.wb_sunny_outlined,
Colors.amber.shade700,
),
], ],
); );
}, },

View File

@ -42,24 +42,22 @@ class Evaporasi {
} }
// Evaporasi (mm) // Evaporasi (mm)
// Dibalik: Mengambil dari field tinggi_air karena ditukar
final evaporasiRaw = toDoubleSafe( final evaporasiRaw = toDoubleSafe(
json['tinggi_air_cm'] ?? json['evaporasi_mm'] ??
json['tinggi_air'] ?? json['evaporasi'] ??
json['tinggiAir'] ?? json['evaporasiMm'] ??
json['tinggiAir_cm'] ?? json['evaporation_mm'] ??
json['water_level'] ?? json['evap_mm'] ??
json['waterLevel'] ?? json['evaporasi_value'] ??
json['tinggi_air_m'] ?? json['evaporasi_k'],
json['tinggiAir_m'],
); );
// Konversi cm ke mm dengan mengalikan 10 // Sudah dalam mm, tidak perlu konversi
final evaporasiVal = evaporasiRaw * 10.0; final evaporasiVal = evaporasiRaw;
// Suhu (°C) // Suhu (°C)
// FIX: tambah 'suhu_air_c' sesuai field yang dikirim ESP32 // FIX: tambah 'suhu_air_c' sesuai field yang dikirim ESP32
final suhuRaw = toDoubleSafe( final suhuRaw = toDoubleSafe(
json['suhu_air_c'] ?? // ESP32 kirim field ini json['suhu_air_c'] ?? // ESP32 kirim field ini
json['suhu_air'] ?? json['suhu_air'] ??
json['suhu'] ?? json['suhu'] ??
json['suhuAir'] ?? json['suhuAir'] ??
@ -69,29 +67,27 @@ class Evaporasi {
final suhuVal = (suhuRaw < -50 || suhuRaw > 100) ? -1.0 : suhuRaw; final suhuVal = (suhuRaw < -50 || suhuRaw > 100) ? -1.0 : suhuRaw;
// Tinggi Air (cm) // Tinggi Air (cm)
// Dibalik: Mengambil dari field evaporasi karena ditukar
final tinggiRaw = toDoubleSafe( final tinggiRaw = toDoubleSafe(
json['evaporasi_mm'] ?? json['tinggi_air_cm'] ??
json['evaporasi'] ?? json['tinggi_air'] ??
json['evaporasiMm'] ?? json['tinggiAir'] ??
json['evaporation_mm'] ?? json['tinggiAir_cm'] ??
json['evap_mm'] ?? json['water_level'] ??
json['evaporasi_value'] ?? json['waterLevel'] ??
json['evaporasi_k'], json['tinggi_air_m'] ??
json['tinggiAir_m'],
); );
// Konversi mm ke cm dengan membagi 10 (misal 241.78 mm -> 24.18 cm) // Sudah dalam cm, tidak perlu konversi
final tinggiVal = tinggiRaw / 10.0; final tinggiVal = tinggiRaw;
// Sanity check // Sanity check
final evaporasiFiltered = final evaporasiFiltered = (evaporasiVal < 0 || evaporasiVal > 50)
(evaporasiVal < 0 || evaporasiVal > 50) ? 0.0 : evaporasiVal; ? 0.0
final tinggiFiltered = : evaporasiVal;
(tinggiVal < 0 || tinggiVal > 100) ? 0.0 : tinggiVal; final tinggiFiltered = (tinggiVal < 0 || tinggiVal > 100) ? 0.0 : tinggiVal;
// Acuan Pagi (cm) // Acuan Pagi (cm)
final acuanPagiVal = toDoubleSafe( final acuanPagiVal = toDoubleSafe(json['acuan_pagi_cm'] ?? 0.0);
json['acuan_pagi_cm'] ?? json['acuan_pagi'] ?? 0.0,
);
// Status // Status
final statusVal = json['status']?.toString() ?? "Normal"; final statusVal = json['status']?.toString() ?? "Normal";
@ -103,8 +99,9 @@ class Evaporasi {
try { try {
if (rawTimestamp is int) { if (rawTimestamp is int) {
if (rawTimestamp < 1000000000000) { if (rawTimestamp < 1000000000000) {
return DateTime.fromMillisecondsSinceEpoch(rawTimestamp * 1000) return DateTime.fromMillisecondsSinceEpoch(
.toLocal(); rawTimestamp * 1000,
).toLocal();
} }
return DateTime.fromMillisecondsSinceEpoch(rawTimestamp).toLocal(); return DateTime.fromMillisecondsSinceEpoch(rawTimestamp).toLocal();
} }
@ -124,8 +121,9 @@ class Evaporasi {
final unixValue = int.tryParse(s); final unixValue = int.tryParse(s);
if (unixValue != null) { if (unixValue != null) {
if (unixValue < 1000000000000) { if (unixValue < 1000000000000) {
return DateTime.fromMillisecondsSinceEpoch(unixValue * 1000) return DateTime.fromMillisecondsSinceEpoch(
.toLocal(); unixValue * 1000,
).toLocal();
} }
return DateTime.fromMillisecondsSinceEpoch(unixValue).toLocal(); return DateTime.fromMillisecondsSinceEpoch(unixValue).toLocal();
} }
@ -151,8 +149,7 @@ class Evaporasi {
DateTime timestamp = DateTime.fromMillisecondsSinceEpoch(0); DateTime timestamp = DateTime.fromMillisecondsSinceEpoch(0);
final rawTimestamp = final rawTimestamp = json['timestamp'] ?? json['time'] ?? json['datetime'];
json['timestamp'] ?? json['time'] ?? json['datetime'];
if (rawTimestamp != null) { if (rawTimestamp != null) {
timestamp = parseTimestamp(rawTimestamp); timestamp = parseTimestamp(rawTimestamp);
@ -164,11 +161,9 @@ class Evaporasi {
if (parts.length >= 2) { if (parts.length >= 2) {
final jam = int.tryParse(parts[0]) ?? 0; final jam = int.tryParse(parts[0]) ?? 0;
final menit = int.tryParse(parts[1]) ?? 0; final menit = int.tryParse(parts[1]) ?? 0;
final detik = final detik = parts.length >= 3 ? (int.tryParse(parts[2]) ?? 0) : 0;
parts.length >= 3 ? (int.tryParse(parts[2]) ?? 0) : 0;
final now = DateTime.now(); final now = DateTime.now();
timestamp = timestamp = DateTime(now.year, now.month, now.day, jam, menit, detik);
DateTime(now.year, now.month, now.day, jam, menit, detik);
} }
} }
} }
@ -182,4 +177,4 @@ class Evaporasi {
timestamp: timestamp, timestamp: timestamp,
); );
} }
} }