From 46815585cbf48d75e23d314a5908242499cec6b2 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 14 May 2025 10:47:54 +0300 Subject: [PATCH 1/5] Fixed error in `AnalyticsErrorWidget` where it used to add the default error message to the `errorMessage`. --- lib/pages/analytics/widgets/analytics_error_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/analytics/widgets/analytics_error_widget.dart b/lib/pages/analytics/widgets/analytics_error_widget.dart index 354eb31a..60167992 100644 --- a/lib/pages/analytics/widgets/analytics_error_widget.dart +++ b/lib/pages/analytics/widgets/analytics_error_widget.dart @@ -12,7 +12,7 @@ class AnalyticsErrorWidget extends StatelessWidget { return Visibility( visible: errorMessage != null || (errorMessage?.isNotEmpty ?? false), child: Text( - '$errorMessage ?? "Something went wrong"', + errorMessage ?? 'Something went wrong', maxLines: 1, overflow: TextOverflow.ellipsis, style: context.textTheme.bodySmall?.copyWith( From c1dab3400b575c69c9a31091f48c13e7e40a0169 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 14 May 2025 10:48:28 +0300 Subject: [PATCH 2/5] removed a force unwrap from `OccupancyHeatMap._maxValue` to avoid any bugs. --- .../analytics/modules/occupancy/widgets/occupancy_heat_map.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart index 6fd4b259..dd094133 100644 --- a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart +++ b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart @@ -15,7 +15,7 @@ class OccupancyHeatMap extends StatelessWidget { static const _totalWeeks = 53; int get _maxValue => heatMapData.isNotEmpty - ? heatMapData.keys.map((key) => heatMapData[key]!).reduce(math.max) + ? heatMapData.keys.map((key) => heatMapData[key] ?? 0).reduce(math.max) : 0; DateTime _getStartingDate() { From 300f9ae358c95afae6a748ea33957418158058fd Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 14 May 2025 10:49:32 +0300 Subject: [PATCH 3/5] Matched the `GetOccupancyHeatMapParam` with what the API expects and removed the `communityId` since it is no longer necessary for the api, and renamed `spaceId` to `spaceUuid` for more clarity. --- .../helpers/fetch_occupancy_data_helper.dart | 3 +-- .../params/get_occupancy_heat_map_param.dart | 12 +++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart b/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart index ffd52af1..403a752d 100644 --- a/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart +++ b/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart @@ -35,8 +35,7 @@ abstract final class FetchOccupancyDataHelper { context.read().add( LoadOccupancyHeatMapEvent( GetOccupancyHeatMapParam( - spaceId: spaceId, - communityId: communityId, + spaceUuid: spaceId, year: datePickerState.yearlyDate, ), ), diff --git a/lib/pages/analytics/params/get_occupancy_heat_map_param.dart b/lib/pages/analytics/params/get_occupancy_heat_map_param.dart index f6649f78..0bcad002 100644 --- a/lib/pages/analytics/params/get_occupancy_heat_map_param.dart +++ b/lib/pages/analytics/params/get_occupancy_heat_map_param.dart @@ -1,19 +1,13 @@ class GetOccupancyHeatMapParam { final DateTime year; - final String communityId; - final String spaceId; + final String spaceUuid; const GetOccupancyHeatMapParam({ required this.year, - required this.communityId, - required this.spaceId, + required this.spaceUuid, }); Map toJson() { - return { - 'year': year.toIso8601String(), - 'communityId': communityId, - 'spaceId': spaceId, - }; + return {'year': year.year}; } } From d1bb8da4848bdbfc4bde0a6b870f0fa0e79b7afe Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 14 May 2025 10:51:19 +0300 Subject: [PATCH 4/5] Updated `OccupancyHeatMapModel` model with what the api returns, and only used the necessary fields that the api returns for this feature to work. --- .../models/occupancy_heat_map_model.dart | 20 ++++++++++++------- .../widgets/occupancy_heat_map_box.dart | 5 ++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/pages/analytics/models/occupancy_heat_map_model.dart b/lib/pages/analytics/models/occupancy_heat_map_model.dart index 52016cc9..73e7d5d7 100644 --- a/lib/pages/analytics/models/occupancy_heat_map_model.dart +++ b/lib/pages/analytics/models/occupancy_heat_map_model.dart @@ -1,22 +1,28 @@ import 'package:equatable/equatable.dart'; class OccupancyHeatMapModel extends Equatable { - final DateTime date; + final String uuid; - final int occupancy; + final DateTime eventDate; + + final int countTotalPresenceDetected; const OccupancyHeatMapModel({ - required this.date, - required this.occupancy, + required this.uuid, + required this.eventDate, + required this.countTotalPresenceDetected, }); factory OccupancyHeatMapModel.fromJson(Map json) { return OccupancyHeatMapModel( - date: DateTime.parse(json['date'] as String), - occupancy: json['occupancy'] as int, + uuid: json['uuid'] as String? ?? '', + eventDate: DateTime.parse( + json['event_date'] as String? ?? '${DateTime.now()}', + ), + countTotalPresenceDetected: json['count_total_presence_detected'] as int? ?? 0, ); } @override - List get props => [date, occupancy]; + List get props => [uuid, eventDate, countTotalPresenceDetected]; } diff --git a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart index c4cda268..d52604bf 100644 --- a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart +++ b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart @@ -68,7 +68,10 @@ class OccupancyHeatMapBox extends StatelessWidget { Expanded( child: OccupancyHeatMap( heatMapData: state.heatMapData.asMap().map( - (_, value) => MapEntry(value.date, value.occupancy), + (_, value) => MapEntry( + value.eventDate, + value.countTotalPresenceDetected, + ), ), ), ), From 4d9e57c8b5d35c0225571bffe6eb4a5ff394c4f9 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 14 May 2025 10:51:37 +0300 Subject: [PATCH 5/5] Created and connected a remote implementation that fetches the heat map occupancy per space from the API. --- .../analytics/views/analytics_page.dart | 6 ++-- .../fake_occupancy_heat_map_service.dart | 25 ------------- .../remote_occupancy_heat_map_service.dart | 35 +++++++++++++++++++ 3 files changed, 39 insertions(+), 27 deletions(-) delete mode 100644 lib/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart create mode 100644 lib/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart diff --git a/lib/pages/analytics/modules/analytics/views/analytics_page.dart b/lib/pages/analytics/modules/analytics/views/analytics_page.dart index 354455e9..6674667c 100644 --- a/lib/pages/analytics/modules/analytics/views/analytics_page.dart +++ b/lib/pages/analytics/modules/analytics/views/analytics_page.dart @@ -14,7 +14,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart'; -import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart'; +import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart'; import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart'; import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/remote_total_energy_consumption_service.dart'; @@ -60,7 +60,9 @@ class AnalyticsPage extends StatelessWidget { ), BlocProvider(create: (context) => OccupancyBloc(FakeOccupacyService())), BlocProvider( - create: (context) => OccupancyHeatMapBloc(FakeOccupancyHeatMapService()), + create: (context) => OccupancyHeatMapBloc( + RemoteOccupancyHeatMapService(HTTPService()), + ), ), BlocProvider(create: (context) => AnalyticsDatePickerBloc()), ], diff --git a/lib/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart b/lib/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart deleted file mode 100644 index 852e9e8c..00000000 --- a/lib/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:syncrow_web/pages/analytics/models/occupancy_heat_map_model.dart'; -import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart'; -import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/occupancy_heat_map_service.dart'; - -class FakeOccupancyHeatMapService implements OccupancyHeatMapService { - @override - Future> load(GetOccupancyHeatMapParam param) { - return Future.delayed(const Duration(milliseconds: 200), () { - final now = DateTime.now(); - final startOfYear = DateTime(now.year, 1, 1); - final endOfYear = DateTime(now.year, 12, 31); - final daysInYear = endOfYear.difference(startOfYear).inDays + 1; - - final List data = List.generate( - daysInYear, - (index) => OccupancyHeatMapModel( - date: startOfYear.add(Duration(days: index)), - occupancy: ((index + 1) * 10) % 100, - ), - ); - - return data; - }); - } -} diff --git a/lib/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart b/lib/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart new file mode 100644 index 00000000..ac06ccf7 --- /dev/null +++ b/lib/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart @@ -0,0 +1,35 @@ +import 'package:syncrow_web/pages/analytics/models/occupancy_heat_map_model.dart'; +import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart'; +import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/occupancy_heat_map_service.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService { + const RemoteOccupancyHeatMapService(this._httpService); + + final HTTPService _httpService; + + @override + Future> load(GetOccupancyHeatMapParam param) async { + try { + final response = await _httpService.get( + path: '/occupancy/heat-map/space/${param.spaceUuid}', + showServerMessage: true, + queryParameters: param.toJson(), + expectedResponseModel: (response) { + final json = response as Map; + final dailyData = json['data'] as List? ?? []; + + final result = dailyData.map( + (json) => OccupancyHeatMapModel.fromJson(json as Map), + ); + + return result.toList(); + }, + ); + + return response; + } catch (e) { + throw Exception('Failed to load total energy consumption:'); + } + } +}