mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-27 12:54:55 +00:00
Compare commits
27 Commits
SP-1530-FE
...
SP-1509-FE
| Author | SHA1 | Date | |
|---|---|---|---|
| 887ac58f40 | |||
| c709477500 | |||
| 63e7b3faa2 | |||
| 0e61e52bf8 | |||
| 7515b347ce | |||
| 3dfbcb5935 | |||
| 4fd4a9b5bf | |||
| 14fa1b355e | |||
| 78d4e58996 | |||
| 23b9cb5b78 | |||
| 401d0a9788 | |||
| ac2b0d3fac | |||
| 3be7a377c0 | |||
| e4ee456384 | |||
| f02c5d71ba | |||
| ad227febc1 | |||
| 4d9e57c8b5 | |||
| d1bb8da484 | |||
| 300f9ae358 | |||
| c1dab3400b | |||
| 46815585cb | |||
| 7f9d044f7e | |||
| 5645fb7826 | |||
| e8f7c29652 | |||
| c7fef11aec | |||
| ef29d78d70 | |||
| cd9941f544 |
13
lib/pages/analytics/models/analytics_device.dart
Normal file
13
lib/pages/analytics/models/analytics_device.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
class AnalyticsDevice {
|
||||||
|
const AnalyticsDevice({required this.name, required this.uuid});
|
||||||
|
|
||||||
|
final String uuid;
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
factory AnalyticsDevice.fromJson(Map<String, dynamic> json) {
|
||||||
|
return AnalyticsDevice(
|
||||||
|
uuid: json['uuid'] as String? ?? '',
|
||||||
|
name: json['name'] as String? ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,22 +1,28 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class OccupancyHeatMapModel extends Equatable {
|
class OccupancyHeatMapModel extends Equatable {
|
||||||
final DateTime date;
|
final String uuid;
|
||||||
|
|
||||||
final int occupancy;
|
final DateTime eventDate;
|
||||||
|
|
||||||
|
final int countTotalPresenceDetected;
|
||||||
|
|
||||||
const OccupancyHeatMapModel({
|
const OccupancyHeatMapModel({
|
||||||
required this.date,
|
required this.uuid,
|
||||||
required this.occupancy,
|
required this.eventDate,
|
||||||
|
required this.countTotalPresenceDetected,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory OccupancyHeatMapModel.fromJson(Map<String, dynamic> json) {
|
factory OccupancyHeatMapModel.fromJson(Map<String, dynamic> json) {
|
||||||
return OccupancyHeatMapModel(
|
return OccupancyHeatMapModel(
|
||||||
date: DateTime.parse(json['date'] as String),
|
uuid: json['uuid'] as String? ?? '',
|
||||||
occupancy: json['occupancy'] as int,
|
eventDate: DateTime.parse(
|
||||||
|
json['event_date'] as String? ?? '${DateTime.now()}',
|
||||||
|
),
|
||||||
|
countTotalPresenceDetected: json['count_total_presence_detected'] as int? ?? 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [date, occupancy];
|
List<Object?> get props => [uuid, eventDate, countTotalPresenceDetected];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service.dart';
|
||||||
|
|
||||||
|
part 'analytics_devices_event.dart';
|
||||||
|
part 'analytics_devices_state.dart';
|
||||||
|
|
||||||
|
class AnalyticsDevicesBloc
|
||||||
|
extends Bloc<AnalyticsDevicesEvent, AnalyticsDevicesState> {
|
||||||
|
AnalyticsDevicesBloc(
|
||||||
|
this._analyticsDevicesService,
|
||||||
|
) : super(const AnalyticsDevicesState()) {
|
||||||
|
on<LoadAnalyticsDevicesEvent>(_onLoadAnalyticsDevices);
|
||||||
|
on<SelectAnalyticsDeviceEvent>(_onSelectAnalyticsDevice);
|
||||||
|
on<ClearAnalyticsDeviceEvent>(_onClearAnalyticsDevice);
|
||||||
|
}
|
||||||
|
final AnalyticsDevicesService _analyticsDevicesService;
|
||||||
|
|
||||||
|
Future<void> _onLoadAnalyticsDevices(
|
||||||
|
LoadAnalyticsDevicesEvent event,
|
||||||
|
Emitter<AnalyticsDevicesState> emit,
|
||||||
|
) async {
|
||||||
|
emit(const AnalyticsDevicesState(status: AnalyticsDevicesStatus.loading));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final devices = await _analyticsDevicesService.getDevices(event.param);
|
||||||
|
emit(
|
||||||
|
AnalyticsDevicesState(
|
||||||
|
status: AnalyticsDevicesStatus.loaded,
|
||||||
|
devices: devices,
|
||||||
|
selectedDevice: devices.firstOrNull,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (devices.isNotEmpty) {
|
||||||
|
event.onSuccess(devices.first);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(
|
||||||
|
AnalyticsDevicesState(
|
||||||
|
status: AnalyticsDevicesStatus.failure,
|
||||||
|
errorMessage: e.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSelectAnalyticsDevice(
|
||||||
|
SelectAnalyticsDeviceEvent event,
|
||||||
|
Emitter<AnalyticsDevicesState> emit,
|
||||||
|
) {
|
||||||
|
emit(
|
||||||
|
AnalyticsDevicesState(
|
||||||
|
selectedDevice: event.device,
|
||||||
|
devices: state.devices,
|
||||||
|
errorMessage: state.errorMessage,
|
||||||
|
status: state.status,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onClearAnalyticsDevice(
|
||||||
|
ClearAnalyticsDeviceEvent event,
|
||||||
|
Emitter<AnalyticsDevicesState> emit,
|
||||||
|
) {
|
||||||
|
emit(const AnalyticsDevicesState());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
part of 'analytics_devices_bloc.dart';
|
||||||
|
|
||||||
|
sealed class AnalyticsDevicesEvent extends Equatable {
|
||||||
|
const AnalyticsDevicesEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class LoadAnalyticsDevicesEvent extends AnalyticsDevicesEvent {
|
||||||
|
const LoadAnalyticsDevicesEvent({required this.param, required this.onSuccess});
|
||||||
|
|
||||||
|
final GetAnalyticsDevicesParam param;
|
||||||
|
final void Function(AnalyticsDevice device) onSuccess;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [param];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SelectAnalyticsDeviceEvent extends AnalyticsDevicesEvent {
|
||||||
|
const SelectAnalyticsDeviceEvent(this.device);
|
||||||
|
|
||||||
|
final AnalyticsDevice device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [device];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ClearAnalyticsDeviceEvent extends AnalyticsDevicesEvent {
|
||||||
|
const ClearAnalyticsDeviceEvent();
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
part of 'analytics_devices_bloc.dart';
|
||||||
|
|
||||||
|
enum AnalyticsDevicesStatus { initial, loading, loaded, failure }
|
||||||
|
|
||||||
|
final class AnalyticsDevicesState extends Equatable {
|
||||||
|
const AnalyticsDevicesState({
|
||||||
|
this.status = AnalyticsDevicesStatus.initial,
|
||||||
|
this.devices = const [],
|
||||||
|
this.errorMessage,
|
||||||
|
this.selectedDevice,
|
||||||
|
});
|
||||||
|
|
||||||
|
final AnalyticsDevicesStatus status;
|
||||||
|
final List<AnalyticsDevice> devices;
|
||||||
|
final AnalyticsDevice? selectedDevice;
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [status, devices, errorMessage, selectedDevice];
|
||||||
|
}
|
||||||
@ -14,13 +14,25 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
) {
|
) {
|
||||||
|
// Add to space tree bloc first
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnCommunitySelected(
|
OnCommunitySelected(
|
||||||
community.uuid,
|
community.uuid,
|
||||||
spaces,
|
spaces,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
if (spaceTreeState.selectedCommunities.contains(community.uuid)) {
|
||||||
|
clearData(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||||
|
context,
|
||||||
|
communityId: community.uuid,
|
||||||
|
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -36,7 +48,19 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
|||||||
space.children,
|
space.children,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
if (spaceTreeState.selectedCommunities.contains(community.uuid) ||
|
||||||
|
spaceTreeState.selectedSpaces.contains(space.uuid)) {
|
||||||
|
clearData(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||||
|
context,
|
||||||
|
communityId: community.uuid,
|
||||||
|
spaceId: space.uuid ?? '',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -45,14 +69,7 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
SpaceModel child,
|
SpaceModel child,
|
||||||
) {
|
) {
|
||||||
context.read<SpaceTreeBloc>().add(
|
// Do nothing else as per original implementation
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
child.uuid ?? '',
|
|
||||||
child.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -17,10 +17,20 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnCommunitySelected(
|
OnCommunitySelected(
|
||||||
community.uuid,
|
community.uuid,
|
||||||
spaces,
|
spaces.isNotEmpty ? [spaces.first] : [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
if (spaceTreeState.selectedCommunities.contains(community.uuid)) {
|
||||||
|
clearData(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
|
context,
|
||||||
|
communityId: community.uuid,
|
||||||
|
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -29,14 +39,32 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
SpaceModel space,
|
SpaceModel space,
|
||||||
) {
|
) {
|
||||||
context.read<SpaceTreeBloc>().add(
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
OnSpaceSelected(
|
final selectedSpacesIds = spaceTreeBloc.state.selectedSpaces;
|
||||||
community,
|
final isSpaceSelected = selectedSpacesIds.contains(space.uuid);
|
||||||
space.uuid ?? '',
|
|
||||||
space.children,
|
if (selectedSpacesIds.isEmpty) {
|
||||||
),
|
spaceTreeBloc.add(OnCommunitySelected(community.uuid, [space]));
|
||||||
);
|
} else if (isSpaceSelected) {
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
spaceTreeBloc.add(const SpaceTreeClearSelectionEvent());
|
||||||
|
} else {
|
||||||
|
spaceTreeBloc
|
||||||
|
..add(const SpaceTreeClearSelectionEvent())
|
||||||
|
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||||
|
}
|
||||||
|
|
||||||
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
if (spaceTreeState.selectedCommunities.contains(community.uuid) ||
|
||||||
|
spaceTreeState.selectedSpaces.contains(space.uuid)) {
|
||||||
|
clearData(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
|
context,
|
||||||
|
communityId: community.uuid,
|
||||||
|
spaceId: space.uuid ?? '',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -45,18 +73,12 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
SpaceModel child,
|
SpaceModel child,
|
||||||
) {
|
) {
|
||||||
context.read<SpaceTreeBloc>().add(
|
// Do nothing
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
child.uuid ?? '',
|
|
||||||
child.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clearData(BuildContext context) {
|
void clearData(BuildContext context) {
|
||||||
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
||||||
|
FetchOccupancyDataHelper.clearAllData(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.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_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/widgets/analytics_communities_sidebar.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
|
||||||
@ -11,10 +12,13 @@ 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/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/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/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.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';
|
||||||
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/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/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/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/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/remote_total_energy_consumption_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/remote_total_energy_consumption_service.dart';
|
||||||
@ -23,9 +27,22 @@ 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';
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
|
|
||||||
class AnalyticsPage extends StatelessWidget {
|
class AnalyticsPage extends StatefulWidget {
|
||||||
const AnalyticsPage({super.key});
|
const AnalyticsPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AnalyticsPage> createState() => _AnalyticsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnalyticsPageState extends State<AnalyticsPage> {
|
||||||
|
late final HTTPService _httpService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_httpService = HTTPService();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
@ -35,7 +52,7 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => TotalEnergyConsumptionBloc(
|
create: (context) => TotalEnergyConsumptionBloc(
|
||||||
RemoteTotalEnergyConsumptionService(HTTPService()),
|
RemoteTotalEnergyConsumptionService(_httpService),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
@ -50,7 +67,7 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => PowerClampInfoBloc(
|
create: (context) => PowerClampInfoBloc(
|
||||||
RemotePowerClampInfoService(HTTPService()),
|
RemotePowerClampInfoService(_httpService),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider<RealtimeDeviceChangesBloc>(
|
BlocProvider<RealtimeDeviceChangesBloc>(
|
||||||
@ -60,9 +77,19 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocProvider(create: (context) => OccupancyBloc(FakeOccupacyService())),
|
BlocProvider(create: (context) => OccupancyBloc(FakeOccupacyService())),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => OccupancyHeatMapBloc(FakeOccupancyHeatMapService()),
|
create: (context) => OccupancyHeatMapBloc(
|
||||||
|
RemoteOccupancyHeatMapService(_httpService),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => AnalyticsDatePickerBloc()),
|
BlocProvider(create: (context) => AnalyticsDatePickerBloc()),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => AnalyticsDevicesBloc(
|
||||||
|
AnalyticsDevicesServiceDelegate(
|
||||||
|
RemoteOccupancyAnalyticsDevicesService(_httpService),
|
||||||
|
RemoteEnergyManagementAnalyticsDevicesService(_httpService),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const AnalyticsPageForm(),
|
child: const AnalyticsPageForm(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,29 +9,21 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Builder(
|
final selectedTab = context.watch<AnalyticsTabBloc>().state;
|
||||||
builder: (context) {
|
final strategy = AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||||
final selectedTab = context.read<AnalyticsTabBloc>().state;
|
|
||||||
final strategy =
|
|
||||||
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
|
||||||
|
|
||||||
// Clear data when tab changes
|
return Expanded(
|
||||||
strategy.clearData(context);
|
child: AnalyticsSpaceTreeView(
|
||||||
|
onSelectCommunity: (community, spaces) {
|
||||||
return Expanded(
|
strategy.onCommunitySelected(context, community, spaces);
|
||||||
child: AnalyticsSpaceTreeView(
|
},
|
||||||
onSelectCommunity: (community, spaces) {
|
onSelectSpace: (community, space) {
|
||||||
strategy.onCommunitySelected(context, community, spaces);
|
strategy.onSpaceSelected(context, community, space);
|
||||||
},
|
},
|
||||||
onSelectSpace: (community, space) {
|
onSelectChildSpace: (community, child) {
|
||||||
strategy.onSpaceSelected(context, community, space);
|
strategy.onChildSpaceSelected(context, community, child);
|
||||||
},
|
},
|
||||||
onSelectChildSpace: (community, child) {
|
),
|
||||||
strategy.onChildSpaceSelected(context, community, child);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_pa
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tab_button.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tab_button.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.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/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||||
@ -13,6 +14,7 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
return BlocBuilder<AnalyticsTabBloc, AnalyticsPageTab>(
|
return BlocBuilder<AnalyticsTabBloc, AnalyticsPageTab>(
|
||||||
buildWhen: (previous, current) => previous != current,
|
buildWhen: (previous, current) => previous != current,
|
||||||
builder: (context, selectedTab) => Column(
|
builder: (context, selectedTab) => Column(
|
||||||
@ -66,10 +68,14 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
context.read<AnalyticsDatePickerBloc>().add(
|
context.read<AnalyticsDatePickerBloc>().add(
|
||||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||||
);
|
);
|
||||||
FetchEnergyManagementDataHelper
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||||
.fetchEnergyManagementData(
|
|
||||||
context,
|
context,
|
||||||
selectedDate: value,
|
selectedDate: value,
|
||||||
|
communityId:
|
||||||
|
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||||
|
'',
|
||||||
|
spaceId:
|
||||||
|
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
selectedDate: context
|
selectedDate: context
|
||||||
|
|||||||
@ -1,66 +1,50 @@
|
|||||||
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/analytics_device.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_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/energy_consumption_by_phases/energy_consumption_by_phases_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/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/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';
|
||||||
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/blocs/total_energy_consumption/total_energy_consumption_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.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_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_energy_consumption_per_device_param.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_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 {
|
abstract final class FetchEnergyManagementDataHelper {
|
||||||
const FetchEnergyManagementDataHelper._();
|
const FetchEnergyManagementDataHelper._();
|
||||||
|
|
||||||
static void fetchEnergyManagementData(
|
// static const String _powerClampId = 'cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa';
|
||||||
|
static AnalyticsDevice? getSelectedDevice(BuildContext context) {
|
||||||
|
return context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadEnergyManagementData(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
required String communityId,
|
||||||
|
required String spaceId,
|
||||||
DateTime? selectedDate,
|
DateTime? selectedDate,
|
||||||
}) {
|
}) {
|
||||||
final (selectedCommunities, selectedSpaces) =
|
if (communityId.isEmpty && spaceId.isEmpty) {
|
||||||
getSelectedCommunitiesAndSpaces(context);
|
|
||||||
|
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
|
||||||
clearAllData(context);
|
clearAllData(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||||
|
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
|
||||||
loadTotalEnergyConsumption(context, selectedDate: datePickerState.monthlyDate);
|
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
||||||
loadEnergyConsumptionByPhases(
|
loadTotalEnergyConsumption(
|
||||||
context,
|
context,
|
||||||
selectedDate: datePickerState.monthlyDate,
|
selectedDate: selectedDate0,
|
||||||
|
communityId: communityId,
|
||||||
|
spaceId: spaceId,
|
||||||
);
|
);
|
||||||
|
loadEnergyConsumptionByPhases(context, selectedDate: selectedDate);
|
||||||
loadEnergyConsumptionPerDevice(context);
|
loadEnergyConsumptionPerDevice(context);
|
||||||
return;
|
loadRealtimeDeviceChanges(context);
|
||||||
}
|
loadPowerClampInfo(context);
|
||||||
|
|
||||||
static void loadEnergyManagementData(BuildContext context) {
|
|
||||||
final (selectedCommunities, selectedSpaces) =
|
|
||||||
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) return;
|
|
||||||
|
|
||||||
FetchEnergyManagementDataHelper.fetchEnergyManagementData(context,
|
|
||||||
selectedDate: DateTime.now());
|
|
||||||
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(context);
|
|
||||||
context.read<PowerClampInfoBloc>().add(const ClearPowerClampInfoEvent());
|
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
|
||||||
context.read<PowerClampInfoBloc>().add(
|
|
||||||
const ClearPowerClampInfoEvent(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
FetchEnergyManagementDataHelper.loadPowerClampInfo(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
static void loadEnergyConsumptionByPhases(
|
||||||
@ -79,13 +63,12 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
static void loadTotalEnergyConsumption(
|
static void loadTotalEnergyConsumption(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
DateTime? selectedDate,
|
DateTime? selectedDate,
|
||||||
|
required String communityId,
|
||||||
|
required String spaceId,
|
||||||
}) {
|
}) {
|
||||||
final (selectedCommunities, selectedSpaces) =
|
|
||||||
getSelectedCommunitiesAndSpaces(context);
|
|
||||||
|
|
||||||
final param = GetTotalEnergyConsumptionParam(
|
final param = GetTotalEnergyConsumptionParam(
|
||||||
spaceId: selectedCommunities.firstOrNull,
|
spaceId: spaceId,
|
||||||
communityId: selectedCommunities.firstOrNull,
|
communityId: communityId,
|
||||||
monthDate: selectedDate,
|
monthDate: selectedDate,
|
||||||
);
|
);
|
||||||
context.read<TotalEnergyConsumptionBloc>().add(
|
context.read<TotalEnergyConsumptionBloc>().add(
|
||||||
@ -101,25 +84,58 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void loadPowerClampInfo(BuildContext context) {
|
static void loadPowerClampInfo(BuildContext context) {
|
||||||
context.read<PowerClampInfoBloc>().add(
|
final selectedDevice = getSelectedDevice(context);
|
||||||
const LoadPowerClampInfoEvent('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
|
if (selectedDevice case final AnalyticsDevice device) {
|
||||||
|
context.read<PowerClampInfoBloc>().add(
|
||||||
|
LoadPowerClampInfoEvent(device.uuid),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadRealtimeDeviceChanges(
|
||||||
|
BuildContext context, {
|
||||||
|
String? deviceUuid,
|
||||||
|
}) {
|
||||||
|
final selectedDevice = getSelectedDevice(context);
|
||||||
|
|
||||||
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
|
RealtimeDeviceChangesStarted(deviceUuid ?? selectedDevice?.uuid ?? ''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadRealtimeDeviceChanges(BuildContext context) {
|
static void loadAnalyticsDevices(
|
||||||
context.read<RealtimeDeviceChangesBloc>().add(
|
BuildContext context, {
|
||||||
const RealtimeDeviceChangesStarted('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
|
required String communityUuid,
|
||||||
|
required String spaceUuid,
|
||||||
|
}) {
|
||||||
|
context.read<AnalyticsDevicesBloc>().add(
|
||||||
|
LoadAnalyticsDevicesEvent(
|
||||||
|
onSuccess: (device) {
|
||||||
|
context.read<PowerClampInfoBloc>().add(
|
||||||
|
LoadPowerClampInfoEvent(device.uuid),
|
||||||
|
);
|
||||||
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
|
RealtimeDeviceChangesStarted(device.uuid),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
param: GetAnalyticsDevicesParam(
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
spaceUuid: spaceUuid,
|
||||||
|
deviceTypes: ['PC'],
|
||||||
|
requestType: AnalyticsDeviceRequestType.energyManagement,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearAllData(BuildContext context) {
|
static void clearAllData(BuildContext context) {
|
||||||
|
context.read<PowerClampInfoBloc>().add(
|
||||||
|
const ClearPowerClampInfoEvent(),
|
||||||
|
);
|
||||||
context.read<RealtimeDeviceChangesBloc>().add(
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
const RealtimeDeviceChangesClosed(),
|
const RealtimeDeviceChangesClosed(),
|
||||||
);
|
);
|
||||||
|
|
||||||
context.read<PowerClampInfoBloc>().add(
|
|
||||||
const ClearPowerClampInfoEvent(),
|
|
||||||
);
|
|
||||||
context.read<EnergyConsumptionPerDeviceBloc>().add(
|
context.read<EnergyConsumptionPerDeviceBloc>().add(
|
||||||
const ClearEnergyConsumptionPerDeviceEvent(),
|
const ClearEnergyConsumptionPerDeviceEvent(),
|
||||||
);
|
);
|
||||||
@ -131,5 +147,6 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
context.read<EnergyConsumptionByPhasesBloc>().add(
|
context.read<EnergyConsumptionByPhasesBloc>().add(
|
||||||
const ClearEnergyConsumptionByPhasesEvent(),
|
const ClearEnergyConsumptionByPhasesEvent(),
|
||||||
);
|
);
|
||||||
|
context.read<AnalyticsDevicesBloc>().add(const ClearAnalyticsDeviceEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
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/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.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/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
|
||||||
class AnalyticsEnergyManagementView extends StatefulWidget {
|
class AnalyticsEnergyManagementView extends StatefulWidget {
|
||||||
const AnalyticsEnergyManagementView({super.key});
|
const AnalyticsEnergyManagementView({super.key});
|
||||||
@ -16,7 +18,14 @@ class _AnalyticsEnergyManagementViewState
|
|||||||
extends State<AnalyticsEnergyManagementView> {
|
extends State<AnalyticsEnergyManagementView> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
|
final communityId = spaceTreeBloc.state.selectedCommunities.firstOrNull;
|
||||||
|
final spaceId = spaceTreeBloc.state.selectedSpaces.firstOrNull;
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||||
|
context,
|
||||||
|
communityId: communityId ?? '',
|
||||||
|
spaceId: spaceId ?? '',
|
||||||
|
);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class AnalyticsDeviceDropdown extends StatelessWidget {
|
||||||
|
const AnalyticsDeviceDropdown({required this.onChanged, super.key});
|
||||||
|
|
||||||
|
final ValueChanged<AnalyticsDevice> onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<AnalyticsDevicesBloc, AnalyticsDevicesState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Visibility(
|
||||||
|
visible: state.devices.isNotEmpty,
|
||||||
|
replacement: _buildNoDevicesFound(context),
|
||||||
|
child: _buildDevicesDropdown(context, state),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const _defaultPadding = EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildNoDevicesFound(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: _defaultPadding,
|
||||||
|
child: Text(
|
||||||
|
'no devices found',
|
||||||
|
style: _getTextStyle(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) {
|
||||||
|
return DropdownButton<AnalyticsDevice?>(
|
||||||
|
value: state.selectedDevice,
|
||||||
|
isDense: true,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
dropdownColor: ColorsManager.whiteColors,
|
||||||
|
underline: const SizedBox.shrink(),
|
||||||
|
icon: const RotatedBox(
|
||||||
|
quarterTurns: 1,
|
||||||
|
child: Icon(Icons.chevron_right, size: 16),
|
||||||
|
),
|
||||||
|
style: _getTextStyle(context),
|
||||||
|
padding: _defaultPadding,
|
||||||
|
items: state.devices.map((e) {
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: e,
|
||||||
|
child: Text(e.name),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value case final AnalyticsDevice device) {
|
||||||
|
context.read<AnalyticsDevicesBloc>().add(
|
||||||
|
SelectAnalyticsDeviceEvent(device),
|
||||||
|
);
|
||||||
|
onChanged.call(device);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle? _getTextStyle(BuildContext context) {
|
||||||
|
return context.textTheme.labelSmall?.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontSize: 14,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class PowerClampEnergyDataDeviceDropdown extends StatelessWidget {
|
|
||||||
const PowerClampEnergyDataDeviceDropdown({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.greyColor,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: DropdownButton<String>(
|
|
||||||
value: 'Device 1',
|
|
||||||
isDense: true,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
dropdownColor: ColorsManager.whiteColors,
|
|
||||||
underline: const SizedBox.shrink(),
|
|
||||||
icon: const RotatedBox(
|
|
||||||
quarterTurns: 1,
|
|
||||||
child: Icon(Icons.chevron_right, size: 16),
|
|
||||||
),
|
|
||||||
style: context.textTheme.labelSmall?.copyWith(
|
|
||||||
color: ColorsManager.textPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
|
||||||
horizontal: 20,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
items: [
|
|
||||||
for (var i = 1; i < 10; i++)
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Device $i',
|
|
||||||
child: Text(
|
|
||||||
'Device $i',
|
|
||||||
style: context.textTheme.labelSmall?.copyWith(
|
|
||||||
color: ColorsManager.textPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,12 @@
|
|||||||
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_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';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart_box.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart_box.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_device_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart';
|
||||||
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';
|
||||||
@ -50,7 +52,8 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
state.powerClampModel?.productUuid ?? 'N/A',
|
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ??
|
||||||
|
'N/A',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
@ -107,7 +110,7 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 3,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
@ -122,11 +125,19 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const Expanded(
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
child: PowerClampEnergyDataDeviceDropdown(),
|
child: AnalyticsDeviceDropdown(
|
||||||
|
onChanged: (value) {
|
||||||
|
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
||||||
|
context,
|
||||||
|
deviceUuid: value.uuid,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,38 +1,40 @@
|
|||||||
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/analytics_device.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_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';
|
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/helpers/fetch_energy_management_data_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/occupancy_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/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
||||||
|
|
||||||
abstract final class FetchOccupancyDataHelper {
|
abstract final class FetchOccupancyDataHelper {
|
||||||
const FetchOccupancyDataHelper._();
|
const FetchOccupancyDataHelper._();
|
||||||
|
|
||||||
static void loadOccupancyData(BuildContext context) {
|
static void loadOccupancyData(
|
||||||
final (selectedCommunities, selectedSpaces) =
|
BuildContext context, {
|
||||||
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
required String communityId,
|
||||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
required String spaceId,
|
||||||
context.read<OccupancyBloc>().add(
|
}) {
|
||||||
const ClearOccupancyEvent(),
|
if (communityId.isEmpty && spaceId.isEmpty) {
|
||||||
);
|
clearAllData(context);
|
||||||
context.read<OccupancyHeatMapBloc>().add(
|
|
||||||
const ClearOccupancyHeatMapEvent(),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||||
|
|
||||||
|
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
||||||
|
final selectedDevice = context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||||
|
|
||||||
context.read<OccupancyBloc>().add(
|
context.read<OccupancyBloc>().add(
|
||||||
LoadOccupancyEvent(
|
LoadOccupancyEvent(
|
||||||
GetOccupancyParam(
|
GetOccupancyParam(
|
||||||
monthDate:
|
monthDate:
|
||||||
'${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}',
|
'${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}',
|
||||||
spaceUuid: selectedSpaces.firstOrNull,
|
spaceUuid: spaceId,
|
||||||
communityUuid: selectedCommunities.first,
|
communityUuid: communityId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -40,18 +42,56 @@ abstract final class FetchOccupancyDataHelper {
|
|||||||
context.read<OccupancyHeatMapBloc>().add(
|
context.read<OccupancyHeatMapBloc>().add(
|
||||||
LoadOccupancyHeatMapEvent(
|
LoadOccupancyHeatMapEvent(
|
||||||
GetOccupancyHeatMapParam(
|
GetOccupancyHeatMapParam(
|
||||||
spaceId: selectedSpaces.isNotEmpty ? selectedSpaces.first : '',
|
spaceUuid: spaceId,
|
||||||
communityId:
|
|
||||||
selectedCommunities.isNotEmpty ? selectedCommunities.first : '',
|
|
||||||
year: datePickerState.yearlyDate,
|
year: datePickerState.yearlyDate,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
context.read<RealtimeDeviceChangesBloc>()
|
if (selectedDevice case final AnalyticsDevice device) {
|
||||||
..add(const RealtimeDeviceChangesClosed())
|
context.read<RealtimeDeviceChangesBloc>()
|
||||||
..add(
|
..add(const RealtimeDeviceChangesClosed())
|
||||||
const RealtimeDeviceChangesStarted('14fe6e7e-47af-4a07-ae0a-7c4a26ef8135'),
|
..add(
|
||||||
);
|
RealtimeDeviceChangesStarted(device.uuid),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadAnalyticsDevices(
|
||||||
|
BuildContext context, {
|
||||||
|
required String communityUuid,
|
||||||
|
required String spaceUuid,
|
||||||
|
}) {
|
||||||
|
context.read<AnalyticsDevicesBloc>().add(
|
||||||
|
LoadAnalyticsDevicesEvent(
|
||||||
|
param: GetAnalyticsDevicesParam(
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
spaceUuid: spaceUuid,
|
||||||
|
deviceTypes: ['WPS', 'CPS'],
|
||||||
|
requestType: AnalyticsDeviceRequestType.occupancy,
|
||||||
|
),
|
||||||
|
onSuccess: (device) {
|
||||||
|
context.read<RealtimeDeviceChangesBloc>()
|
||||||
|
..add(const RealtimeDeviceChangesClosed())
|
||||||
|
..add(RealtimeDeviceChangesStarted(device.uuid));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearAllData(BuildContext context) {
|
||||||
|
context.read<OccupancyBloc>().add(
|
||||||
|
const ClearOccupancyEvent(),
|
||||||
|
);
|
||||||
|
context.read<OccupancyHeatMapBloc>().add(
|
||||||
|
const ClearOccupancyHeatMapEvent(),
|
||||||
|
);
|
||||||
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
|
const RealtimeDeviceChangesClosed(),
|
||||||
|
);
|
||||||
|
|
||||||
|
context.read<AnalyticsDevicesBloc>().add(
|
||||||
|
const ClearAnalyticsDeviceEvent(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_end_side_bar.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_end_side_bar.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart';
|
||||||
|
|
||||||
class AnalyticsOccupancyView extends StatefulWidget {
|
class AnalyticsOccupancyView extends StatelessWidget {
|
||||||
const AnalyticsOccupancyView({super.key});
|
const AnalyticsOccupancyView({super.key});
|
||||||
|
|
||||||
static const _padding = EdgeInsetsDirectional.all(32);
|
static const _padding = EdgeInsetsDirectional.all(32);
|
||||||
|
|
||||||
@override
|
|
||||||
State<AnalyticsOccupancyView> createState() => _AnalyticsOccupancyViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final height = MediaQuery.sizeOf(context).height;
|
final height = MediaQuery.sizeOf(context).height;
|
||||||
@ -28,7 +16,7 @@ class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
|||||||
final isMediumOrLess = constraints.maxWidth <= 900;
|
final isMediumOrLess = constraints.maxWidth <= 900;
|
||||||
if (isMediumOrLess) {
|
if (isMediumOrLess) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: AnalyticsOccupancyView._padding,
|
padding: _padding,
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 32,
|
spacing: 32,
|
||||||
children: [
|
children: [
|
||||||
@ -42,7 +30,7 @@ class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
|||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: AnalyticsOccupancyView._padding,
|
padding: _padding,
|
||||||
height: height * 0.9,
|
height: height * 0.9,
|
||||||
child: const Row(
|
child: const Row(
|
||||||
spacing: 32,
|
spacing: 32,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/oc
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart.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/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class OccupancyChartBox extends StatelessWidget {
|
class OccupancyChartBox extends StatelessWidget {
|
||||||
@ -14,6 +15,7 @@ class OccupancyChartBox extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||||
return BlocBuilder<OccupancyBloc, OccupancyState>(
|
return BlocBuilder<OccupancyBloc, OccupancyState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -45,7 +47,11 @@ class OccupancyChartBox extends StatelessWidget {
|
|||||||
context.read<AnalyticsDatePickerBloc>().add(
|
context.read<AnalyticsDatePickerBloc>().add(
|
||||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||||
);
|
);
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
|
context,
|
||||||
|
communityId: spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||||
|
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
selectedDate: context
|
selectedDate: context
|
||||||
.watch<AnalyticsDatePickerBloc>()
|
.watch<AnalyticsDatePickerBloc>()
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
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_devices/analytics_devices_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';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_device_dropdown.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.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';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class OccupancyEndSideBar extends StatelessWidget {
|
class OccupancyEndSideBar extends StatelessWidget {
|
||||||
const OccupancyEndSideBar({super.key});
|
const OccupancyEndSideBar({super.key});
|
||||||
@ -37,7 +38,8 @@ class OccupancyEndSideBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
(const Uuid().v4()),
|
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ??
|
||||||
|
'N/A',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
@ -105,7 +107,7 @@ class OccupancyEndSideBar extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 3,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
@ -120,11 +122,18 @@ class OccupancyEndSideBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const Expanded(
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
child: PowerClampEnergyDataDeviceDropdown(),
|
child: AnalyticsDeviceDropdown(
|
||||||
|
onChanged: (value) =>
|
||||||
|
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
||||||
|
context,
|
||||||
|
deviceUuid: value.uuid,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -15,7 +15,7 @@ class OccupancyHeatMap extends StatelessWidget {
|
|||||||
static const _totalWeeks = 53;
|
static const _totalWeeks = 53;
|
||||||
|
|
||||||
int get _maxValue => heatMapData.isNotEmpty
|
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;
|
: 0;
|
||||||
|
|
||||||
DateTime _getStartingDate() {
|
DateTime _getStartingDate() {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.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/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class OccupancyHeatMapBox extends StatelessWidget {
|
class OccupancyHeatMapBox extends StatelessWidget {
|
||||||
@ -14,6 +15,7 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||||
return BlocBuilder<OccupancyHeatMapBloc, OccupancyHeatMapState>(
|
return BlocBuilder<OccupancyHeatMapBloc, OccupancyHeatMapState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -45,7 +47,12 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
|||||||
context.read<AnalyticsDatePickerBloc>().add(
|
context.read<AnalyticsDatePickerBloc>().add(
|
||||||
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
||||||
);
|
);
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
|
context,
|
||||||
|
communityId:
|
||||||
|
spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||||
|
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
datePickerType: DatePickerType.year,
|
datePickerType: DatePickerType.year,
|
||||||
selectedDate: context
|
selectedDate: context
|
||||||
@ -61,7 +68,10 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: OccupancyHeatMap(
|
child: OccupancyHeatMap(
|
||||||
heatMapData: state.heatMapData.asMap().map(
|
heatMapData: state.heatMapData.asMap().map(
|
||||||
(_, value) => MapEntry(value.date, value.occupancy),
|
(_, value) => MapEntry(
|
||||||
|
value.eventDate,
|
||||||
|
value.countTotalPresenceDetected,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
22
lib/pages/analytics/params/get_analytics_devices_param.dart
Normal file
22
lib/pages/analytics/params/get_analytics_devices_param.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
enum AnalyticsDeviceRequestType { energyManagement, occupancy }
|
||||||
|
|
||||||
|
class GetAnalyticsDevicesParam {
|
||||||
|
final String? spaceUuid;
|
||||||
|
final List<String> deviceTypes;
|
||||||
|
final String? communityUuid;
|
||||||
|
final AnalyticsDeviceRequestType requestType;
|
||||||
|
|
||||||
|
const GetAnalyticsDevicesParam({
|
||||||
|
required this.requestType,
|
||||||
|
required this.spaceUuid,
|
||||||
|
required this.deviceTypes,
|
||||||
|
required this.communityUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
if (spaceUuid != null) 'spaceUuid': spaceUuid,
|
||||||
|
if (communityUuid != null) 'communityUuid': communityUuid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,13 @@
|
|||||||
class GetOccupancyHeatMapParam {
|
class GetOccupancyHeatMapParam {
|
||||||
final DateTime year;
|
final DateTime year;
|
||||||
final String communityId;
|
final String spaceUuid;
|
||||||
final String spaceId;
|
|
||||||
|
|
||||||
const GetOccupancyHeatMapParam({
|
const GetOccupancyHeatMapParam({
|
||||||
required this.year,
|
required this.year,
|
||||||
required this.communityId,
|
required this.spaceUuid,
|
||||||
required this.spaceId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {'year': year.year};
|
||||||
'year': year.toIso8601String(),
|
|
||||||
'communityId': communityId,
|
|
||||||
'spaceId': spaceId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
|
|
||||||
|
abstract interface class AnalyticsDevicesService {
|
||||||
|
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service.dart';
|
||||||
|
|
||||||
|
class AnalyticsDevicesServiceDelegate implements AnalyticsDevicesService {
|
||||||
|
const AnalyticsDevicesServiceDelegate(
|
||||||
|
this._occupancyService,
|
||||||
|
this._energyManagementService,
|
||||||
|
);
|
||||||
|
|
||||||
|
final AnalyticsDevicesService _occupancyService;
|
||||||
|
final AnalyticsDevicesService _energyManagementService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<AnalyticsDevice>> getDevices(
|
||||||
|
GetAnalyticsDevicesParam param,
|
||||||
|
) {
|
||||||
|
return switch (param.requestType) {
|
||||||
|
AnalyticsDeviceRequestType.occupancy => _occupancyService.getDevices(param),
|
||||||
|
AnalyticsDeviceRequestType.energyManagement =>
|
||||||
|
_energyManagementService.getDevices(param),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
|
final class RemoteEnergyManagementAnalyticsDevicesService
|
||||||
|
implements AnalyticsDevicesService {
|
||||||
|
const RemoteEnergyManagementAnalyticsDevicesService(this._httpService);
|
||||||
|
|
||||||
|
final HTTPService _httpService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||||
|
try {
|
||||||
|
final response = await _httpService.get(
|
||||||
|
path: '/devices-space-community/recursive-child',
|
||||||
|
queryParameters: param.toJson()
|
||||||
|
..addAll({'productType': param.deviceTypes.first}),
|
||||||
|
expectedResponseModel: (response) {
|
||||||
|
final json = response as Map<String, dynamic>;
|
||||||
|
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
||||||
|
|
||||||
|
final result = dailyData.map(
|
||||||
|
(json) => AnalyticsDevice.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.toList();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to load total energy consumption: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
|
class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService {
|
||||||
|
const RemoteOccupancyAnalyticsDevicesService(this._httpService);
|
||||||
|
|
||||||
|
final HTTPService _httpService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||||
|
try {
|
||||||
|
final requests = await Future.wait<List<AnalyticsDevice>>(
|
||||||
|
param.deviceTypes.map((e) {
|
||||||
|
final mappedParam = GetAnalyticsDevicesParam(
|
||||||
|
requestType: AnalyticsDeviceRequestType.occupancy,
|
||||||
|
spaceUuid: param.spaceUuid,
|
||||||
|
deviceTypes: [e],
|
||||||
|
communityUuid: param.communityUuid,
|
||||||
|
);
|
||||||
|
return _makeRequest(mappedParam);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = requests.map((e) => e.first).toList();
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to load total energy consumption: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<AnalyticsDevice>> _makeRequest(GetAnalyticsDevicesParam param) async {
|
||||||
|
try {
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID();
|
||||||
|
|
||||||
|
final response = await _httpService.get(
|
||||||
|
path:
|
||||||
|
'/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}/devices',
|
||||||
|
queryParameters: {
|
||||||
|
'requestType': param.requestType.name,
|
||||||
|
'communityUuid': param.communityUuid,
|
||||||
|
'spaceUuid': param.spaceUuid,
|
||||||
|
'productType': param.deviceTypes.first,
|
||||||
|
},
|
||||||
|
expectedResponseModel: (response) {
|
||||||
|
final json = response as Map<String, dynamic>;
|
||||||
|
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
||||||
|
|
||||||
|
final result = dailyData.map(
|
||||||
|
(json) => AnalyticsDevice.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
return result.toList();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<List<OccupancyHeatMapModel>> 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<OccupancyHeatMapModel> data = List.generate(
|
|
||||||
daysInYear,
|
|
||||||
(index) => OccupancyHeatMapModel(
|
|
||||||
date: startOfYear.add(Duration(days: index)),
|
|
||||||
occupancy: ((index + 1) * 10) % 100,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<List<OccupancyHeatMapModel>> 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<String, dynamic>;
|
||||||
|
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
||||||
|
|
||||||
|
final result = dailyData.map(
|
||||||
|
(json) => OccupancyHeatMapModel.fromJson(json as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.toList();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to load total energy consumption:');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@ class AnalyticsErrorWidget extends StatelessWidget {
|
|||||||
return Visibility(
|
return Visibility(
|
||||||
visible: errorMessage != null || (errorMessage?.isNotEmpty ?? false),
|
visible: errorMessage != null || (errorMessage?.isNotEmpty ?? false),
|
||||||
child: Text(
|
child: Text(
|
||||||
'$errorMessage ?? "Something went wrong"',
|
errorMessage ?? 'Something went wrong',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
|||||||
@ -536,7 +536,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// 'entityId': 'tab_to_run',
|
// 'entityId': 'tab_to_run',
|
||||||
// 'uniqueCustomId': const Uuid().v4(),
|
// 'uniqueCustomId': const Uuid().v4(),
|
||||||
// 'deviceId': 'tab_to_run',
|
// 'deviceId': 'tab_to_run',
|
||||||
// 'title': 'Tab to run',
|
// 'title': 'Tap to run',
|
||||||
// 'productType': 'tab_to_run',
|
// 'productType': 'tab_to_run',
|
||||||
// 'imagePath': Assets.tabToRun,
|
// 'imagePath': Assets.tabToRun,
|
||||||
// }
|
// }
|
||||||
@ -771,7 +771,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
'entityId': 'tab_to_run',
|
'entityId': 'tab_to_run',
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
'uniqueCustomId': const Uuid().v4(),
|
||||||
'deviceId': 'tab_to_run',
|
'deviceId': 'tab_to_run',
|
||||||
'title': 'Tab to run',
|
'title': 'Tap to run',
|
||||||
'productType': 'tab_to_run',
|
'productType': 'tab_to_run',
|
||||||
'imagePath': Assets.tabToRun,
|
'imagePath': Assets.tabToRun,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,7 +162,7 @@ class SaveRoutineHelper {
|
|||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
title: const Text('Tab to run'),
|
title: const Text('Tap to run'),
|
||||||
),
|
),
|
||||||
if (state.isAutomation)
|
if (state.isAutomation)
|
||||||
...state.ifItems.map((item) {
|
...state.ifItems.map((item) {
|
||||||
|
|||||||
@ -29,11 +29,11 @@ class ConditionsRoutinesDevicesView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
DraggableCard(
|
DraggableCard(
|
||||||
imagePath: Assets.tabToRun,
|
imagePath: Assets.tabToRun,
|
||||||
title: 'Tab to run',
|
title: 'Tap to run',
|
||||||
deviceData: {
|
deviceData: {
|
||||||
'deviceId': 'tab_to_run',
|
'deviceId': 'tab_to_run',
|
||||||
'type': 'trigger',
|
'type': 'trigger',
|
||||||
'name': 'Tab to run',
|
'name': 'Tap to run',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
DraggableCard(
|
DraggableCard(
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class IfContainer extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
DraggableCard(
|
DraggableCard(
|
||||||
imagePath: Assets.tabToRun,
|
imagePath: Assets.tabToRun,
|
||||||
title: 'Tab to run',
|
title: 'Tap to run',
|
||||||
deviceData: {},
|
deviceData: {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildListTitle(context, "Scenes (Tab to Run)"),
|
_buildListTitle(context, "Scenes (Tap to Run)"),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: state.scenes.isNotEmpty,
|
visible: state.scenes.isNotEmpty,
|
||||||
|
|||||||
Reference in New Issue
Block a user