mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-15 01:35:25 +00:00
Sp 1494 api integration (#180)
* SP-1494-api-integration. * fixed left stide titles intervals in total energy consumption chart. * Adjusted tooltip and title intervals in energy management charts to improve accuracy by incrementing displayed values by one. * Refactor AnalyticsCommunitiesSidebar to use AnalyticsSpaceTreeView and enhance community/space selection handling * Gave every tab its own selection logic using the strategy design pattern, along with clearing the selection when changing tabes to avoid collision between features.
This commit is contained in:
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
|
||||||
|
abstract class AnalyticsDataLoadingStrategy {
|
||||||
|
void onCommunitySelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
);
|
||||||
|
void onSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel space,
|
||||||
|
);
|
||||||
|
void onChildSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel child,
|
||||||
|
);
|
||||||
|
void clearData(BuildContext context);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/energy_management_data_loading_strategy.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/occupancy_data_loading_strategy.dart';
|
||||||
|
|
||||||
|
abstract final class AnalyticsDataLoadingStrategyFactory {
|
||||||
|
const AnalyticsDataLoadingStrategyFactory._();
|
||||||
|
static AnalyticsDataLoadingStrategy getStrategy(AnalyticsPageTab tab) {
|
||||||
|
return switch (tab) {
|
||||||
|
AnalyticsPageTab.energyManagement => EnergyManagementDataLoadingStrategy(),
|
||||||
|
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.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/spaces_management/all_spaces/model/community_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
|
||||||
|
class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||||
|
@override
|
||||||
|
void onCommunitySelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnCommunitySelected(
|
||||||
|
community.uuid,
|
||||||
|
spaces,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel space,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceSelected(
|
||||||
|
community,
|
||||||
|
space.uuid ?? '',
|
||||||
|
space.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onChildSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel child,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceSelected(
|
||||||
|
community,
|
||||||
|
child.uuid ?? '',
|
||||||
|
child.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clearData(BuildContext context) {
|
||||||
|
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
||||||
|
FetchEnergyManagementDataHelper.clearAllData(context);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_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/spaces_management/all_spaces/model/community_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
|
||||||
|
class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||||
|
@override
|
||||||
|
void onCommunitySelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnCommunitySelected(
|
||||||
|
community.uuid,
|
||||||
|
spaces,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel space,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceSelected(
|
||||||
|
community,
|
||||||
|
space.uuid ?? '',
|
||||||
|
space.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onChildSpaceSelected(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel child,
|
||||||
|
) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceSelected(
|
||||||
|
community,
|
||||||
|
child.uuid ?? '',
|
||||||
|
child.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clearData(BuildContext context) {
|
||||||
|
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_devi
|
|||||||
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/power_clamp_info/remote_power_clamp_info_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/realtime_device_service/firebase_realtime_device_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/fake_total_energy_consumption_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/remote_total_energy_consumption_service.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||||
@ -32,7 +32,7 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => TotalEnergyConsumptionBloc(
|
create: (context) => TotalEnergyConsumptionBloc(
|
||||||
FakeTotalEnergyConsumptionService(),
|
RemoteTotalEnergyConsumptionService(HTTPService()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
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/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy_factory.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/sidebar/analytics_space_tree_view.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
|
||||||
|
|
||||||
class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
||||||
const AnalyticsCommunitiesSidebar({super.key});
|
const AnalyticsCommunitiesSidebar({super.key});
|
||||||
@ -13,32 +11,27 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Builder(
|
return Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final selectedTab = context.read<AnalyticsTabBloc>().state;
|
||||||
|
final strategy =
|
||||||
|
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||||
|
|
||||||
|
// Clear data when tab changes
|
||||||
|
strategy.clearData(context);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: SpaceTreeView(
|
child: AnalyticsSpaceTreeView(
|
||||||
title: const Text('Communities'),
|
onSelectCommunity: (community, spaces) {
|
||||||
shouldDisableDeselectingChildrenOfSelectedParent: true,
|
strategy.onCommunitySelected(context, community, spaces);
|
||||||
onSelect: () {
|
},
|
||||||
/// Necessary to wait for the state to update before fethcing the data.
|
onSelectSpace: (community, space) {
|
||||||
Future.delayed(const Duration(milliseconds: 100), () {
|
strategy.onSpaceSelected(context, community, space);
|
||||||
if (context.mounted) _loadBasedOnSelectedTab(context);
|
},
|
||||||
});
|
onSelectChildSpace: (community, child) {
|
||||||
|
strategy.onChildSpaceSelected(context, community, child);
|
||||||
},
|
},
|
||||||
isSide: false,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadBasedOnSelectedTab(BuildContext context) {
|
|
||||||
final selectedTab = context.read<AnalyticsTabBloc>().state;
|
|
||||||
return switch (selectedTab) {
|
|
||||||
AnalyticsPageTab.energyManagement =>
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context),
|
|
||||||
AnalyticsPageTab.occupancy =>
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context),
|
|
||||||
// ignore: unreachable_switch_case
|
|
||||||
_ => () {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ 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/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy_factory.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class AnalyticsPageTabButton extends StatelessWidget {
|
class AnalyticsPageTabButton extends StatelessWidget {
|
||||||
@ -17,9 +18,12 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
onPressed: () => context.read<AnalyticsTabBloc>().add(
|
onPressed: () {
|
||||||
|
AnalyticsDataLoadingStrategyFactory.getStrategy(tab).clearData(context);
|
||||||
|
context.read<AnalyticsTabBloc>().add(
|
||||||
UpdateAnalyticsTabEvent(tab),
|
UpdateAnalyticsTabEvent(tab),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
tab.title,
|
tab.title,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -51,17 +51,14 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
_buildAnimation(
|
Visibility(
|
||||||
child: Visibility(
|
visible: selectedTab == AnalyticsPageTab.energyManagement,
|
||||||
key: ValueKey(selectedTab),
|
child: const Expanded(
|
||||||
visible: selectedTab == AnalyticsPageTab.energyManagement,
|
flex: 2,
|
||||||
child: const Expanded(
|
child: FittedBox(
|
||||||
flex: 2,
|
fit: BoxFit.scaleDown,
|
||||||
child: FittedBox(
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
fit: BoxFit.scaleDown,
|
child: AnalyticsDateFilterButton(),
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
|
||||||
child: AnalyticsDateFilterButton(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,188 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
||||||
|
import 'package:syncrow_web/common/widgets/sidebar_communities_list.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/bloc/space_tree_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/view/custom_expansion.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class AnalyticsSpaceTreeView extends StatefulWidget {
|
||||||
|
const AnalyticsSpaceTreeView({
|
||||||
|
super.key,
|
||||||
|
this.onSelectCommunity,
|
||||||
|
this.onSelectSpace,
|
||||||
|
this.onSelectChildSpace,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(
|
||||||
|
CommunityModel community,
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
)? onSelectCommunity;
|
||||||
|
final void Function(
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel space,
|
||||||
|
)? onSelectSpace;
|
||||||
|
final void Function(
|
||||||
|
CommunityModel community,
|
||||||
|
SpaceModel child,
|
||||||
|
)? onSelectChildSpace;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AnalyticsSpaceTreeView> createState() => _AnalyticsSpaceTreeViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||||
|
late final ScrollController _scrollController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||||
|
final communities = state.searchQuery.isNotEmpty
|
||||||
|
? state.filteredCommunity
|
||||||
|
: state.communityList;
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.sizeOf(context).height,
|
||||||
|
decoration: const BoxDecoration(color: ColorsManager.whiteColors),
|
||||||
|
child: state is SpaceTreeLoadingState
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
child: const Text('Communities'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CustomSearchBar(
|
||||||
|
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
||||||
|
SearchQueryEvent(query),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Expanded(
|
||||||
|
child: state.isSearching
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: SidebarCommunitiesList(
|
||||||
|
onScrollToEnd: () {
|
||||||
|
if (!state.paginationIsLoading) {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
PaginationEvent(
|
||||||
|
state.paginationModel,
|
||||||
|
state.communityList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollController: _scrollController,
|
||||||
|
communities: communities,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return CustomExpansionTileSpaceTree(
|
||||||
|
title: communities[index].name,
|
||||||
|
isSelected: state.selectedCommunities
|
||||||
|
.contains(communities[index].uuid),
|
||||||
|
isSoldCheck: state.selectedCommunities
|
||||||
|
.contains(communities[index].uuid),
|
||||||
|
onExpansionChanged: () =>
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnCommunityExpanded(
|
||||||
|
communities[index].uuid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
isExpanded: state.expandedCommunities.contains(
|
||||||
|
communities[index].uuid,
|
||||||
|
),
|
||||||
|
onItemSelected: () => widget.onSelectCommunity?.call(
|
||||||
|
communities[index],
|
||||||
|
communities[index].spaces,
|
||||||
|
),
|
||||||
|
children: communities[index].spaces.map(
|
||||||
|
(space) {
|
||||||
|
return CustomExpansionTileSpaceTree(
|
||||||
|
title: space.name,
|
||||||
|
isExpanded:
|
||||||
|
state.expandedSpaces.contains(space.uuid),
|
||||||
|
onItemSelected: () =>
|
||||||
|
widget.onSelectSpace?.call(
|
||||||
|
communities[index],
|
||||||
|
space,
|
||||||
|
),
|
||||||
|
onExpansionChanged: () =>
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceExpanded(
|
||||||
|
communities[index].uuid,
|
||||||
|
space.uuid ?? '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
isSelected: state.selectedSpaces
|
||||||
|
.contains(space.uuid) ||
|
||||||
|
state.soldCheck.contains(space.uuid),
|
||||||
|
isSoldCheck:
|
||||||
|
state.soldCheck.contains(space.uuid),
|
||||||
|
children: _buildNestedSpaces(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
space,
|
||||||
|
communities[index],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildNestedSpaces(
|
||||||
|
BuildContext context,
|
||||||
|
SpaceTreeState state,
|
||||||
|
SpaceModel space,
|
||||||
|
CommunityModel community,
|
||||||
|
) {
|
||||||
|
return space.children.map((child) {
|
||||||
|
return CustomExpansionTileSpaceTree(
|
||||||
|
isSelected: state.selectedSpaces.contains(child.uuid) ||
|
||||||
|
state.soldCheck.contains(child.uuid),
|
||||||
|
isSoldCheck: state.soldCheck.contains(child.uuid),
|
||||||
|
title: child.name,
|
||||||
|
isExpanded: state.expandedSpaces.contains(child.uuid),
|
||||||
|
onItemSelected: () {
|
||||||
|
widget.onSelectChildSpace?.call(community, child);
|
||||||
|
},
|
||||||
|
onExpansionChanged: () {
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceExpanded(community.uuid, child.uuid ?? ''),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
children: _buildNestedSpaces(context, state, child, community),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
value.toString(),
|
(value + 1).toString(),
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.greyColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -70,7 +70,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
||||||
return touchedSpots.map((spot) {
|
return touchedSpots.map((spot) {
|
||||||
return LineTooltipItem(
|
return LineTooltipItem(
|
||||||
getToolTipLabel(spot.x, spot.y),
|
getToolTipLabel(spot.x + 1, spot.y),
|
||||||
const TextStyle(
|
const TextStyle(
|
||||||
color: ColorsManager.textPrimaryColor,
|
color: ColorsManager.textPrimaryColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
@ -25,7 +25,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTotalEnergyConsumption(context);
|
loadTotalEnergyConsumption(context, selectedDate: selectedDate);
|
||||||
loadEnergyConsumptionByPhases(context);
|
loadEnergyConsumptionByPhases(context);
|
||||||
loadEnergyConsumptionPerDevice(context);
|
loadEnergyConsumptionPerDevice(context);
|
||||||
return;
|
return;
|
||||||
@ -36,7 +36,8 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) return;
|
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) return;
|
||||||
|
|
||||||
FetchEnergyManagementDataHelper.fetchEnergyManagementData(context);
|
FetchEnergyManagementDataHelper.fetchEnergyManagementData(context,
|
||||||
|
selectedDate: DateTime.now());
|
||||||
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(context);
|
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(context);
|
||||||
context.read<PowerClampInfoBloc>().add(const ClearPowerClampInfoEvent());
|
context.read<PowerClampInfoBloc>().add(const ClearPowerClampInfoEvent());
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
||||||
@ -79,7 +80,8 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
|
|
||||||
final param = GetTotalEnergyConsumptionParam(
|
final param = GetTotalEnergyConsumptionParam(
|
||||||
spaceId: selectedCommunities.firstOrNull,
|
spaceId: selectedCommunities.firstOrNull,
|
||||||
startDate: selectedDate,
|
communityId: selectedCommunities.firstOrNull,
|
||||||
|
monthDate: selectedDate,
|
||||||
);
|
);
|
||||||
context.read<TotalEnergyConsumptionBloc>().add(
|
context.read<TotalEnergyConsumptionBloc>().add(
|
||||||
TotalEnergyConsumptionLoadEvent(param: param),
|
TotalEnergyConsumptionLoadEvent(param: param),
|
||||||
|
@ -23,10 +23,7 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
titlesData: EnergyManagementChartsHelper.titlesData(
|
titlesData: EnergyManagementChartsHelper.titlesData(context),
|
||||||
context,
|
|
||||||
leftTitlesInterval: 5000,
|
|
||||||
),
|
|
||||||
gridData: EnergyManagementChartsHelper.gridData(),
|
gridData: EnergyManagementChartsHelper.gridData(),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
class GetTotalEnergyConsumptionParam {
|
class GetTotalEnergyConsumptionParam {
|
||||||
final DateTime? startDate;
|
final DateTime? monthDate;
|
||||||
final DateTime? endDate;
|
|
||||||
final String? spaceId;
|
final String? spaceId;
|
||||||
|
final String? communityId;
|
||||||
|
|
||||||
const GetTotalEnergyConsumptionParam({
|
const GetTotalEnergyConsumptionParam({
|
||||||
this.startDate,
|
this.monthDate,
|
||||||
this.endDate,
|
|
||||||
this.spaceId,
|
this.spaceId,
|
||||||
|
this.communityId,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'startDate': startDate?.toIso8601String(),
|
'monthDate':
|
||||||
'endDate': endDate?.toIso8601String(),
|
'${monthDate?.year}-${monthDate?.month.toString().padLeft(2, '0')}',
|
||||||
'spaceId': spaceId,
|
if (communityId == null || communityId!.isEmpty) 'spaceUuid': spaceId,
|
||||||
|
'communityUuid': communityId,
|
||||||
|
'groupByDevice': false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/total_energy_consumption_service.dart';
|
|
||||||
|
|
||||||
class FakeTotalEnergyConsumptionService implements TotalEnergyConsumptionService {
|
|
||||||
@override
|
|
||||||
Future<List<EnergyDataModel>> load(
|
|
||||||
GetTotalEnergyConsumptionParam param,
|
|
||||||
) {
|
|
||||||
return Future.value(
|
|
||||||
List.generate(30, (index) {
|
|
||||||
return EnergyDataModel(
|
|
||||||
date: DateTime(2025, 1, index + 1),
|
|
||||||
value: 20000 + (index * 1000) % 5000,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,20 +14,37 @@ class RemoteTotalEnergyConsumptionService implements TotalEnergyConsumptionServi
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: 'endpoint',
|
path: '/power-clamp/historical',
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (data) {
|
queryParameters: param.toJson(),
|
||||||
final json = data as Map<String, dynamic>? ?? {};
|
expectedResponseModel: _TotalEnergyConsumptionResponseMapper.map,
|
||||||
final mappedData = json['data'] as List<dynamic>? ?? [];
|
|
||||||
return mappedData.map((e) {
|
|
||||||
final jsonData = e as Map<String, dynamic>;
|
|
||||||
return EnergyDataModel.fromJson(jsonData);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to load total energy consumption: $e');
|
throw Exception('Failed to load total energy consumption: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract final class _TotalEnergyConsumptionResponseMapper {
|
||||||
|
const _TotalEnergyConsumptionResponseMapper._();
|
||||||
|
|
||||||
|
static List<EnergyDataModel> map(dynamic data) {
|
||||||
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
|
final dailyData = json['data'] as List<dynamic>? ?? [];
|
||||||
|
|
||||||
|
return dailyData.map((dayData) {
|
||||||
|
final date = dayData['date'] as String;
|
||||||
|
final energyValue = double.tryParse(
|
||||||
|
dayData['total_energy_consumed_kw'] as String? ?? '0',
|
||||||
|
) ??
|
||||||
|
0.0;
|
||||||
|
|
||||||
|
return EnergyDataModel(
|
||||||
|
date: DateTime.parse(date),
|
||||||
|
value: energyValue,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user