diff --git a/.gitignore b/.gitignore index 29a3a501..fa9d8443 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +pubspec.lock # Symbolication related app.*.symbols diff --git a/analysis_options.yaml b/analysis_options.yaml index 81bdd00d..80a63bcb 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,6 +10,7 @@ analyzer: errors: constant_identifier_names: ignore + overridden_fields: ignore include: package:flutter_lints/flutter.yaml linter: diff --git a/lib/pages/analytics/models/analytics_device.dart b/lib/pages/analytics/models/analytics_device.dart index 6f066407..88f18ec5 100644 --- a/lib/pages/analytics/models/analytics_device.dart +++ b/lib/pages/analytics/models/analytics_device.dart @@ -1,13 +1,71 @@ class AnalyticsDevice { - const AnalyticsDevice({required this.name, required this.uuid}); + const AnalyticsDevice({ + required this.uuid, + required this.name, + this.createdAt, + this.updatedAt, + this.deviceTuyaUuid, + this.isActive, + this.productDevice, + this.spaceUuid, + }); final String uuid; final String name; + final DateTime? createdAt; + final DateTime? updatedAt; + final String? deviceTuyaUuid; + final bool? isActive; + final ProductDevice? productDevice; + final String? spaceUuid; factory AnalyticsDevice.fromJson(Map json) { return AnalyticsDevice( - uuid: json['uuid'] as String? ?? '', - name: json['name'] as String? ?? '', + uuid: json['uuid'] as String, + name: json['name'] as String, + createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null, + updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null, + deviceTuyaUuid: json['deviceTuyaUuid'] as String?, + isActive: json['isActive'] as bool?, + productDevice: json['productDevice'] != null + ? ProductDevice.fromJson(json['productDevice'] as Map) + : null, + spaceUuid: (json['spaces'] as List?) + ?.map((e) => e['uuid']) + .firstOrNull + ?.toString(), + ); + } +} + +class ProductDevice { + const ProductDevice({ + this.uuid, + this.createdAt, + this.updatedAt, + this.catName, + this.prodId, + this.name, + this.prodType, + }); + + final String? uuid; + final DateTime? createdAt; + final DateTime? updatedAt; + final String? catName; + final String? prodId; + final String? name; + final String? prodType; + + factory ProductDevice.fromJson(Map json) { + return ProductDevice( + uuid: json['uuid'] as String?, + createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null, + updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null, + catName: json['catName'] as String?, + prodId: json['prodId'] as String?, + name: json['name'] as String?, + prodType: json['prodType'] as String?, ); } } diff --git a/lib/pages/analytics/models/phases_energy_consumption.dart b/lib/pages/analytics/models/phases_energy_consumption.dart index f986c3ad..a826bac0 100644 --- a/lib/pages/analytics/models/phases_energy_consumption.dart +++ b/lib/pages/analytics/models/phases_energy_consumption.dart @@ -1,27 +1,66 @@ import 'package:equatable/equatable.dart'; class PhasesEnergyConsumption extends Equatable { - final int month; - final double phaseA; - final double phaseB; - final double phaseC; + final String uuid; + final DateTime createdAt; + final DateTime updatedAt; + final String deviceUuid; + final DateTime date; + final double energyConsumedKw; + final double energyConsumedA; + final double energyConsumedB; + final double energyConsumedC; const PhasesEnergyConsumption({ - required this.month, - required this.phaseA, - required this.phaseB, - required this.phaseC, + required this.uuid, + required this.createdAt, + required this.updatedAt, + required this.deviceUuid, + required this.date, + required this.energyConsumedKw, + required this.energyConsumedA, + required this.energyConsumedB, + required this.energyConsumedC, }); @override - List get props => [month, phaseA, phaseB, phaseC]; + List get props => [ + uuid, + createdAt, + updatedAt, + deviceUuid, + date, + energyConsumedKw, + energyConsumedA, + energyConsumedB, + energyConsumedC, + ]; factory PhasesEnergyConsumption.fromJson(Map json) { return PhasesEnergyConsumption( - month: json['month'] as int, - phaseA: (json['phaseA'] as num).toDouble(), - phaseB: (json['phaseB'] as num).toDouble(), - phaseC: (json['phaseC'] as num).toDouble(), + uuid: json['uuid'] as String, + createdAt: DateTime.parse(json['createdAt'] as String), + updatedAt: DateTime.parse(json['updatedAt'] as String), + deviceUuid: json['deviceUuid'] as String, + date: DateTime.parse(json['date'] as String), + energyConsumedKw: double.parse(json['energyConsumedKw']), + energyConsumedA: double.parse(json['energyConsumedA']), + energyConsumedB: double.parse(json['energyConsumedB']), + energyConsumedC: double.parse(json['energyConsumedC']), ); } + + Map toJson() { + return { + 'uuid': uuid, + 'createdAt': createdAt.toIso8601String(), + 'updatedAt': updatedAt.toIso8601String(), + 'deviceUuid': deviceUuid, + 'date': date.toIso8601String().split('T')[0], + 'energyConsumedKw': energyConsumedKw.toString(), + 'energyConsumedA': energyConsumedA.toString(), + 'energyConsumedB': energyConsumedB.toString(), + 'energyConsumedC': energyConsumedC.toString(), + }; + } } diff --git a/lib/pages/analytics/modules/analytics/views/analytics_page.dart b/lib/pages/analytics/modules/analytics/views/analytics_page.dart index 883672a2..72cc7d51 100644 --- a/lib/pages/analytics/modules/analytics/views/analytics_page.dart +++ b/lib/pages/analytics/modules/analytics/views/analytics_page.dart @@ -15,7 +15,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/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/remote_energy_consumption_by_phases_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart'; import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart'; @@ -57,7 +57,7 @@ class _AnalyticsPageState extends State { ), BlocProvider( create: (context) => EnergyConsumptionByPhasesBloc( - FakeEnergyConsumptionByPhasesService(), + RemoteEnergyConsumptionByPhasesService(_httpService), ), ), BlocProvider( diff --git a/lib/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart b/lib/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart index 2a063599..5e9e347a 100644 --- a/lib/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart +++ b/lib/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart @@ -14,7 +14,6 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget { @override Widget build(BuildContext context) { - final spaceTreeState = context.read().state; return BlocBuilder( buildWhen: (previous, current) => previous != current, builder: (context, selectedTab) => Column( @@ -68,15 +67,22 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget { context.read().add( UpdateAnalyticsDatePickerEvent(montlyDate: value), ); - FetchEnergyManagementDataHelper.loadEnergyManagementData( - context, - selectedDate: value, - communityId: - spaceTreeState.selectedCommunities.firstOrNull ?? - '', - spaceId: - spaceTreeState.selectedSpaces.firstOrNull ?? '', - ); + + final spaceTreeState = + context.read().state; + if (spaceTreeState.selectedSpaces.isNotEmpty) { + FetchEnergyManagementDataHelper + .loadEnergyManagementData( + context, + shouldFetchAnalyticsDevices: false, + selectedDate: value, + communityId: + spaceTreeState.selectedCommunities.firstOrNull ?? + '', + spaceId: + spaceTreeState.selectedSpaces.firstOrNull ?? '', + ); + } }, selectedDate: context .watch() diff --git a/lib/pages/analytics/modules/analytics/widgets/month_picker_widget.dart b/lib/pages/analytics/modules/analytics/widgets/month_picker_widget.dart index 53ba31af..57133b02 100644 --- a/lib/pages/analytics/modules/analytics/widgets/month_picker_widget.dart +++ b/lib/pages/analytics/modules/analytics/widgets/month_picker_widget.dart @@ -45,7 +45,7 @@ class _MonthPickerWidgetState extends State { @override Widget build(BuildContext context) { return Dialog( - backgroundColor: Theme.of(context).colorScheme.surface, + backgroundColor: ColorsManager.whiteColors, child: Container( padding: const EdgeInsetsDirectional.all(20), width: 320, @@ -121,6 +121,7 @@ class _MonthPickerWidgetState extends State { } Row _buildYearSelector() { + final currentYear = DateTime.now().year; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -134,17 +135,35 @@ class _MonthPickerWidgetState extends State { ), const Spacer(), IconButton( - onPressed: () => setState(() => _currentYear = _currentYear - 1), + onPressed: () { + setState(() { + _currentYear = _currentYear - 1; + }); + }, icon: const Icon( Icons.chevron_left, color: ColorsManager.grey700, ), ), IconButton( - onPressed: () => setState(() => _currentYear = _currentYear + 1), - icon: const Icon( + onPressed: _currentYear < currentYear + ? () { + setState(() { + _currentYear = _currentYear + 1; + // Clear selected month if it becomes invalid in the new year + if (_currentYear == currentYear && + _selectedMonth != null && + _selectedMonth! > DateTime.now().month - 1) { + _selectedMonth = null; + } + }); + } + : null, + icon: Icon( Icons.chevron_right, - color: ColorsManager.grey700, + color: _currentYear < currentYear + ? ColorsManager.grey700 + : ColorsManager.grey700.withValues(alpha: 0.3), ), ), ], @@ -152,11 +171,13 @@ class _MonthPickerWidgetState extends State { } Widget _buildMonthsGrid() { + final currentDate = DateTime.now(); + final isCurrentYear = _currentYear == currentDate.year; + return GridView.builder( shrinkWrap: true, itemCount: 12, physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 2.5, @@ -165,25 +186,43 @@ class _MonthPickerWidgetState extends State { ), itemBuilder: (context, index) { final isSelected = _selectedMonth == index; + final isFutureMonth = isCurrentYear && index > currentDate.month - 1; + return InkWell( - onTap: () => setState(() => _selectedMonth = index), - child: Container( - alignment: Alignment.center, + onTap: isFutureMonth ? null : () => setState(() => _selectedMonth = index), + child: DecoratedBox( decoration: BoxDecoration( - color: isSelected - ? ColorsManager.vividBlue.withValues(alpha: 0.7) - : const Color(0xFFEDF2F7), - borderRadius: - isSelected ? BorderRadius.circular(15) : BorderRadius.zero, + 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, + bottomRight: + index % 3 == 2 ? const Radius.circular(16) : Radius.zero, + ), ), - child: Text( - _monthNames[index], - style: context.textTheme.titleSmall?.copyWith( - fontSize: 12, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( color: isSelected - ? ColorsManager.whiteColors - : ColorsManager.blackColor.withValues(alpha: 0.8), - fontWeight: FontWeight.w500, + ? ColorsManager.vividBlue.withValues(alpha: 0.7) + : isFutureMonth + ? ColorsManager.grey700.withValues(alpha: 0.1) + : const Color(0xFFEDF2F7), + borderRadius: + isSelected ? BorderRadius.circular(15) : BorderRadius.zero, + ), + child: Text( + _monthNames[index], + style: context.textTheme.titleSmall?.copyWith( + fontSize: 12, + color: isSelected + ? ColorsManager.whiteColors + : isFutureMonth + ? ColorsManager.blackColor.withValues(alpha: 0.3) + : ColorsManager.blackColor.withValues(alpha: 0.8), + fontWeight: FontWeight.w500, + ), ), ), ), diff --git a/lib/pages/analytics/modules/analytics/widgets/year_picker_widget.dart b/lib/pages/analytics/modules/analytics/widgets/year_picker_widget.dart index 4c7bb748..22eb6646 100644 --- a/lib/pages/analytics/modules/analytics/widgets/year_picker_widget.dart +++ b/lib/pages/analytics/modules/analytics/widgets/year_picker_widget.dart @@ -20,9 +20,9 @@ class _YearPickerWidgetState extends State { late int _currentYear; static final years = List.generate( - DateTime.now().year - 2020 + 1, + DateTime.now().year - (DateTime.now().year - 5) + 1, (index) => (2020 + index), - ); + ).where((year) => year <= DateTime.now().year).toList(); @override void initState() { @@ -33,7 +33,7 @@ class _YearPickerWidgetState extends State { @override Widget build(BuildContext context) { return Dialog( - backgroundColor: Theme.of(context).colorScheme.surface, + backgroundColor: ColorsManager.whiteColors, child: Container( padding: const EdgeInsetsDirectional.all(20), width: 320, @@ -109,7 +109,6 @@ class _YearPickerWidgetState extends State { shrinkWrap: true, itemCount: years.length, physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 2.5, @@ -120,23 +119,35 @@ class _YearPickerWidgetState extends State { final isSelected = _currentYear == years[index]; return InkWell( onTap: () => setState(() => _currentYear = years[index]), - child: Container( - alignment: Alignment.center, + child: DecoratedBox( decoration: BoxDecoration( - color: isSelected - ? ColorsManager.vividBlue.withValues(alpha: 0.7) - : const Color(0xFFEDF2F7), - borderRadius: - isSelected ? BorderRadius.circular(15) : BorderRadius.zero, + 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, + bottomRight: + index % 3 == 2 ? const Radius.circular(16) : Radius.zero, + ), ), - child: Text( - years[index].toString(), - style: context.textTheme.titleSmall?.copyWith( - fontSize: 12, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( color: isSelected - ? ColorsManager.whiteColors - : ColorsManager.blackColor.withValues(alpha: 0.8), - fontWeight: FontWeight.w500, + ? ColorsManager.vividBlue.withValues(alpha: 0.7) + : const Color(0xFFEDF2F7), + borderRadius: + isSelected ? BorderRadius.circular(15) : BorderRadius.zero, + ), + child: Text( + years[index].toString(), + style: context.textTheme.titleSmall?.copyWith( + fontSize: 12, + color: isSelected + ? ColorsManager.whiteColors + : ColorsManager.blackColor.withValues(alpha: 0.8), + fontWeight: FontWeight.w500, + ), ), ), ), diff --git a/lib/pages/analytics/modules/energy_management/helpers/energy_consumption_by_phases_chart_helper.dart b/lib/pages/analytics/modules/energy_management/helpers/energy_consumption_by_phases_chart_helper.dart deleted file mode 100644 index e79fe48d..00000000 --- a/lib/pages/analytics/modules/energy_management/helpers/energy_consumption_by_phases_chart_helper.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart'; - -abstract final class EnergyConsumptionByPhasesChartHelper { - const EnergyConsumptionByPhasesChartHelper._(); - - static const fakeData = [ - PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400), - PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500), - PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600), - PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100), - PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500), - PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400), - PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400), - PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100), - PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200), - PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50), - PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130), - PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 80, phaseC: 100), - ]; -} diff --git a/lib/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart b/lib/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart index 5c56d229..567e03ed 100644 --- a/lib/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart +++ b/lib/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart @@ -21,12 +21,13 @@ abstract final class EnergyManagementChartsHelper { reservedSize: 32, showTitles: true, maxIncluded: true, + minIncluded: true, getTitlesWidget: (value, meta) => Padding( padding: const EdgeInsetsDirectional.only(top: 20.0), child: Text( - (value + 1).toString(), + value.toString(), style: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.greyColor, + color: ColorsManager.lightGreyColor, fontSize: 12, ), ), @@ -36,7 +37,8 @@ abstract final class EnergyManagementChartsHelper { leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - maxIncluded: true, + maxIncluded: false, + minIncluded: true, interval: leftTitlesInterval, reservedSize: 110, getTitlesWidget: (value, meta) => Padding( @@ -70,7 +72,7 @@ abstract final class EnergyManagementChartsHelper { static List getTooltipItems(List touchedSpots) { return touchedSpots.map((spot) { return LineTooltipItem( - getToolTipLabel(spot.x + 1, spot.y), + getToolTipLabel(spot.x, spot.y), const TextStyle( color: ColorsManager.textPrimaryColor, fontWeight: FontWeight.w600, @@ -91,31 +93,38 @@ abstract final class EnergyManagementChartsHelper { ); } - static FlBorderData borderData() { - return FlBorderData( - show: true, - border: const Border.symmetric( - horizontal: BorderSide( - color: ColorsManager.greyColor, - style: BorderStyle.solid, - width: 1, - ), - ), - ); - } - static FlGridData gridData() { - return const FlGridData( + return FlGridData( show: true, drawVerticalLine: false, drawHorizontalLine: true, + horizontalInterval: 250, + getDrawingHorizontalLine: (value) { + return FlLine( + color: ColorsManager.greyColor, + strokeWidth: 1, + dashArray: value == 0 ? null : [5, 5], + ); + }, + ); + } + + static FlBorderData borderData() { + return FlBorderData( + border: const Border( + bottom: BorderSide( + color: ColorsManager.greyColor, + style: BorderStyle.solid, + ), + ), + show: true, ); } static LineTouchData lineTouchData() { return LineTouchData( handleBuiltInTouches: true, - touchSpotThreshold: 2, + touchSpotThreshold: 16, touchTooltipData: EnergyManagementChartsHelper.lineTouchTooltipData(), ); } diff --git a/lib/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart b/lib/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart index 96fbb224..a6fe4703 100644 --- a/lib/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart +++ b/lib/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart @@ -26,6 +26,7 @@ abstract final class FetchEnergyManagementDataHelper { required String communityId, required String spaceId, DateTime? selectedDate, + bool shouldFetchAnalyticsDevices = true, }) { if (communityId.isEmpty && spaceId.isEmpty) { clearAllData(context); @@ -34,31 +35,46 @@ abstract final class FetchEnergyManagementDataHelper { final datePickerState = context.read().state; final selectedDate0 = selectedDate ?? datePickerState.monthlyDate; - loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId); + if (shouldFetchAnalyticsDevices) { + loadAnalyticsDevices( + context, + communityUuid: communityId, + spaceUuid: spaceId, + selectedDate: selectedDate0, + ); + loadRealtimeDeviceChanges(context); + loadPowerClampInfo(context); + } loadTotalEnergyConsumption( context, selectedDate: selectedDate0, communityId: communityId, spaceId: spaceId, ); - loadEnergyConsumptionByPhases(context, selectedDate: selectedDate); + final selectedDevice = getSelectedDevice(context); + if (selectedDevice case final AnalyticsDevice device) { + loadEnergyConsumptionByPhases( + context, + powerClampUuid: device.uuid, + selectedDate: selectedDate0, + ); + } loadEnergyConsumptionPerDevice( context, communityId: communityId, spaceId: spaceId, selectedDate: selectedDate0, ); - loadRealtimeDeviceChanges(context); - loadPowerClampInfo(context); } static void loadEnergyConsumptionByPhases( BuildContext context, { + required String powerClampUuid, DateTime? selectedDate, }) { final param = GetEnergyConsumptionByPhasesParam( - startDate: selectedDate, - spaceId: '', + date: selectedDate, + powerClampUuid: powerClampUuid, ); context.read().add( LoadEnergyConsumptionByPhasesEvent(param: param), @@ -121,6 +137,7 @@ abstract final class FetchEnergyManagementDataHelper { BuildContext context, { required String communityUuid, required String spaceUuid, + required DateTime selectedDate, }) { context.read().add( LoadAnalyticsDevicesEvent( @@ -128,6 +145,11 @@ abstract final class FetchEnergyManagementDataHelper { context.read().add( LoadPowerClampInfoEvent(device.uuid), ); + loadEnergyConsumptionByPhases( + context, + powerClampUuid: device.uuid, + selectedDate: selectedDate, + ); context.read().add( RealtimeDeviceChangesStarted(device.uuid), ); diff --git a/lib/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart b/lib/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart index 0d96fb70..65157aa4 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart @@ -48,6 +48,7 @@ class AnalyticsDeviceDropdown extends StatelessWidget { } Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) { + final spaceUuid = state.selectedDevice?.spaceUuid; return DropdownButton( value: state.selectedDevice, isDense: true, @@ -60,10 +61,30 @@ class AnalyticsDeviceDropdown extends StatelessWidget { ), style: _getTextStyle(context), padding: _defaultPadding, + selectedItemBuilder: (context) { + return state.devices.map((e) => Text(e.name)).toList(); + }, items: state.devices.map((e) { return DropdownMenuItem( value: e, - child: Text(e.name), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(e.name), + if (spaceUuid != null) + FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerStart, + child: Text( + spaceUuid, + style: _getTextStyle(context)?.copyWith( + fontSize: 10, + ), + ), + ), + ], + ), ); }).toList(), onChanged: (value) { diff --git a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart.dart b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart.dart index c94755bb..1497d0fd 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_by_phases_chart.dart @@ -1,6 +1,6 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/analytics/helpers/get_month_name_from_int.dart'; +import 'package:intl/intl.dart'; import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -18,7 +18,10 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { Widget build(BuildContext context) { return BarChart( BarChartData( - gridData: EnergyManagementChartsHelper.gridData(), + gridData: EnergyManagementChartsHelper.gridData().copyWith( + checkToShowHorizontalLine: (value) => true, + horizontalInterval: 250, + ), borderData: EnergyManagementChartsHelper.borderData(), barTouchData: _barTouchData(context), titlesData: _titlesData(context), @@ -31,25 +34,29 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { barRods: [ BarChartRodData( color: ColorsManager.vividBlue.withValues(alpha: 0.1), - toY: data.phaseA + data.phaseB + data.phaseC, + toY: data.energyConsumedA + + data.energyConsumedB + + data.energyConsumedC, rodStackItems: [ BarChartRodStackItem( 0, - data.phaseA, + data.energyConsumedA, ColorsManager.vividBlue.withValues(alpha: 0.8), ), BarChartRodStackItem( - data.phaseA, - data.phaseA + data.phaseB, + data.energyConsumedA, + data.energyConsumedA + data.energyConsumedB, ColorsManager.vividBlue.withValues(alpha: 0.4), ), BarChartRodStackItem( - data.phaseA + data.phaseB, - data.phaseA + data.phaseB + data.phaseC, + data.energyConsumedA + data.energyConsumedB, + data.energyConsumedA + + data.energyConsumedB + + data.energyConsumedC, ColorsManager.vividBlue.withValues(alpha: 0.15), ), ], - width: 16, + width: 8, borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), @@ -59,6 +66,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { ); }).toList(), ), + duration: Duration.zero, ); } @@ -91,18 +99,27 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { }) { final data = energyData; - final month = data[group.x.toInt()].month.getMonthName; - final phaseA = data[group.x.toInt()].phaseA; - final phaseB = data[group.x.toInt()].phaseB; - final phaseC = data[group.x.toInt()].phaseC; + 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; return BarTooltipItem( - '$month\n', + '$date\n', context.textTheme.bodyMedium!.copyWith( color: ColorsManager.blackColor, fontSize: 14, ), + textAlign: TextAlign.start, children: [ + TextSpan( + text: 'Total: $total\n', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + ), + ), TextSpan( text: 'Phase A: $phaseA\n', style: context.textTheme.bodySmall?.copyWith( @@ -144,9 +161,9 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, _) { - final month = energyData[value.toInt()].month.getMonthName; + final month = DateFormat('d').format(energyData[value.toInt()].date); return FittedBox( - alignment: AlignmentDirectional.bottomCenter, + alignment: AlignmentDirectional.center, fit: BoxFit.scaleDown, child: RotatedBox( quarterTurns: 3, @@ -160,7 +177,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget { ), ); }, - reservedSize: 36, + reservedSize: 18, ), ); diff --git a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart index 8a779d63..fcf7d384 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart @@ -16,7 +16,11 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget { context, leftTitlesInterval: 250, ), - gridData: EnergyManagementChartsHelper.gridData(), + + gridData: EnergyManagementChartsHelper.gridData().copyWith( + checkToShowHorizontalLine: (value) => true, + horizontalInterval: 250, + ), borderData: EnergyManagementChartsHelper.borderData(), lineTouchData: EnergyManagementChartsHelper.lineTouchData(), lineBarsData: chartData.map((e) { @@ -33,7 +37,7 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget { ); }).toList(), ), - duration: Durations.extralong1, + duration: Duration.zero, curve: Curves.easeIn, ); } diff --git a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart index b62ebe54..f22517d5 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_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_per_device/energy_consumption_per_device_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart'; @@ -46,6 +47,7 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget { flex: 2, child: EnergyConsumptionPerDeviceDevicesList( chartData: state.chartData, + devices: context.watch().state.devices, ), ), ], diff --git a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart index da7a59a8..e6996f53 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart @@ -1,10 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/analytics/models/analytics_device.dart'; import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class EnergyConsumptionPerDeviceDevicesList extends StatelessWidget { - const EnergyConsumptionPerDeviceDevicesList({required this.chartData, super.key}); + const EnergyConsumptionPerDeviceDevicesList({ + required this.chartData, + required this.devices, + super.key, + }); + final List devices; final List chartData; @override @@ -16,45 +22,60 @@ class EnergyConsumptionPerDeviceDevicesList extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, - children: chartData.map((e) => _buildDeviceCell(context, e)).toList(), + children: devices.map((e) => _buildDeviceCell(context, e)).toList(), ), ); } - Widget _buildDeviceCell(BuildContext context, DeviceEnergyDataModel device) { - return Container( - height: MediaQuery.sizeOf(context).height * 0.0365, - padding: const EdgeInsetsDirectional.symmetric( - vertical: 8, - horizontal: 12, - ), - decoration: BoxDecoration( - borderRadius: BorderRadiusDirectional.circular(8), - border: Border.all( - color: ColorsManager.greyColor, - width: 1, + Widget _buildDeviceCell(BuildContext context, AnalyticsDevice device) { + final deviceColor = chartData + .firstWhere( + (element) => element.deviceId == device.uuid, + orElse: () => const DeviceEnergyDataModel( + energy: [], + deviceName: '', + deviceId: '', + color: Colors.red, + ), + ) + .color; + + return Tooltip( + message: '${device.name}\n${device.productDevice?.uuid ?? ''}', + child: Container( + height: MediaQuery.sizeOf(context).height * 0.0365, + padding: const EdgeInsetsDirectional.symmetric( + vertical: 8, + horizontal: 12, ), - ), - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: Alignment.center, - child: Row( - spacing: 6, - children: [ - CircleAvatar( - radius: 4, - backgroundColor: device.color, - ), - Text( - device.deviceName, - textAlign: TextAlign.center, - style: const TextStyle( - color: ColorsManager.blackColor, - fontWeight: FontWeight.w400, - fontSize: 14, + decoration: BoxDecoration( + borderRadius: BorderRadiusDirectional.circular(8), + border: Border.all( + color: ColorsManager.greyColor, + width: 1, + ), + ), + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.center, + child: Row( + spacing: 6, + children: [ + CircleAvatar( + radius: 4, + backgroundColor: deviceColor, ), - ), - ], + Text( + device.name, + textAlign: TextAlign.center, + style: const TextStyle( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ), + ], + ), ), ), ); diff --git a/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart b/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart index 95f343c2..8ae6cd7f 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.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/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/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'; @@ -132,6 +133,12 @@ class PowerClampEnergyDataWidget extends StatelessWidget { alignment: AlignmentDirectional.centerEnd, child: AnalyticsDeviceDropdown( onChanged: (value) { + FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases( + context, + powerClampUuid: value.uuid, + selectedDate: + context.read().state.monthlyDate, + ); FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges( context, deviceUuid: value.uuid, diff --git a/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart b/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart index 27d74ae0..fc957035 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart @@ -48,6 +48,9 @@ class PowerClampEnergyStatusWidget extends StatelessWidget { fontWeight: FontWeight.w400, fontSize: 16, ), + softWrap: true, + maxLines: 2, + overflow: TextOverflow.ellipsis, ), trailing: Text.rich( TextSpan( diff --git a/lib/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart b/lib/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart index 70170180..204261df 100644 --- a/lib/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart +++ b/lib/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart @@ -4,15 +4,6 @@ import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -// energy_consumption_chart will return id, name and consumption -const phasesJson = { - "1": { - "phaseOne": 1000, - "phaseTwo": 2000, - "phaseThree": 3000, - } -}; - class TotalEnergyConsumptionChart extends StatelessWidget { const TotalEnergyConsumptionChart({required this.chartData, super.key}); @@ -23,13 +14,19 @@ class TotalEnergyConsumptionChart extends StatelessWidget { return Expanded( child: LineChart( LineChartData( - titlesData: EnergyManagementChartsHelper.titlesData(context), - gridData: EnergyManagementChartsHelper.gridData(), + titlesData: EnergyManagementChartsHelper.titlesData( + context, + leftTitlesInterval: 250, + ), + gridData: EnergyManagementChartsHelper.gridData().copyWith( + checkToShowHorizontalLine: (value) => true, + horizontalInterval: 250, + ), borderData: EnergyManagementChartsHelper.borderData(), lineTouchData: EnergyManagementChartsHelper.lineTouchData(), lineBarsData: _lineBarsData, ), - duration: Durations.extralong1, + duration: Duration.zero, curve: Curves.easeIn, ), ); @@ -46,7 +43,7 @@ class TotalEnergyConsumptionChart extends StatelessWidget { .entries .map( (entry) => FlSpot( - entry.key.toDouble(), + entry.value.date.day.toDouble(), entry.value.value, ), ) diff --git a/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart b/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart index be21027b..5882ada5 100644 --- a/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart +++ b/lib/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart @@ -28,25 +28,13 @@ abstract final class FetchOccupancyDataHelper { loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId); final selectedDevice = context.read().state.selectedDevice; - context.read().add( - LoadOccupancyEvent( - GetOccupancyParam( - monthDate: - '${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}', - spaceUuid: spaceId, - communityUuid: communityId, - ), - ), - ); - - context.read().add( - LoadOccupancyHeatMapEvent( - GetOccupancyHeatMapParam( - spaceUuid: spaceId, - year: datePickerState.yearlyDate, - ), - ), - ); + loadOccupancyChartData( + context, + communityUuid: communityId, + spaceUuid: spaceId, + date: datePickerState.monthlyDate, + ); + loadHeatMapData(context, spaceUuid: spaceId, year: datePickerState.yearlyDate); if (selectedDevice case final AnalyticsDevice device) { context.read() @@ -57,6 +45,35 @@ abstract final class FetchOccupancyDataHelper { } } + static void loadHeatMapData( + BuildContext context, { + required String spaceUuid, + required DateTime year, + }) { + context.read().add( + LoadOccupancyHeatMapEvent( + GetOccupancyHeatMapParam(spaceUuid: spaceUuid, year: year), + ), + ); + } + + static void loadOccupancyChartData( + BuildContext context, { + required String communityUuid, + required String spaceUuid, + required DateTime date, + }) { + context.read().add( + LoadOccupancyEvent( + GetOccupancyParam( + monthDate: '${date.year}-${date.month}', + spaceUuid: spaceUuid, + communityUuid: communityUuid, + ), + ), + ); + } + static void loadAnalyticsDevices( BuildContext context, { required String communityUuid, diff --git a/lib/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart b/lib/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart index df4ba4a8..679c9927 100644 --- a/lib/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart +++ b/lib/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart @@ -20,9 +20,9 @@ class AnalyticsOccupancyView extends StatelessWidget { child: Column( spacing: 32, children: [ - SizedBox(height: height * 0.45, child: const OccupancyEndSideBar()), + SizedBox(height: height * 0.46, child: const OccupancyEndSideBar()), SizedBox(height: height * 0.5, child: const OccupancyChartBox()), - SizedBox(height: height * 0.5, child: const Placeholder()), + SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()), ], ), ); diff --git a/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart b/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart index dc1357bc..3e362f5d 100644 --- a/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart +++ b/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart @@ -19,11 +19,18 @@ class OccupancyChart extends StatelessWidget { maxY: 1.0, gridData: EnergyManagementChartsHelper.gridData().copyWith( checkToShowHorizontalLine: (value) => true, - horizontalInterval: 0.25, + horizontalInterval: 0.2, ), borderData: EnergyManagementChartsHelper.borderData(), barTouchData: _barTouchData(context), - titlesData: _titlesData(context), + titlesData: _titlesData(context).copyWith( + leftTitles: _titlesData(context).leftTitles.copyWith( + sideTitles: _titlesData(context).leftTitles.sideTitles.copyWith( + maxIncluded: true, + minIncluded: true, + ), + ), + ), barGroups: List.generate(chartData.length, (index) { final actual = chartData[index]; return BarChartGroupData( @@ -101,7 +108,7 @@ class OccupancyChart extends StatelessWidget { final leftTitles = titlesData.leftTitles.copyWith( sideTitles: titlesData.leftTitles.sideTitles.copyWith( reservedSize: 70, - interval: 0.25, + interval: 0.2, getTitlesWidget: (value, meta) => Padding( padding: const EdgeInsetsDirectional.only(end: 12), child: FittedBox( diff --git a/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart b/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart index 464a05dd..8c41da48 100644 --- a/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart +++ b/lib/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart @@ -47,11 +47,17 @@ class OccupancyChartBox extends StatelessWidget { context.read().add( UpdateAnalyticsDatePickerEvent(montlyDate: value), ); - FetchOccupancyDataHelper.loadOccupancyData( - context, - communityId: spaceTreeState.selectedCommunities.firstOrNull ?? '', - spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '', - ); + if (spaceTreeState.selectedSpaces.isNotEmpty) { + FetchOccupancyDataHelper.loadOccupancyChartData( + context, + communityUuid: + spaceTreeState.selectedCommunities.firstOrNull ?? + '', + spaceUuid: + spaceTreeState.selectedSpaces.firstOrNull ?? '', + date: value, + ); + } }, selectedDate: context .watch() diff --git a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart index d52604bf..cab9eab4 100644 --- a/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart +++ b/lib/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart @@ -47,12 +47,14 @@ class OccupancyHeatMapBox extends StatelessWidget { context.read().add( UpdateAnalyticsDatePickerEvent(yearlyDate: value), ); - FetchOccupancyDataHelper.loadOccupancyData( - context, - communityId: - spaceTreeState.selectedCommunities.firstOrNull ?? '', - spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '', - ); + if (spaceTreeState.selectedSpaces.isNotEmpty) { + FetchOccupancyDataHelper.loadHeatMapData( + context, + spaceUuid: + spaceTreeState.selectedSpaces.firstOrNull ?? '', + year: value, + ); + } }, datePickerType: DatePickerType.year, selectedDate: context diff --git a/lib/pages/analytics/params/get_energy_consumption_by_phases_param.dart b/lib/pages/analytics/params/get_energy_consumption_by_phases_param.dart index 169e2753..243e156e 100644 --- a/lib/pages/analytics/params/get_energy_consumption_by_phases_param.dart +++ b/lib/pages/analytics/params/get_energy_consumption_by_phases_param.dart @@ -1,24 +1,20 @@ import 'package:equatable/equatable.dart'; class GetEnergyConsumptionByPhasesParam extends Equatable { - final DateTime? startDate; - final DateTime? endDate; - final String? spaceId; + final String powerClampUuid; + final DateTime? date; const GetEnergyConsumptionByPhasesParam({ - this.startDate, - this.endDate, - this.spaceId, + required this.powerClampUuid, + this.date, }); Map toJson() { return { - 'startDate': startDate?.toIso8601String(), - 'endDate': endDate?.toIso8601String(), - 'spaceId': spaceId, + 'monthDate': '${date?.year}-${date?.month.toString().padLeft(2, '0')}', }; } @override - List get props => [startDate, endDate, spaceId]; + List get props => [powerClampUuid, date]; } diff --git a/lib/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart b/lib/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart index 95a4aacf..91bbe1f4 100644 --- a/lib/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart +++ b/lib/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart @@ -39,7 +39,6 @@ class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService 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, diff --git a/lib/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart b/lib/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart deleted file mode 100644 index f6ce67c9..00000000 --- a/lib/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart'; -import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart'; -import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/energy_consumption_by_phases_service.dart'; - -class FakeEnergyConsumptionByPhasesService - implements EnergyConsumptionByPhasesService { - @override - Future> load( - GetEnergyConsumptionByPhasesParam param, - ) { - return Future.delayed( - const Duration(milliseconds: 500), - () => const [ - PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400), - PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500), - PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600), - PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100), - PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500), - PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400), - PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400), - PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100), - PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200), - PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50), - PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130), - PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 100, phaseC: 100), - ], - ); - } -} diff --git a/lib/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart b/lib/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart index f0ac31ed..28df5eed 100644 --- a/lib/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart +++ b/lib/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart @@ -15,8 +15,9 @@ final class RemoteEnergyConsumptionByPhasesService ) async { try { final response = await _httpService.get( - path: 'endpoint', + path: '/power-clamp/${param.powerClampUuid}/historical', showServerMessage: true, + queryParameters: param.toJson(), expectedResponseModel: (data) { final json = data as Map? ?? {}; final mappedData = json['data'] as List? ?? []; @@ -28,7 +29,7 @@ final class RemoteEnergyConsumptionByPhasesService ); return response; } catch (e) { - throw Exception('Failed to load energy consumption per device: $e'); + throw Exception('Failed to load energy consumption per phase: $e'); } } } diff --git a/lib/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart b/lib/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart deleted file mode 100644 index b1608eea..00000000 --- a/lib/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:math' as math show Random; - -import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart'; -import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart'; -import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart'; -import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/energy_consumption_per_device_service.dart'; - -class FakeEnergyConsumptionPerDeviceService - implements EnergyConsumptionPerDeviceService { - @override - Future> load( - GetEnergyConsumptionPerDeviceParam param, - ) { - final random = math.Random(); - return Future.delayed(const Duration(milliseconds: 500), () { - return [ - (Colors.redAccent, 1), - (Colors.lightBlueAccent, 2), - (Colors.purpleAccent, 3), - ].map((e) { - final (color, index) = e; - return DeviceEnergyDataModel( - color: color, - energy: List.generate(30, (i) => i) - .map( - (index) => EnergyDataModel( - date: DateTime(2025, 1, index + 1), - value: random.nextInt(100) + (index * 100), - ), - ) - .toList(), - deviceName: 'Device $index', - deviceId: 'device_$index', - ); - }).toList(); - }); - } -} diff --git a/lib/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart b/lib/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart index 8ceb6d80..165ab5ab 100644 --- a/lib/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart +++ b/lib/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart @@ -34,10 +34,7 @@ abstract final class _EnergyConsumptionPerDeviceMapper { static List map(dynamic data) { final json = data as Map? ?? {}; final mappedData = json['data'] as List? ?? []; - return mappedData.where((e) { - final deviceData = (e as Map)['data'] as List? ?? []; - return deviceData.isNotEmpty; - }).map((e) { + return mappedData.map((e) { final deviceData = e as Map; final energyData = deviceData['data'] as List; diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 60abc0d2..3ec902ef 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -63,7 +63,8 @@ class _DynamicTableState extends State { } } - bool _compareListOfLists(List> oldList, List> newList) { + bool _compareListOfLists( + List> oldList, List> newList) { // Check if the old and new lists are the same if (oldList.length != newList.length) return false; @@ -132,46 +133,60 @@ class _DynamicTableState extends State { children: [ if (widget.withCheckBox) _buildSelectAllCheckbox(), ...List.generate(widget.headers.length, (index) { - return _buildTableHeaderCell(widget.headers[index], index); + return _buildTableHeaderCell( + widget.headers[index], index); }) //...widget.headers.map((header) => _buildTableHeaderCell(header)), ], ), ), widget.isEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - children: [ - SvgPicture.asset(Assets.emptyTable), - const SizedBox( - height: 15, - ), - Text( - widget.tableName == 'AccessManagement' ? 'No Password ' : 'No Devices', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor), - ) - ], - ), - ], - ), - ], + ? SizedBox( + height: widget.size.height * 0.5, + width: widget.size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + SvgPicture.asset(Assets.emptyTable), + const SizedBox( + height: 15, + ), + Text( + widget.tableName == 'AccessManagement' + ? 'No Password ' + : 'No Devices', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: + ColorsManager.grayColor), + ) + ], + ), + ], + ), + ], + ), ) : Column( - children: List.generate(widget.data.length, (index) { + children: + List.generate(widget.data.length, (index) { final row = widget.data[index]; return Row( children: [ - if (widget.withCheckBox) _buildRowCheckbox(index, widget.size.height * 0.08), - ...row.map((cell) => _buildTableCell(cell.toString(), widget.size.height * 0.08)), + if (widget.withCheckBox) + _buildRowCheckbox( + index, widget.size.height * 0.08), + ...row.map((cell) => _buildTableCell( + cell.toString(), + widget.size.height * 0.08)), ], ); }), @@ -196,7 +211,9 @@ class _DynamicTableState extends State { ), child: Checkbox( value: _selectAll, - onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null, + onChanged: widget.withSelectAll && widget.data.isNotEmpty + ? _toggleSelectAll + : null, ), ); } @@ -238,7 +255,9 @@ class _DynamicTableState extends State { constraints: const BoxConstraints.expand(height: 40), alignment: Alignment.centerLeft, child: Padding( - padding: EdgeInsets.symmetric(horizontal: index == widget.headers.length - 1 ? 12 : 8.0, vertical: 4), + padding: EdgeInsets.symmetric( + horizontal: index == widget.headers.length - 1 ? 12 : 8.0, + vertical: 4), child: Text( title, style: context.textTheme.titleSmall!.copyWith( diff --git a/lib/pages/device_managment/shared/device_control_dialog.dart b/lib/pages/device_managment/shared/device_control_dialog.dart index 7304dd07..c9cd4648 100644 --- a/lib/pages/device_managment/shared/device_control_dialog.dart +++ b/lib/pages/device_managment/shared/device_control_dialog.dart @@ -97,7 +97,8 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode { children: [ _buildInfoRow('Space Name:', device.spaces?.firstOrNull?.spaceName ?? 'N/A'), - _buildInfoRow('Room:', device.subspace?.subspaceName ?? 'N/A'), + _buildInfoRow( + 'Sub space:', device.subspace?.subspaceName ?? 'N/A'), ], ), TableRow( diff --git a/lib/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart b/lib/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart index bb4a7a1e..659d3261 100644 --- a/lib/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart +++ b/lib/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart @@ -26,8 +26,10 @@ class FunctionBloc extends Bloc { functionCode: event.functionData.functionCode, operationName: event.functionData.operationName, value: event.functionData.value ?? existingData.value, - valueDescription: event.functionData.valueDescription ?? existingData.valueDescription, + valueDescription: event.functionData.valueDescription ?? + existingData.valueDescription, condition: event.functionData.condition ?? existingData.condition, + step: event.functionData.step ?? existingData.step, ); } else { functions.clear(); @@ -59,8 +61,10 @@ class FunctionBloc extends Bloc { ); } - FutureOr _onSelectFunction(SelectFunction event, Emitter emit) { + FutureOr _onSelectFunction( + SelectFunction event, Emitter emit) { emit(state.copyWith( - selectedFunction: event.functionCode, selectedOperationName: event.operationName)); + selectedFunction: event.functionCode, + selectedOperationName: event.operationName)); } } diff --git a/lib/pages/routines/models/ac/ac_function.dart b/lib/pages/routines/models/ac/ac_function.dart index 0b534e88..edc377dd 100644 --- a/lib/pages/routines/models/ac/ac_function.dart +++ b/lib/pages/routines/models/ac/ac_function.dart @@ -14,6 +14,10 @@ abstract class ACFunction extends DeviceFunction { required super.operationName, required super.icon, required this.type, + super.step, + super.unit, + super.max, + super.min, }); List getOperationalValues(); @@ -75,26 +79,24 @@ class ModeFunction extends ACFunction { } class TempSetFunction extends ACFunction { - final int min; - final int max; - final int step; - - TempSetFunction( - {required super.deviceId, required super.deviceName, required type}) - : min = 160, - max = 300, - step = 1, - super( + TempSetFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( code: 'temp_set', operationName: 'Set Temperature', icon: Assets.assetsTempreture, - type: type, + min: 200, + max: 300, + step: 1, + unit: "°C", ); @override List getOperationalValues() { List values = []; - for (int temp = min; temp <= max; temp += step) { + for (int temp = min!.toInt(); temp <= max!; temp += step!.toInt()) { values.add(ACOperationalValue( icon: Assets.assetsTempreture, description: "${temp / 10}°C", @@ -104,7 +106,6 @@ class TempSetFunction extends ACFunction { return values; } } - class LevelFunction extends ACFunction { LevelFunction( {required super.deviceId, required super.deviceName, required type}) @@ -166,9 +167,10 @@ class ChildLockFunction extends ACFunction { } class CurrentTempFunction extends ACFunction { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; + final String unit = "°C"; CurrentTempFunction( {required super.deviceId, required super.deviceName, required type}) @@ -185,7 +187,7 @@ class CurrentTempFunction extends ACFunction { @override List getOperationalValues() { List values = []; - for (int temp = min; temp <= max; temp += step) { + for (int temp = min.toInt(); temp <= max; temp += step.toInt()) { values.add(ACOperationalValue( icon: Assets.currentTemp, description: "${temp / 10}°C", diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 6dbe5cf6..122d8ea1 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -6,10 +6,12 @@ class CpsOperationalValue { final String description; final dynamic value; + CpsOperationalValue({ required this.icon, required this.description, required this.value, + }); } @@ -94,9 +96,9 @@ final class CpsSensitivityFunction extends CpsFunctions { icon: Assets.sensitivity, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; static const _images = [ Assets.sensitivityFeature1, @@ -115,10 +117,10 @@ final class CpsSensitivityFunction extends CpsFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min; value <= max; value += step.toInt()) { values.add( CpsOperationalValue( - icon: _images[value], + icon: _images[value.toInt()], description: '$value', value: value, ), @@ -142,9 +144,9 @@ final class CpsMovingSpeedFunction extends CpsFunctions { icon: Assets.speedoMeter, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; @override List getOperationalValues() { @@ -173,9 +175,9 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { icon: Assets.spatialStaticValue, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; @override List getOperationalValues() { @@ -204,9 +206,9 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { icon: Assets.spatialMotionValue, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; @override List getOperationalValues() { @@ -375,9 +377,9 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { icon: Assets.presenceJudgementThrshold, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; @override List getOperationalValues() { @@ -406,9 +408,9 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { icon: Assets.presenceJudgementThrshold, ); - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; @override List getOperationalValues() { diff --git a/lib/pages/routines/models/device_functions.dart b/lib/pages/routines/models/device_functions.dart index 59b63a4f..b895dccc 100644 --- a/lib/pages/routines/models/device_functions.dart +++ b/lib/pages/routines/models/device_functions.dart @@ -4,6 +4,11 @@ abstract class DeviceFunction { final String code; final String operationName; final String icon; + final double? step; + final String? unit; + final double? max; + final double? min; + DeviceFunction({ required this.deviceId, @@ -11,6 +16,10 @@ abstract class DeviceFunction { required this.code, required this.operationName, required this.icon, + this.step, + this.unit, + this.max, + this.min, }); } @@ -22,6 +31,10 @@ class DeviceFunctionData { final dynamic value; final String? condition; final String? valueDescription; + final double? step; + final String? unit; + final double? max; + final double? min; DeviceFunctionData({ required this.entityId, @@ -31,6 +44,10 @@ class DeviceFunctionData { required this.value, this.condition, this.valueDescription, + this.step, + this.unit, + this.max, + this.min, }); Map toJson() { @@ -42,6 +59,10 @@ class DeviceFunctionData { 'value': value, if (condition != null) 'condition': condition, if (valueDescription != null) 'valueDescription': valueDescription, + if (step != null) 'step': step, + if (unit != null) 'unit': unit, + if (max != null) 'max': max, + if (min != null) 'min': min, }; } @@ -54,6 +75,10 @@ class DeviceFunctionData { value: json['value'], condition: json['condition'], valueDescription: json['valueDescription'], + step: json['step']?.toDouble(), + unit: json['unit'], + max: json['max']?.toDouble(), + min: json['min']?.toDouble(), ); } @@ -68,7 +93,11 @@ class DeviceFunctionData { other.operationName == operationName && other.value == value && other.condition == condition && - other.valueDescription == valueDescription; + other.valueDescription == valueDescription && + other.step == step && + other.unit == unit && + other.max == max && + other.min == min; } @override @@ -79,6 +108,10 @@ class DeviceFunctionData { operationName.hashCode ^ value.hashCode ^ condition.hashCode ^ - valueDescription.hashCode; + valueDescription.hashCode ^ + step.hashCode ^ + unit.hashCode ^ + max.hashCode ^ + min.hashCode; } } diff --git a/lib/pages/routines/models/flush/flush_functions.dart b/lib/pages/routines/models/flush/flush_functions.dart index 5013c0b8..a8f6ccd4 100644 --- a/lib/pages/routines/models/flush/flush_functions.dart +++ b/lib/pages/routines/models/flush/flush_functions.dart @@ -20,12 +20,11 @@ abstract class FlushFunctions } class FlushPresenceDelayFunction extends FlushFunctions { - final int min; FlushPresenceDelayFunction({ required super.deviceId, required super.deviceName, required super.type, - }) : min = 0, + }) : super( code: FlushMountedPresenceSensorModel.codePresenceState, operationName: 'Presence State', @@ -50,9 +49,9 @@ class FlushPresenceDelayFunction extends FlushFunctions { } class FlushSensiReduceFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; FlushSensiReduceFunction({ required super.deviceId, @@ -80,8 +79,8 @@ class FlushSensiReduceFunction extends FlushFunctions { } class FlushNoneDelayFunction extends FlushFunctions { - final int min; - final int max; + final double min; + final double max; final String unit; FlushNoneDelayFunction({ @@ -110,9 +109,9 @@ class FlushNoneDelayFunction extends FlushFunctions { } class FlushIlluminanceFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; FlushIlluminanceFunction({ required super.deviceId, @@ -130,7 +129,7 @@ class FlushIlluminanceFunction extends FlushFunctions { @override List getOperationalValues() { List values = []; - for (int lux = min; lux <= max; lux += step) { + for (int lux = min.toInt(); lux <= max; lux += step.toInt()) { values.add(FlushOperationalValue( icon: Assets.IlluminanceIcon, description: "$lux Lux", @@ -142,9 +141,9 @@ class FlushIlluminanceFunction extends FlushFunctions { } class FlushOccurDistReduceFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; FlushOccurDistReduceFunction({ required super.deviceId, @@ -173,9 +172,9 @@ class FlushOccurDistReduceFunction extends FlushFunctions { // ==== then functions ==== class FlushSensitivityFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; FlushSensitivityFunction({ required super.deviceId, @@ -203,9 +202,9 @@ class FlushSensitivityFunction extends FlushFunctions { } class FlushNearDetectionFunction extends FlushFunctions { - final int min; + final double min; final double max; - final int step; + final double step; final String unit; FlushNearDetectionFunction({ @@ -225,7 +224,7 @@ class FlushNearDetectionFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min.toDouble(); value <= max; value += step) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', @@ -237,9 +236,9 @@ class FlushNearDetectionFunction extends FlushFunctions { } class FlushMaxDetectDistFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; FlushMaxDetectDistFunction({ @@ -259,7 +258,7 @@ class FlushMaxDetectDistFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min; value <= max; value += step.toInt()) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', @@ -271,9 +270,9 @@ class FlushMaxDetectDistFunction extends FlushFunctions { } class FlushTargetConfirmTimeFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; FlushTargetConfirmTimeFunction({ @@ -293,7 +292,7 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min.toDouble(); value <= max; value += step) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', @@ -305,9 +304,9 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions { } class FlushDisappeDelayFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; FlushDisappeDelayFunction({ @@ -327,7 +326,7 @@ class FlushDisappeDelayFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min.toDouble(); value <= max; value += step) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', @@ -339,9 +338,9 @@ class FlushDisappeDelayFunction extends FlushFunctions { } class FlushIndentLevelFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; FlushIndentLevelFunction({ @@ -361,7 +360,7 @@ class FlushIndentLevelFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min.toDouble(); value <= max; value += step) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', @@ -373,9 +372,9 @@ class FlushIndentLevelFunction extends FlushFunctions { } class FlushTriggerLevelFunction extends FlushFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; FlushTriggerLevelFunction({ @@ -395,7 +394,7 @@ class FlushTriggerLevelFunction extends FlushFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min.toDouble(); value <= max; value += step) { values.add(FlushOperationalValue( icon: Assets.nobodyTime, description: '$value $unit', diff --git a/lib/pages/routines/models/water_heater/water_heater_functions.dart b/lib/pages/routines/models/water_heater/water_heater_functions.dart index 571e15f7..7ebea019 100644 --- a/lib/pages/routines/models/water_heater/water_heater_functions.dart +++ b/lib/pages/routines/models/water_heater/water_heater_functions.dart @@ -20,17 +20,16 @@ abstract class WaterHeaterFunctions } class WHRestartStatusFunction extends WaterHeaterFunctions { - final int min; WHRestartStatusFunction({ required super.deviceId, required super.deviceName, required super.type, - }) : min = 0, - super( + }) : super( code: 'relay_status', operationName: 'Restart Status', icon: Assets.refreshStatusIcon, ); + @override List getOperationalValues() { @@ -55,13 +54,11 @@ class WHRestartStatusFunction extends WaterHeaterFunctions { } class WHSwitchFunction extends WaterHeaterFunctions { - final int min; WHSwitchFunction({ required super.deviceId, required super.deviceName, required super.type, - }) : min = 0, - super( + }) : super( code: 'switch_1', operationName: 'Switch', icon: Assets.assetsAcPower, @@ -104,12 +101,11 @@ class TimerConfirmTimeFunction extends WaterHeaterFunctions { } class BacklightFunction extends WaterHeaterFunctions { - final int min; BacklightFunction({ required super.deviceId, required super.deviceName, required super.type, - }) : min = 0, + }) : super( code: 'switch_backlight', operationName: 'Backlight', diff --git a/lib/pages/routines/models/wps/wps_functions.dart b/lib/pages/routines/models/wps/wps_functions.dart index 8907927c..101c5cf0 100644 --- a/lib/pages/routines/models/wps/wps_functions.dart +++ b/lib/pages/routines/models/wps/wps_functions.dart @@ -4,7 +4,7 @@ import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart import 'package:syncrow_web/utils/constants/assets.dart'; abstract class WpsFunctions extends DeviceFunction { - final String type; + final String type; WpsFunctions({ required super.deviceId, @@ -13,6 +13,10 @@ abstract class WpsFunctions extends DeviceFunction { required super.operationName, required super.icon, required this.type, + super.step, + super.unit, + super.max, + super.min, }); List getOperationalValues(); @@ -20,9 +24,13 @@ abstract class WpsFunctions extends DeviceFunction { // For far_detection (75-600cm in 75cm steps) class FarDetectionFunction extends WpsFunctions { - final int min; - final int max; - final int step; + + final double min; + @override + final double max; + @override + final double step; + @override final String unit; FarDetectionFunction( @@ -41,7 +49,7 @@ class FarDetectionFunction extends WpsFunctions { @override List getOperationalValues() { final values = []; - for (var value = min; value <= max; value += step) { + for (var value = min; value <= max; value += step.toInt()) { values.add(WpsOperationalValue( icon: Assets.currentDistanceIcon, description: '$value $unit', @@ -54,9 +62,9 @@ class FarDetectionFunction extends WpsFunctions { // For presence_time (0-65535 minutes) class PresenceTimeFunction extends WpsFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; final String unit; PresenceTimeFunction( @@ -86,9 +94,9 @@ class PresenceTimeFunction extends WpsFunctions { // For motion_sensitivity_value (1-5 levels) class MotionSensitivityFunction extends WpsFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; MotionSensitivityFunction( {required super.deviceId, required super.deviceName, required type}) @@ -116,9 +124,9 @@ class MotionSensitivityFunction extends WpsFunctions { } class MotionLessSensitivityFunction extends WpsFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; MotionLessSensitivityFunction( {required super.deviceId, required super.deviceName, required type}) @@ -171,8 +179,8 @@ class IndicatorFunction extends WpsFunctions { } class NoOneTimeFunction extends WpsFunctions { - final int min; - final int max; + final double min; + final double max; final String unit; NoOneTimeFunction( @@ -225,9 +233,9 @@ class PresenceStateFunction extends WpsFunctions { } class CurrentDistanceFunction extends WpsFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; CurrentDistanceFunction( {required super.deviceId, required super.deviceName, required type}) @@ -244,11 +252,10 @@ class CurrentDistanceFunction extends WpsFunctions { @override List getOperationalValues() { List values = []; - for (int cm = min; cm <= max; cm += step) { + for (int cm = min.toInt(); cm <= max; cm += step.toInt()) { values.add(WpsOperationalValue( icon: Assets.assetsTempreture, description: "${cm}CM", - value: cm, )); } @@ -257,9 +264,9 @@ class CurrentDistanceFunction extends WpsFunctions { } class IlluminanceValueFunction extends WpsFunctions { - final int min; - final int max; - final int step; + final double min; + final double max; + final double step; IlluminanceValueFunction({ required super.deviceId, @@ -277,7 +284,7 @@ class IlluminanceValueFunction extends WpsFunctions { @override List getOperationalValues() { List values = []; - for (int lux = min; lux <= max; lux += step) { + for (int lux = min.toInt(); lux <= max; lux += step.toInt()) { values.add(WpsOperationalValue( icon: Assets.IlluminanceIcon, description: "$lux Lux", diff --git a/lib/pages/routines/widgets/custom_routines_textbox.dart b/lib/pages/routines/widgets/custom_routines_textbox.dart new file mode 100644 index 00000000..e9ada1c2 --- /dev/null +++ b/lib/pages/routines/widgets/custom_routines_textbox.dart @@ -0,0 +1,297 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart'; +import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class CustomRoutinesTextbox extends StatefulWidget { + final String? currentCondition; + final String dialogType; + final (double, double) sliderRange; + final dynamic displayedValue; + final dynamic initialValue; + final void Function(String condition) onConditionChanged; + final void Function(double value) onTextChanged; + final String unit; + final double dividendOfRange; + final double stepIncreaseAmount; + final bool withSpecialChar; + + const CustomRoutinesTextbox({ + required this.dialogType, + required this.sliderRange, + required this.displayedValue, + required this.initialValue, + required this.onConditionChanged, + required this.onTextChanged, + required this.currentCondition, + required this.unit, + required this.dividendOfRange, + required this.stepIncreaseAmount, + required this.withSpecialChar, + super.key, + }); + + @override + State createState() => _CustomRoutinesTextboxState(); +} + +class _CustomRoutinesTextboxState extends State { + late final TextEditingController _controller; + bool hasError = false; + String? errorMessage; + + int getDecimalPlaces(double step) { + String stepStr = step.toString(); + if (stepStr.contains('.')) { + List parts = stepStr.split('.'); + String decimalPart = parts[1]; + decimalPart = decimalPart.replaceAll(RegExp(r'0+$'), ''); + return decimalPart.isEmpty ? 0 : decimalPart.length; + } else { + return 0; + } + } + + @override + void initState() { + super.initState(); + int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount); + double initialValue; + if (widget.initialValue != null && + widget.initialValue is num && + (widget.initialValue as num) == 0) { + initialValue = 0.0; + } else { + initialValue = double.tryParse(widget.displayedValue) ?? 0.0; + } + _controller = TextEditingController( + text: initialValue.toStringAsFixed(decimalPlaces), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _validateInput(String value) { + final doubleValue = double.tryParse(value); + if (doubleValue == null) { + setState(() { + errorMessage = "Invalid number"; + hasError = true; + }); + return; + } + + final min = widget.sliderRange.$1; + final max = widget.sliderRange.$2; + + if (doubleValue < min) { + setState(() { + errorMessage = "Value must be at least $min"; + hasError = true; + }); + } else if (doubleValue > max) { + setState(() { + errorMessage = "Value must be at most $max"; + hasError = true; + }); + } else { + int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount); + int factor = pow(10, decimalPlaces).toInt(); + int scaledStep = (widget.stepIncreaseAmount * factor).round(); + int scaledValue = (doubleValue * factor).round(); + + if (scaledValue % scaledStep != 0) { + setState(() { + errorMessage = "must be a multiple of ${widget.stepIncreaseAmount}"; + hasError = true; + }); + } else { + setState(() { + errorMessage = null; + hasError = false; + }); + } + } + } + + @override + void didUpdateWidget(CustomRoutinesTextbox oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.initialValue != oldWidget.initialValue) { + if (widget.initialValue != null && + widget.initialValue is num && + (widget.initialValue as num) == 0) { + int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount); + _controller.text = 0.0.toStringAsFixed(decimalPlaces); + } + } + } + + void _correctAndUpdateValue(String value) { + final doubleValue = double.tryParse(value) ?? 0.0; + int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount); + double rounded = (doubleValue / widget.stepIncreaseAmount).round() * + widget.stepIncreaseAmount; + rounded = rounded.clamp(widget.sliderRange.$1, widget.sliderRange.$2); + rounded = double.parse(rounded.toStringAsFixed(decimalPlaces)); + + setState(() { + hasError = false; + errorMessage = null; + }); + + _controller.text = rounded.toStringAsFixed(decimalPlaces); + _controller.selection = TextSelection.fromPosition( + TextPosition(offset: _controller.text.length), + ); + widget.onTextChanged(rounded); + } + + @override + Widget build(BuildContext context) { + int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount); + + List formatters = []; + if (decimalPlaces == 0) { + formatters.add(FilteringTextInputFormatter.digitsOnly); + } else { + formatters.add(FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d{0,' + decimalPlaces.toString() + r'}$'), + )); + } + formatters.add(RangeInputFormatter( + min: widget.sliderRange.$1, + max: widget.sliderRange.$2, + )); + + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.dialogType == 'IF') + ConditionToggle( + currentCondition: widget.currentCondition, + onChanged: widget.onConditionChanged, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 2), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + 'Step: ${widget.stepIncreaseAmount}', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.grayColor, + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Center( + child: Container( + width: 170, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + decoration: BoxDecoration( + color: const Color(0xFFF8F8F8), + borderRadius: BorderRadius.circular(20), + border: hasError + ? Border.all(color: Colors.red, width: 1) + : Border.all( + color: ColorsManager.lightGrayBorderColor, width: 1), + boxShadow: [ + BoxShadow( + color: ColorsManager.blackColor.withOpacity(0.05), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + children: [ + Expanded( + child: TextFormField( + controller: _controller, + style: context.textTheme.bodyLarge?.copyWith( + fontSize: 20, + fontWeight: FontWeight.bold, + color: ColorsManager.blackColor, + ), + keyboardType: TextInputType.number, + inputFormatters: widget.withSpecialChar == true + ? [FilteringTextInputFormatter.digitsOnly] + : null, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + contentPadding: EdgeInsets.zero, + ), + onChanged: _validateInput, + onFieldSubmitted: _correctAndUpdateValue, + onTapOutside: (_) => + _correctAndUpdateValue(_controller.text), + ), + ), + const SizedBox(width: 12), + Text( + widget.unit, + style: context.textTheme.bodyMedium?.copyWith( + fontSize: 20, + fontWeight: FontWeight.bold, + color: ColorsManager.vividBlue, + ), + ), + ], + ), + ), + ), + if (errorMessage != null) + Padding( + padding: const EdgeInsets.only(top: 2.0), + child: Text( + errorMessage!, + style: context.textTheme.bodySmall?.copyWith( + color: Colors.red, + fontSize: 10, + ), + ), + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Min. ${widget.sliderRange.$1.toInt()}${widget.unit}', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.grayColor, + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + Text( + 'Max. ${widget.sliderRange.$2.toInt()}${widget.unit}', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.grayColor, + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + const SizedBox(height: 16), + ], + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/ac_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/ac_dialog.dart index 1de9b0d4..fc58500e 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ac_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ac_dialog.dart @@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart'; @@ -76,24 +77,20 @@ class ACHelper { context: context, acFunctions: acFunctions, device: device, - onFunctionSelected: (functionCode, operationName) { + onFunctionSelected: + (functionCode, operationName) { RoutineTapFunctionHelper.onTapFunction( - context, - functionCode: functionCode, - functionOperationName: operationName, - functionValueDescription: - selectedFunctionData.valueDescription, - deviceUuid: device?.uuid, - codesToAddIntoFunctionsWithDefaultValue: [ - 'temp_set', - 'temp_current', - ], - defaultValue: functionCode == 'temp_set' - ? 200 - : functionCode == 'temp_current' - ? -100 - : 0, - ); + context, + functionCode: functionCode, + functionOperationName: operationName, + functionValueDescription: + selectedFunctionData.valueDescription, + deviceUuid: device?.uuid, + codesToAddIntoFunctionsWithDefaultValue: [ + 'temp_set', + 'temp_current', + ], + defaultValue: 0); }, ), ), @@ -206,27 +203,61 @@ class ACHelper { required String operationName, bool? removeComparators, }) { - final initialVal = selectedFunction == 'temp_set' ? 200 : -100; + final selectedFn = + acFunctions.firstWhere((f) => f.code == selectedFunction); + if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') { - final initialValue = selectedFunctionData?.value ?? initialVal; - return _buildTemperatureSelector( - context: context, - initialValue: initialValue, - selectCode: selectedFunction, + // Convert stored integer value to display value + final displayValue = + (selectedFunctionData?.value ?? selectedFn.min ?? 0) / 10; + final minValue = selectedFn.min! / 10; + final maxValue = selectedFn.max! / 10; + return CustomRoutinesTextbox( + withSpecialChar: true, + dividendOfRange: maxValue, currentCondition: selectedFunctionData?.condition, - device: device, - operationName: operationName, - selectedFunctionData: selectedFunctionData, - removeComparators: removeComparators, + dialogType: selectedFn.type, + sliderRange: (minValue, maxValue), + displayedValue: displayValue.toStringAsFixed(1), + initialValue: displayValue.toDouble(), + unit: selectedFn.unit!, + onConditionChanged: (condition) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: selectedFn.operationName, + condition: condition, + value: 0, + step: selectedFn.step, + unit: selectedFn.unit, + max: selectedFn.max, + min: selectedFn.min, + ), + ), + ), + onTextChanged: (value) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: selectedFn.operationName, + value: (value * 10).round(), // Store as integer + condition: selectedFunctionData?.condition, + step: selectedFn.step, + unit: selectedFn.unit, + max: selectedFn.max, + min: selectedFn.min, + ), + ), + ), + stepIncreaseAmount: selectedFn.step! / 10, // Convert step for display ); } - final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction); - final values = selectedFn.getOperationalValues(); - return _buildOperationalValuesList( context: context, - values: values, + values: selectedFn.getOperationalValues(), selectedValue: selectedFunctionData?.value, device: device, operationName: operationName, @@ -235,150 +266,151 @@ class ACHelper { ); } - /// Build temperature selector for AC functions dialog - static Widget _buildTemperatureSelector({ - required BuildContext context, - required dynamic initialValue, - required String? currentCondition, - required String selectCode, - AllDevicesModel? device, - required String operationName, - DeviceFunctionData? selectedFunctionData, - bool? removeComparators, - }) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (removeComparators != true) - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), - const SizedBox(height: 20), - _buildTemperatureDisplay( - context, - initialValue, - device, - operationName, - selectedFunctionData, - selectCode, - ), - const SizedBox(height: 20), - _buildTemperatureSlider( - context, - initialValue, - device, - operationName, - selectedFunctionData, - selectCode, - ), - ], - ); - } + // /// Build temperature selector for AC functions dialog + // static Widget _buildTemperatureSelector({ + // required BuildContext context, + // required dynamic initialValue, + // required String? currentCondition, + // required String selectCode, + // AllDevicesModel? device, + // required String operationName, + // DeviceFunctionData? selectedFunctionData, + // bool? removeComparators, + // }) { + // return Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // if (removeComparators != true) + // _buildConditionToggle( + // context, + // currentCondition, + // selectCode, + // device, + // operationName, + // selectedFunctionData, + // ), + // const SizedBox(height: 20), + // _buildTemperatureDisplay( + // context, + // initialValue, + // device, + // operationName, + // selectedFunctionData, + // selectCode, + // ), + // const SizedBox(height: 20), + // _buildTemperatureSlider( + // context, + // initialValue, + // device, + // operationName, + // selectedFunctionData, + // selectCode, + // ), + // ], + // ); + // } - /// Build condition toggle for AC functions dialog - static Widget _buildConditionToggle( - BuildContext context, - String? currentCondition, - String selectCode, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, + // /// Build condition toggle for AC functions dialog + // static Widget _buildConditionToggle( + // BuildContext context, + // String? currentCondition, + // String selectCode, + // AllDevicesModel? device, + // String operationName, + // DeviceFunctionData? selectedFunctionData, - // Function(String) onConditionChanged, - ) { - final conditions = ["<", "==", ">"]; + // // Function(String) onConditionChanged, + // ) { + // final conditions = ["<", "==", ">"]; - return ToggleButtons( - onPressed: (int index) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - condition: conditions[index], - value: selectedFunctionData?.value ?? selectCode == 'temp_set' - ? 200 - : -100, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - }, - borderRadius: const BorderRadius.all(Radius.circular(8)), - selectedBorderColor: ColorsManager.primaryColorWithOpacity, - selectedColor: Colors.white, - fillColor: ColorsManager.primaryColorWithOpacity, - color: ColorsManager.primaryColorWithOpacity, - constraints: const BoxConstraints( - minHeight: 40.0, - minWidth: 40.0, - ), - isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(), - children: conditions.map((c) => Text(c)).toList(), - ); - } + // return ToggleButtons( + // onPressed: (int index) { + // context.read().add( + // AddFunction( + // functionData: DeviceFunctionData( + // entityId: device?.uuid ?? '', + // functionCode: selectCode, + // operationName: operationName, + // condition: conditions[index], + // value: selectedFunctionData?.value ?? selectCode == 'temp_set' + // ? 200 + // : -100, + // valueDescription: selectedFunctionData?.valueDescription, + // ), + // ), + // ); + // }, + // borderRadius: const BorderRadius.all(Radius.circular(8)), + // selectedBorderColor: ColorsManager.primaryColorWithOpacity, + // selectedColor: Colors.white, + // fillColor: ColorsManager.primaryColorWithOpacity, + // color: ColorsManager.primaryColorWithOpacity, + // constraints: const BoxConstraints( + // minHeight: 40.0, + // minWidth: 40.0, + // ), + // isSelected: + // conditions.map((c) => c == (currentCondition ?? "==")).toList(), + // children: conditions.map((c) => Text(c)).toList(), + // ); + // } - /// Build temperature display for AC functions dialog - static Widget _buildTemperatureDisplay( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode, - ) { - final initialVal = selectCode == 'temp_set' ? 200 : -100; - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - '${(initialValue ?? initialVal) / 10}°C', - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ); - } + // /// Build temperature display for AC functions dialog + // static Widget _buildTemperatureDisplay( + // BuildContext context, + // dynamic initialValue, + // AllDevicesModel? device, + // String operationName, + // DeviceFunctionData? selectedFunctionData, + // String selectCode, + // ) { + // final initialVal = selectCode == 'temp_set' ? 200 : -100; + // return Container( + // padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + // decoration: BoxDecoration( + // color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), + // borderRadius: BorderRadius.circular(10), + // ), + // child: Text( + // '${(initialValue ?? initialVal) / 10}°C', + // style: context.textTheme.headlineMedium!.copyWith( + // color: ColorsManager.primaryColorWithOpacity, + // ), + // ), + // ); + // } - static Widget _buildTemperatureSlider( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode, - ) { - return Slider( - value: initialValue is int ? initialValue.toDouble() : 200.0, - min: selectCode == 'temp_current' ? -100 : 200, - max: selectCode == 'temp_current' ? 900 : 300, - divisions: 10, - label: '${((initialValue ?? 160) / 10).toInt()}°C', - onChanged: (value) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - value: value, - condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - }, - ); - } + // static Widget _buildTemperatureSlider( + // BuildContext context, + // dynamic initialValue, + // AllDevicesModel? device, + // String operationName, + // DeviceFunctionData? selectedFunctionData, + // String selectCode, + // ) { + // return Slider( + // value: initialValue is int ? initialValue.toDouble() : 200.0, + // min: selectCode == 'temp_current' ? -100 : 200, + // max: selectCode == 'temp_current' ? 900 : 300, + // divisions: 10, + // label: '${((initialValue ?? 160) / 10).toInt()}°C', + // onChanged: (value) { + // context.read().add( + // AddFunction( + // functionData: DeviceFunctionData( + // entityId: device?.uuid ?? '', + // functionCode: selectCode, + // operationName: operationName, + // value: value, + // condition: selectedFunctionData?.condition, + // valueDescription: selectedFunctionData?.valueDescription, + // ), + // ), + // ); + // }, + // ); + // } static Widget _buildOperationalValuesList({ required BuildContext context, @@ -414,7 +446,9 @@ class ACHelper { style: context.textTheme.bodyMedium, ), trailing: Icon( - isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, size: 24, color: isSelected ? ColorsManager.primaryColorWithOpacity @@ -430,7 +464,8 @@ class ACHelper { operationName: operationName, value: value.value, condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, + valueDescription: + selectedFunctionData?.valueDescription, ), ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart index f3d07a66..8fab09e8 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart @@ -41,7 +41,8 @@ class _CeilingSensorDialogState extends State { void initState() { super.initState(); - _cpsFunctions = widget.functions.whereType().where((function) { + _cpsFunctions = + widget.functions.whereType().where((function) { if (widget.dialogType == 'THEN') { return function.type == 'THEN' || function.type == 'BOTH'; } @@ -149,6 +150,7 @@ class _CeilingSensorDialogState extends State { device: widget.device, ) : CpsDialogSliderSelector( + step: selectedCpsFunctions.step!, operations: operations, selectedFunction: selectedFunction ?? '', selectedFunctionData: selectedFunctionData, diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart index cd8e4c46..3d2473c9 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart'; import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; @@ -16,6 +17,7 @@ class CpsDialogSliderSelector extends StatelessWidget { required this.device, required this.operationName, required this.dialogType, + required this.step, super.key, }); @@ -26,13 +28,16 @@ class CpsDialogSliderSelector extends StatelessWidget { final AllDevicesModel? device; final String operationName; final String dialogType; + final double step; @override Widget build(BuildContext context) { - return SliderValueSelector( + return CustomRoutinesTextbox( + withSpecialChar: false, currentCondition: selectedFunctionData.condition, dialogType: dialogType, - sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode), + sliderRange: + CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode), displayedValue: CpsSliderHelpers.displayText( value: selectedFunctionData.value, functionCode: selectedFunctionData.functionCode, @@ -50,7 +55,7 @@ class CpsDialogSliderSelector extends StatelessWidget { ), ), ), - onSliderChanged: (value) => context.read().add( + onTextChanged: (value) => context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', @@ -64,6 +69,7 @@ class CpsDialogSliderSelector extends StatelessWidget { dividendOfRange: CpsSliderHelpers.dividendOfRange( selectedFunctionData.functionCode, ), + stepIncreaseAmount: step, ); } } diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart index efc57653..d11871a7 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart @@ -34,30 +34,33 @@ class CpsFunctionsList extends StatelessWidget { itemBuilder: (context, index) { final function = cpsFunctions[index]; return RoutineDialogFunctionListTile( - iconPath: function.icon, - operationName: function.operationName, - onTap: () => RoutineTapFunctionHelper.onTapFunction( - context, - functionCode: function.code, - functionOperationName: function.operationName, - functionValueDescription: selectedFunctionData?.valueDescription, - deviceUuid: device?.uuid, - codesToAddIntoFunctionsWithDefaultValue: [ - 'static_max_dis', - 'presence_reference', - 'moving_reference', - 'perceptual_boundary', - 'moving_boundary', - 'moving_rigger_time', - 'moving_static_time', - 'none_body_time', - 'moving_max_dis', - 'moving_range', - 'presence_range', - if (dialogType == "IF") 'sensitivity', - ], - ), - ); + iconPath: function.icon, + operationName: function.operationName, + onTap: () { + RoutineTapFunctionHelper.onTapFunction( + context, + step: function.step, + functionCode: function.code, + functionOperationName: function.operationName, + functionValueDescription: + selectedFunctionData?.valueDescription, + deviceUuid: device?.uuid, + codesToAddIntoFunctionsWithDefaultValue: [ + 'static_max_dis', + 'presence_reference', + 'moving_reference', + 'perceptual_boundary', + 'moving_boundary', + 'moving_rigger_time', + 'moving_static_time', + 'none_body_time', + 'moving_max_dis', + 'moving_range', + 'presence_range', + if (dialogType == "IF") 'sensitivity', + ], + ); + }); }, ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart index 3e618c35..4c780058 100644 --- a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart'; @@ -21,22 +22,20 @@ class FlushOperationalValuesList extends StatelessWidget { @override Widget build(BuildContext context) { - return ListView.builder( - padding: const EdgeInsets.all(20), - itemCount: values.length, - itemBuilder: (context, index) => - _buildValueItem(context, values[index]), - ); + return ListView.builder( + padding: const EdgeInsets.all(20), + itemCount: values.length, + itemBuilder: (context, index) => _buildValueItem(context, values[index]), + ); } - - Widget _buildValueItem(BuildContext context, FlushOperationalValue value) { return Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + SvgPicture.asset(value.icon, width: 25, height: 25), Expanded(child: _buildValueDescription(value)), _buildValueRadio(context, value), ], @@ -44,9 +43,6 @@ class FlushOperationalValuesList extends StatelessWidget { ); } - - - Widget _buildValueDescription(FlushOperationalValue value) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), @@ -60,6 +56,4 @@ class FlushOperationalValuesList extends StatelessWidget { groupValue: selectedValue, onChanged: (_) => onSelect(value)); } - - } diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart index 64f060e5..7ca89edb 100644 --- a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart'; import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; @@ -66,7 +67,8 @@ class FlushValueSelectorWidget extends StatelessWidget { if (isDistanceDetection) { initialValue = initialValue / 100; } - return SliderValueSelector( + return CustomRoutinesTextbox( + withSpecialChar: true, currentCondition: functionData.condition, dialogType: dialogType, sliderRange: sliderRange, @@ -83,7 +85,7 @@ class FlushValueSelectorWidget extends StatelessWidget { ), ), ), - onSliderChanged: (value) { + onTextChanged: (value) { final roundedValue = _roundToStep(value, stepSize); final finalValue = isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue; @@ -102,6 +104,7 @@ class FlushValueSelectorWidget extends StatelessWidget { }, unit: _unit, dividendOfRange: stepSize, + stepIncreaseAmount: stepSize, ); } diff --git a/lib/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart b/lib/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart index 2b09f579..a3223abd 100644 --- a/lib/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart +++ b/lib/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart @@ -8,6 +8,7 @@ abstract final class RoutineTapFunctionHelper { static void onTapFunction( BuildContext context, { + double? step, required String functionCode, required String functionOperationName, required String? functionValueDescription, diff --git a/lib/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart index 3c786045..641fd234 100644 --- a/lib/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart @@ -4,11 +4,11 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart'; @@ -87,14 +87,15 @@ class OneGangSwitchHelper { size: 16, color: ColorsManager.textGray, ), - onTap: () => - RoutineTapFunctionHelper.onTapFunction( + onTap: () => RoutineTapFunctionHelper + .onTapFunction( context, functionCode: function.code, functionOperationName: function.operationName, functionValueDescription: - selectedFunctionData.valueDescription, + selectedFunctionData + .valueDescription, deviceUuid: device?.uuid, codesToAddIntoFunctionsWithDefaultValue: [ 'countdown_1', @@ -108,14 +109,16 @@ class OneGangSwitchHelper { if (selectedFunction != null) Expanded( child: _buildValueSelector( - context: context, - selectedFunction: selectedFunction, - selectedFunctionData: selectedFunctionData, - acFunctions: oneGangFunctions, - device: device, - operationName: selectedOperationName ?? '', - removeComparetors: removeComparetors, - ), + context: context, + selectedFunction: selectedFunction, + selectedFunctionData: + selectedFunctionData, + acFunctions: oneGangFunctions, + device: device, + operationName: + selectedOperationName ?? '', + removeComparetors: removeComparetors, + dialogType: dialogType), ), ], ), @@ -172,6 +175,7 @@ class OneGangSwitchHelper { AllDevicesModel? device, required String operationName, required bool removeComparetors, + required String dialogType, }) { if (selectedFunction == 'countdown_1') { final initialValue = selectedFunctionData?.value ?? 0; @@ -184,6 +188,7 @@ class OneGangSwitchHelper { operationName: operationName, selectedFunctionData: selectedFunctionData, removeComparetors: removeComparetors, + dialogType: dialogType, ); } final selectedFn = acFunctions.firstWhere( @@ -216,93 +221,18 @@ class OneGangSwitchHelper { required String operationName, DeviceFunctionData? selectedFunctionData, required bool removeComparetors, + String? dialogType, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (removeComparetors != true) - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), - const SizedBox(height: 20), - _buildCountDownDisplay(context, initialValue, device, operationName, - selectedFunctionData, selectCode), const SizedBox(height: 20), _buildCountDownSlider(context, initialValue, device, operationName, - selectedFunctionData, selectCode), + selectedFunctionData, selectCode, dialogType!), ], ); } - /// Build condition toggle for AC functions dialog - static Widget _buildConditionToggle( - BuildContext context, - String? currentCondition, - String selectCode, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - // Function(String) onConditionChanged, - ) { - final conditions = ["<", "==", ">"]; - - return ToggleButtons( - onPressed: (int index) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - condition: conditions[index], - value: selectedFunctionData?.value ?? 0, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - }, - borderRadius: const BorderRadius.all(Radius.circular(8)), - selectedBorderColor: ColorsManager.primaryColorWithOpacity, - selectedColor: Colors.white, - fillColor: ColorsManager.primaryColorWithOpacity, - color: ColorsManager.primaryColorWithOpacity, - constraints: const BoxConstraints( - minHeight: 40.0, - minWidth: 40.0, - ), - isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(), - children: conditions.map((c) => Text(c)).toList(), - ); - } - - /// Build temperature display for AC functions dialog - static Widget _buildCountDownDisplay( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0), - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ); - } - static Widget _buildCountDownSlider( BuildContext context, dynamic initialValue, @@ -310,38 +240,47 @@ class OneGangSwitchHelper { String operationName, DeviceFunctionData? selectedFunctionData, String selectCode, + String dialogType, ) { - const twelveHoursInSeconds = 43200.0; - final operationalValues = SwitchOperationalValue( - icon: '', - description: "sec", - value: 0.0, - minValue: 0, - maxValue: twelveHoursInSeconds, - stepValue: 1, - ); - return Slider( - value: (initialValue ?? 0).toDouble(), - min: operationalValues.minValue?.toDouble() ?? 0.0, - max: operationalValues.maxValue?.toDouble() ?? 0.0, - divisions: - (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / - (operationalValues.stepValue ?? 1)) - .round(), - onChanged: (value) { + return CustomRoutinesTextbox( + withSpecialChar: false, + currentCondition: selectedFunctionData?.condition, + dialogType: dialogType, + sliderRange: (0, 43200), + displayedValue: (initialValue ?? 0).toString(), + initialValue: (initialValue ?? 0).toString(), + onConditionChanged: (condition) { context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectCode, operationName: operationName, - value: value, + condition: condition, + value: selectedFunctionData?.value ?? 0, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + onTextChanged: (value) { + final roundedValue = value.round(); + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: roundedValue, condition: selectedFunctionData?.condition, valueDescription: selectedFunctionData?.valueDescription, ), ), ); }, + unit: 'sec', + dividendOfRange: 1, + stepIncreaseAmount: 1, ); } @@ -377,7 +316,9 @@ class OneGangSwitchHelper { style: context.textTheme.bodyMedium, ), trailing: Icon( - isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, size: 24, color: isSelected ? ColorsManager.primaryColorWithOpacity @@ -393,7 +334,8 @@ class OneGangSwitchHelper { operationName: operationName, value: value.value, condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, + valueDescription: + selectedFunctionData?.valueDescription, ), ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart index 44a367ef..5e50c11d 100644 --- a/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart @@ -4,10 +4,10 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart'; @@ -86,20 +86,21 @@ class ThreeGangSwitchHelper { size: 16, color: ColorsManager.textGray, ), - onTap: () => - RoutineTapFunctionHelper.onTapFunction( + onTap: () => RoutineTapFunctionHelper + .onTapFunction( context, functionCode: function.code, functionOperationName: function.operationName, functionValueDescription: - selectedFunctionData.valueDescription, + selectedFunctionData + .valueDescription, deviceUuid: device?.uuid, - codesToAddIntoFunctionsWithDefaultValue: [ - 'countdown_1', - 'countdown_2', - 'countdown_3', - ], + codesToAddIntoFunctionsWithDefaultValue: + function.code + .startsWith('countdown') + ? [function.code] + : [], ), ); }, @@ -109,14 +110,16 @@ class ThreeGangSwitchHelper { if (selectedFunction != null) Expanded( child: _buildValueSelector( - context: context, - selectedFunction: selectedFunction, - selectedFunctionData: selectedFunctionData, - switchFunctions: switchFunctions, - device: device, - operationName: selectedOperationName ?? '', - removeComparetors: removeComparetors, - ), + context: context, + selectedFunction: selectedFunction, + selectedFunctionData: + selectedFunctionData, + switchFunctions: switchFunctions, + device: device, + operationName: + selectedOperationName ?? '', + removeComparetors: removeComparetors, + dialogType: dialogType), ), ], ), @@ -133,14 +136,6 @@ class ThreeGangSwitchHelper { onConfirm: state.addedFunctions.isNotEmpty ? () { /// add the functions to the routine bloc - // for (var function in state.addedFunctions) { - // context.read().add( - // AddFunctionToRoutine( - // function, - // uniqueCustomId, - // ), - // ); - // } context.read().add( AddFunctionToRoutine( state.addedFunctions, @@ -173,24 +168,26 @@ class ThreeGangSwitchHelper { AllDevicesModel? device, required String operationName, required bool removeComparetors, + required String dialogType, }) { if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2' || selectedFunction == 'countdown_3') { final initialValue = selectedFunctionData?.value ?? 0; return _buildTemperatureSelector( - context: context, - initialValue: initialValue, - selectCode: selectedFunction, - currentCondition: selectedFunctionData?.condition, - device: device, - operationName: operationName, - selectedFunctionData: selectedFunctionData, - removeComparetors: removeComparetors, - ); + context: context, + initialValue: initialValue, + selectCode: selectedFunction, + currentCondition: selectedFunctionData?.condition, + device: device, + operationName: operationName, + selectedFunctionData: selectedFunctionData, + removeComparetors: removeComparetors, + dialogType: dialogType); } - final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction); + final selectedFn = + switchFunctions.firstWhere((f) => f.code == selectedFunction); final values = selectedFn.getOperationalValues(); return _buildOperationalValuesList( @@ -213,93 +210,18 @@ class ThreeGangSwitchHelper { required String operationName, DeviceFunctionData? selectedFunctionData, bool? removeComparetors, + required String dialogType, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (removeComparetors != true) - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), - const SizedBox(height: 20), - _buildCountDownDisplay(context, initialValue, device, operationName, - selectedFunctionData, selectCode), const SizedBox(height: 20), _buildCountDownSlider(context, initialValue, device, operationName, - selectedFunctionData, selectCode), + selectedFunctionData, selectCode, dialogType), ], ); } - /// Build condition toggle for AC functions dialog - static Widget _buildConditionToggle( - BuildContext context, - String? currentCondition, - String selectCode, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - // Function(String) onConditionChanged, - ) { - final conditions = ["<", "==", ">"]; - - return ToggleButtons( - onPressed: (int index) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - condition: conditions[index], - value: selectedFunctionData?.value ?? 0, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - }, - borderRadius: const BorderRadius.all(Radius.circular(8)), - selectedBorderColor: ColorsManager.primaryColorWithOpacity, - selectedColor: Colors.white, - fillColor: ColorsManager.primaryColorWithOpacity, - color: ColorsManager.primaryColorWithOpacity, - constraints: const BoxConstraints( - minHeight: 40.0, - minWidth: 40.0, - ), - isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(), - children: conditions.map((c) => Text(c)).toList(), - ); - } - - /// Build temperature display for AC functions dialog - static Widget _buildCountDownDisplay( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0), - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ); - } - static Widget _buildCountDownSlider( BuildContext context, dynamic initialValue, @@ -307,38 +229,47 @@ class ThreeGangSwitchHelper { String operationName, DeviceFunctionData? selectedFunctionData, String selectCode, + String dialogType, ) { - const twelveHoursInSeconds = 43200.0; - final operationalValues = SwitchOperationalValue( - icon: '', - description: "sec", - value: 0.0, - minValue: 0, - maxValue: twelveHoursInSeconds, - stepValue: 1, - ); - return Slider( - value: (initialValue ?? 0).toDouble(), - min: operationalValues.minValue?.toDouble() ?? 0.0, - max: operationalValues.maxValue?.toDouble() ?? 0.0, - divisions: - (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / - (operationalValues.stepValue ?? 1)) - .round(), - onChanged: (value) { + return CustomRoutinesTextbox( + withSpecialChar: true, + currentCondition: selectedFunctionData?.condition, + dialogType: dialogType, + sliderRange: (0, 43200), + displayedValue: (initialValue ?? 0).toString(), + initialValue: (initialValue ?? 0).toString(), + onConditionChanged: (condition) { context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectCode, operationName: operationName, - value: value, + condition: condition, + value: selectedFunctionData?.value ?? 0, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + onTextChanged: (value) { + final roundedValue = value.round(); + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: roundedValue, condition: selectedFunctionData?.condition, valueDescription: selectedFunctionData?.valueDescription, ), ), ); }, + unit: 'sec', + dividendOfRange: 1, + stepIncreaseAmount: 1, ); } @@ -374,7 +305,9 @@ class ThreeGangSwitchHelper { style: context.textTheme.bodyMedium, ), trailing: Icon( - isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, size: 24, color: isSelected ? ColorsManager.primaryColorWithOpacity @@ -390,7 +323,8 @@ class ThreeGangSwitchHelper { operationName: operationName, value: value.value, condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, + valueDescription: + selectedFunctionData?.valueDescription, ), ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart index f551d21b..6b3dc813 100644 --- a/lib/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart @@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart'; @@ -86,14 +87,15 @@ class TwoGangSwitchHelper { size: 16, color: ColorsManager.textGray, ), - onTap: () => - RoutineTapFunctionHelper.onTapFunction( + onTap: () => RoutineTapFunctionHelper + .onTapFunction( context, functionCode: function.code, functionOperationName: function.operationName, functionValueDescription: - selectedFunctionData.valueDescription, + selectedFunctionData + .valueDescription, deviceUuid: device?.uuid, codesToAddIntoFunctionsWithDefaultValue: [ 'countdown_1', @@ -115,6 +117,7 @@ class TwoGangSwitchHelper { device: device, operationName: selectedOperationName ?? '', removeComparetors: removeComparetors, + dialogType: dialogType, ), ), ], @@ -172,22 +175,25 @@ class TwoGangSwitchHelper { AllDevicesModel? device, required String operationName, required bool removeComparetors, + required String dialogType, }) { - if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') { + if (selectedFunction == 'countdown_1' || + selectedFunction == 'countdown_2') { final initialValue = selectedFunctionData?.value ?? 0; return _buildTemperatureSelector( - context: context, - initialValue: initialValue, - selectCode: selectedFunction, - currentCondition: selectedFunctionData?.condition, - device: device, - operationName: operationName, - selectedFunctionData: selectedFunctionData, - removeComparetors: removeComparetors, - ); + context: context, + initialValue: initialValue, + selectCode: selectedFunction, + currentCondition: selectedFunctionData?.condition, + device: device, + operationName: operationName, + selectedFunctionData: selectedFunctionData, + removeComparetors: removeComparetors, + dialogType: dialogType); } - final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction); + final selectedFn = + switchFunctions.firstWhere((f) => f.code == selectedFunction); final values = selectedFn.getOperationalValues(); return _buildOperationalValuesList( @@ -210,25 +216,13 @@ class TwoGangSwitchHelper { required String operationName, DeviceFunctionData? selectedFunctionData, bool? removeComparetors, + String? dialogType, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (removeComparetors != true) - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), - const SizedBox(height: 20), - _buildCountDownDisplay(context, initialValue, device, operationName, - selectedFunctionData, selectCode), - const SizedBox(height: 20), _buildCountDownSlider(context, initialValue, device, operationName, - selectedFunctionData, selectCode), + selectedFunctionData, selectCode, dialogType!), ], ); } @@ -269,7 +263,8 @@ class TwoGangSwitchHelper { minHeight: 40.0, minWidth: 40.0, ), - isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(), + isSelected: + conditions.map((c) => c == (currentCondition ?? "==")).toList(), children: conditions.map((c) => Text(c)).toList(), ); } @@ -304,38 +299,48 @@ class TwoGangSwitchHelper { String operationName, DeviceFunctionData? selectedFunctionData, String selectCode, + String dialogType, ) { - const twelveHoursInSeconds = 43200.0; - final operationalValues = SwitchOperationalValue( - icon: '', - description: "sec", - value: 0.0, - minValue: 0, - maxValue: twelveHoursInSeconds, - stepValue: 1, - ); - return Slider( - value: (initialValue ?? 0).toDouble(), - min: operationalValues.minValue?.toDouble() ?? 0.0, - max: operationalValues.maxValue?.toDouble() ?? 0.0, - divisions: - (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / - (operationalValues.stepValue ?? 1)) - .round(), - onChanged: (value) { + return CustomRoutinesTextbox( + withSpecialChar: true, + currentCondition: selectedFunctionData?.condition, + dialogType: dialogType, + sliderRange: (0, 43200), + displayedValue: (initialValue ?? 0).toString(), + initialValue: (initialValue ?? 0).toString(), + onConditionChanged: (condition) { context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectCode, operationName: operationName, - value: value, + condition: condition, + value: selectedFunctionData?.value ?? 0, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + onTextChanged: (value) { + final roundedValue = + value.round(); // Round to nearest integer (stepSize 1) + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: roundedValue, condition: selectedFunctionData?.condition, valueDescription: selectedFunctionData?.valueDescription, ), ), ); }, + unit: 'sec', + dividendOfRange: 1, + stepIncreaseAmount: 1, ); } @@ -371,7 +376,9 @@ class TwoGangSwitchHelper { style: context.textTheme.bodyMedium, ), trailing: Icon( - isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, size: 24, color: isSelected ? ColorsManager.primaryColorWithOpacity @@ -387,7 +394,8 @@ class TwoGangSwitchHelper { operationName: operationName, value: value.value, condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, + valueDescription: + selectedFunctionData?.valueDescription, ), ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart index 30232846..677c26ee 100644 --- a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart @@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart'; -import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; class WpsValueSelectorWidget extends StatelessWidget { final String selectedFunction; @@ -27,11 +27,13 @@ class WpsValueSelectorWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction); + final selectedFn = + wpsFunctions.firstWhere((f) => f.code == selectedFunction); final values = selectedFn.getOperationalValues(); if (_isSliderFunction(selectedFunction)) { - return SliderValueSelector( + return CustomRoutinesTextbox( + withSpecialChar: false, currentCondition: functionData.condition, dialogType: dialogType, sliderRange: sliderRange, @@ -48,7 +50,7 @@ class WpsValueSelectorWidget extends StatelessWidget { ), ), ), - onSliderChanged: (value) => context.read().add( + onTextChanged: (value) => context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', @@ -61,6 +63,7 @@ class WpsValueSelectorWidget extends StatelessWidget { ), unit: _unit, dividendOfRange: 1, + stepIncreaseAmount: _steps, ); } @@ -99,4 +102,10 @@ class WpsValueSelectorWidget extends StatelessWidget { 'illuminance_value' => 'Lux', _ => '', }; + double get _steps => switch (functionData.functionCode) { + 'presence_time' => 1, + 'dis_current' => 1, + 'illuminance_value' => 1, + _ => 1, + }; } diff --git a/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_presence_sensor.dart b/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_presence_sensor.dart index 8721613e..d87e8484 100644 --- a/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_presence_sensor.dart +++ b/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_presence_sensor.dart @@ -176,6 +176,7 @@ class _WaterHeaterDialogRoutinesState extends State { functionData: functionData, whFunctions: _waterHeaterFunctions, device: widget.device, + dialogType: widget.dialogType, ), ); } diff --git a/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_value_selector_widget.dart b/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_value_selector_widget.dart index d305e5f6..a09bbba7 100644 --- a/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_value_selector_widget.dart +++ b/lib/pages/routines/widgets/routine_dialogs/water_heater/water_heater_value_selector_widget.dart @@ -2,25 +2,24 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; -import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; -import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_operational_values_list.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; class WaterHeaterValueSelectorWidget extends StatelessWidget { final String selectedFunction; final DeviceFunctionData functionData; final List whFunctions; final AllDevicesModel? device; + final String dialogType; const WaterHeaterValueSelectorWidget({ required this.selectedFunction, required this.functionData, required this.whFunctions, required this.device, + required this.dialogType, super.key, }); @@ -39,22 +38,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildConditionToggle( - context, - functionData.condition, - selectedFunction, - device, - selectedFn.operationName, - functionData, - ), - _buildCountDownDisplay( - context, - functionData.value, - device, - selectedFn.operationName, - functionData, - selectedFunction, - ), _buildCountDownSlider( context, functionData.value, @@ -62,6 +45,7 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget { selectedFn.operationName, functionData, selectedFunction, + dialogType ), const SizedBox(height: 10), ], @@ -90,28 +74,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget { ); } - static Widget _buildCountDownDisplay( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0), - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ); - } - static Widget _buildCountDownSlider( BuildContext context, dynamic initialValue, @@ -119,78 +81,47 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget { String operationName, DeviceFunctionData? selectedFunctionData, String selectCode, + String dialogType, ) { - const twelveHoursInSeconds = 43200.0; - final operationalValues = SwitchOperationalValue( - icon: '', - description: "sec", - value: 0.0, - minValue: 0, - maxValue: twelveHoursInSeconds, - stepValue: 1, - ); - return Slider( - value: (initialValue ?? 0).toDouble(), - min: operationalValues.minValue?.toDouble() ?? 0.0, - max: operationalValues.maxValue?.toDouble() ?? 0.0, - divisions: (((operationalValues.maxValue ?? 0) - - (operationalValues.minValue ?? 0)) / - (operationalValues.stepValue ?? 1)) - .round(), - onChanged: (value) { + return CustomRoutinesTextbox( + withSpecialChar: false, + currentCondition: selectedFunctionData?.condition, + dialogType: dialogType, + sliderRange: (0, 43200), + displayedValue: (initialValue ?? 0).toString(), + initialValue: (initialValue ?? 0).toString(), + onConditionChanged: (condition) { context.read().add( AddFunction( functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectCode, operationName: operationName, - value: value, + value: condition, + condition: condition, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + onTextChanged: (value) { + final roundedValue = value.round(); + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: roundedValue, condition: selectedFunctionData?.condition, valueDescription: selectedFunctionData?.valueDescription, ), ), ); }, - ); - } - - static Widget _buildConditionToggle( - BuildContext context, - String? currentCondition, - String selectCode, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - ) { - final conditions = ["<", "==", ">"]; - - return ToggleButtons( - onPressed: (int index) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - condition: conditions[index], - value: selectedFunctionData?.value ?? 0, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - }, - borderRadius: const BorderRadius.all(Radius.circular(8)), - selectedBorderColor: ColorsManager.primaryColorWithOpacity, - selectedColor: Colors.white, - fillColor: ColorsManager.primaryColorWithOpacity, - color: ColorsManager.primaryColorWithOpacity, - constraints: const BoxConstraints( - minHeight: 40.0, - minWidth: 40.0, - ), - isSelected: - conditions.map((c) => c == (currentCondition ?? "==")).toList(), - children: conditions.map((c) => Text(c)).toList(), + unit: 'sec', + dividendOfRange: 1, + stepIncreaseAmount: 1, ); } } diff --git a/lib/utils/user_drop_down_menu.dart b/lib/utils/user_drop_down_menu.dart index c9fe492e..15da1f3a 100644 --- a/lib/utils/user_drop_down_menu.dart +++ b/lib/utils/user_drop_down_menu.dart @@ -41,13 +41,24 @@ class _UserDropdownMenuState extends State { _isDropdownOpen = false; }); }, - child: Transform.rotate( - angle: _isDropdownOpen ? -1.5708 : 1.5708, - child: const Icon( - Icons.arrow_forward_ios, - color: Colors.white, - size: 16, - ), + child: Row( + children: [ + const SizedBox(width: 12), + if (widget.user != null) + Text( + '${widget.user!.firstName} ${widget.user!.lastName}', + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(width: 12), + Transform.rotate( + angle: _isDropdownOpen ? -1.5708 : 1.5708, + child: const Icon( + Icons.arrow_forward_ios, + color: Colors.white, + size: 16, + ), + ), + ], ), ), ], diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 02b81522..3cfe171e 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -92,13 +92,6 @@ class DesktopAppBar extends StatelessWidget { if (rightBody != null) rightBody!, const SizedBox(width: 24), _UserAvatar(), - const SizedBox(width: 12), - if (user != null) - Text( - '${user.firstName} ${user.lastName}', - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(width: 12), UserDropdownMenu(user: user), ], ); @@ -146,14 +139,6 @@ class TabletAppBar extends StatelessWidget { if (rightBody != null) rightBody!, const SizedBox(width: 16), _UserAvatar(), - if (user != null) ...[ - const SizedBox(width: 8), - Text( - '${user.firstName} ${user.lastName}', - style: - Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14), - ), - ], UserDropdownMenu(user: user), ], ); @@ -215,14 +200,6 @@ class MobileAppBar extends StatelessWidget { return Row( children: [ _UserAvatar(), - if (user != null) ...[ - const SizedBox(width: 8), - Text( - '${user.firstName} ${user.lastName}', - style: - Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14), - ), - ], UserDropdownMenu(user: user), ], ); diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index fb0d6a22..00000000 --- a/pubspec.lock +++ /dev/null @@ -1,906 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: e051259913915ea5bc8fe18664596bea08592fd123930605d562969cd7315fcd - url: "https://pub.dev" - source: hosted - version: "1.3.51" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - bloc: - dependency: "direct main" - description: - name: bloc - sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" - url: "https://pub.dev" - source: hosted - version: "8.1.4" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf - url: "https://pub.dev" - source: hosted - version: "1.19.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - csslib: - dependency: transitive - description: - name: csslib - sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" - url: "https://pub.dev" - source: hosted - version: "0.17.3" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - data_table_2: - dependency: "direct main" - description: - name: data_table_2 - sha256: f02ec9b24f44420816a87370ff4f4e533e15b274f6267e4c9a88a585ad1a0473 - url: "https://pub.dev" - source: hosted - version: "2.5.15" - dio: - dependency: "direct main" - description: - name: dio - sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 - url: "https://pub.dev" - source: hosted - version: "5.5.0+1" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - dropdown_button2: - dependency: "direct main" - description: - name: dropdown_button2 - sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 - url: "https://pub.dev" - source: hosted - version: "2.3.9" - dropdown_search: - dependency: "direct main" - description: - name: dropdown_search - sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" - url: "https://pub.dev" - source: hosted - version: "5.0.6" - equatable: - dependency: "direct main" - description: - name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 - url: "https://pub.dev" - source: hosted - version: "2.0.5" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - sha256: "47428047a0778f72af53a3c7cb5d556e1cb25e2327cc8aa40d544971dc6245b2" - url: "https://pub.dev" - source: hosted - version: "11.4.2" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - sha256: "1076f4b041f76143e14878c70f0758f17fe5910c0cd992db9e93bd3c3584512b" - url: "https://pub.dev" - source: hosted - version: "4.3.2" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - sha256: "8f6dd64ea6d28b7f5b9e739d183a9e1c7f17027794a3e9aba1879621d42426ef" - url: "https://pub.dev" - source: hosted - version: "0.5.10+8" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "93dc4dd12f9b02c5767f235307f609e61ed9211047132d07f9e02c668f0bfc33" - url: "https://pub.dev" - source: hosted - version: "3.11.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf - url: "https://pub.dev" - source: hosted - version: "5.4.0" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "0e13c80f0de8acaa5d0519cbe23c8b4cc138a2d5d508b5755c861bdfc9762678" - url: "https://pub.dev" - source: hosted - version: "2.20.0" - firebase_crashlytics: - dependency: "direct main" - description: - name: firebase_crashlytics - sha256: "6273ed71bcd8a6fb4d0ca13d3abddbb3301796807efaad8782b5f90156f26f03" - url: "https://pub.dev" - source: hosted - version: "4.3.2" - firebase_crashlytics_platform_interface: - dependency: transitive - description: - name: firebase_crashlytics_platform_interface - sha256: "94f3986e1a10e5a883f2ad5e3d719aef98a8a0f9a49357f6e45b7d3696ea6a97" - url: "https://pub.dev" - source: hosted - version: "3.8.2" - firebase_database: - dependency: "direct main" - description: - name: firebase_database - sha256: cd2354dfef68e52c0713b5efbb7f4e10dfc2aff2f945c7bc8db34d1934170627 - url: "https://pub.dev" - source: hosted - version: "11.3.2" - firebase_database_platform_interface: - dependency: transitive - description: - name: firebase_database_platform_interface - sha256: d430983f4d877c9f72f88b3d715cca9a50021dd7ccd8e3ae6fb79603853317de - url: "https://pub.dev" - source: hosted - version: "0.2.6+2" - firebase_database_web: - dependency: transitive - description: - name: firebase_database_web - sha256: f64edae62c5beaa08e9e611a0736d64ab11a812983a0aa132695d2d191311ea7 - url: "https://pub.dev" - source: hosted - version: "0.2.6+8" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - fl_chart: - dependency: "direct main" - description: - name: fl_chart - sha256: "94307bef3a324a0d329d3ab77b2f0c6e5ed739185ffc029ed28c0f9b019ea7ef" - url: "https://pub.dev" - source: hosted - version: "0.69.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_bloc: - dependency: "direct main" - description: - name: flutter_bloc - sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 - url: "https://pub.dev" - source: hosted - version: "8.1.5" - flutter_dotenv: - dependency: "direct main" - description: - name: flutter_dotenv - sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" - url: "https://pub.dev" - source: hosted - version: "5.1.0" - flutter_html: - dependency: "direct main" - description: - name: flutter_html - sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - flutter_secure_storage: - dependency: "direct main" - description: - name: flutter_secure_storage - sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" - url: "https://pub.dev" - source: hosted - version: "9.2.2" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" - url: "https://pub.dev" - source: hosted - version: "2.0.10+1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - get_it: - dependency: "direct main" - description: - name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 - url: "https://pub.dev" - source: hosted - version: "7.7.0" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459" - url: "https://pub.dev" - source: hosted - version: "14.2.7" - graphview: - dependency: "direct main" - description: - name: graphview - sha256: bdba183583b23c30c71edea09ad5f0beef612572d3e39e855467a925bd08392f - url: "https://pub.dev" - source: hosted - version: "1.2.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: transitive - description: - name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 - url: "https://pub.dev" - source: hosted - version: "1.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - intl_phone_field: - dependency: "direct main" - description: - name: intl_phone_field - sha256: "73819d3dfcb68d2c85663606f6842597c3ddf6688ac777f051b17814fe767bbf" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" - url: "https://pub.dev" - source: hosted - version: "10.0.7" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" - url: "https://pub.dev" - source: hosted - version: "3.0.8" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - list_counter: - dependency: transitive - description: - name: list_counter - sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - number_pagination: - dependency: "direct main" - description: - name: number_pagination - sha256: "75d3a28616196e7c8df431d0fb7c48e811e462155f4cf3b5b4167b3408421327" - url: "https://pub.dev" - source: hosted - version: "1.1.6" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 - url: "https://pub.dev" - source: hosted - version: "2.1.3" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: e84c8a53fe1510ef4582f118c7b4bdf15b03002b51d7c2b66983c65843d61193 - url: "https://pub.dev" - source: hosted - version: "2.2.8" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 - url: "https://pub.dev" - source: hosted - version: "2.4.0" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - platform: - dependency: transitive - description: - name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" - url: "https://pub.dev" - source: hosted - version: "3.1.5" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - provider: - dependency: transitive - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: c3f888ba2d659f3e75f4686112cc1e71f46177f74452d40d8307edc332296ead - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e - url: "https://pub.dev" - source: hosted - version: "2.4.2" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" - url: "https://pub.dev" - source: hosted - version: "0.7.3" - time_picker_spinner: - dependency: "direct main" - description: - name: time_picker_spinner - sha256: "53d824801d108890d22756501e7ade9db48b53dac1ec41580499dd4ebd128e3c" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" - url: "https://pub.dev" - source: hosted - version: "6.3.1" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" - url: "https://pub.dev" - source: hosted - version: "6.3.14" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" - url: "https://pub.dev" - source: hosted - version: "6.3.2" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" - url: "https://pub.dev" - source: hosted - version: "3.2.2" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - uuid: - dependency: "direct main" - description: - name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff - url: "https://pub.dev" - source: hosted - version: "4.5.1" - vector_graphics: - dependency: transitive - description: - name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" - vector_graphics_codec: - dependency: transitive - description: - name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" - vector_graphics_compiler: - dependency: transitive - description: - name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b - url: "https://pub.dev" - source: hosted - version: "14.3.0" - web: - dependency: transitive - description: - name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb - url: "https://pub.dev" - source: hosted - version: "1.1.0" - win32: - dependency: transitive - description: - name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 - url: "https://pub.dev" - source: hosted - version: "5.5.1" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" -sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0"