preparing for integration, by fetching data when selecting a community.

This commit is contained in:
Faris Armoush
2025-05-07 10:57:16 +03:00
parent 55a6974bdc
commit a11e20147e
14 changed files with 234 additions and 96 deletions

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
class AnalyticsCommunitiesSidebar extends StatelessWidget { class AnalyticsCommunitiesSidebar extends StatelessWidget {
@ -9,15 +7,28 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Builder(
child: SpaceTreeView( builder: (context) {
title: const Text('Communities'), return Expanded(
shouldDisableDeselectingChildrenOfSelectedParent: true, child: SpaceTreeView(
onSelect: () { title: const Text('Communities'),
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent()); shouldDisableDeselectingChildrenOfSelectedParent: true,
}, onSelect: () {
isSide: false, /// Necessary to wait for the state to update before fethcing the data.
), Future.delayed(
const Duration(milliseconds: 100),
() {
if (context.mounted) {
FetchEnergyManagementDataHelper.fetchEnergyManagementData(
context);
}
},
);
},
isSide: false,
),
);
},
); );
} }
} }

View File

@ -13,6 +13,7 @@ class EnergyConsumptionByPhasesBloc
this._energyConsumptionByPhasesService, this._energyConsumptionByPhasesService,
) : super(const EnergyConsumptionByPhasesState()) { ) : super(const EnergyConsumptionByPhasesState()) {
on<LoadEnergyConsumptionByPhasesEvent>(_onLoadEnergyConsumptionByPhasesEvent); on<LoadEnergyConsumptionByPhasesEvent>(_onLoadEnergyConsumptionByPhasesEvent);
on<ClearEnergyConsumptionByPhasesEvent>(_onClearEnergyConsumptionByPhasesEvent);
} }
final EnergyConsumptionByPhasesService _energyConsumptionByPhasesService; final EnergyConsumptionByPhasesService _energyConsumptionByPhasesService;
@ -39,4 +40,11 @@ class EnergyConsumptionByPhasesBloc
); );
} }
} }
void _onClearEnergyConsumptionByPhasesEvent(
ClearEnergyConsumptionByPhasesEvent event,
Emitter<EnergyConsumptionByPhasesState> emit,
) async {
emit(const EnergyConsumptionByPhasesState());
}
} }

View File

@ -17,3 +17,7 @@ class LoadEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent
@override @override
List<Object> get props => [param]; List<Object> get props => [param];
} }
final class ClearEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent {
const ClearEnergyConsumptionByPhasesEvent();
}

View File

@ -13,6 +13,7 @@ class EnergyConsumptionPerDeviceBloc
this._energyConsumptionPerDeviceService, this._energyConsumptionPerDeviceService,
) : super(const EnergyConsumptionPerDeviceState()) { ) : super(const EnergyConsumptionPerDeviceState()) {
on<LoadEnergyConsumptionPerDeviceEvent>(_onLoadEnergyConsumptionPerDeviceEvent); on<LoadEnergyConsumptionPerDeviceEvent>(_onLoadEnergyConsumptionPerDeviceEvent);
on<ClearEnergyConsumptionPerDeviceEvent>(_onClearEnergyConsumptionPerDeviceEvent);
} }
final EnergyConsumptionPerDeviceService _energyConsumptionPerDeviceService; final EnergyConsumptionPerDeviceService _energyConsumptionPerDeviceService;
@ -39,4 +40,11 @@ class EnergyConsumptionPerDeviceBloc
); );
} }
} }
void _onClearEnergyConsumptionPerDeviceEvent(
ClearEnergyConsumptionPerDeviceEvent event,
Emitter<EnergyConsumptionPerDeviceState> emit,
) async {
emit(const EnergyConsumptionPerDeviceState());
}
} }

View File

@ -16,3 +16,8 @@ final class LoadEnergyConsumptionPerDeviceEvent
@override @override
List<Object> get props => [param]; List<Object> get props => [param];
} }
final class ClearEnergyConsumptionPerDeviceEvent
extends EnergyConsumptionPerDeviceEvent {
const ClearEnergyConsumptionPerDeviceEvent();
}

View File

@ -13,6 +13,7 @@ class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState>
) : super(const PowerClampInfoState()) { ) : super(const PowerClampInfoState()) {
on<LoadPowerClampInfoEvent>(_onLoadPowerClampInfoEvent); on<LoadPowerClampInfoEvent>(_onLoadPowerClampInfoEvent);
on<UpdatePowerClampStatusEvent>(_onUpdatePowerClampStatusEvent); on<UpdatePowerClampStatusEvent>(_onUpdatePowerClampStatusEvent);
on<ClearPowerClampInfoEvent>(_onClearPowerClampInfoEvent);
} }
final PowerClampInfoService _powerClampInfoService; final PowerClampInfoService _powerClampInfoService;
@ -52,4 +53,11 @@ class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState>
emit(state.copyWith(powerClampModel: updatedModel)); emit(state.copyWith(powerClampModel: updatedModel));
} }
void _onClearPowerClampInfoEvent(
ClearPowerClampInfoEvent event,
Emitter<PowerClampInfoState> emit,
) {
emit(const PowerClampInfoState());
}
} }

View File

@ -24,4 +24,8 @@ final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
@override @override
List<Object> get props => [statusList]; List<Object> get props => [statusList];
}
final class ClearPowerClampInfoEvent extends PowerClampInfoEvent {
const ClearPowerClampInfoEvent();
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/realtime_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/realtime_device_service/realtime_device_service.dart';
@ -12,35 +14,66 @@ class RealtimeDeviceChangesBloc
this._realtimeDeviceService, this._realtimeDeviceService,
) : super(const RealtimeDeviceChangesState()) { ) : super(const RealtimeDeviceChangesState()) {
on<RealtimeDeviceChangesStarted>(_onRealtimeDeviceChangesStarted); on<RealtimeDeviceChangesStarted>(_onRealtimeDeviceChangesStarted);
on<RealtimeDeviceChangesClosed>(_onRealtimeDeviceChangesClosed);
on<_RealtimeDeviceChangesUpdated>(_onRealtimeDeviceChangesUpdated);
} }
final RealtimeDeviceService _realtimeDeviceService; final RealtimeDeviceService _realtimeDeviceService;
StreamSubscription<List<Status>>? _subscription;
Future<void> _onRealtimeDeviceChangesStarted( Future<void> _onRealtimeDeviceChangesStarted(
RealtimeDeviceChangesStarted event, RealtimeDeviceChangesStarted event,
Emitter<RealtimeDeviceChangesState> emit, Emitter<RealtimeDeviceChangesState> emit,
) async { ) async {
await emit.onEach( await _subscription?.cancel();
_realtimeDeviceService.subscribe(event.deviceId), _subscription = _realtimeDeviceService.subscribe(event.deviceId).listen(
onData: (data) { (data) {
final currentState = state; add(_RealtimeDeviceChangesUpdated(data));
},
onError: (error) {
emit( emit(
state.copyWith( state.copyWith(
status: RealtimeDeviceChangesStatus.loaded, status: RealtimeDeviceChangesStatus.failure,
deviceStatusList: [ errorMessage: '$error',
...currentState.deviceStatusList.where((device) =>
!data.any((newDevice) => newDevice.code == device.code)),
...data,
],
), ),
); );
}, },
onError: (error, _) => emit( );
state.copyWith( }
status: RealtimeDeviceChangesStatus.failure,
errorMessage: '$error', void _onRealtimeDeviceChangesClosed(
), RealtimeDeviceChangesClosed event,
Emitter<RealtimeDeviceChangesState> emit,
) async {
await _subscription?.cancel();
_subscription = null;
emit(state.copyWith(status: RealtimeDeviceChangesStatus.closed));
}
void _onRealtimeDeviceChangesUpdated(
_RealtimeDeviceChangesUpdated event,
Emitter<RealtimeDeviceChangesState> emit,
) {
final currentState = state;
final updatedList = [
...currentState.deviceStatusList.where(
(device) => !event.deviceStatusList
.any((newDevice) => newDevice.code == device.code),
),
...event.deviceStatusList,
];
emit(
state.copyWith(
status: RealtimeDeviceChangesStatus.loaded,
deviceStatusList: updatedList,
), ),
); );
} }
@override
Future<void> close() {
_subscription?.cancel();
return super.close();
}
} }

View File

@ -15,3 +15,13 @@ final class RealtimeDeviceChangesStarted extends RealtimeDeviceChangesEvent {
@override @override
List<Object> get props => [deviceId]; List<Object> get props => [deviceId];
} }
final class RealtimeDeviceChangesClosed extends RealtimeDeviceChangesEvent {
const RealtimeDeviceChangesClosed();
}
class _RealtimeDeviceChangesUpdated extends RealtimeDeviceChangesEvent {
final List<Status> deviceStatusList;
const _RealtimeDeviceChangesUpdated(this.deviceStatusList);
}

View File

@ -1,6 +1,6 @@
part of 'realtime_device_changes_bloc.dart'; part of 'realtime_device_changes_bloc.dart';
enum RealtimeDeviceChangesStatus { initial, loaded, failure } enum RealtimeDeviceChangesStatus { initial, loaded, failure, closed }
final class RealtimeDeviceChangesState extends Equatable { final class RealtimeDeviceChangesState extends Equatable {
const RealtimeDeviceChangesState({ const RealtimeDeviceChangesState({

View File

@ -13,6 +13,7 @@ class TotalEnergyConsumptionBloc
this._totalEnergyConsumptionService, this._totalEnergyConsumptionService,
) : super(const TotalEnergyConsumptionState()) { ) : super(const TotalEnergyConsumptionState()) {
on<TotalEnergyConsumptionLoadEvent>(_onTotalEnergyConsumptionLoadEvent); on<TotalEnergyConsumptionLoadEvent>(_onTotalEnergyConsumptionLoadEvent);
on<ClearTotalEnergyConsumptionEvent>(_onClearTotalEnergyConsumptionEvent);
} }
final TotalEnergyConsumptionService _totalEnergyConsumptionService; final TotalEnergyConsumptionService _totalEnergyConsumptionService;
@ -39,4 +40,11 @@ class TotalEnergyConsumptionBloc
); );
} }
} }
void _onClearTotalEnergyConsumptionEvent(
ClearTotalEnergyConsumptionEvent event,
Emitter<TotalEnergyConsumptionState> emit,
) async {
emit(const TotalEnergyConsumptionState());
}
} }

View File

@ -10,8 +10,12 @@ sealed class TotalEnergyConsumptionEvent extends Equatable {
final class TotalEnergyConsumptionLoadEvent extends TotalEnergyConsumptionEvent { final class TotalEnergyConsumptionLoadEvent extends TotalEnergyConsumptionEvent {
const TotalEnergyConsumptionLoadEvent({required this.param}); const TotalEnergyConsumptionLoadEvent({required this.param});
final GetTotalEnergyConsumptionParam param ; final GetTotalEnergyConsumptionParam param;
@override @override
List<Object?> get props => [param]; List<Object?> get props => [param];
} }
final class ClearTotalEnergyConsumptionEvent extends TotalEnergyConsumptionEvent {
const ClearTotalEnergyConsumptionEvent();
}

View File

@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_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/total_energy_consumption/total_energy_consumption_bloc.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_param.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
abstract final class FetchEnergyManagementDataHelper {
const FetchEnergyManagementDataHelper._();
static void fetchEnergyManagementData(BuildContext context) {
final (selectedCommunities, selectedSpaces) =
_getSelectedCommunitiesAndSpaces(context);
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
clearAllData(context);
return;
}
loadTotalEnergyConsumption(context);
loadEnergyConsumptionByPhases(context);
loadPowerClampInfo(context);
loadEnergyConsumptionPerDevice(context);
loadRealtimeDeviceChanges(context);
return;
}
static (List<String> selectedCommunities, List<String> selectedSpaces)
_getSelectedCommunitiesAndSpaces(BuildContext context) {
final spaceTreeState = context.read<SpaceTreeBloc>().state;
final selectedCommunities = spaceTreeState.selectedCommunities;
final selectedSpaces = spaceTreeState.selectedSpaces;
return (selectedCommunities, selectedSpaces);
}
static void loadEnergyConsumptionByPhases(BuildContext context) {
const param = GetEnergyConsumptionByPhasesParam();
context.read<EnergyConsumptionByPhasesBloc>().add(
const LoadEnergyConsumptionByPhasesEvent(param: param),
);
}
static void loadTotalEnergyConsumption(BuildContext context) {
final (selectedCommunities, selectedSpaces) =
_getSelectedCommunitiesAndSpaces(context);
final param = GetTotalEnergyConsumptionParam(
spaceId: selectedCommunities.firstOrNull,
);
context.read<TotalEnergyConsumptionBloc>().add(
TotalEnergyConsumptionLoadEvent(param: param),
);
}
static void loadEnergyConsumptionPerDevice(BuildContext context) {
const param = GetEnergyConsumptionPerDeviceParam();
context.read<EnergyConsumptionPerDeviceBloc>().add(
const LoadEnergyConsumptionPerDeviceEvent(param),
);
}
static void loadPowerClampInfo(BuildContext context) {
context.read<PowerClampInfoBloc>().add(
const LoadPowerClampInfoEvent('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
);
}
static void loadRealtimeDeviceChanges(BuildContext context) {
context.read<RealtimeDeviceChangesBloc>().add(
const RealtimeDeviceChangesStarted('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
);
}
static void clearAllData(BuildContext context) {
context.read<RealtimeDeviceChangesBloc>().add(
const RealtimeDeviceChangesClosed(),
);
context.read<PowerClampInfoBloc>().add(
const ClearPowerClampInfoEvent(),
);
context.read<EnergyConsumptionPerDeviceBloc>().add(
const ClearEnergyConsumptionPerDeviceEvent(),
);
context.read<TotalEnergyConsumptionBloc>().add(
const ClearTotalEnergyConsumptionEvent(),
);
context.read<EnergyConsumptionByPhasesBloc>().add(
const ClearEnergyConsumptionByPhasesEvent(),
);
}
}

View File

@ -1,70 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_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/total_energy_consumption/total_energy_consumption_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart_box.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart_box.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_param.dart';
import 'package:syncrow_web/utils/style.dart';
class AnalyticsEnergyManagementView extends StatefulWidget { class AnalyticsEnergyManagementView extends StatelessWidget {
const AnalyticsEnergyManagementView({super.key}); const AnalyticsEnergyManagementView({super.key});
@override
State<AnalyticsEnergyManagementView> createState() =>
_AnalyticsEnergyManagementViewState();
}
class _AnalyticsEnergyManagementViewState
extends State<AnalyticsEnergyManagementView> {
@override
void initState() {
_loadEnergyConsumptionByPhases();
_loadTotalEnergyConsumption();
_loadEnergyConsumptionPerDevice();
_loadPowerClampInfo();
context.read<RealtimeDeviceChangesBloc>().add(
const RealtimeDeviceChangesStarted(
'cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa',
),
);
super.initState();
}
void _loadEnergyConsumptionByPhases() {
const param = GetEnergyConsumptionByPhasesParam();
context.read<EnergyConsumptionByPhasesBloc>().add(
const LoadEnergyConsumptionByPhasesEvent(param: param),
);
}
void _loadTotalEnergyConsumption() {
const param = GetTotalEnergyConsumptionParam();
context.read<TotalEnergyConsumptionBloc>().add(
const TotalEnergyConsumptionLoadEvent(param: param),
);
}
void _loadEnergyConsumptionPerDevice() {
const param = GetEnergyConsumptionPerDeviceParam();
context.read<EnergyConsumptionPerDeviceBloc>().add(
const LoadEnergyConsumptionPerDeviceEvent(param),
);
}
void _loadPowerClampInfo() {
context.read<PowerClampInfoBloc>().add(
const LoadPowerClampInfoEvent('deviceId'),
);
}
static const _padding = EdgeInsetsDirectional.all(32); static const _padding = EdgeInsetsDirectional.all(32);
@override @override
@ -80,13 +21,7 @@ class _AnalyticsEnergyManagementViewState
children: [ children: [
SizedBox( SizedBox(
height: MediaQuery.sizeOf(context).height * 1.2, height: MediaQuery.sizeOf(context).height * 1.2,
child: Container( child: const PowerClampEnergyDataWidget(),
decoration: subSectionContainerDecoration.copyWith(
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsetsDirectional.all(32),
child: const PowerClampEnergyDataWidget(),
),
), ),
SizedBox( SizedBox(
height: MediaQuery.sizeOf(context).height * 0.5, height: MediaQuery.sizeOf(context).height * 0.5,