mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-26 17:54:55 +00:00
formatted all files.
This commit is contained in:
@ -1,5 +1,3 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DashedBorderPainter extends CustomPainter {
|
||||
@ -20,27 +18,28 @@ class DashedBorderPainter extends CustomPainter {
|
||||
..strokeWidth = 0.5
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final Path topPath = Path()
|
||||
final topPath = Path()
|
||||
..moveTo(0, 0)
|
||||
..lineTo(size.width, 0);
|
||||
|
||||
final Path bottomPath = Path()
|
||||
final bottomPath = Path()
|
||||
..moveTo(0, size.height)
|
||||
..lineTo(size.width, size.height);
|
||||
|
||||
final dashedTopPath = _createDashedPath(topPath, dashWidth, dashSpace);
|
||||
final dashedBottomPath = _createDashedPath(bottomPath, dashWidth, dashSpace);
|
||||
final dashedBottomPath =
|
||||
_createDashedPath(bottomPath, dashWidth, dashSpace);
|
||||
|
||||
canvas.drawPath(dashedTopPath, paint);
|
||||
canvas.drawPath(dashedBottomPath, paint);
|
||||
}
|
||||
|
||||
Path _createDashedPath(Path source, double dashWidth, double dashSpace) {
|
||||
final Path dashedPath = Path();
|
||||
for (PathMetric pathMetric in source.computeMetrics()) {
|
||||
double distance = 0.0;
|
||||
final dashedPath = Path();
|
||||
for (final pathMetric in source.computeMetrics()) {
|
||||
var distance = 0.0;
|
||||
while (distance < pathMetric.length) {
|
||||
final double nextDistance = distance + dashWidth;
|
||||
final nextDistance = distance + dashWidth;
|
||||
dashedPath.addPath(
|
||||
pathMetric.extractPath(distance, nextDistance),
|
||||
Offset.zero,
|
||||
|
||||
@ -16,4 +16,4 @@ extension GetMonthNameFromNumber on num {
|
||||
_ => 'N/A'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ class AirQualityDataModel extends Equatable {
|
||||
return AirQualityDataModel(
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
data: (json['data'] as List<dynamic>)
|
||||
.map((e) => AirQualityPercentageData.fromJson(e as Map<String, dynamic>))
|
||||
.map((e) =>
|
||||
AirQualityPercentageData.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
@ -46,7 +47,7 @@ class AirQualityPercentageData extends Equatable {
|
||||
|
||||
factory AirQualityPercentageData.fromJson(Map<String, dynamic> json) {
|
||||
return AirQualityPercentageData(
|
||||
type: json['type'] as String? ?? '',
|
||||
type: json['type'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
percentage: (json['percentage'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
|
||||
@ -36,11 +36,14 @@ class AnalyticsDevice {
|
||||
deviceTuyaUuid: json['deviceTuyaUuid'] as String?,
|
||||
isActive: json['isActive'] as bool?,
|
||||
productDevice: json['productDevice'] != null
|
||||
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
|
||||
? ProductDevice.fromJson(
|
||||
json['productDevice'] as Map<String, dynamic>)
|
||||
: null,
|
||||
spaceUuid: json['spaceUuid'] as String?,
|
||||
latitude: json['lat'] != null ? double.parse(json['lat'] as String) : null,
|
||||
longitude: json['lon'] != null ? double.parse(json['lon'] as String) : null,
|
||||
latitude:
|
||||
json['lat'] != null ? double.parse(json['lat'] as String) : null,
|
||||
longitude:
|
||||
json['lon'] != null ? double.parse(json['lon'] as String) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ class Occupacy extends Equatable {
|
||||
|
||||
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
||||
return Occupacy(
|
||||
date: DateTime.parse(json['event_date'] as String? ?? '${DateTime.now()}'),
|
||||
date:
|
||||
DateTime.parse(json['event_date'] as String? ?? '${DateTime.now()}'),
|
||||
occupancy: (json['occupancy_percentage'] ?? 0).toString(),
|
||||
spaceUuid: json['space_uuid'] as String? ?? '',
|
||||
occupiedSeconds: json['occupied_seconds'] as int? ?? 0,
|
||||
|
||||
@ -19,7 +19,8 @@ class OccupancyHeatMapModel extends Equatable {
|
||||
eventDate: DateTime.parse(
|
||||
json['event_date'] as String? ?? '${DateTime.now()}',
|
||||
),
|
||||
countTotalPresenceDetected: json['count_total_presence_detected'] as int? ?? 0,
|
||||
countTotalPresenceDetected:
|
||||
json['count_total_presence_detected'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,8 @@ class AirQualityDistributionBloc
|
||||
state.copyWith(
|
||||
status: AirQualityDistributionStatus.success,
|
||||
chartData: result,
|
||||
filteredChartData: _arrangeChartDataByType(result, state.selectedAqiType),
|
||||
filteredChartData:
|
||||
_arrangeChartDataByType(result, state.selectedAqiType),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
@ -61,7 +62,8 @@ class AirQualityDistributionBloc
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedAqiType: event.aqiType,
|
||||
filteredChartData: _arrangeChartDataByType(state.chartData, event.aqiType),
|
||||
filteredChartData:
|
||||
_arrangeChartDataByType(state.chartData, event.aqiType),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,8 @@ import 'package:syncrow_web/pages/analytics/services/device_location/device_loca
|
||||
part 'device_location_event.dart';
|
||||
part 'device_location_state.dart';
|
||||
|
||||
class DeviceLocationBloc extends Bloc<DeviceLocationEvent, DeviceLocationState> {
|
||||
class DeviceLocationBloc
|
||||
extends Bloc<DeviceLocationEvent, DeviceLocationState> {
|
||||
DeviceLocationBloc(
|
||||
this._deviceLocationService,
|
||||
) : super(const DeviceLocationState()) {
|
||||
|
||||
@ -53,7 +53,8 @@ class RangeOfAqiBloc extends Bloc<RangeOfAqiEvent, RangeOfAqiState> {
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedAqiType: event.aqiType,
|
||||
filteredRangeOfAqi: _arrangeChartDataByType(state.rangeOfAqi, event.aqiType),
|
||||
filteredRangeOfAqi:
|
||||
_arrangeChartDataByType(state.rangeOfAqi, event.aqiType),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -105,7 +105,8 @@ abstract final class RangeOfAqiChartsHelper {
|
||||
tooltipRoundedRadius: 16,
|
||||
showOnTopOfTheChartBoxArea: false,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (touchedSpots) => RangeOfAqiChartsHelper.getTooltipItems(
|
||||
getTooltipItems: (touchedSpots) =>
|
||||
RangeOfAqiChartsHelper.getTooltipItems(
|
||||
touchedSpots,
|
||||
chartData,
|
||||
),
|
||||
|
||||
@ -81,7 +81,8 @@ class AqiDeviceInfo extends StatelessWidget {
|
||||
aqiLevel: status
|
||||
.firstWhere(
|
||||
(e) => e.code == 'air_quality_index',
|
||||
orElse: () => Status(code: 'air_quality_index', value: ''),
|
||||
orElse: () =>
|
||||
Status(code: 'air_quality_index', value: ''),
|
||||
)
|
||||
.value
|
||||
.toString(),
|
||||
|
||||
@ -36,23 +36,25 @@ class AqiDistributionChart extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
List<BarChartGroupData> _buildBarGroups(List<AirQualityDataModel> sortedData) {
|
||||
List<BarChartGroupData> _buildBarGroups(
|
||||
List<AirQualityDataModel> sortedData) {
|
||||
return List.generate(sortedData.length, (index) {
|
||||
final data = sortedData[index];
|
||||
final stackItems = <BarChartRodData>[];
|
||||
double currentY = 0;
|
||||
bool isFirstElement = true;
|
||||
var isFirstElement = true;
|
||||
|
||||
// Sort data by type to ensure consistent order
|
||||
final sortedPercentageData = List<AirQualityPercentageData>.from(data.data)
|
||||
..sort((a, b) => a.type.compareTo(b.type));
|
||||
final sortedPercentageData =
|
||||
List<AirQualityPercentageData>.from(data.data)
|
||||
..sort((a, b) => a.type.compareTo(b.type));
|
||||
|
||||
for (final percentageData in sortedPercentageData) {
|
||||
stackItems.add(
|
||||
BarChartRodData(
|
||||
fromY: currentY,
|
||||
toY: currentY + percentageData.percentage ,
|
||||
color: AirQualityDataModel.metricColors[percentageData.name]!,
|
||||
toY: currentY + percentageData.percentage,
|
||||
color: AirQualityDataModel.metricColors[percentageData.name],
|
||||
borderRadius: isFirstElement
|
||||
? const BorderRadius.only(
|
||||
topLeft: Radius.circular(22),
|
||||
@ -84,9 +86,9 @@ class AqiDistributionChart extends StatelessWidget {
|
||||
tooltipRoundedRadius: 16,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
||||
final data = chartData[group.x.toInt()];
|
||||
final data = chartData[group.x];
|
||||
|
||||
final List<TextSpan> children = [];
|
||||
final children = <TextSpan>[];
|
||||
|
||||
final textStyle = context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
@ -94,8 +96,9 @@ class AqiDistributionChart extends StatelessWidget {
|
||||
);
|
||||
|
||||
// Sort data by type to ensure consistent order
|
||||
final sortedPercentageData = List<AirQualityPercentageData>.from(data.data)
|
||||
..sort((a, b) => a.type.compareTo(b.type));
|
||||
final sortedPercentageData =
|
||||
List<AirQualityPercentageData>.from(data.data)
|
||||
..sort((a, b) => a.type.compareTo(b.type));
|
||||
|
||||
for (final percentageData in sortedPercentageData) {
|
||||
children.add(TextSpan(
|
||||
|
||||
@ -49,7 +49,7 @@ class AqiSubValueWidget extends StatelessWidget {
|
||||
|
||||
int _getActiveSegmentByRange(double value, (double min, double max) range) {
|
||||
final ranges = _getRangesForValue(range);
|
||||
for (int i = 0; i < ranges.length; i++) {
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (value <= ranges[i].max) return i;
|
||||
}
|
||||
return ranges.length - 1;
|
||||
|
||||
@ -29,7 +29,8 @@ class AqiTypeDropdown extends StatefulWidget {
|
||||
class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
|
||||
AqiType? _selectedItem = AqiType.aqi;
|
||||
|
||||
void _updateSelectedItem(AqiType? item) => setState(() => _selectedItem = item);
|
||||
void _updateSelectedItem(AqiType? item) =>
|
||||
setState(() => _selectedItem = item);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@ -63,7 +63,7 @@ class RangeOfAqiChart extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
stops: [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
stops: const [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
colors: RangeOfAqiChartsHelper.gradientData.map((e) {
|
||||
final (color, _) = e;
|
||||
return color.withValues(alpha: 0.6);
|
||||
@ -99,7 +99,8 @@ class RangeOfAqiChart extends StatelessWidget {
|
||||
}) {
|
||||
const invisibleDot = FlDotData(show: false);
|
||||
return LineChartBarData(
|
||||
spots: List.generate(values.length, (i) => FlSpot(i.toDouble(), values[i])),
|
||||
spots:
|
||||
List.generate(values.length, (i) => FlSpot(i.toDouble(), values[i])),
|
||||
isCurved: true,
|
||||
color: color,
|
||||
barWidth: 4,
|
||||
|
||||
@ -32,7 +32,8 @@ class RangeOfAqiChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(child: RangeOfAqiChart(chartData: state.filteredRangeOfAqi)),
|
||||
Expanded(
|
||||
child: RangeOfAqiChart(chartData: state.filteredRangeOfAqi)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -8,7 +8,8 @@ sealed class AnalyticsDevicesEvent extends Equatable {
|
||||
}
|
||||
|
||||
final class LoadAnalyticsDevicesEvent extends AnalyticsDevicesEvent {
|
||||
const LoadAnalyticsDevicesEvent({required this.param, required this.onSuccess});
|
||||
const LoadAnalyticsDevicesEvent(
|
||||
{required this.param, required this.onSuccess});
|
||||
|
||||
final GetAnalyticsDevicesParam param;
|
||||
final void Function(AnalyticsDevice device) onSuccess;
|
||||
|
||||
@ -7,7 +7,7 @@ sealed class AnalyticsTabEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class UpdateAnalyticsTabEvent extends AnalyticsTabEvent {
|
||||
class UpdateAnalyticsTabEvent extends AnalyticsTabEvent {
|
||||
const UpdateAnalyticsTabEvent(this.analyticsTab);
|
||||
|
||||
final AnalyticsPageTab analyticsTab;
|
||||
|
||||
@ -8,7 +8,8 @@ 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';
|
||||
|
||||
final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
final class AirQualityDataLoadingStrategy
|
||||
implements AnalyticsDataLoadingStrategy {
|
||||
@override
|
||||
void onCommunitySelected(
|
||||
BuildContext context,
|
||||
@ -25,7 +26,8 @@ final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
SpaceModel space,
|
||||
) {
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
final isSpaceSelected =
|
||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
|
||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||
if (hasSelectedSpaces) clearData(context);
|
||||
@ -34,7 +36,7 @@ final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
|
||||
spaceTreeBloc
|
||||
..add(const SpaceTreeClearSelectionEvent())
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', const []));
|
||||
|
||||
FetchAirQualityDataHelper.loadAirQualityData(
|
||||
context,
|
||||
|
||||
@ -8,7 +8,8 @@ abstract final class AnalyticsDataLoadingStrategyFactory {
|
||||
const AnalyticsDataLoadingStrategyFactory._();
|
||||
static AnalyticsDataLoadingStrategy getStrategy(AnalyticsPageTab tab) {
|
||||
return switch (tab) {
|
||||
AnalyticsPageTab.energyManagement => EnergyManagementDataLoadingStrategy(),
|
||||
AnalyticsPageTab.energyManagement =>
|
||||
EnergyManagementDataLoadingStrategy(),
|
||||
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
||||
AnalyticsPageTab.airQuality => AirQualityDataLoadingStrategy(),
|
||||
};
|
||||
|
||||
@ -7,7 +7,8 @@ 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 {
|
||||
class EnergyManagementDataLoadingStrategy
|
||||
implements AnalyticsDataLoadingStrategy {
|
||||
@override
|
||||
void onCommunitySelected(
|
||||
BuildContext context,
|
||||
@ -31,7 +32,8 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
SpaceModel space,
|
||||
) {
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
final isSpaceSelected =
|
||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||
|
||||
if (isSpaceSelected) {
|
||||
|
||||
@ -24,7 +24,8 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
SpaceModel space,
|
||||
) {
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
final isSpaceSelected =
|
||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
|
||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||
if (hasSelectedSpaces) clearData(context);
|
||||
@ -33,7 +34,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
|
||||
spaceTreeBloc
|
||||
..add(const SpaceTreeClearSelectionEvent())
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', const []));
|
||||
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
|
||||
@ -10,7 +10,8 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedTab = context.watch<AnalyticsTabBloc>().state;
|
||||
final strategy = AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||
final strategy =
|
||||
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||
|
||||
return Expanded(
|
||||
child: AnalyticsSpaceTreeView(
|
||||
|
||||
@ -20,7 +20,7 @@ class AnalyticsDateFilterButton extends StatefulWidget {
|
||||
final void Function(DateTime)? onDateSelected;
|
||||
final DatePickerType datePickerType;
|
||||
|
||||
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||
static final Color _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||
|
||||
@override
|
||||
State<AnalyticsDateFilterButton> createState() =>
|
||||
|
||||
@ -21,8 +21,8 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
AnalyticsDataLoadingStrategyFactory.getStrategy(tab).clearData(context);
|
||||
context.read<AnalyticsTabBloc>().add(
|
||||
UpdateAnalyticsTabEvent(tab),
|
||||
);
|
||||
UpdateAnalyticsTabEvent(tab),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
tab.title,
|
||||
@ -33,8 +33,9 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color:
|
||||
isSelected ? ColorsManager.slidingBlueColor : ColorsManager.textGray,
|
||||
color: isSelected
|
||||
? ColorsManager.slidingBlueColor
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -21,18 +21,18 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
int? _selectedMonth;
|
||||
|
||||
static const _monthNames = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
|
||||
@override
|
||||
@ -189,14 +189,19 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
final isFutureMonth = isCurrentYear && index > currentDate.month - 1;
|
||||
|
||||
return InkWell(
|
||||
onTap: isFutureMonth ? null : () => setState(() => _selectedMonth = index),
|
||||
onTap: isFutureMonth
|
||||
? null
|
||||
: () => setState(() => _selectedMonth = index),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEDF2F7),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
topLeft:
|
||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomLeft:
|
||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
topRight:
|
||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomRight:
|
||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
),
|
||||
|
||||
@ -53,7 +53,8 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(
|
||||
builder: (context, state) {
|
||||
final communities = state.searchQuery.isNotEmpty
|
||||
? state.filteredCommunity
|
||||
: state.communityList;
|
||||
@ -76,9 +77,10 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||
),
|
||||
),
|
||||
CustomSearchBar(
|
||||
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
||||
SearchQueryEvent(query),
|
||||
),
|
||||
onSearchChanged: (query) =>
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
SearchQueryEvent(query),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
@ -113,7 +115,8 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||
isExpanded: state.expandedCommunities.contains(
|
||||
communities[index].uuid,
|
||||
),
|
||||
onItemSelected: () => widget.onSelectCommunity?.call(
|
||||
onItemSelected: () =>
|
||||
widget.onSelectCommunity?.call(
|
||||
communities[index],
|
||||
communities[index].spaces,
|
||||
),
|
||||
@ -121,8 +124,8 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||
(space) {
|
||||
return CustomExpansionTileSpaceTree(
|
||||
title: space.name,
|
||||
isExpanded:
|
||||
state.expandedSpaces.contains(space.uuid),
|
||||
isExpanded: state.expandedSpaces
|
||||
.contains(space.uuid),
|
||||
onItemSelected: () =>
|
||||
widget.onSelectSpace?.call(
|
||||
communities[index],
|
||||
@ -153,7 +156,8 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
||||
if (state.paginationIsLoading)
|
||||
const CircularProgressIndicator(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -19,9 +19,9 @@ class YearPickerWidget extends StatefulWidget {
|
||||
class _YearPickerWidgetState extends State<YearPickerWidget> {
|
||||
late int _currentYear;
|
||||
|
||||
static final years = List.generate(
|
||||
static final List<int> years = List.generate(
|
||||
DateTime.now().year - (DateTime.now().year - 5) + 1,
|
||||
(index) => (2020 + index),
|
||||
(index) => 2020 + index,
|
||||
).where((year) => year <= DateTime.now().year).toList();
|
||||
|
||||
@override
|
||||
@ -123,9 +123,12 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEDF2F7),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
topLeft:
|
||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomLeft:
|
||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||
topRight:
|
||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
bottomRight:
|
||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||
),
|
||||
|
||||
@ -8,13 +8,15 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
part 'energy_consumption_by_phases_event.dart';
|
||||
part 'energy_consumption_by_phases_state.dart';
|
||||
|
||||
class EnergyConsumptionByPhasesBloc
|
||||
extends Bloc<EnergyConsumptionByPhasesEvent, EnergyConsumptionByPhasesState> {
|
||||
class EnergyConsumptionByPhasesBloc extends Bloc<EnergyConsumptionByPhasesEvent,
|
||||
EnergyConsumptionByPhasesState> {
|
||||
EnergyConsumptionByPhasesBloc(
|
||||
this._energyConsumptionByPhasesService,
|
||||
) : super(const EnergyConsumptionByPhasesState()) {
|
||||
on<LoadEnergyConsumptionByPhasesEvent>(_onLoadEnergyConsumptionByPhasesEvent);
|
||||
on<ClearEnergyConsumptionByPhasesEvent>(_onClearEnergyConsumptionByPhasesEvent);
|
||||
on<LoadEnergyConsumptionByPhasesEvent>(
|
||||
_onLoadEnergyConsumptionByPhasesEvent);
|
||||
on<ClearEnergyConsumptionByPhasesEvent>(
|
||||
_onClearEnergyConsumptionByPhasesEvent);
|
||||
}
|
||||
|
||||
final EnergyConsumptionByPhasesService _energyConsumptionByPhasesService;
|
||||
@ -25,7 +27,8 @@ class EnergyConsumptionByPhasesBloc
|
||||
) async {
|
||||
emit(state.copyWith(status: EnergyConsumptionByPhasesStatus.loading));
|
||||
try {
|
||||
final chartData = await _energyConsumptionByPhasesService.load(event.param);
|
||||
final chartData =
|
||||
await _energyConsumptionByPhasesService.load(event.param);
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: EnergyConsumptionByPhasesStatus.loaded,
|
||||
@ -49,7 +52,7 @@ class EnergyConsumptionByPhasesBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onClearEnergyConsumptionByPhasesEvent(
|
||||
Future<void> _onClearEnergyConsumptionByPhasesEvent(
|
||||
ClearEnergyConsumptionByPhasesEvent event,
|
||||
Emitter<EnergyConsumptionByPhasesState> emit,
|
||||
) async {
|
||||
|
||||
@ -7,7 +7,8 @@ sealed class EnergyConsumptionByPhasesEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent {
|
||||
class LoadEnergyConsumptionByPhasesEvent
|
||||
extends EnergyConsumptionByPhasesEvent {
|
||||
const LoadEnergyConsumptionByPhasesEvent({
|
||||
required this.param,
|
||||
});
|
||||
@ -18,6 +19,7 @@ class LoadEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent
|
||||
List<Object> get props => [param];
|
||||
}
|
||||
|
||||
final class ClearEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent {
|
||||
final class ClearEnergyConsumptionByPhasesEvent
|
||||
extends EnergyConsumptionByPhasesEvent {
|
||||
const ClearEnergyConsumptionByPhasesEvent();
|
||||
}
|
||||
|
||||
@ -8,12 +8,13 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
part 'energy_consumption_per_device_event.dart';
|
||||
part 'energy_consumption_per_device_state.dart';
|
||||
|
||||
class EnergyConsumptionPerDeviceBloc
|
||||
extends Bloc<EnergyConsumptionPerDeviceEvent, EnergyConsumptionPerDeviceState> {
|
||||
class EnergyConsumptionPerDeviceBloc extends Bloc<
|
||||
EnergyConsumptionPerDeviceEvent, EnergyConsumptionPerDeviceState> {
|
||||
EnergyConsumptionPerDeviceBloc(
|
||||
this._energyConsumptionPerDeviceService,
|
||||
) : super(const EnergyConsumptionPerDeviceState()) {
|
||||
on<LoadEnergyConsumptionPerDeviceEvent>(_onLoadEnergyConsumptionPerDeviceEvent);
|
||||
on<LoadEnergyConsumptionPerDeviceEvent>(
|
||||
_onLoadEnergyConsumptionPerDeviceEvent);
|
||||
on<ClearEnergyConsumptionPerDeviceEvent>(
|
||||
_onClearEnergyConsumptionPerDeviceEvent);
|
||||
}
|
||||
@ -26,7 +27,8 @@ class EnergyConsumptionPerDeviceBloc
|
||||
) async {
|
||||
emit(state.copyWith(status: EnergyConsumptionPerDeviceStatus.loading));
|
||||
try {
|
||||
final chartData = await _energyConsumptionPerDeviceService.load(event.param);
|
||||
final chartData =
|
||||
await _energyConsumptionPerDeviceService.load(event.param);
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: EnergyConsumptionPerDeviceStatus.loaded,
|
||||
@ -50,7 +52,7 @@ class EnergyConsumptionPerDeviceBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onClearEnergyConsumptionPerDeviceEvent(
|
||||
Future<void> _onClearEnergyConsumptionPerDeviceEvent(
|
||||
ClearEnergyConsumptionPerDeviceEvent event,
|
||||
Emitter<EnergyConsumptionPerDeviceState> emit,
|
||||
) async {
|
||||
|
||||
@ -8,7 +8,8 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
part 'power_clamp_info_event.dart';
|
||||
part 'power_clamp_info_state.dart';
|
||||
|
||||
class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState> {
|
||||
class PowerClampInfoBloc
|
||||
extends Bloc<PowerClampInfoEvent, PowerClampInfoState> {
|
||||
PowerClampInfoBloc(
|
||||
this._powerClampInfoService,
|
||||
) : super(const PowerClampInfoState()) {
|
||||
@ -25,7 +26,8 @@ class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState>
|
||||
) async {
|
||||
emit(state.copyWith(status: PowerClampInfoStatus.loading));
|
||||
try {
|
||||
final powerClampModel = await _powerClampInfoService.getInfo(event.deviceId);
|
||||
final powerClampModel =
|
||||
await _powerClampInfoService.getInfo(event.deviceId);
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: PowerClampInfoStatus.loaded,
|
||||
@ -49,7 +51,7 @@ class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState>
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdatePowerClampStatusEvent(
|
||||
Future<void> _onUpdatePowerClampStatusEvent(
|
||||
UpdatePowerClampStatusEvent event,
|
||||
Emitter<PowerClampInfoState> emit,
|
||||
) async {
|
||||
|
||||
@ -16,7 +16,6 @@ final class LoadPowerClampInfoEvent extends PowerClampInfoEvent {
|
||||
List<Object> get props => [deviceId];
|
||||
}
|
||||
|
||||
|
||||
final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
|
||||
const UpdatePowerClampStatusEvent(this.statusList);
|
||||
|
||||
@ -28,4 +27,4 @@ final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
|
||||
|
||||
final class ClearPowerClampInfoEvent extends PowerClampInfoEvent {
|
||||
const ClearPowerClampInfoEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,4 +24,4 @@ class _RealtimeDeviceChangesUpdated extends RealtimeDeviceChangesEvent {
|
||||
final List<Status> deviceStatusList;
|
||||
|
||||
const _RealtimeDeviceChangesUpdated(this.deviceStatusList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ class TotalEnergyConsumptionBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onClearTotalEnergyConsumptionEvent(
|
||||
Future<void> _onClearTotalEnergyConsumptionEvent(
|
||||
ClearTotalEnergyConsumptionEvent event,
|
||||
Emitter<TotalEnergyConsumptionState> emit,
|
||||
) async {
|
||||
|
||||
@ -7,7 +7,8 @@ sealed class TotalEnergyConsumptionEvent extends Equatable {
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class TotalEnergyConsumptionLoadEvent extends TotalEnergyConsumptionEvent {
|
||||
final class TotalEnergyConsumptionLoadEvent
|
||||
extends TotalEnergyConsumptionEvent {
|
||||
const TotalEnergyConsumptionLoadEvent({required this.param});
|
||||
|
||||
final GetTotalEnergyConsumptionParam param;
|
||||
@ -16,6 +17,7 @@ final class TotalEnergyConsumptionLoadEvent extends TotalEnergyConsumptionEvent
|
||||
List<Object?> get props => [param];
|
||||
}
|
||||
|
||||
final class ClearTotalEnergyConsumptionEvent extends TotalEnergyConsumptionEvent {
|
||||
final class ClearTotalEnergyConsumptionEvent
|
||||
extends TotalEnergyConsumptionEvent {
|
||||
const ClearTotalEnergyConsumptionEvent();
|
||||
}
|
||||
|
||||
@ -69,7 +69,8 @@ abstract final class EnergyManagementChartsHelper {
|
||||
return labels.where((element) => element.isNotEmpty).join(', ');
|
||||
}
|
||||
|
||||
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
||||
static List<LineTooltipItem?> getTooltipItems(
|
||||
List<LineBarSpot> touchedSpots) {
|
||||
return touchedSpots.map((spot) {
|
||||
return LineTooltipItem(
|
||||
getToolTipLabel(spot.x, spot.y),
|
||||
@ -85,7 +86,8 @@ abstract final class EnergyManagementChartsHelper {
|
||||
static LineTouchTooltipData lineTouchTooltipData() {
|
||||
return LineTouchTooltipData(
|
||||
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
|
||||
tooltipBorder: const BorderSide(color: ColorsManager.semiTransparentBlack),
|
||||
tooltipBorder:
|
||||
const BorderSide(color: ColorsManager.semiTransparentBlack),
|
||||
tooltipRoundedRadius: 16,
|
||||
showOnTopOfTheChartBoxArea: false,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
|
||||
@ -122,7 +122,8 @@ abstract final class FetchEnergyManagementDataHelper {
|
||||
final selectedDevice = getSelectedDevice(context);
|
||||
|
||||
context.read<RealtimeDeviceChangesBloc>().add(
|
||||
RealtimeDeviceChangesStarted(deviceUuid ?? selectedDevice?.uuid ?? ''),
|
||||
RealtimeDeviceChangesStarted(
|
||||
deviceUuid ?? selectedDevice?.uuid ?? ''),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +51,8 @@ class AnalyticsEnergyManagementView extends StatelessWidget {
|
||||
spacing: 20,
|
||||
children: [
|
||||
Expanded(child: TotalEnergyConsumptionChartBox()),
|
||||
Expanded(child: EnergyConsumptionPerDeviceChartBox()),
|
||||
Expanded(
|
||||
child: EnergyConsumptionPerDeviceChartBox()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -52,7 +52,8 @@ class AnalyticsDeviceDropdown extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) {
|
||||
Widget _buildDevicesDropdown(
|
||||
BuildContext context, AnalyticsDevicesState state) {
|
||||
final spaceUuid = state.selectedDevice?.spaceUuid;
|
||||
return DropdownButton<AnalyticsDevice?>(
|
||||
value: state.selectedDevice,
|
||||
|
||||
@ -18,7 +18,6 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 250,
|
||||
@ -100,11 +99,11 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
||||
}) {
|
||||
final data = energyData;
|
||||
|
||||
final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date);
|
||||
final phaseA = data[group.x.toInt()].energyConsumedA;
|
||||
final phaseB = data[group.x.toInt()].energyConsumedB;
|
||||
final phaseC = data[group.x.toInt()].energyConsumedC;
|
||||
final total = data[group.x.toInt()].energyConsumedKw;
|
||||
final date = DateFormat('dd/MM/yyyy').format(data[group.x].date);
|
||||
final phaseA = data[group.x].energyConsumedA;
|
||||
final phaseB = data[group.x].energyConsumedB;
|
||||
final phaseC = data[group.x].energyConsumedC;
|
||||
final total = data[group.x].energyConsumedKw;
|
||||
|
||||
return BarTooltipItem(
|
||||
'$date\n',
|
||||
|
||||
@ -22,7 +22,8 @@ class EnergyConsumptionByPhasesChartBox extends StatelessWidget {
|
||||
children: [
|
||||
AnalyticsErrorWidget(state.errorMessage),
|
||||
EnergyConsumptionByPhasesTitle(
|
||||
isLoading: state.status == EnergyConsumptionByPhasesStatus.loading,
|
||||
isLoading:
|
||||
state.status == EnergyConsumptionByPhasesStatus.loading,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
|
||||
@ -17,7 +17,6 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
|
||||
context,
|
||||
leftTitlesInterval: 250,
|
||||
),
|
||||
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 250,
|
||||
|
||||
@ -46,7 +46,8 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
||||
flex: 2,
|
||||
child: EnergyConsumptionPerDeviceDevicesList(
|
||||
chartData: state.chartData,
|
||||
devices: context.watch<AnalyticsDevicesBloc>().state.devices,
|
||||
devices:
|
||||
context.watch<AnalyticsDevicesBloc>().state.devices,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -55,7 +56,8 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: EnergyConsumptionPerDeviceChart(chartData: state.chartData),
|
||||
child:
|
||||
EnergyConsumptionPerDeviceChart(chartData: state.chartData),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -43,11 +43,14 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
||||
title: 'Smart Power Clamp',
|
||||
showSpaceUuidInDevicesDropdown: true,
|
||||
onChanged: (device) {
|
||||
FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases(
|
||||
FetchEnergyManagementDataHelper
|
||||
.loadEnergyConsumptionByPhases(
|
||||
context,
|
||||
powerClampUuid: device.uuid,
|
||||
selectedDate:
|
||||
context.read<AnalyticsDatePickerBloc>().state.monthlyDate,
|
||||
selectedDate: context
|
||||
.read<AnalyticsDatePickerBloc>()
|
||||
.state
|
||||
.monthlyDate,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -91,7 +94,8 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const Expanded(flex: 3, child: EnergyConsumptionByPhasesChartBox()),
|
||||
const Expanded(
|
||||
flex: 3, child: EnergyConsumptionByPhasesChartBox()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -140,9 +140,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
||||
|
||||
String _formatCurrentValue(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
var str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
if (str.length == 1) return '${str[0]}.0';
|
||||
return '${str[0]}.${str.substring(1)}';
|
||||
@ -150,9 +150,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
||||
|
||||
String _formatPowerFactor(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
var str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
final intValue = int.tryParse(str);
|
||||
if (intValue == null) return '--';
|
||||
@ -162,9 +162,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
||||
|
||||
String _formatVoltage(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
var str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
if (str.length == 1) return '0.${str[0]}';
|
||||
return '${str.substring(0, str.length - 1)}.${str.substring(str.length - 1)}';
|
||||
|
||||
@ -29,7 +29,6 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
||||
),
|
||||
duration: Duration.zero,
|
||||
curve: Curves.easeIn,
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,7 +25,8 @@ class TotalEnergyConsumptionChartBox extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
ChartsLoadingWidget(
|
||||
isLoading: state.status == TotalEnergyConsumptionStatus.loading,
|
||||
isLoading:
|
||||
state.status == TotalEnergyConsumptionStatus.loading,
|
||||
),
|
||||
const Expanded(
|
||||
flex: 3,
|
||||
|
||||
@ -23,9 +23,11 @@ class OccupancyBloc extends Bloc<OccupancyEvent, OccupancyState> {
|
||||
emit(state.copyWith(status: OccupancyStatus.loading));
|
||||
try {
|
||||
final chartData = await _occupacyService.load(event.param);
|
||||
emit(state.copyWith(chartData: chartData, status: OccupancyStatus.loaded));
|
||||
emit(
|
||||
state.copyWith(chartData: chartData, status: OccupancyStatus.loaded));
|
||||
} on APIException catch (e) {
|
||||
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: e.message));
|
||||
emit(state.copyWith(
|
||||
status: OccupancyStatus.failure, errorMessage: e.message));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: '$e'));
|
||||
}
|
||||
|
||||
@ -25,15 +25,18 @@ abstract final class FetchOccupancyDataHelper {
|
||||
|
||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||
|
||||
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
||||
final selectedDevice = context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||
loadAnalyticsDevices(context,
|
||||
communityUuid: communityId, spaceUuid: spaceId);
|
||||
final selectedDevice =
|
||||
context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||
|
||||
loadOccupancyChartData(
|
||||
context,
|
||||
spaceUuid: spaceId,
|
||||
date: datePickerState.monthlyDate,
|
||||
);
|
||||
loadHeatMapData(context, spaceUuid: spaceId, year: datePickerState.yearlyDate);
|
||||
loadHeatMapData(context,
|
||||
spaceUuid: spaceId, year: datePickerState.yearlyDate);
|
||||
|
||||
if (selectedDevice case final AnalyticsDevice device) {
|
||||
context.read<RealtimeDeviceChangesBloc>()
|
||||
@ -64,7 +67,8 @@ abstract final class FetchOccupancyDataHelper {
|
||||
context.read<OccupancyBloc>().add(
|
||||
LoadOccupancyEvent(
|
||||
GetOccupancyParam(
|
||||
monthDate: '${date.year}-${date.month.toString().padLeft(2, '0')}',
|
||||
monthDate:
|
||||
'${date.year}-${date.month.toString().padLeft(2, '0')}',
|
||||
spaceUuid: spaceUuid,
|
||||
),
|
||||
),
|
||||
|
||||
@ -20,9 +20,12 @@ class AnalyticsOccupancyView extends StatelessWidget {
|
||||
child: Column(
|
||||
spacing: 32,
|
||||
children: [
|
||||
SizedBox(height: height * 0.46, child: const OccupancyEndSideBar()),
|
||||
SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
|
||||
SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()),
|
||||
SizedBox(
|
||||
height: height * 0.46, child: const OccupancyEndSideBar()),
|
||||
SizedBox(
|
||||
height: height * 0.5, child: const OccupancyChartBox()),
|
||||
SizedBox(
|
||||
height: height * 0.5, child: const OccupancyHeatMapBox()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -41,7 +41,8 @@ class OccupancyChart extends StatelessWidget {
|
||||
barRods: [
|
||||
BarChartRodData(
|
||||
toY: 100.0,
|
||||
fromY: occupancyValue == 0 ? occupancyValue : occupancyValue + 2.5,
|
||||
fromY:
|
||||
occupancyValue == 0 ? occupancyValue : occupancyValue + 2.5,
|
||||
color: ColorsManager.graysColor,
|
||||
width: _chartWidth,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
@ -88,8 +89,8 @@ class OccupancyChart extends StatelessWidget {
|
||||
}) {
|
||||
final data = chartData;
|
||||
|
||||
final occupancyValue = double.parse(data[group.x.toInt()].occupancy);
|
||||
final percentage = '${(occupancyValue).toStringAsFixed(0)}%';
|
||||
final occupancyValue = double.parse(data[group.x].occupancy);
|
||||
final percentage = '${occupancyValue.toStringAsFixed(0)}%';
|
||||
|
||||
return BarTooltipItem(
|
||||
percentage,
|
||||
@ -116,7 +117,7 @@ class OccupancyChart extends StatelessWidget {
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
'${(value).toStringAsFixed(0)}%',
|
||||
'${value.toStringAsFixed(0)}%',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 12,
|
||||
color: ColorsManager.greyColor,
|
||||
|
||||
@ -44,13 +44,15 @@ class OccupancyChartBox extends StatelessWidget {
|
||||
child: AnalyticsDateFilterButton(
|
||||
onDateSelected: (DateTime value) {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||
UpdateAnalyticsDatePickerEvent(
|
||||
montlyDate: value),
|
||||
);
|
||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||
FetchOccupancyDataHelper.loadOccupancyChartData(
|
||||
context,
|
||||
spaceUuid:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
spaceTreeState.selectedSpaces.firstOrNull ??
|
||||
'',
|
||||
date: value,
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,13 +44,15 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
||||
child: AnalyticsDateFilterButton(
|
||||
onDateSelected: (DateTime value) {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
||||
UpdateAnalyticsDatePickerEvent(
|
||||
yearlyDate: value),
|
||||
);
|
||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||
FetchOccupancyDataHelper.loadHeatMapData(
|
||||
context,
|
||||
spaceUuid:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
spaceTreeState.selectedSpaces.firstOrNull ??
|
||||
'',
|
||||
year: value,
|
||||
);
|
||||
}
|
||||
|
||||
@ -28,11 +28,11 @@ class OccupancyPainter extends CustomPainter {
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint fillPaint = Paint();
|
||||
final Paint borderPaint = Paint()
|
||||
final fillPaint = Paint();
|
||||
final borderPaint = Paint()
|
||||
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
||||
..style = PaintingStyle.stroke;
|
||||
final Paint hoveredBorderPaint = Paint()
|
||||
final hoveredBorderPaint = Paint()
|
||||
..color = Colors.black
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.5;
|
||||
@ -66,24 +66,24 @@ class OccupancyPainter extends CustomPainter {
|
||||
);
|
||||
|
||||
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
||||
canvas.drawLine(Offset(x + cellSize, y), Offset(x + cellSize, y + cellSize),
|
||||
borderPaint);
|
||||
canvas.drawLine(Offset(x + cellSize, y),
|
||||
Offset(x + cellSize, y + cellSize), borderPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint) {
|
||||
const double dashWidth = 2.0;
|
||||
const double dashSpace = 4.0;
|
||||
final double totalLength = (end - start).distance;
|
||||
final Offset direction = (end - start) / (end - start).distance;
|
||||
const dashWidth = 2.0;
|
||||
const dashSpace = 4.0;
|
||||
final totalLength = (end - start).distance;
|
||||
final direction = (end - start) / (end - start).distance;
|
||||
|
||||
double currentLength = 0.0;
|
||||
var currentLength = 0.0;
|
||||
while (currentLength < totalLength) {
|
||||
final Offset dashStart = start + direction * currentLength;
|
||||
final double nextLength = currentLength + dashWidth;
|
||||
final Offset dashEnd =
|
||||
start + direction * (nextLength < totalLength ? nextLength : totalLength);
|
||||
final dashStart = start + direction * currentLength;
|
||||
final nextLength = currentLength + dashWidth;
|
||||
final dashEnd = start +
|
||||
direction * (nextLength < totalLength ? nextLength : totalLength);
|
||||
canvas.drawLine(dashStart, dashEnd, paint);
|
||||
currentLength = nextLength + dashSpace;
|
||||
}
|
||||
|
||||
@ -5,7 +5,8 @@ import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type
|
||||
import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart';
|
||||
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
||||
|
||||
class FakeAirQualityDistributionService implements AirQualityDistributionService {
|
||||
class FakeAirQualityDistributionService
|
||||
implements AirQualityDistributionService {
|
||||
final _random = Random();
|
||||
|
||||
@override
|
||||
@ -43,7 +44,6 @@ class FakeAirQualityDistributionService implements AirQualityDistributionService
|
||||
name: 'poor',
|
||||
percentage: nonNullValues[2],
|
||||
type: AqiType.hcho.code,
|
||||
|
||||
),
|
||||
AirQualityPercentageData(
|
||||
name: 'unhealthy',
|
||||
@ -71,7 +71,7 @@ class FakeAirQualityDistributionService implements AirQualityDistributionService
|
||||
List<bool> nullMask,
|
||||
) {
|
||||
double nonNullSum = 0;
|
||||
for (int i = 0; i < originalValues.length; i++) {
|
||||
for (var i = 0; i < originalValues.length; i++) {
|
||||
if (!nullMask[i]) {
|
||||
nonNullSum += originalValues[i];
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_
|
||||
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
|
||||
class RemoteAirQualityDistributionService implements AirQualityDistributionService {
|
||||
class RemoteAirQualityDistributionService
|
||||
implements AirQualityDistributionService {
|
||||
RemoteAirQualityDistributionService(this._httpService);
|
||||
|
||||
final HTTPService _httpService;
|
||||
|
||||
@ -16,7 +16,8 @@ class AnalyticsDevicesServiceDelegate implements AnalyticsDevicesService {
|
||||
GetAnalyticsDevicesParam param,
|
||||
) {
|
||||
return switch (param.requestType) {
|
||||
AnalyticsDeviceRequestType.occupancy => _occupancyService.getDevices(param),
|
||||
AnalyticsDeviceRequestType.occupancy =>
|
||||
_occupancyService.getDevices(param),
|
||||
AnalyticsDeviceRequestType.energyManagement =>
|
||||
_energyManagementService.getDevices(param),
|
||||
};
|
||||
|
||||
@ -14,7 +14,8 @@ final class RemoteEnergyManagementAnalyticsDevicesService
|
||||
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
||||
|
||||
@override
|
||||
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||
Future<List<AnalyticsDevice>> getDevices(
|
||||
GetAnalyticsDevicesParam param) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: '/devices-space-community/recursive-child',
|
||||
@ -37,7 +38,8 @@ final class RemoteEnergyManagementAnalyticsDevicesService
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
throw APIException('$_defaultErrorMessage: $e');
|
||||
|
||||
@ -6,7 +6,8 @@ import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
|
||||
class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService {
|
||||
class RemoteOccupancyAnalyticsDevicesService
|
||||
implements AnalyticsDevicesService {
|
||||
const RemoteOccupancyAnalyticsDevicesService(this._httpService);
|
||||
|
||||
final HTTPService _httpService;
|
||||
@ -14,7 +15,8 @@ class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService
|
||||
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
||||
|
||||
@override
|
||||
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||
Future<List<AnalyticsDevice>> getDevices(
|
||||
GetAnalyticsDevicesParam param) async {
|
||||
try {
|
||||
final requests = await Future.wait<List<AnalyticsDevice>>(
|
||||
param.deviceTypes.map((e) {
|
||||
@ -34,15 +36,18 @@ class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, e.toString()].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, e.toString()].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<AnalyticsDevice>> _makeRequest(GetAnalyticsDevicesParam param) async {
|
||||
Future<List<AnalyticsDevice>> _makeRequest(
|
||||
GetAnalyticsDevicesParam param) async {
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID();
|
||||
|
||||
@ -69,7 +74,8 @@ class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
throw APIException('$_defaultErrorMessage: $e');
|
||||
|
||||
@ -34,7 +34,7 @@ class DeviceLocationDetailsServiceDecorator implements DeviceLocationService {
|
||||
|
||||
return deviceLocationInfo;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to load device location info: ${e.toString()}');
|
||||
throw Exception('Failed to load device location info: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,8 @@ final class RemoteEnergyConsumptionByPhasesService
|
||||
|
||||
final HTTPService _httpService;
|
||||
|
||||
static const _defaultErrorMessage = 'Failed to load energy consumption per phase';
|
||||
static const _defaultErrorMessage =
|
||||
'Failed to load energy consumption per phase';
|
||||
|
||||
@override
|
||||
Future<List<PhasesEnergyConsumption>> load(
|
||||
@ -36,7 +37,8 @@ final class RemoteEnergyConsumptionByPhasesService
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
|
||||
@ -13,7 +13,8 @@ class RemoteEnergyConsumptionPerDeviceService
|
||||
|
||||
final HTTPService _httpService;
|
||||
|
||||
static const _defaultErrorMessage = 'Failed to load energy consumption per device';
|
||||
static const _defaultErrorMessage =
|
||||
'Failed to load energy consumption per device';
|
||||
|
||||
@override
|
||||
Future<List<DeviceEnergyDataModel>> load(
|
||||
@ -31,7 +32,8 @@ class RemoteEnergyConsumptionPerDeviceService
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
@ -59,7 +61,8 @@ abstract final class _EnergyConsumptionPerDeviceMapper {
|
||||
final energyJson = data as Map<String, dynamic>;
|
||||
return EnergyDataModel(
|
||||
date: DateTime.parse(energyJson['date'] as String),
|
||||
value: double.parse(energyJson['total_energy_consumed_kw'] as String),
|
||||
value:
|
||||
double.parse(energyJson['total_energy_consumed_kw'] as String),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
@ -33,7 +33,8 @@ final class RemoteOccupancyService implements OccupacyService {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
|
||||
@ -13,7 +13,8 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
||||
static const _defaultErrorMessage = 'Failed to load occupancy heat map';
|
||||
|
||||
@override
|
||||
Future<List<OccupancyHeatMapModel>> load(GetOccupancyHeatMapParam param) async {
|
||||
Future<List<OccupancyHeatMapModel>> load(
|
||||
GetOccupancyHeatMapParam param) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: '/occupancy/heat-map/space/${param.spaceUuid}',
|
||||
@ -24,7 +25,8 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
||||
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
||||
|
||||
final result = dailyData.map(
|
||||
(json) => OccupancyHeatMapModel.fromJson(json as Map<String, dynamic>),
|
||||
(json) =>
|
||||
OccupancyHeatMapModel.fromJson(json as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
return result.toList();
|
||||
@ -36,7 +38,8 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
|
||||
@ -28,7 +28,8 @@ final class RemotePowerClampInfoService implements PowerClampInfoService {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
|
||||
@ -6,7 +6,7 @@ import 'package:syncrow_web/pages/analytics/services/range_of_aqi/range_of_aqi_s
|
||||
class FakeRangeOfAqiService implements RangeOfAqiService {
|
||||
@override
|
||||
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param) async {
|
||||
return await Future.delayed(const Duration(milliseconds: 800), () {
|
||||
return Future.delayed(const Duration(milliseconds: 800), () {
|
||||
final random = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
return List.generate(30, (index) {
|
||||
@ -19,14 +19,20 @@ class FakeRangeOfAqiService implements RangeOfAqiService {
|
||||
final avg = (min + avgDelta).clamp(0.0, 301.0);
|
||||
final max = (avg + maxDelta).clamp(0.0, 301.0);
|
||||
|
||||
return RangeOfAqi(
|
||||
return RangeOfAqi(
|
||||
data: [
|
||||
RangeOfAqiValue(type: AqiType.aqi.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(type: AqiType.pm25.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(type: AqiType.pm10.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(type: AqiType.hcho.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(type: AqiType.tvoc.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(type: AqiType.co2.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.aqi.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.pm25.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.pm10.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.hcho.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.tvoc.code, min: min, average: avg, max: max),
|
||||
RangeOfAqiValue(
|
||||
type: AqiType.co2.code, min: min, average: avg, max: max),
|
||||
],
|
||||
date: date,
|
||||
);
|
||||
|
||||
@ -3,4 +3,4 @@ import 'package:syncrow_web/pages/analytics/params/get_range_of_aqi_param.dart';
|
||||
|
||||
abstract interface class RangeOfAqiService {
|
||||
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,4 +2,4 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
|
||||
|
||||
abstract interface class RealtimeDeviceService {
|
||||
Stream<List<Status>> subscribe(String deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,8 @@ import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/to
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
|
||||
class RemoteTotalEnergyConsumptionService implements TotalEnergyConsumptionService {
|
||||
class RemoteTotalEnergyConsumptionService
|
||||
implements TotalEnergyConsumptionService {
|
||||
const RemoteTotalEnergyConsumptionService(this._httpService);
|
||||
|
||||
final HTTPService _httpService;
|
||||
@ -29,7 +30,8 @@ class RemoteTotalEnergyConsumptionService implements TotalEnergyConsumptionServi
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||
final formattedErrorMessage =
|
||||
[_defaultErrorMessage, errorMessage].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
|
||||
@ -75,7 +75,8 @@ class AnalyticsSidebarHeader extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
SelectableText(
|
||||
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ?? 'N/A',
|
||||
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ??
|
||||
'N/A',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
|
||||
Reference in New Issue
Block a user