SP-1506-FE-chart per phase api integration.

This commit is contained in:
Faris Armoush
2025-05-15 10:51:09 +03:00
parent 5279020d08
commit c07b53107e
9 changed files with 120 additions and 93 deletions

View File

@ -1,27 +1,66 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class PhasesEnergyConsumption extends Equatable { class PhasesEnergyConsumption extends Equatable {
final int month; final String uuid;
final double phaseA; final DateTime createdAt;
final double phaseB; final DateTime updatedAt;
final double phaseC; final String deviceUuid;
final DateTime date;
final double energyConsumedKw;
final double energyConsumedA;
final double energyConsumedB;
final double energyConsumedC;
const PhasesEnergyConsumption({ const PhasesEnergyConsumption({
required this.month, required this.uuid,
required this.phaseA, required this.createdAt,
required this.phaseB, required this.updatedAt,
required this.phaseC, required this.deviceUuid,
required this.date,
required this.energyConsumedKw,
required this.energyConsumedA,
required this.energyConsumedB,
required this.energyConsumedC,
}); });
@override @override
List<Object?> get props => [month, phaseA, phaseB, phaseC]; List<Object?> get props => [
uuid,
createdAt,
updatedAt,
deviceUuid,
date,
energyConsumedKw,
energyConsumedA,
energyConsumedB,
energyConsumedC,
];
factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) { factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) {
return PhasesEnergyConsumption( return PhasesEnergyConsumption(
month: json['month'] as int, uuid: json['uuid'] as String,
phaseA: (json['phaseA'] as num).toDouble(), createdAt: DateTime.parse(json['createdAt'] as String),
phaseB: (json['phaseB'] as num).toDouble(), updatedAt: DateTime.parse(json['updatedAt'] as String),
phaseC: (json['phaseC'] as num).toDouble(), deviceUuid: json['deviceUuid'] as String,
date: DateTime.parse(json['date'] as String),
energyConsumedKw: double.parse(json['energyConsumedKw']),
energyConsumedA: double.parse(json['energyConsumedA']),
energyConsumedB: double.parse(json['energyConsumedB']),
energyConsumedC: double.parse(json['energyConsumedC']),
); );
} }
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
'deviceUuid': deviceUuid,
'date': date.toIso8601String().split('T')[0],
'energyConsumedKw': energyConsumedKw.toString(),
'energyConsumedA': energyConsumedA.toString(),
'energyConsumedB': energyConsumedB.toString(),
'energyConsumedC': energyConsumedC.toString(),
};
}
} }

View File

@ -15,7 +15,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.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_energy_management_analytics_devices_service.dart';
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart';
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_by_phases/remote_energy_consumption_by_phases_service.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_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/occupacy/fake_occupacy_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/occupancy_heat_map/remote_occupancy_heat_map_service.dart';
@ -57,7 +57,7 @@ class _AnalyticsPageState extends State<AnalyticsPage> {
), ),
BlocProvider( BlocProvider(
create: (context) => EnergyConsumptionByPhasesBloc( create: (context) => EnergyConsumptionByPhasesBloc(
FakeEnergyConsumptionByPhasesService(), RemoteEnergyConsumptionByPhasesService(_httpService),
), ),
), ),
BlocProvider( BlocProvider(

View File

@ -1,20 +0,0 @@
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
abstract final class EnergyConsumptionByPhasesChartHelper {
const EnergyConsumptionByPhasesChartHelper._();
static const fakeData = <PhasesEnergyConsumption>[
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 80, phaseC: 100),
];
}

View File

@ -34,14 +34,26 @@ abstract final class FetchEnergyManagementDataHelper {
final datePickerState = context.read<AnalyticsDatePickerBloc>().state; final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate; final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId); loadAnalyticsDevices(
context,
communityUuid: communityId,
spaceUuid: spaceId,
selectedDate: selectedDate0,
);
loadTotalEnergyConsumption( loadTotalEnergyConsumption(
context, context,
selectedDate: selectedDate0, selectedDate: selectedDate0,
communityId: communityId, communityId: communityId,
spaceId: spaceId, spaceId: spaceId,
); );
loadEnergyConsumptionByPhases(context, selectedDate: selectedDate); final selectedDevice = getSelectedDevice(context);
if (selectedDevice case final AnalyticsDevice device) {
loadEnergyConsumptionByPhases(
context,
powerClampUuid: device.uuid,
selectedDate: selectedDate0,
);
}
loadEnergyConsumptionPerDevice( loadEnergyConsumptionPerDevice(
context, context,
communityId: communityId, communityId: communityId,
@ -54,11 +66,12 @@ abstract final class FetchEnergyManagementDataHelper {
static void loadEnergyConsumptionByPhases( static void loadEnergyConsumptionByPhases(
BuildContext context, { BuildContext context, {
required String powerClampUuid,
DateTime? selectedDate, DateTime? selectedDate,
}) { }) {
final param = GetEnergyConsumptionByPhasesParam( final param = GetEnergyConsumptionByPhasesParam(
startDate: selectedDate, date: selectedDate,
spaceId: '', powerClampUuid: powerClampUuid,
); );
context.read<EnergyConsumptionByPhasesBloc>().add( context.read<EnergyConsumptionByPhasesBloc>().add(
LoadEnergyConsumptionByPhasesEvent(param: param), LoadEnergyConsumptionByPhasesEvent(param: param),
@ -121,6 +134,7 @@ abstract final class FetchEnergyManagementDataHelper {
BuildContext context, { BuildContext context, {
required String communityUuid, required String communityUuid,
required String spaceUuid, required String spaceUuid,
required DateTime selectedDate,
}) { }) {
context.read<AnalyticsDevicesBloc>().add( context.read<AnalyticsDevicesBloc>().add(
LoadAnalyticsDevicesEvent( LoadAnalyticsDevicesEvent(
@ -128,6 +142,11 @@ abstract final class FetchEnergyManagementDataHelper {
context.read<PowerClampInfoBloc>().add( context.read<PowerClampInfoBloc>().add(
LoadPowerClampInfoEvent(device.uuid), LoadPowerClampInfoEvent(device.uuid),
); );
loadEnergyConsumptionByPhases(
context,
powerClampUuid: device.uuid,
selectedDate: selectedDate,
);
context.read<RealtimeDeviceChangesBloc>().add( context.read<RealtimeDeviceChangesBloc>().add(
RealtimeDeviceChangesStarted(device.uuid), RealtimeDeviceChangesStarted(device.uuid),
); );

View File

@ -1,6 +1,6 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/analytics/helpers/get_month_name_from_int.dart'; import 'package:intl/intl.dart';
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart'; import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -31,21 +31,25 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
barRods: [ barRods: [
BarChartRodData( BarChartRodData(
color: ColorsManager.vividBlue.withValues(alpha: 0.1), color: ColorsManager.vividBlue.withValues(alpha: 0.1),
toY: data.phaseA + data.phaseB + data.phaseC, toY: data.energyConsumedA +
data.energyConsumedB +
data.energyConsumedC,
rodStackItems: [ rodStackItems: [
BarChartRodStackItem( BarChartRodStackItem(
0, 0,
data.phaseA, data.energyConsumedA,
ColorsManager.vividBlue.withValues(alpha: 0.8), ColorsManager.vividBlue.withValues(alpha: 0.8),
), ),
BarChartRodStackItem( BarChartRodStackItem(
data.phaseA, data.energyConsumedA,
data.phaseA + data.phaseB, data.energyConsumedA + data.energyConsumedB,
ColorsManager.vividBlue.withValues(alpha: 0.4), ColorsManager.vividBlue.withValues(alpha: 0.4),
), ),
BarChartRodStackItem( BarChartRodStackItem(
data.phaseA + data.phaseB, data.energyConsumedA + data.energyConsumedB,
data.phaseA + data.phaseB + data.phaseC, data.energyConsumedA +
data.energyConsumedB +
data.energyConsumedC,
ColorsManager.vividBlue.withValues(alpha: 0.15), ColorsManager.vividBlue.withValues(alpha: 0.15),
), ),
], ],
@ -91,18 +95,27 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
}) { }) {
final data = energyData; final data = energyData;
final month = data[group.x.toInt()].month.getMonthName; final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date);
final phaseA = data[group.x.toInt()].phaseA; final phaseA = data[group.x.toInt()].energyConsumedA;
final phaseB = data[group.x.toInt()].phaseB; final phaseB = data[group.x.toInt()].energyConsumedB;
final phaseC = data[group.x.toInt()].phaseC; final phaseC = data[group.x.toInt()].energyConsumedC;
final total = data[group.x.toInt()].energyConsumedKw;
return BarTooltipItem( return BarTooltipItem(
'$month\n', '$date\n',
context.textTheme.bodyMedium!.copyWith( context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.blackColor, color: ColorsManager.blackColor,
fontSize: 14, fontSize: 14,
), ),
textAlign: TextAlign.start,
children: [ children: [
TextSpan(
text: 'Total: $total\n',
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
),
),
TextSpan( TextSpan(
text: 'Phase A: $phaseA\n', text: 'Phase A: $phaseA\n',
style: context.textTheme.bodySmall?.copyWith( style: context.textTheme.bodySmall?.copyWith(
@ -144,7 +157,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
final month = energyData[value.toInt()].month.getMonthName; final month = DateFormat('dd/MM').format(energyData[value.toInt()].date);
return FittedBox( return FittedBox(
alignment: AlignmentDirectional.bottomCenter, alignment: AlignmentDirectional.bottomCenter,
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.dart'; import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.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/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
@ -11,6 +12,7 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/po
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_phases_data_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_phases_data_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart'; import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/device_managment/power_clamp/models/power_clamp_model.dart'; import 'package:syncrow_web/pages/device_managment/power_clamp/models/power_clamp_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -132,6 +134,12 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
alignment: AlignmentDirectional.centerEnd, alignment: AlignmentDirectional.centerEnd,
child: AnalyticsDeviceDropdown( child: AnalyticsDeviceDropdown(
onChanged: (value) { onChanged: (value) {
FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases(
context,
powerClampUuid: value.uuid,
selectedDate:
context.read<AnalyticsDatePickerBloc>().state.monthlyDate,
);
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges( FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
context, context,
deviceUuid: value.uuid, deviceUuid: value.uuid,

View File

@ -1,24 +1,20 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class GetEnergyConsumptionByPhasesParam extends Equatable { class GetEnergyConsumptionByPhasesParam extends Equatable {
final DateTime? startDate; final String powerClampUuid;
final DateTime? endDate; final DateTime? date;
final String? spaceId;
const GetEnergyConsumptionByPhasesParam({ const GetEnergyConsumptionByPhasesParam({
this.startDate, required this.powerClampUuid,
this.endDate, this.date,
this.spaceId,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'startDate': startDate?.toIso8601String(), 'monthDate': '${date?.year}-${date?.month.toString().padLeft(2, '0')}',
'endDate': endDate?.toIso8601String(),
'spaceId': spaceId,
}; };
} }
@override @override
List<Object?> get props => [startDate, endDate, spaceId]; List<Object?> get props => [powerClampUuid, date];
} }

View File

@ -1,29 +0,0 @@
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/energy_consumption_by_phases_service.dart';
class FakeEnergyConsumptionByPhasesService
implements EnergyConsumptionByPhasesService {
@override
Future<List<PhasesEnergyConsumption>> load(
GetEnergyConsumptionByPhasesParam param,
) {
return Future.delayed(
const Duration(milliseconds: 500),
() => const [
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 100, phaseC: 100),
],
);
}
}

View File

@ -15,8 +15,9 @@ final class RemoteEnergyConsumptionByPhasesService
) async { ) async {
try { try {
final response = await _httpService.get( final response = await _httpService.get(
path: 'endpoint', path: '/power-clamp/${param.powerClampUuid}/historical',
showServerMessage: true, showServerMessage: true,
queryParameters: param.toJson(),
expectedResponseModel: (data) { expectedResponseModel: (data) {
final json = data as Map<String, dynamic>? ?? {}; final json = data as Map<String, dynamic>? ?? {};
final mappedData = json['data'] as List<dynamic>? ?? []; final mappedData = json['data'] as List<dynamic>? ?? [];
@ -28,7 +29,7 @@ final class RemoteEnergyConsumptionByPhasesService
); );
return response; return response;
} catch (e) { } catch (e) {
throw Exception('Failed to load energy consumption per device: $e'); throw Exception('Failed to load energy consumption per phase: $e');
} }
} }
} }