diff --git a/lib/pages/analytics/models/air_quality_data_model.dart b/lib/pages/analytics/models/air_quality_data_model.dart index 2eab2ddb..38b72abc 100644 --- a/lib/pages/analytics/models/air_quality_data_model.dart +++ b/lib/pages/analytics/models/air_quality_data_model.dart @@ -15,7 +15,9 @@ class AirQualityDataModel extends Equatable { return AirQualityDataModel( date: DateTime.parse(json['date'] as String), data: (json['data'] as List) - .map((e) => AirQualityPercentageData.fromJson(e as Map)) + .map( + (e) => AirQualityPercentageData.fromJson(e as Map), + ) .toList(), ); } @@ -23,9 +25,9 @@ class AirQualityDataModel extends Equatable { static final Map metricColors = { 'good': ColorsManager.goodGreen.withValues(alpha: 0.7), 'moderate': ColorsManager.moderateYellow.withValues(alpha: 0.7), - 'poor': ColorsManager.poorOrange.withValues(alpha: 0.7), + 'unhealthy_sensitive': ColorsManager.poorOrange.withValues(alpha: 0.7), 'unhealthy': ColorsManager.unhealthyRed.withValues(alpha: 0.7), - 'severe': ColorsManager.severePink.withValues(alpha: 0.7), + 'very_unhealthy': ColorsManager.severePink.withValues(alpha: 0.7), 'hazardous': ColorsManager.hazardousPurple.withValues(alpha: 0.7), }; @@ -36,22 +38,19 @@ class AirQualityDataModel extends Equatable { class AirQualityPercentageData extends Equatable { const AirQualityPercentageData({ required this.type, - required this.name, required this.percentage, }); final String type; - final String name; final double percentage; factory AirQualityPercentageData.fromJson(Map json) { return AirQualityPercentageData( - type: json['type'] as String? ?? '', - name: json['name'] as String? ?? '', + type: json['type'] as String? ?? '', percentage: (json['percentage'] as num?)?.toDouble() ?? 0, ); } @override - List get props => [type, name, percentage]; + List get props => [type, percentage]; } diff --git a/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart b/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart index cb37484c..223c0357 100644 --- a/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart +++ b/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/device_location/device_location_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart'; +import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart'; @@ -22,6 +23,7 @@ abstract final class FetchAirQualityDataHelper { bool shouldFetchAnalyticsDevices = true, }) { final date = context.read().state.monthlyDate; + final aqiType = context.read().state.selectedAqiType; loadAnalyticsDevices( context, communityUuid: communityUuid, @@ -36,6 +38,7 @@ abstract final class FetchAirQualityDataHelper { context, spaceUuid: spaceUuid, date: date, + aqiType: aqiType, ); } @@ -104,10 +107,15 @@ abstract final class FetchAirQualityDataHelper { BuildContext context, { required String spaceUuid, required DateTime date, + required AqiType aqiType, }) { context.read().add( LoadAirQualityDistribution( - GetAirQualityDistributionParam(spaceUuid: spaceUuid, date: date), + GetAirQualityDistributionParam( + spaceUuid: spaceUuid, + date: date, + aqiType: aqiType, + ), ), ); } diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart index 8a57fe0b..25cfd19d 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart @@ -33,7 +33,7 @@ class AqiDistributionChartBox extends StatelessWidget { const Divider(), const SizedBox(height: 20), Expanded( - child: AqiDistributionChart(chartData: state.filteredChartData), + child: AqiDistributionChart(chartData: state.chartData), ), ], ), diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart index 4cf79263..926d28e1 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart @@ -34,14 +34,14 @@ class AqiDistributionChartTitle extends StatelessWidget { child: AqiTypeDropdown( onChanged: (value) { if (value != null) { + final bloc = context.read(); try { - final bloc = context.read(); final param = _makeLoadAqiDistributionParam(context, value); - bloc - ..add(UpdateAqiTypeEvent(value)) - ..add(LoadAirQualityDistribution(param)); + bloc.add(LoadAirQualityDistribution(param)); } catch (_) { return; + } finally { + bloc.add(UpdateAqiTypeEvent(value)); } } }, diff --git a/lib/pages/analytics/modules/analytics/views/analytics_page.dart b/lib/pages/analytics/modules/analytics/views/analytics_page.dart index 903b0325..3d8b1eb3 100644 --- a/lib/pages/analytics/modules/analytics/views/analytics_page.dart +++ b/lib/pages/analytics/modules/analytics/views/analytics_page.dart @@ -16,7 +16,7 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/real import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/occupancy_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.dart'; -import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/fake_air_quality_distribution_service.dart'; +import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/remote_air_quality_distribution_service.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_energy_management_analytics_devices_service.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart'; @@ -109,7 +109,7 @@ class _AnalyticsPageState extends State { ), BlocProvider( create: (context) => AirQualityDistributionBloc( - FakeAirQualityDistributionService(), + RemoteAirQualityDistributionService(_httpService), ), ), BlocProvider( diff --git a/lib/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart b/lib/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart index af70cd86..616688dd 100644 --- a/lib/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart +++ b/lib/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart @@ -20,7 +20,7 @@ class AnalyticsDateFilterButton extends StatefulWidget { final void Function(DateTime)? onDateSelected; final DatePickerType datePickerType; - static final _color = ColorsManager.blackColor.withValues(alpha: 0.8); + static final Color _color = ColorsManager.blackColor.withValues(alpha: 0.8); @override State createState() => @@ -60,23 +60,21 @@ class _AnalyticsDateFilterButtonState extends State { ), ), onPressed: () { - showDialog( + showDialog( context: context, - builder: (_) { - return switch (widget.datePickerType) { - DatePickerType.month => MonthPickerWidget( - selectedDate: widget.selectedDate, - onDateSelected: (value) { - widget.onDateSelected?.call(value); - }, - ), - DatePickerType.year => YearPickerWidget( - selectedDate: widget.selectedDate, - onDateSelected: (value) { - widget.onDateSelected?.call(value); - }, - ), - }; + builder: (_) => switch (widget.datePickerType) { + DatePickerType.month => MonthPickerWidget( + selectedDate: widget.selectedDate, + onDateSelected: (value) { + widget.onDateSelected?.call(value); + }, + ), + DatePickerType.year => YearPickerWidget( + selectedDate: widget.selectedDate, + onDateSelected: (value) { + widget.onDateSelected?.call(value); + }, + ), }, ); }, diff --git a/lib/pages/analytics/params/get_air_quality_distribution_param.dart b/lib/pages/analytics/params/get_air_quality_distribution_param.dart index f1d3fe9f..ecad66b0 100644 --- a/lib/pages/analytics/params/get_air_quality_distribution_param.dart +++ b/lib/pages/analytics/params/get_air_quality_distribution_param.dart @@ -1,9 +1,14 @@ +import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart'; + class GetAirQualityDistributionParam { final DateTime date; final String spaceUuid; + final AqiType aqiType; - const GetAirQualityDistributionParam({ + const GetAirQualityDistributionParam( + { required this.date, required this.spaceUuid, + required this.aqiType, }); } diff --git a/lib/pages/analytics/services/air_quality_distribution/fake_air_quality_distribution_service.dart b/lib/pages/analytics/services/air_quality_distribution/fake_air_quality_distribution_service.dart deleted file mode 100644 index e0023f53..00000000 --- a/lib/pages/analytics/services/air_quality_distribution/fake_air_quality_distribution_service.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'dart:math'; - -import 'package:syncrow_web/pages/analytics/models/air_quality_data_model.dart'; -import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart'; -import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart'; -import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart'; - -class FakeAirQualityDistributionService implements AirQualityDistributionService { - final _random = Random(); - - @override - Future> getAirQualityDistribution( - GetAirQualityDistributionParam param, - ) async { - return Future.delayed( - const Duration(milliseconds: 400), - () => List.generate(30, (index) { - final date = DateTime(2025, 5, 1).add(Duration(days: index)); - - final values = _generateRandomPercentages(); - final nullMask = List.generate(6, (_) => _shouldBeNull()); - - if (nullMask.every((isNull) => isNull)) { - nullMask[_random.nextInt(6)] = false; - } - - final nonNullValues = _redistributePercentages(values, nullMask); - - return AirQualityDataModel( - date: date, - data: [ - AirQualityPercentageData( - type: AqiType.aqi.code, - percentage: nonNullValues[0], - name: 'good', - ), - AirQualityPercentageData( - name: 'moderate', - type: AqiType.co2.code, - percentage: nonNullValues[1], - ), - AirQualityPercentageData( - name: 'poor', - percentage: nonNullValues[2], - type: AqiType.hcho.code, - - ), - AirQualityPercentageData( - name: 'unhealthy', - percentage: nonNullValues[3], - type: AqiType.pm10.code, - ), - AirQualityPercentageData( - name: 'severe', - type: AqiType.pm25.code, - percentage: nonNullValues[4], - ), - AirQualityPercentageData( - name: 'hazardous', - percentage: nonNullValues[5], - type: AqiType.co2.code, - ), - ], - ); - }), - ); - } - - List _redistributePercentages( - List originalValues, - List nullMask, - ) { - double nonNullSum = 0; - for (int i = 0; i < originalValues.length; i++) { - if (!nullMask[i]) { - nonNullSum += originalValues[i]; - } - } - - return List.generate(originalValues.length, (i) { - if (nullMask[i]) return 0; - return (originalValues[i] / nonNullSum * 100).roundToDouble(); - }); - } - - bool _shouldBeNull() => _random.nextDouble() < 0.6; - - List _generateRandomPercentages() { - final values = List.generate(6, (_) => _random.nextDouble()); - - final sum = values.reduce((a, b) => a + b); - - return values.map((value) => (value / sum * 100).roundToDouble()).toList(); - } -} diff --git a/lib/pages/analytics/services/air_quality_distribution/remote_air_quality_distribution_service.dart b/lib/pages/analytics/services/air_quality_distribution/remote_air_quality_distribution_service.dart index dcf00600..49755f7b 100644 --- a/lib/pages/analytics/services/air_quality_distribution/remote_air_quality_distribution_service.dart +++ b/lib/pages/analytics/services/air_quality_distribution/remote_air_quality_distribution_service.dart @@ -3,7 +3,8 @@ import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_ import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart'; import 'package:syncrow_web/services/api/http_service.dart'; -class RemoteAirQualityDistributionService implements AirQualityDistributionService { +final class RemoteAirQualityDistributionService + implements AirQualityDistributionService { RemoteAirQualityDistributionService(this._httpService); final HTTPService _httpService; @@ -14,10 +15,10 @@ class RemoteAirQualityDistributionService implements AirQualityDistributionServi ) async { try { final response = await _httpService.get( - path: 'endpoint', + path: '/aqi/distribution/space/${param.spaceUuid}', queryParameters: { - 'spaceUuid': param.spaceUuid, - 'date': param.date.toIso8601String(), + 'monthDate': _formatDate(param.date), + 'pollutantType': param.aqiType.code, }, expectedResponseModel: (data) { final json = data as Map? ?? {}; @@ -33,4 +34,8 @@ class RemoteAirQualityDistributionService implements AirQualityDistributionServi throw Exception('Failed to load energy consumption per phase: $e'); } } + + static String _formatDate(DateTime date) { + return '${date.year}-${date.month.toString().padLeft(2, '0')}'; + } } diff --git a/lib/pages/analytics/services/device_location/device_location_details_service_decorator.dart b/lib/pages/analytics/services/device_location/device_location_details_service_decorator.dart index 0239bcb7..f38f607d 100644 --- a/lib/pages/analytics/services/device_location/device_location_details_service_decorator.dart +++ b/lib/pages/analytics/services/device_location/device_location_details_service_decorator.dart @@ -26,15 +26,15 @@ class DeviceLocationDetailsServiceDecorator implements DeviceLocationService { if (data != null) { final addressData = data['address'] as Map; return deviceLocationInfo.copyWith( - city: addressData['city'], - country: addressData['country_code'].toString().toUpperCase(), - address: addressData['state'], + city: addressData['city'] as String?, + country: addressData['country_code']?.toString().toUpperCase(), + address: addressData['state'] as String?, ); } return deviceLocationInfo; } catch (e) { - throw Exception('Failed to load device location info: ${e.toString()}'); + throw Exception('Failed to load device location info: $e'); } } } diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 585688ef..00000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import firebase_analytics -import firebase_core -import firebase_crashlytics -import firebase_database -import flutter_secure_storage_macos -import path_provider_foundation -import shared_preferences_foundation -import url_launcher_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin")) - FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) - FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) - FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseDatabasePlugin")) - FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) -}