From 6c691d4b3ceb71c468ee2802a298c5f1c47c07eb Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 12:46:43 +0300 Subject: [PATCH 01/48] displays ceiling sensor in routine --- .../helper/dialog_helper/device_dialog_helper.dart | 2 +- lib/pages/routines/widgets/if_container.dart | 5 +++-- lib/pages/routines/widgets/routine_devices.dart | 3 ++- lib/pages/routines/widgets/then_container.dart | 9 +++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart index 96919364..caf92489 100644 --- a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart'; -import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart'; class DeviceDialogHelper { diff --git a/lib/pages/routines/widgets/if_container.dart b/lib/pages/routines/widgets/if_container.dart index eebf3fb7..5ca549fb 100644 --- a/lib/pages/routines/widgets/if_container.dart +++ b/lib/pages/routines/widgets/if_container.dart @@ -71,7 +71,8 @@ class IfContainer extends StatelessWidget { '1G', '2G', '3G', - 'WPS' + 'WPS', + 'CPS', ].contains( state.ifItems[index]['productType'])) { context.read().add( @@ -129,7 +130,7 @@ class IfContainer extends StatelessWidget { context .read() .add(AddToIfContainer(mutableData, false)); - } else if (!['AC', '1G', '2G', '3G', 'WPS'] + } else if (!['AC', '1G', '2G', '3G', 'WPS', 'CPS'] .contains(mutableData['productType'])) { context .read() diff --git a/lib/pages/routines/widgets/routine_devices.dart b/lib/pages/routines/widgets/routine_devices.dart index 2d3f7236..5f642bac 100644 --- a/lib/pages/routines/widgets/routine_devices.dart +++ b/lib/pages/routines/widgets/routine_devices.dart @@ -38,7 +38,8 @@ class _RoutineDevicesState extends State { device.productType == '1G' || device.productType == '2G' || device.productType == '3G' || - device.productType == 'WPS') + device.productType == 'WPS' || + device.productType == 'CPS') .toList(); return Wrap( diff --git a/lib/pages/routines/widgets/then_container.dart b/lib/pages/routines/widgets/then_container.dart index a4d0461d..d1856879 100644 --- a/lib/pages/routines/widgets/then_container.dart +++ b/lib/pages/routines/widgets/then_container.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart'; -import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart'; import 'package:syncrow_web/pages/routines/helper/dialog_helper/device_dialog_helper.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:uuid/uuid.dart'; @@ -113,7 +113,8 @@ class ThenContainer extends StatelessWidget { '1G', '2G', '3G', - 'WPS' + 'WPS', + 'CPS', ].contains(state.thenItems[index] ['productType'])) { context.read().add( @@ -229,7 +230,7 @@ class ThenContainer extends StatelessWidget { dialogType: "THEN"); if (result != null) { context.read().add(AddToThenContainer(mutableData)); - } else if (!['AC', '1G', '2G', '3G', 'WPS'] + } else if (!['AC', '1G', '2G', '3G', 'WPS', 'CPS'] .contains(mutableData['productType'])) { context.read().add(AddToThenContainer(mutableData)); } From 4ea91b93330f06dc92b4b4bcccaadc5f3691a918 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 13:16:36 +0300 Subject: [PATCH 02/48] SP-1276/ Show Configuration Pop-up on Drop. --- .../dialog_helper/device_dialog_helper.dart | 12 ++- .../ceiling_sensor/ceiling_sensor_dialog.dart | 77 +++++++++++++++++++ .../ceiling_sensor/ceiling_sensor_helper.dart | 36 +++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart diff --git a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart index caf92489..df440004 100644 --- a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart'; @@ -49,7 +50,6 @@ class DeviceDialogHelper { final deviceSelectedFunctions = routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; - switch (productType) { case 'AC': return ACHelper.showACFunctionsDialog( @@ -98,6 +98,16 @@ class DeviceDialogHelper { deviceSelectedFunctions: deviceSelectedFunctions, uniqueCustomId: data['uniqueCustomId'], removeComparetors: removeComparetors); + case 'CPS': + return CeilingSensorHelper.showCeilingSensorDialog( + context: context, + functions: functions, + device: data['device'], + deviceSelectedFunctions: deviceSelectedFunctions, + uniqueCustomId: data['uniqueCustomId'], + dialogType: dialogType, + ); + default: return null; } 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 new file mode 100644 index 00000000..8820d305 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart @@ -0,0 +1,77 @@ +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/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; +import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; + +class CeilingSensorDialog extends StatelessWidget { + const CeilingSensorDialog({ + required this.uniqueCustomId, + required this.functions, + required this.deviceSelectedFunctions, + required this.device, + required this.dialogType, + super.key, + }); + final String? uniqueCustomId; + final List functions; + final List deviceSelectedFunctions; + final AllDevicesModel? device; + final String dialogType; + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: BlocBuilder( + builder: (context, state) { + final selectedFunction = state.selectedFunction; + final selectedOperationName = state.selectedOperationName; + final selectedFunctionData = state.addedFunctions + .firstWhere((f) => f.functionCode == selectedFunction, + orElse: () => DeviceFunctionData( + entityId: '', + functionCode: selectedFunction ?? '', + operationName: '', + value: null, + )); + + return Container( + width: selectedFunction != null ? 600 : 360, + height: 450, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const DialogHeader('Presence Sensor Condition'), + DialogFooter( + onCancel: () => Navigator.pop(context), + onConfirm: state.addedFunctions.isNotEmpty + ? () { + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, '{uniqueCustomId}'), + ); + + Navigator.pop(context, { + // 'deviceId': functions.first.deviceId, + }); + } + : null, + isConfirmEnabled: selectedFunction != null, + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart new file mode 100644 index 00000000..310ca5c4 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart @@ -0,0 +1,36 @@ +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/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart'; + +abstract final class CeilingSensorHelper { + const CeilingSensorHelper._(); + + static Future?> showCeilingSensorDialog({ + required BuildContext context, + required String? uniqueCustomId, + required List functions, + required List deviceSelectedFunctions, + required AllDevicesModel? device, + required String dialogType, + }) { + return showDialog( + context: context, + builder: (context) => BlocProvider( + create: (context) => FunctionBloc() + ..add( + InitializeFunctions(deviceSelectedFunctions), + ), + child: CeilingSensorDialog( + uniqueCustomId: uniqueCustomId, + functions: functions, + deviceSelectedFunctions: deviceSelectedFunctions, + device: device, + dialogType: dialogType, + ), + ), + ); + } +} From c0662bb19ecd31b35a786ae3316f95421c461c6e Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 15:23:56 +0300 Subject: [PATCH 03/48] Added assets. --- assets/icons/boundary.svg | 28 +++++++++++++ assets/icons/close_to_motion.svg | 10 +++++ assets/icons/cps_custom_mode.svg | 15 +++++++ assets/icons/cps_mode1.svg | 6 +++ assets/icons/cps_mode2.svg | 7 ++++ assets/icons/cps_mode3.svg | 6 +++ assets/icons/cps_mode4.svg | 6 +++ assets/icons/far_away_motion.svg | 11 +++++ assets/icons/motion_meter.svg | 10 +++++ assets/icons/moving_speed.svg | 9 ++++ assets/icons/presence_judgement_threshold.svg | 15 +++++++ assets/icons/radar_fault.svg | 14 +++++++ assets/icons/self_testing_failure.svg | 23 +++++++++++ assets/icons/self_testing_success.svg | 23 +++++++++++ assets/icons/self_testing_timeout.svg | 41 +++++++++++++++++++ assets/icons/space_type.svg | 10 +++++ assets/icons/spatial_motion_value.svg | 18 ++++++++ assets/icons/spatial_static_value.svg | 6 +++ assets/icons/sports_para.svg | 6 +++ lib/utils/constants/assets.dart | 22 +++++++++- 20 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 assets/icons/boundary.svg create mode 100644 assets/icons/close_to_motion.svg create mode 100644 assets/icons/cps_custom_mode.svg create mode 100644 assets/icons/cps_mode1.svg create mode 100644 assets/icons/cps_mode2.svg create mode 100644 assets/icons/cps_mode3.svg create mode 100644 assets/icons/cps_mode4.svg create mode 100644 assets/icons/far_away_motion.svg create mode 100644 assets/icons/motion_meter.svg create mode 100644 assets/icons/moving_speed.svg create mode 100644 assets/icons/presence_judgement_threshold.svg create mode 100644 assets/icons/radar_fault.svg create mode 100644 assets/icons/self_testing_failure.svg create mode 100644 assets/icons/self_testing_success.svg create mode 100644 assets/icons/self_testing_timeout.svg create mode 100644 assets/icons/space_type.svg create mode 100644 assets/icons/spatial_motion_value.svg create mode 100644 assets/icons/spatial_static_value.svg create mode 100644 assets/icons/sports_para.svg diff --git a/assets/icons/boundary.svg b/assets/icons/boundary.svg new file mode 100644 index 00000000..73b4ab24 --- /dev/null +++ b/assets/icons/boundary.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/close_to_motion.svg b/assets/icons/close_to_motion.svg new file mode 100644 index 00000000..8ba6c8c6 --- /dev/null +++ b/assets/icons/close_to_motion.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/cps_custom_mode.svg b/assets/icons/cps_custom_mode.svg new file mode 100644 index 00000000..4176c939 --- /dev/null +++ b/assets/icons/cps_custom_mode.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/cps_mode1.svg b/assets/icons/cps_mode1.svg new file mode 100644 index 00000000..407105df --- /dev/null +++ b/assets/icons/cps_mode1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/cps_mode2.svg b/assets/icons/cps_mode2.svg new file mode 100644 index 00000000..9464a7ea --- /dev/null +++ b/assets/icons/cps_mode2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/cps_mode3.svg b/assets/icons/cps_mode3.svg new file mode 100644 index 00000000..998329fd --- /dev/null +++ b/assets/icons/cps_mode3.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/cps_mode4.svg b/assets/icons/cps_mode4.svg new file mode 100644 index 00000000..3136a806 --- /dev/null +++ b/assets/icons/cps_mode4.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/far_away_motion.svg b/assets/icons/far_away_motion.svg new file mode 100644 index 00000000..9458eb0d --- /dev/null +++ b/assets/icons/far_away_motion.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/motion_meter.svg b/assets/icons/motion_meter.svg new file mode 100644 index 00000000..85469973 --- /dev/null +++ b/assets/icons/motion_meter.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/moving_speed.svg b/assets/icons/moving_speed.svg new file mode 100644 index 00000000..6db52050 --- /dev/null +++ b/assets/icons/moving_speed.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/presence_judgement_threshold.svg b/assets/icons/presence_judgement_threshold.svg new file mode 100644 index 00000000..d5537da7 --- /dev/null +++ b/assets/icons/presence_judgement_threshold.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/radar_fault.svg b/assets/icons/radar_fault.svg new file mode 100644 index 00000000..b2295d1a --- /dev/null +++ b/assets/icons/radar_fault.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/self_testing_failure.svg b/assets/icons/self_testing_failure.svg new file mode 100644 index 00000000..c86c9ec2 --- /dev/null +++ b/assets/icons/self_testing_failure.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/self_testing_success.svg b/assets/icons/self_testing_success.svg new file mode 100644 index 00000000..1f8976b0 --- /dev/null +++ b/assets/icons/self_testing_success.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/self_testing_timeout.svg b/assets/icons/self_testing_timeout.svg new file mode 100644 index 00000000..55c1e632 --- /dev/null +++ b/assets/icons/self_testing_timeout.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/space_type.svg b/assets/icons/space_type.svg new file mode 100644 index 00000000..af5f6845 --- /dev/null +++ b/assets/icons/space_type.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/spatial_motion_value.svg b/assets/icons/spatial_motion_value.svg new file mode 100644 index 00000000..018da674 --- /dev/null +++ b/assets/icons/spatial_motion_value.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/spatial_static_value.svg b/assets/icons/spatial_static_value.svg new file mode 100644 index 00000000..95ca6112 --- /dev/null +++ b/assets/icons/spatial_static_value.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/sports_para.svg b/assets/icons/sports_para.svg new file mode 100644 index 00000000..6f9d5ece --- /dev/null +++ b/assets/icons/sports_para.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index d9788ee8..7bcd1484 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -428,6 +428,24 @@ class Assets { static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg'; static const String gear = 'assets/icons/gear.svg'; static const String activeBell='assets/icons/active_bell.svg'; - - + static const String cpsCustomMode = 'assets/icons/cps_custom_mode.svg'; + static const String cpsMode1 = 'assets/icons/cps_mode1.svg'; + static const String cpsMode2 = 'assets/icons/cps_mode2.svg'; + static const String cpsMode3 = 'assets/icons/cps_mode3.svg'; + static const String cpsMode4 = 'assets/icons/cps_mode4.svg'; + static const String closeToMotion = 'assets/icons/close_to_motion.svg'; + static const String farAwayMotion = 'assets/icons/far_away_motion.svg'; + static const String communicationFault = 'assets/icons/communication_fault.svg'; + static const String radarFault = 'assets/icons/radar_fault.svg'; + static const String selfTestingSuccess = 'assets/icons/self_testing_success.svg'; + static const String selfTestingFailure = 'assets/icons/self_testing_failure.svg'; + static const String selfTestingTimeout = 'assets/icons/self_testing_timeout.svg'; + static const String movingSpeed = 'assets/icons/moving_speed.svg'; + static const String boundary = 'assets/icons/boundary.svg'; + static const String motionMeter = 'assets/icons/motion_meter.svg'; + static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg'; + static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg'; + static const String presenceJudgementThrshold = 'assets/icons/presence_judgement_threshold.svg'; + static const String spaceType = 'assets/icons/space_type.svg'; + static const String sportsPara = 'assets/icons/sports_para.svg'; } From cd9ed5861a8ebacd71fbea6bb75b8d3ca3d6361c Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 15:40:44 +0300 Subject: [PATCH 04/48] Implemented some operations for the cps feature. --- .../ceiling_presence_sensor_functions.dart | 635 ++++++++++++++++++ 1 file changed, 635 insertions(+) create mode 100644 lib/pages/routines/models/ceiling_presence_sensor_functions.dart diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart new file mode 100644 index 00000000..5bd15e66 --- /dev/null +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -0,0 +1,635 @@ +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CpsOperationalValue { + final String icon; + final String description; + final dynamic value; + + CpsOperationalValue({ + required this.icon, + required this.description, + required this.value, + }); +} + +abstract class CpsFunctions extends DeviceFunction { + CpsFunctions({ + required super.deviceId, + required super.deviceName, + required super.code, + required super.operationName, + required super.icon, + required this.type, + }); + + final String type; + + List getOperationalValues(); +} + +final class CpsRadarSwitchFunction extends CpsFunctions { + CpsRadarSwitchFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'radar_switch', + operationName: 'Radar Switch', + icon: Assets.acPower, + ); + + @override + List getOperationalValues() => [ + CpsOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + CpsOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ]; +} + +final class CpsSpatialParameterSwitchFunction extends CpsFunctions { + CpsSpatialParameterSwitchFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'space_para_switch', + operationName: 'Spatial Parameter Switch', + icon: Assets.acPower, + ); + + @override + List getOperationalValues() => [ + CpsOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + CpsOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ]; +} + +final class CpsSensitivityFunction extends CpsFunctions { + CpsSensitivityFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 1, + max = 10, + step = 1, + scale = 0, + super( + code: 'far_detection', + operationName: 'Far Detection', + icon: Assets.sensitivity, + ); + + final int min; + final int max; + final int step; + final int scale; + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add( + CpsOperationalValue( + icon: Assets.sensitivity, + description: '$value', + value: value, + ), + ); + } + return values; + } +} + +final class CpsMovingSpeedFunction extends CpsFunctions { + CpsMovingSpeedFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.speedoMeter, + ); + @override + List getOperationalValues() { + return []; + } +} + +final class CpsSpatialStaticValueFunction extends CpsFunctions { + CpsSpatialStaticValueFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.spatialStaticValue, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsSpatialMotionValueFunction extends CpsFunctions { + CpsSpatialMotionValueFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.spatialMotionValue, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { + CpsMaxDistanceOfDetectionFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.currentDistanceIcon, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { + CpsMaxDistanceOfStaticDetectionFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.currentDistanceIcon, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsDetectionRangeFunction extends CpsFunctions { + CpsDetectionRangeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.farDetection, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { + CpsDistanceOfMovingObjectsFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.currentDistanceIcon, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { + CpsPresenceJudgementThrsholdFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.presenceJudgementThrshold, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + +final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { + CpsMotionAmplitudeTriggerThresholdFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.presenceJudgementThrshold, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsPerpetualBoundaryFunction extends CpsFunctions { + CpsPerpetualBoundaryFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.boundary, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + + + +final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { + CpsMotionTriggerBoundaryFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.motionMeter, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + +final class CpsMotionTriggerTimeFunction extends CpsFunctions { + CpsMotionTriggerTimeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.motionMeter, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + +final class CpsMotionToStaticTimeFunction extends CpsFunctions { + CpsMotionToStaticTimeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.motionMeter, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + +final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { + CpsEnteringNoBodyStateTimeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.motionMeter, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} + + + +final class CpsSelfTestResultFunctions extends CpsFunctions { + CpsSelfTestResultFunctions({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'self_test_result', + operationName: 'Self-Test Result', + icon: Assets.selfTestResult, + ); + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + description: 'Self Testing', + icon: Assets.selfTestResult, + value: 'self_testing', + ), + CpsOperationalValue( + description: 'Self Testing Success', + icon: Assets.selfTestingSuccess, + value: 'self_testing_success', + ), + CpsOperationalValue( + description: 'Self Testing Failure', + icon: Assets.selfTestingFailure, + value: 'self_testing_failure', + ), + CpsOperationalValue( + description: 'Self Testing Timeout', + icon: Assets.selfTestingTimeout, + value: 'self_testing_timeout', + ), + CpsOperationalValue( + description: 'Communication Fault', + icon: Assets.communicationFault, + value: 'communication_fault', + ), + ]; + } +} + +final class CpsNobodyTimeFunction extends CpsFunctions { + CpsNobodyTimeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'nobody_time', + operationName: 'Entering Nobody Time', + icon: Assets.assetsNobodyTime, + ); + + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: 'None', + value: 'none', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '10sec', + value: '10s', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '30sec', + value: '30s', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '1min', + value: '1min', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '2min', + value: '2min', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '5min', + value: '5min', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '10min', + value: '10min', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '30min', + value: '30min', + ), + CpsOperationalValue( + icon: Assets.assetsNobodyTime, + description: '1hour', + value: '1hr', + ), + ]; + } +} + +final class CpsMovementFunctions extends CpsFunctions { + CpsMovementFunctions({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'movement', + operationName: 'Movement', + icon: Assets.motion, + ); + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + description: 'None', + icon: Assets.nobodyTime, + value: 'none', + ), + CpsOperationalValue( + description: 'Close To', + icon: Assets.closeToMotion, + value: 'parlour', + ), + CpsOperationalValue( + description: 'Far Away', + icon: Assets.farAwayMotion, + value: 'far_away', + ), + ]; + } +} + + +final class CpsCustomModeFunction extends CpsFunctions { + CpsCustomModeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'custom_mode', + operationName: 'Custom Mode', + icon: Assets.cpsCustomMode, + ); + + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + icon: Assets.cpsMode1, + description: 'Mode 1', + value: 'mode1', + ), + CpsOperationalValue( + icon: Assets.cpsMode2, + description: 'Mode 2', + value: 'mode2', + ), + CpsOperationalValue( + icon: Assets.cpsMode3, + description: 'Mode 3', + value: 'mode3', + ), + CpsOperationalValue( + icon: Assets.cpsMode4, + description: 'Mode 4', + value: 'mode4', + ), + ]; + } +} + +final class CpsSpaceTypeFunctions extends CpsFunctions { + CpsSpaceTypeFunctions({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'space_type', + operationName: 'Space Type', + icon: Assets.spaceType, + ); + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + description: 'Office', + icon: Assets.office, + value: 'office', + ), + CpsOperationalValue( + description: 'Parlour', + icon: Assets.parlour, + value: 'parlour', + ), + CpsOperationalValue( + description: 'Bathroom', + icon: Assets.bathroom, + value: 'bathroom', + ), + CpsOperationalValue( + description: 'Bedroom', + icon: Assets.bedroom, + value: 'bedroom', + ), + CpsOperationalValue( + description: 'DIY', + icon: Assets.dyi, + value: 'dyi', + ), + ]; + } +} + +class CpsPresenceStatusFunctions extends CpsFunctions { + CpsPresenceStatusFunctions({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: 'presence_state', + operationName: 'Presence Status', + icon: Assets.presenceSensor, + ); + + @override + List getOperationalValues() { + return [ + CpsOperationalValue( + icon: Assets.nobodyTime, + description: 'None', + value: 'none', + ), + CpsOperationalValue( + icon: Assets.presenceState, + description: 'Presence', + value: 'presence', + ), + CpsOperationalValue( + icon: Assets.motion, + description: 'Motion', + value: 'motion', + ), + ]; + } +} + +final class CpsSportsParaFunction extends CpsFunctions { + CpsSportsParaFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : super( + code: '', + operationName: '', + icon: Assets.sportsPara, + ); + @override + List getOperationalValues() { + // TODO: implement getOperationalValues + throw UnimplementedError(); + } +} \ No newline at end of file From c1d9846899212b768011b828b2ec73427f48d7ae Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 15:41:21 +0300 Subject: [PATCH 05/48] Enhance CeilingSensorHelper: Add deviceName and deviceId parameters to sensor functions --- .../ceiling_sensor/ceiling_sensor_helper.dart | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart index 310ca5c4..67083504 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart @@ -2,6 +2,7 @@ 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/models/ceiling_presence_sensor_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart'; @@ -33,4 +34,132 @@ abstract final class CeilingSensorHelper { ), ); } + + static List> getCeilingSensorFunctions({ + required String uuid, + required String name, + }) { + return [ + CpsRadarSwitchFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsSpatialParameterSwitchFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsSensitivityFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMovingSpeedFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsSpatialStaticValueFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsSpatialMotionValueFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsMaxDistanceOfDetectionFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMaxDistanceOfStaticDetectionFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsDetectionRangeFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsDistanceOfMovingObjectsFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsPresenceJudgementThrsholdFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMotionAmplitudeTriggerThresholdFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsPerpetualBoundaryFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMotionTriggerBoundaryFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMotionTriggerTimeFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMotionToStaticTimeFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsEnteringNoBodyStateTimeFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsSelfTestResultFunctions( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsNobodyTimeFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsMovementFunctions( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsCustomModeFunction( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsSpaceTypeFunctions( + deviceName: name, + deviceId: uuid, + type: 'BOTH', + ), + CpsPresenceStatusFunctions( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + CpsSportsParaFunction( + deviceName: name, + deviceId: uuid, + type: 'IF', + ), + ]; + } } From 120ed85d105c8b1a2c86d16fd7b5fac1d0964f84 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 15:41:28 +0300 Subject: [PATCH 06/48] Add CPS case to device function switch for ceiling sensor integration --- .../device_managment/all_devices/models/devices_model.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pages/device_managment/all_devices/models/devices_model.dart b/lib/pages/device_managment/all_devices/models/devices_model.dart index 2ea085c5..9069f8a3 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switc import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gateway.dart'; import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/enum/device_types.dart'; @@ -337,6 +338,11 @@ SOS type: 'BOTH', ), ]; + case 'CPS': + return CeilingSensorHelper.getCeilingSensorFunctions( + uuid: uuid ?? '', + name: name ?? '', + ); default: return []; } From 3f565788d5c6fa5051c61d54902040de5496d8a4 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 15:42:22 +0300 Subject: [PATCH 07/48] Refactor CeilingSensorDialog to be a StatefulWidget. --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 8820d305..62594d38 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 @@ -7,7 +7,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; -class CeilingSensorDialog extends StatelessWidget { +class CeilingSensorDialog extends StatefulWidget { const CeilingSensorDialog({ required this.uniqueCustomId, required this.functions, @@ -22,6 +22,11 @@ class CeilingSensorDialog extends StatelessWidget { final AllDevicesModel? device; final String dialogType; + @override + State createState() => _CeilingSensorDialogState(); +} + +class _CeilingSensorDialogState extends State { @override Widget build(BuildContext context) { return AlertDialog( From 6d612398ed96f09d02e5ca79aea864163122f0bd Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 16:41:03 +0300 Subject: [PATCH 08/48] Update operation names in CpsFunctions and add toggle codes to CeilingSensorHelper --- assets/icons/communication_fault.svg | 22 +++ .../ceiling_presence_sensor_functions.dart | 55 +++---- .../ceiling_sensor/ceiling_sensor_dialog.dart | 151 +++++++++++++++++- .../ceiling_sensor/ceiling_sensor_helper.dart | 11 ++ 4 files changed, 205 insertions(+), 34 deletions(-) create mode 100644 assets/icons/communication_fault.svg diff --git a/assets/icons/communication_fault.svg b/assets/icons/communication_fault.svg new file mode 100644 index 00000000..e2ab1b40 --- /dev/null +++ b/assets/icons/communication_fault.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 5bd15e66..4aa2e650 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -90,8 +90,8 @@ final class CpsSensitivityFunction extends CpsFunctions { step = 1, scale = 0, super( - code: 'far_detection', - operationName: 'Far Detection', + code: 'sensitivity', + operationName: 'Sensitivity', icon: Assets.sensitivity, ); @@ -123,7 +123,7 @@ final class CpsMovingSpeedFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Moving Speed', icon: Assets.speedoMeter, ); @override @@ -139,7 +139,7 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Spacial Static Value', icon: Assets.spatialStaticValue, ); @override @@ -149,7 +149,6 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { } } - final class CpsSpatialMotionValueFunction extends CpsFunctions { CpsSpatialMotionValueFunction({ required super.deviceId, @@ -157,7 +156,7 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Spatial Motion Value', icon: Assets.spatialMotionValue, ); @override @@ -167,7 +166,6 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { } } - final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { CpsMaxDistanceOfDetectionFunction({ required super.deviceId, @@ -175,7 +173,7 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Maximum Distance Of Detection', icon: Assets.currentDistanceIcon, ); @override @@ -185,7 +183,6 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { } } - final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { CpsMaxDistanceOfStaticDetectionFunction({ required super.deviceId, @@ -193,7 +190,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @override @@ -203,7 +200,6 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { } } - final class CpsDetectionRangeFunction extends CpsFunctions { CpsDetectionRangeFunction({ required super.deviceId, @@ -211,7 +207,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Detection Range', icon: Assets.farDetection, ); @override @@ -221,7 +217,6 @@ final class CpsDetectionRangeFunction extends CpsFunctions { } } - final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { CpsDistanceOfMovingObjectsFunction({ required super.deviceId, @@ -229,7 +224,7 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Distance Of Moving Objects', icon: Assets.currentDistanceIcon, ); @override @@ -239,7 +234,6 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { } } - final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { CpsPresenceJudgementThrsholdFunction({ required super.deviceId, @@ -247,7 +241,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @override @@ -264,7 +258,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); @override @@ -274,7 +268,6 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { } } - final class CpsPerpetualBoundaryFunction extends CpsFunctions { CpsPerpetualBoundaryFunction({ required super.deviceId, @@ -282,7 +275,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @override @@ -292,9 +285,6 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { } } - - - final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { CpsMotionTriggerBoundaryFunction({ required super.deviceId, @@ -302,7 +292,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @override @@ -312,7 +302,6 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { } } - final class CpsMotionTriggerTimeFunction extends CpsFunctions { CpsMotionTriggerTimeFunction({ required super.deviceId, @@ -320,7 +309,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @override @@ -337,7 +326,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @override @@ -354,7 +343,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); @override @@ -364,8 +353,6 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { } } - - final class CpsSelfTestResultFunctions extends CpsFunctions { CpsSelfTestResultFunctions({ required super.deviceId, @@ -404,6 +391,11 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { icon: Assets.communicationFault, value: 'communication_fault', ), + CpsOperationalValue( + description: 'Radar Fault', + icon: Assets.radarFault, + value: 'radar_fault', + ), ]; } } @@ -503,7 +495,6 @@ final class CpsMovementFunctions extends CpsFunctions { } } - final class CpsCustomModeFunction extends CpsFunctions { CpsCustomModeFunction({ required super.deviceId, @@ -624,7 +615,7 @@ final class CpsSportsParaFunction extends CpsFunctions { required super.type, }) : super( code: '', - operationName: '', + operationName: 'Sports Para', icon: Assets.sportsPara, ); @override @@ -632,4 +623,4 @@ final class CpsSportsParaFunction extends CpsFunctions { // TODO: implement getOperationalValues throw UnimplementedError(); } -} \ No newline at end of file +} 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 62594d38..0f19f204 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 @@ -3,9 +3,13 @@ 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/bloc/routine_bloc/routine_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/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; class CeilingSensorDialog extends StatefulWidget { const CeilingSensorDialog({ @@ -27,6 +31,20 @@ class CeilingSensorDialog extends StatefulWidget { } class _CeilingSensorDialogState extends State { + late final List _cpsFunctions; + + @override + void initState() { + super.initState(); + + _cpsFunctions = widget.functions.whereType().where((function) { + if (widget.dialogType == 'THEN') { + return function.type == 'THEN' || function.type == 'BOTH'; + } + return function.type == 'IF' || function.type == 'BOTH'; + }).toList(); + } + @override Widget build(BuildContext context) { return AlertDialog( @@ -56,17 +74,20 @@ class _CeilingSensorDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ const DialogHeader('Presence Sensor Condition'), + Expanded(child: _buildMainContent(context, state)), DialogFooter( onCancel: () => Navigator.pop(context), onConfirm: state.addedFunctions.isNotEmpty ? () { context.read().add( AddFunctionToRoutine( - state.addedFunctions, '{uniqueCustomId}'), + state.addedFunctions, + '${widget.uniqueCustomId}', + ), ); Navigator.pop(context, { - // 'deviceId': functions.first.deviceId, + 'deviceId': widget.functions.first.deviceId, }); } : null, @@ -79,4 +100,130 @@ class _CeilingSensorDialogState extends State { ), ); } + + Widget _buildMainContent(BuildContext context, FunctionBlocState state) { + final selectedFunction = state.selectedFunction; + final selectedOperationName = state.selectedOperationName; + final selectedFunctionData = state.addedFunctions.firstWhere( + (f) => f.functionCode == selectedFunction, + orElse: () => DeviceFunctionData( + entityId: '', + functionCode: selectedFunction ?? '', + operationName: '', + value: null, + ), + ); + final selectedCpsFunctions = _cpsFunctions.firstWhere( + (f) => f.code == selectedFunction, + orElse: () => CpsMovementFunctions( + deviceId: '', + deviceName: '', + type: '', + ), + ); + final operations = selectedCpsFunctions.getOperationalValues(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + CpsFunctionsList(cpsFunctions: _cpsFunctions), + if (state.selectedFunction != null) + Expanded( + child: CpsDialogValueSelector( + operations: operations, + selectedFunction: selectedFunction ?? '', + selectedFunctionData: selectedFunctionData, + cpsFunctions: _cpsFunctions, + operationName: selectedOperationName ?? '', + device: widget.device, + ), + ), + ], + ); + } +} + +class CpsFunctionsList extends StatelessWidget { + const CpsFunctionsList({required this.cpsFunctions, super.key}); + + final List cpsFunctions; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 360, + child: ListView.separated( + shrinkWrap: false, + itemCount: cpsFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider(color: ColorsManager.dividerColor), + ), + itemBuilder: (context, index) { + final function = cpsFunctions[index]; + return RoutineDialogFunctionListTile( + iconPath: function.icon, + operationName: function.operationName, + onTap: () => context.read().add( + SelectFunction( + functionCode: function.code, + operationName: function.operationName, + ), + ), + ); + }, + ), + ); + } +} + +class CpsDialogValueSelector extends StatelessWidget { + const CpsDialogValueSelector({ + required this.operations, + required this.selectedFunction, + required this.selectedFunctionData, + required this.cpsFunctions, + required this.device, + required this.operationName, + super.key, + }); + + final List operations; + final String selectedFunction; + final DeviceFunctionData? selectedFunctionData; + final List cpsFunctions; + final AllDevicesModel? device; + final String operationName; + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: operations.length, + itemBuilder: (context, index) { + final operation = operations[index]; + final isSelected = selectedFunctionData?.value == operation.value; + return RoutineDialogSelectionListTile( + iconPath: operation.icon, + description: operation.description, + isSelected: isSelected, + onTap: () { + if (!isSelected) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: operationName, + value: operation.value, + condition: selectedFunctionData?.condition, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + } + }, + ); + }, + ); + } } diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart index 67083504..291c2f81 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart @@ -162,4 +162,15 @@ abstract final class CeilingSensorHelper { ), ]; } + + static const toggleCodes = { + 'radar_switch', + 'space_para_switch', + 'self_test_result', + 'nobody_time', + 'movement', + 'custom_mode', + 'space_type', + 'presence_state', + }; } From 627ff4e9294d2a1daf82d56a4bde421c03031368 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 9 Apr 2025 17:00:21 +0300 Subject: [PATCH 09/48] Update operation codes and values for various CPS functions. --- .../ceiling_presence_sensor_functions.dart | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 4aa2e650..5408ccd0 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -122,7 +122,7 @@ final class CpsMovingSpeedFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'moving_speed', operationName: 'Moving Speed', icon: Assets.speedoMeter, ); @@ -138,7 +138,7 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'spatial_static_value', operationName: 'Spacial Static Value', icon: Assets.spatialStaticValue, ); @@ -155,7 +155,7 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'spatial_motion_value', operationName: 'Spatial Motion Value', icon: Assets.spatialMotionValue, ); @@ -172,7 +172,7 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'maximum_distance_of_detection', operationName: 'Maximum Distance Of Detection', icon: Assets.currentDistanceIcon, ); @@ -189,7 +189,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'maximum_distance_of_static_detection', operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @@ -206,7 +206,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'detection_range', operationName: 'Detection Range', icon: Assets.farDetection, ); @@ -223,7 +223,7 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'distance_of_moving_objects', operationName: 'Distance Of Moving Objects', icon: Assets.currentDistanceIcon, ); @@ -240,7 +240,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'presence_judgement_threshold', operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -257,7 +257,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'motion_amplitude_trigger_threshold', operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -274,7 +274,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'perpetual_boundary', operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @@ -291,7 +291,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'motion_trigger_boundary', operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @@ -308,7 +308,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'motion_trigger_time', operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @@ -325,7 +325,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'motion_to_static_time', operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @@ -342,7 +342,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'entering_nobody_state_time', operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); @@ -369,22 +369,22 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { CpsOperationalValue( description: 'Self Testing', icon: Assets.selfTestResult, - value: 'self_testing', + value: 'check', ), CpsOperationalValue( description: 'Self Testing Success', icon: Assets.selfTestingSuccess, - value: 'self_testing_success', + value: 'check_success', ), CpsOperationalValue( description: 'Self Testing Failure', icon: Assets.selfTestingFailure, - value: 'self_testing_failure', + value: 'check_failure', ), CpsOperationalValue( description: 'Self Testing Timeout', icon: Assets.selfTestingTimeout, - value: 'self_testing_timeout', + value: 'check_timeout', ), CpsOperationalValue( description: 'Communication Fault', @@ -614,7 +614,7 @@ final class CpsSportsParaFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: '', + code: 'sports_para', operationName: 'Sports Para', icon: Assets.sportsPara, ); From 729080ec4e7c2416d5de0c8fca708912163a4bdd Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 10:11:12 +0300 Subject: [PATCH 10/48] Refactor CPS function codes to use helper methods for consistency and clarity --- .../ceiling_presence_sensor_functions.dart | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 5408ccd0..0a03a803 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -1,6 +1,11 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +extension _Helper on String { + String get correct => this; + String get wrong => this; +} + class CpsOperationalValue { final String icon; final String description; @@ -34,7 +39,7 @@ final class CpsRadarSwitchFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'radar_switch', + code: 'radar_switch'.correct, operationName: 'Radar Switch', icon: Assets.acPower, ); @@ -60,7 +65,7 @@ final class CpsSpatialParameterSwitchFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'space_para_switch', + code: 'space_para_switch'.correct, operationName: 'Spatial Parameter Switch', icon: Assets.acPower, ); @@ -90,7 +95,7 @@ final class CpsSensitivityFunction extends CpsFunctions { step = 1, scale = 0, super( - code: 'sensitivity', + code: 'sensitivity'.correct, operationName: 'Sensitivity', icon: Assets.sensitivity, ); @@ -138,7 +143,7 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'spatial_static_value', + code: 'space_static_val'.correct, operationName: 'Spacial Static Value', icon: Assets.spatialStaticValue, ); @@ -155,7 +160,7 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'spatial_motion_value', + code: 'space_move_val'.correct, operationName: 'Spatial Motion Value', icon: Assets.spatialMotionValue, ); @@ -172,7 +177,7 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'maximum_distance_of_detection', + code: 'moving_max_dis'.correct, operationName: 'Maximum Distance Of Detection', icon: Assets.currentDistanceIcon, ); @@ -189,7 +194,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'maximum_distance_of_static_detection', + code: 'static_max_dis'.correct, operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @@ -206,7 +211,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'detection_range', + code: 'moving_range'.correct, operationName: 'Detection Range', icon: Assets.farDetection, ); @@ -223,7 +228,7 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'distance_of_moving_objects', + code: 'presence_range'.correct, operationName: 'Distance Of Moving Objects', icon: Assets.currentDistanceIcon, ); @@ -240,7 +245,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'presence_judgement_threshold', + code: 'presence_judgement_threshold'.wrong, operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -257,7 +262,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'motion_amplitude_trigger_threshold', + code: 'motion_amplitude_trigger_threshold'.wrong, operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -274,7 +279,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'perpetual_boundary', + code: 'perceptual_boundary'.correct, operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @@ -291,7 +296,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'motion_trigger_boundary', + code: 'moving_boundary'.correct, operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @@ -308,7 +313,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'motion_trigger_time', + code: 'moving_rigger_time'.correct, operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @@ -325,7 +330,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'motion_to_static_time', + code: 'moving_static_time'.correct, operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @@ -342,7 +347,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'entering_nobody_state_time', + code: 'none_body_time', operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); @@ -359,7 +364,7 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'self_test_result', + code: 'checking_result'.correct, operationName: 'Self-Test Result', icon: Assets.selfTestResult, ); @@ -406,7 +411,7 @@ final class CpsNobodyTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'nobody_time', + code: 'nobody_time'.correct, operationName: 'Entering Nobody Time', icon: Assets.assetsNobodyTime, ); @@ -469,7 +474,7 @@ final class CpsMovementFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'movement', + code: 'body_movement'.correct, operationName: 'Movement', icon: Assets.motion, ); @@ -539,7 +544,7 @@ final class CpsSpaceTypeFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'space_type', + code: 'scene'.correct, operationName: 'Space Type', icon: Assets.spaceType, ); @@ -581,7 +586,7 @@ class CpsPresenceStatusFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'presence_state', + code: 'presence_state'.correct, operationName: 'Presence Status', icon: Assets.presenceSensor, ); @@ -614,7 +619,7 @@ final class CpsSportsParaFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'sports_para', + code: 'sports_para'.correct, operationName: 'Sports Para', icon: Assets.sportsPara, ); From 7c68b90aef070aeb01fb43d2d15704dc312d700e Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 10:59:43 +0300 Subject: [PATCH 11/48] Refactor CPS functions to implement operational value generation with min, max, and step parameters. --- .../ceiling_presence_sensor_functions.dart | 347 +++++++++++++++--- 1 file changed, 298 insertions(+), 49 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 0a03a803..f10b0ece 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -1,7 +1,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -extension _Helper on String { +extension _Helper on String { String get correct => this; String get wrong => this; } @@ -93,7 +93,6 @@ final class CpsSensitivityFunction extends CpsFunctions { }) : min = 1, max = 10, step = 1, - scale = 0, super( code: 'sensitivity'.correct, operationName: 'Sensitivity', @@ -103,7 +102,6 @@ final class CpsSensitivityFunction extends CpsFunctions { final int min; final int max; final int step; - final int scale; @override List getOperationalValues() { @@ -126,14 +124,29 @@ final class CpsMovingSpeedFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0, + max = 32, + step = 1, + super( code: 'moving_speed', operationName: 'Moving Speed', icon: Assets.speedoMeter, ); + + final int min; + final int max; + final int step; + @override List getOperationalValues() { - return []; + return List.generate( + (max - min) ~/ step + 1, + (index) => CpsOperationalValue( + icon: Assets.speedoMeter, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); } } @@ -142,15 +155,29 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0, + max = 255, + step = 1, + super( code: 'space_static_val'.correct, operationName: 'Spacial Static Value', icon: Assets.spatialStaticValue, ); + + final int min; + final int max; + final int step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + return List.generate( + (max - min) ~/ step + 1, + (index) => CpsOperationalValue( + icon: Assets.spatialStaticValue, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); } } @@ -159,15 +186,29 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0, + max = 255, + step = 1, + super( code: 'space_move_val'.correct, operationName: 'Spatial Motion Value', icon: Assets.spatialMotionValue, ); + + final int min; + final int max; + final int step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + return List.generate( + (max - min) ~/ step + 1, + (index) => CpsOperationalValue( + icon: Assets.spatialMotionValue, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); } } @@ -176,15 +217,33 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( - code: 'moving_max_dis'.correct, + }) : min = 0.0, + max = 10.0, + step = 0.5, + super( + code: 'moving_max_dis'.correct, operationName: 'Maximum Distance Of Detection', icon: Assets.currentDistanceIcon, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.currentDistanceIcon, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -193,15 +252,33 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 10.0, + step = 0.5, + super( code: 'static_max_dis'.correct, operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.currentDistanceIcon, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -210,15 +287,33 @@ final class CpsDetectionRangeFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 25.5, + step = 0.1, + super( code: 'moving_range'.correct, operationName: 'Detection Range', icon: Assets.farDetection, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.farDetection, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -227,15 +322,33 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 25.5, + step = 0.1, + super( code: 'presence_range'.correct, operationName: 'Distance Of Moving Objects', icon: Assets.currentDistanceIcon, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.currentDistanceIcon, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -244,15 +357,29 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0, + max = 255, + step = 5, + super( code: 'presence_judgement_threshold'.wrong, operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); + + final int min; + final int max; + final int step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + return List.generate( + (max - min) ~/ step + 1, + (index) => CpsOperationalValue( + icon: Assets.presenceJudgementThrshold, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); } } @@ -261,15 +388,29 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0, + max = 255, + step = 5, + super( code: 'motion_amplitude_trigger_threshold'.wrong, operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); + + final int min; + final int max; + final int step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + return List.generate( + (max - min) ~/ step + 1, + (index) => CpsOperationalValue( + icon: Assets.presenceJudgementThrshold, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); } } @@ -278,15 +419,33 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.00, + max = 5.00, + step = 0.50, + super( code: 'perceptual_boundary'.correct, operationName: 'Perpetual Boundary', icon: Assets.boundary, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.boundary, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -295,15 +454,33 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 5.0, + step = 0.5, + super( code: 'moving_boundary'.correct, operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.motionMeter, + description: '${value.toStringAsFixed(1)}M', + value: value, + ); + }, + ); } } @@ -312,15 +489,33 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 2.0, + step = 0.1, + super( code: 'moving_rigger_time'.correct, operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.motionMeter, + description: '${value.toStringAsFixed(3)}sec', + value: value, + ); + }, + ); } } @@ -329,15 +524,33 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 50.0, + step = 1.0, + super( code: 'moving_static_time'.correct, operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.motionMeter, + description: '${value.toStringAsFixed(0)}s', + value: value, + ); + }, + ); } } @@ -346,15 +559,33 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 0.0, + max = 300.0, + step = 5.0, + super( code: 'none_body_time', operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.motionMeter, + description: '${value.toStringAsFixed(0)}sec', + value: value, + ); + }, + ); } } @@ -489,7 +720,7 @@ final class CpsMovementFunctions extends CpsFunctions { CpsOperationalValue( description: 'Close To', icon: Assets.closeToMotion, - value: 'parlour', + value: 'close_to', ), CpsOperationalValue( description: 'Far Away', @@ -618,14 +849,32 @@ final class CpsSportsParaFunction extends CpsFunctions { required super.deviceId, required super.deviceName, required super.type, - }) : super( + }) : min = 1, + max = 100, + step = 1, + super( code: 'sports_para'.correct, operationName: 'Sports Para', icon: Assets.sportsPara, ); + + final double min; + final double max; + final double step; + @override List getOperationalValues() { - // TODO: implement getOperationalValues - throw UnimplementedError(); + final count = ((max - min) / step).round() + 1; + return List.generate( + count, + (index) { + final value = (min + (index * step)); + return CpsOperationalValue( + icon: Assets.motionMeter, + description: value.toStringAsFixed(0), + value: value, + ); + }, + ); } } From 75b2f964285d15be548d648929b58e5450490b86 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 11:27:02 +0300 Subject: [PATCH 12/48] added spaces in operational value descriptions to match figma design. --- .../ceiling_presence_sensor_functions.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index f10b0ece..670dd4d4 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -239,7 +239,7 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.currentDistanceIcon, - description: '${value.toStringAsFixed(1)}M', + description: '${value.toStringAsFixed(1)} M', value: value, ); }, @@ -274,7 +274,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.currentDistanceIcon, - description: '${value.toStringAsFixed(1)}M', + description: '${value.toStringAsFixed(1)} M', value: value, ); }, @@ -309,7 +309,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.farDetection, - description: '${value.toStringAsFixed(1)}M', + description: '${value.toStringAsFixed(1)} M', value: value, ); }, @@ -344,7 +344,7 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.currentDistanceIcon, - description: '${value.toStringAsFixed(1)}M', + description: '${value.toStringAsFixed(1)} M', value: value, ); }, @@ -476,7 +476,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.motionMeter, - description: '${value.toStringAsFixed(1)}M', + description: '${value.toStringAsFixed(1)} M', value: value, ); }, @@ -511,7 +511,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.motionMeter, - description: '${value.toStringAsFixed(3)}sec', + description: '${value.toStringAsFixed(3)} sec', value: value, ); }, @@ -546,7 +546,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.motionMeter, - description: '${value.toStringAsFixed(0)}s', + description: '${value.toStringAsFixed(0)} sec', value: value, ); }, @@ -581,7 +581,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { final value = (min + (index * step)); return CpsOperationalValue( icon: Assets.motionMeter, - description: '${value.toStringAsFixed(0)}sec', + description: '${value.toStringAsFixed(0)} sec', value: value, ); }, From 796409600eae71e84a390c1dc1b1eff576002444 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 14:03:08 +0300 Subject: [PATCH 13/48] Update operation codes for CpsPresenceJudgementThresholdFunction and CpsMotionAmplitudeTriggerThresholdFunction. --- .../routines/models/ceiling_presence_sensor_functions.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 670dd4d4..afa5ba5e 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -361,7 +361,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'presence_judgement_threshold'.wrong, + code: 'presence_reference', operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -392,7 +392,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'motion_amplitude_trigger_threshold'.wrong, + code: 'moving_reference', operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); From 9ca6fb8640d353a17c48730bcfa0fe5d0252b89c Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 14:03:44 +0300 Subject: [PATCH 14/48] Remove unnecessary extension methods for operational value codes in CPS functions --- .../ceiling_presence_sensor_functions.dart | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index afa5ba5e..6e181407 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -1,11 +1,6 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -extension _Helper on String { - String get correct => this; - String get wrong => this; -} - class CpsOperationalValue { final String icon; final String description; @@ -39,7 +34,7 @@ final class CpsRadarSwitchFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'radar_switch'.correct, + code: 'radar_switch', operationName: 'Radar Switch', icon: Assets.acPower, ); @@ -65,7 +60,7 @@ final class CpsSpatialParameterSwitchFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'space_para_switch'.correct, + code: 'space_para_switch', operationName: 'Spatial Parameter Switch', icon: Assets.acPower, ); @@ -94,7 +89,7 @@ final class CpsSensitivityFunction extends CpsFunctions { max = 10, step = 1, super( - code: 'sensitivity'.correct, + code: 'sensitivity', operationName: 'Sensitivity', icon: Assets.sensitivity, ); @@ -159,7 +154,7 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions { max = 255, step = 1, super( - code: 'space_static_val'.correct, + code: 'space_static_val', operationName: 'Spacial Static Value', icon: Assets.spatialStaticValue, ); @@ -190,7 +185,7 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { max = 255, step = 1, super( - code: 'space_move_val'.correct, + code: 'space_move_val', operationName: 'Spatial Motion Value', icon: Assets.spatialMotionValue, ); @@ -221,7 +216,7 @@ final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { max = 10.0, step = 0.5, super( - code: 'moving_max_dis'.correct, + code: 'moving_max_dis', operationName: 'Maximum Distance Of Detection', icon: Assets.currentDistanceIcon, ); @@ -256,7 +251,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { max = 10.0, step = 0.5, super( - code: 'static_max_dis'.correct, + code: 'static_max_dis', operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @@ -291,7 +286,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { max = 25.5, step = 0.1, super( - code: 'moving_range'.correct, + code: 'moving_range', operationName: 'Detection Range', icon: Assets.farDetection, ); @@ -326,7 +321,7 @@ final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions { max = 25.5, step = 0.1, super( - code: 'presence_range'.correct, + code: 'presence_range', operationName: 'Distance Of Moving Objects', icon: Assets.currentDistanceIcon, ); @@ -423,7 +418,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { max = 5.00, step = 0.50, super( - code: 'perceptual_boundary'.correct, + code: 'perceptual_boundary', operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @@ -458,7 +453,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { max = 5.0, step = 0.5, super( - code: 'moving_boundary'.correct, + code: 'moving_boundary', operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @@ -493,7 +488,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { max = 2.0, step = 0.1, super( - code: 'moving_rigger_time'.correct, + code: 'moving_rigger_time', operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @@ -528,7 +523,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { max = 50.0, step = 1.0, super( - code: 'moving_static_time'.correct, + code: 'moving_static_time', operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @@ -595,7 +590,7 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'checking_result'.correct, + code: 'checking_result', operationName: 'Self-Test Result', icon: Assets.selfTestResult, ); @@ -642,7 +637,7 @@ final class CpsNobodyTimeFunction extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'nobody_time'.correct, + code: 'nobody_time', operationName: 'Entering Nobody Time', icon: Assets.assetsNobodyTime, ); @@ -705,7 +700,7 @@ final class CpsMovementFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'body_movement'.correct, + code: 'body_movement', operationName: 'Movement', icon: Assets.motion, ); @@ -775,7 +770,7 @@ final class CpsSpaceTypeFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'scene'.correct, + code: 'scene', operationName: 'Space Type', icon: Assets.spaceType, ); @@ -817,7 +812,7 @@ class CpsPresenceStatusFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'presence_state'.correct, + code: 'presence_state', operationName: 'Presence Status', icon: Assets.presenceSensor, ); @@ -853,7 +848,7 @@ final class CpsSportsParaFunction extends CpsFunctions { max = 100, step = 1, super( - code: 'sports_para'.correct, + code: 'sports_para', operationName: 'Sports Para', icon: Assets.sportsPara, ); From 9d3b58deebe3d4a8c6f12e9a0ec831d7f21a8fc8 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 14:32:09 +0300 Subject: [PATCH 15/48] Refactor wall presence sensor components and add new widgets for improved functionality and clarity to ensure reusability in the future for other devices. --- .../routines/widgets/condition_toggle.dart | 33 ++ .../routines/widgets/function_slider.dart | 26 ++ .../wall_sensor/wall_presence_sensor.dart | 353 +----------------- .../wps_operational_values_list.dart | 114 ++++++ .../wps_value_selector_widget.dart | 95 +++++ .../widgets/slider_value_selector.dart | 67 ++++ lib/pages/routines/widgets/value_display.dart | 31 ++ 7 files changed, 372 insertions(+), 347 deletions(-) create mode 100644 lib/pages/routines/widgets/condition_toggle.dart create mode 100644 lib/pages/routines/widgets/function_slider.dart create mode 100644 lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart create mode 100644 lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart create mode 100644 lib/pages/routines/widgets/slider_value_selector.dart create mode 100644 lib/pages/routines/widgets/value_display.dart diff --git a/lib/pages/routines/widgets/condition_toggle.dart b/lib/pages/routines/widgets/condition_toggle.dart new file mode 100644 index 00000000..99ea2f04 --- /dev/null +++ b/lib/pages/routines/widgets/condition_toggle.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ConditionToggle extends StatelessWidget { + final String? currentCondition; + final void Function(String condition) onChanged; + + const ConditionToggle({ + required this.onChanged, + this.currentCondition, + super.key, + }); + + static const _conditions = ["<", "==", ">"]; + + @override + Widget build(BuildContext context) { + return ToggleButtons( + onPressed: (index) => onChanged(_conditions[index]), + 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(), + ); + } +} diff --git a/lib/pages/routines/widgets/function_slider.dart b/lib/pages/routines/widgets/function_slider.dart new file mode 100644 index 00000000..b7fb0d53 --- /dev/null +++ b/lib/pages/routines/widgets/function_slider.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class FunctionSlider extends StatelessWidget { + final dynamic initialValue; + + final (double min, double max) range; + final void Function(double value) onChanged; + + const FunctionSlider({ + required this.onChanged, + required this.initialValue, + required this.range, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Slider( + value: initialValue is int ? initialValue.toDouble() : range.$1, + min: range.$1, + max: range.$2, + divisions: (range.$2 - range.$1).toInt(), + onChanged: onChanged, + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart index b7733511..91abb34e 100644 --- a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; 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/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart'; -import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.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/wall_sensor/time_wheel.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; class WallPresenceSensor extends StatefulWidget { final List functions; @@ -63,8 +62,7 @@ class _WallPresenceSensorState extends State { @override void initState() { super.initState(); - _wpsFunctions = - widget.functions.whereType().where((function) { + _wpsFunctions = widget.functions.whereType().where((function) { if (widget.dialogType == 'THEN') { return function.type == 'THEN' || function.type == 'BOTH'; } @@ -176,10 +174,10 @@ class _WallPresenceSensorState extends State { ); return Expanded( - child: _ValueSelector( + child: WpsValueSelectorWidget( selectedFunction: selectedFunction, functionData: functionData, - acFunctions: _wpsFunctions, + wpsFunctions: _wpsFunctions, device: widget.device, dialogType: widget.dialogType!, removeComparators: widget.removeComparetors, @@ -208,342 +206,3 @@ class _WallPresenceSensorState extends State { ); } } - -class _ValueSelector extends StatelessWidget { - final String selectedFunction; - final DeviceFunctionData functionData; - final List acFunctions; - final AllDevicesModel? device; - final String dialogType; - final bool removeComparators; - - const _ValueSelector({ - required this.selectedFunction, - required this.functionData, - required this.acFunctions, - required this.device, - required this.dialogType, - required this.removeComparators, - }); - - @override - Widget build(BuildContext context) { - final selectedFn = - acFunctions.firstWhere((f) => f.code == selectedFunction); - final values = selectedFn.getOperationalValues(); - - if (_isSliderFunction(selectedFunction)) { - return _SliderValueSelector( - selectedFunction: selectedFunction, - functionData: functionData, - device: device, - dialogType: dialogType, - ); - } - - return _OperationalValuesList( - values: values, - selectedValue: functionData.value, - device: device, - operationName: selectedFn.operationName, - selectCode: selectedFunction, - ); - } - - bool _isSliderFunction(String function) => - ['dis_current', 'presence_time', 'illuminance_value'].contains(function); -} - -class _SliderValueSelector extends StatelessWidget { - final String selectedFunction; - final DeviceFunctionData functionData; - final AllDevicesModel? device; - final String dialogType; - - const _SliderValueSelector({ - required this.selectedFunction, - required this.functionData, - required this.device, - required this.dialogType, - }); - - @override - Widget build(BuildContext context) { - final initialValue = functionData.value ?? 250; - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 20), - _ConditionToggle( - currentCondition: functionData.condition, - selectCode: selectedFunction, - device: device, - operationName: functionData.operationName, - selectedValue: functionData.value, - ), - _ValueDisplay( - value: initialValue, - functionCode: selectedFunction, - ), - const SizedBox(height: 20), - _FunctionSlider( - initialValue: initialValue, - functionCode: selectedFunction, - functionData: functionData, - device: device, - ), - ], - ); - } -} - -class _ConditionToggle extends StatelessWidget { - final String? currentCondition; - final String selectCode; - final AllDevicesModel? device; - final String operationName; - final dynamic selectedValue; - - const _ConditionToggle({ - this.currentCondition, - required this.selectCode, - this.device, - required this.operationName, - this.selectedValue, - }); - - @override - Widget build(BuildContext context) { - const conditions = ["<", "==", ">"]; - return ToggleButtons( - onPressed: (index) => _updateCondition(context, conditions[index]), - 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(), - ); - } - - void _updateCondition(BuildContext context, String condition) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - condition: condition, - value: selectedValue, - ), - ), - ); - } -} - -class _ValueDisplay extends StatelessWidget { - final dynamic value; - final String functionCode; - - const _ValueDisplay({ - required this.value, - required this.functionCode, - }); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - _getDisplayText(), - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ); - } - - String _getDisplayText() { - final intValue = (value as num?)?.toInt() ?? 0; - switch (functionCode) { - case 'presence_time': - return '$intValue Min'; - case 'dis_current': - return '$intValue CM'; - case 'illuminance_value': - return '$intValue Lux'; - default: - return '$intValue'; - } - } -} - -class _FunctionSlider extends StatelessWidget { - final dynamic initialValue; - final String functionCode; - final DeviceFunctionData functionData; - final AllDevicesModel? device; - - const _FunctionSlider({ - required this.initialValue, - required this.functionCode, - required this.functionData, - required this.device, - }); - - @override - Widget build(BuildContext context) { - final (min, max) = _getSliderRange(); - return Slider( - value: initialValue is int ? initialValue.toDouble() : min, - min: min, - max: max, - divisions: (max - min).toInt(), - onChanged: (value) => _updateValue(context, value.toInt()), - ); - } - - (double, double) _getSliderRange() { - switch (functionCode) { - case 'presence_time': - return (0, 65535); - case 'dis_current': - return (1, 600); - case 'illuminance_value': - return (0, 10000); - default: - return (200, 300); - } - } - - void _updateValue(BuildContext context, int value) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: functionCode, - operationName: functionData.operationName, - value: value, - condition: functionData.condition, - ), - ), - ); - } -} - -class _OperationalValuesList extends StatelessWidget { - final List values; - final dynamic selectedValue; - final AllDevicesModel? device; - final String operationName; - final String selectCode; - - const _OperationalValuesList({ - required this.values, - required this.selectedValue, - required this.device, - required this.operationName, - required this.selectCode, - }); - - @override - Widget build(BuildContext context) { - return operationName == 'Nobody Time' - ? _buildTimeWheel(context) - : ListView.builder( - padding: const EdgeInsets.all(20), - itemCount: values.length, - itemBuilder: (context, index) => - _buildValueItem(context, values[index]), - ); - } - - Widget _buildTimeWheel(BuildContext context) { - final currentTotalSeconds = selectedValue as int? ?? 0; - return TimeWheelPicker( - initialHours: currentTotalSeconds ~/ 3600, - initialMinutes: (currentTotalSeconds % 3600) ~/ 60, - initialSeconds: currentTotalSeconds % 60, - onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s), - ); - } - - Widget _buildValueItem(BuildContext context, WpsOperationalValue value) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildValueIcon(context, value), - Expanded(child: _buildValueDescription(value)), - _buildValueRadio(context, value), - ], - ), - ); - } - - Widget _buildValueIcon(context, WpsOperationalValue value) { - return Column( - children: [ - if (_shouldShowTextDescription) - Text(value.description.replaceAll("cm", '')), - SvgPicture.asset(value.icon, width: 25, height: 25), - ], - ); - } - - bool get _shouldShowTextDescription => - operationName == 'Far Detection' || - operationName == 'Motionless Detection Sensitivity'; - - Widget _buildValueDescription(WpsOperationalValue value) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text(value.description), - ); - } - - Widget _buildValueRadio(context, WpsOperationalValue value) { - return Radio( - value: value.value, - groupValue: selectedValue, - onChanged: (_) => _selectValue(context, value.value), - ); - } - - void _selectValue(BuildContext context, dynamic value) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - value: value, - ), - ), - ); - } - - void _updateTotalSeconds(BuildContext context, int h, int m, int s) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectCode, - operationName: operationName, - value: h * 3600 + m * 60 + s, - ), - ), - ); - } -} diff --git a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart new file mode 100644 index 00000000..6c149cd3 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +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/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart'; + +class WpsOperationalValuesList extends StatelessWidget { + final List values; + final dynamic selectedValue; + final AllDevicesModel? device; + final String operationName; + final String selectCode; + + const WpsOperationalValuesList({ + required this.values, + required this.selectedValue, + required this.device, + required this.operationName, + required this.selectCode, + super.key, + }); + + @override + Widget build(BuildContext context) { + return operationName == 'Nobody Time' + ? _buildTimeWheel(context) + : ListView.builder( + padding: const EdgeInsets.all(20), + itemCount: values.length, + itemBuilder: (context, index) => _buildValueItem(context, values[index]), + ); + } + + Widget _buildTimeWheel(BuildContext context) { + final currentTotalSeconds = selectedValue as int? ?? 0; + return TimeWheelPicker( + initialHours: currentTotalSeconds ~/ 3600, + initialMinutes: (currentTotalSeconds % 3600) ~/ 60, + initialSeconds: currentTotalSeconds % 60, + onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s), + ); + } + + Widget _buildValueItem(BuildContext context, WpsOperationalValue value) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildValueIcon(context, value), + Expanded(child: _buildValueDescription(value)), + _buildValueRadio(context, value), + ], + ), + ); + } + + Widget _buildValueIcon(context, WpsOperationalValue value) { + return Column( + children: [ + if (_shouldShowTextDescription) Text(value.description.replaceAll("cm", '')), + SvgPicture.asset(value.icon, width: 25, height: 25), + ], + ); + } + + bool get _shouldShowTextDescription => + operationName == 'Far Detection' || + operationName == 'Motionless Detection Sensitivity'; + + Widget _buildValueDescription(WpsOperationalValue value) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text(value.description), + ); + } + + Widget _buildValueRadio(context, WpsOperationalValue value) { + return Radio( + value: value.value, + groupValue: selectedValue, + onChanged: (_) => _selectValue(context, value.value), + ); + } + + void _selectValue(BuildContext context, dynamic value) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value, + ), + ), + ); + } + + void _updateTotalSeconds(BuildContext context, int h, int m, int s) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: h * 3600 + m * 60 + s, + ), + ), + ); + } +} 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 new file mode 100644 index 00000000..5f38b240 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart @@ -0,0 +1,95 @@ +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/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_functions.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; + final DeviceFunctionData functionData; + final List wpsFunctions; + final AllDevicesModel? device; + final String dialogType; + final bool removeComparators; + + const WpsValueSelectorWidget({ + required this.selectedFunction, + required this.functionData, + required this.wpsFunctions, + required this.device, + required this.dialogType, + required this.removeComparators, + super.key, + }); + + @override + Widget build(BuildContext context) { + final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction); + final values = selectedFn.getOperationalValues(); + + if (_isSliderFunction(selectedFunction)) { + return SliderValueSelector( + selectedFunction: selectedFunction, + functionData: functionData, + device: device, + dialogType: dialogType, + sliderRange: sliderRange, + displayedValue: getDisplayText, + initialValue: functionData.value ?? 250, + onConditionChanged: (condition) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: functionData.operationName, + condition: condition, + value: functionData.value, + ), + ), + ), + onSliderChanged: (value) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: functionData.operationName, + value: value.toInt(), + condition: functionData.condition, + ), + ), + ), + ); + } + + return WpsOperationalValuesList( + values: values, + selectedValue: functionData.value, + device: device, + operationName: selectedFn.operationName, + selectCode: selectedFunction, + ); + } + + bool _isSliderFunction(String function) => + ['dis_current', 'presence_time', 'illuminance_value'].contains(function); + + (double, double) get sliderRange => switch (functionData.functionCode) { + 'presence_time' => (0, 65535), + 'dis_current' => (0, 600), + 'illuminance_value' => (0, 10000), + _ => (200, 300), + }; + + String get getDisplayText { + final intValue = int.tryParse('${functionData.value ?? ''}'); + return switch (functionData.functionCode) { + 'presence_time' => '${intValue ?? '0'} Min', + 'dis_current' => '${intValue ?? '250'} CM', + 'illuminance_value' => '${intValue ?? '0'} Lux', + _ => '$intValue', + }; + } +} diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart new file mode 100644 index 00000000..6b408008 --- /dev/null +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart'; +import 'package:syncrow_web/pages/routines/widgets/function_slider.dart'; +import 'package:syncrow_web/pages/routines/widgets/value_display.dart'; + +class SliderValueSelector extends StatelessWidget { + final String selectedFunction; + final DeviceFunctionData functionData; + final AllDevicesModel? device; + final String dialogType; + final (double, double) sliderRange; + final String displayedValue; + final Object? initialValue; + final void Function(String condition) onConditionChanged; + final void Function(double value) onSliderChanged; + + const SliderValueSelector({ + required this.selectedFunction, + required this.functionData, + required this.device, + required this.dialogType, + required this.sliderRange, + required this.displayedValue, + required this.initialValue, + required this.onConditionChanged, + required this.onSliderChanged, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 20), + ConditionToggle( + currentCondition: functionData.condition, + onChanged: onConditionChanged, + ), + ValueDisplay( + value: initialValue, + label: displayedValue, + ), + const SizedBox(height: 20), + FunctionSlider( + initialValue: initialValue, + range: sliderRange, onChanged: onSliderChanged, + // void _updateValue(BuildContext context, int value) { + // context.read().add( + // AddFunction( + // functionData: DeviceFunctionData( + // entityId: device?.uuid ?? '', + // functionCode: functionCode, + // operationName: functionData.operationName, + // value: value, + // condition: functionData.condition, + // ), + // ), + // ); + // } + ), + ], + ); + } +} diff --git a/lib/pages/routines/widgets/value_display.dart b/lib/pages/routines/widgets/value_display.dart new file mode 100644 index 00000000..e9b76325 --- /dev/null +++ b/lib/pages/routines/widgets/value_display.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class ValueDisplay extends StatelessWidget { + final dynamic value; + final String label; + + const ValueDisplay({ + required this.value, + required this.label, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + label, + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ); + } +} From 551779c33ae1abbe22db0094ca0dffa9b2c7a476 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 14:41:58 +0300 Subject: [PATCH 16/48] moved widgets to their own files. --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 91 +------------------ .../cps_dialog_value_selector.dart | 58 ++++++++++++ .../ceiling_sensor/cps_functions_list.dart | 40 ++++++++ 3 files changed, 101 insertions(+), 88 deletions(-) create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart 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 0f19f204..50756c69 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 @@ -7,9 +7,8 @@ import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functi import 'package:syncrow_web/pages/routines/models/device_functions.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_dialog_function_list_tile.dart'; -import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart'; class CeilingSensorDialog extends StatefulWidget { const CeilingSensorDialog({ @@ -20,6 +19,7 @@ class CeilingSensorDialog extends StatefulWidget { required this.dialogType, super.key, }); + final String? uniqueCustomId; final List functions; final List deviceSelectedFunctions; @@ -142,88 +142,3 @@ class _CeilingSensorDialogState extends State { ); } } - -class CpsFunctionsList extends StatelessWidget { - const CpsFunctionsList({required this.cpsFunctions, super.key}); - - final List cpsFunctions; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 360, - child: ListView.separated( - shrinkWrap: false, - itemCount: cpsFunctions.length, - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.symmetric(horizontal: 40.0), - child: Divider(color: ColorsManager.dividerColor), - ), - itemBuilder: (context, index) { - final function = cpsFunctions[index]; - return RoutineDialogFunctionListTile( - iconPath: function.icon, - operationName: function.operationName, - onTap: () => context.read().add( - SelectFunction( - functionCode: function.code, - operationName: function.operationName, - ), - ), - ); - }, - ), - ); - } -} - -class CpsDialogValueSelector extends StatelessWidget { - const CpsDialogValueSelector({ - required this.operations, - required this.selectedFunction, - required this.selectedFunctionData, - required this.cpsFunctions, - required this.device, - required this.operationName, - super.key, - }); - - final List operations; - final String selectedFunction; - final DeviceFunctionData? selectedFunctionData; - final List cpsFunctions; - final AllDevicesModel? device; - final String operationName; - - @override - Widget build(BuildContext context) { - return ListView.builder( - itemCount: operations.length, - itemBuilder: (context, index) { - final operation = operations[index]; - final isSelected = selectedFunctionData?.value == operation.value; - return RoutineDialogSelectionListTile( - iconPath: operation.icon, - description: operation.description, - isSelected: isSelected, - onTap: () { - if (!isSelected) { - context.read().add( - AddFunction( - functionData: DeviceFunctionData( - entityId: device?.uuid ?? '', - functionCode: selectedFunction, - operationName: operationName, - value: operation.value, - condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, - ), - ), - ); - } - }, - ); - }, - ); - } -} diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart new file mode 100644 index 00000000..504017a2 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart @@ -0,0 +1,58 @@ +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/models/ceiling_presence_sensor_functions.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart'; + +class CpsDialogValueSelector extends StatelessWidget { + const CpsDialogValueSelector({ + required this.operations, + required this.selectedFunction, + required this.selectedFunctionData, + required this.cpsFunctions, + required this.device, + required this.operationName, + super.key, + }); + + final List operations; + final String selectedFunction; + final DeviceFunctionData? selectedFunctionData; + final List cpsFunctions; + final AllDevicesModel? device; + final String operationName; + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: operations.length, + itemBuilder: (context, index) { + final operation = operations[index]; + final isSelected = selectedFunctionData?.value == operation.value; + return RoutineDialogSelectionListTile( + iconPath: operation.icon, + description: operation.description, + isSelected: isSelected, + onTap: () { + if (!isSelected) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: operationName, + value: operation.value, + condition: selectedFunctionData?.condition, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + } + }, + ); + }, + ); + } +} 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 new file mode 100644 index 00000000..2a35428a --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +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/widgets/routine_dialog_function_list_tile.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CpsFunctionsList extends StatelessWidget { + const CpsFunctionsList({required this.cpsFunctions, super.key}); + + final List cpsFunctions; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 360, + child: ListView.separated( + shrinkWrap: false, + itemCount: cpsFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider(color: ColorsManager.dividerColor), + ), + itemBuilder: (context, index) { + final function = cpsFunctions[index]; + return RoutineDialogFunctionListTile( + iconPath: function.icon, + operationName: function.operationName, + onTap: () => context.read().add( + SelectFunction( + functionCode: function.code, + operationName: function.operationName, + ), + ), + ); + }, + ), + ); + } +} From cf103d5a7c1e67912688afab5f2f81906a600deb Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 15:15:20 +0300 Subject: [PATCH 17/48] Add CpsDialogSliderSelector for enhanced slider functionality and integrate with CeilingSensorDialog --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 31 ++++-- .../cps_dialog_slider_selector.dart | 105 ++++++++++++++++++ .../widgets/slider_value_selector.dart | 16 +-- 3 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart 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 50756c69..7b51d064 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 @@ -7,6 +7,8 @@ import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functi import 'package:syncrow_web/pages/routines/models/device_functions.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/ceiling_sensor/ceiling_sensor_helper.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart'; @@ -122,21 +124,32 @@ class _CeilingSensorDialogState extends State { ), ); final operations = selectedCpsFunctions.getOperationalValues(); - + final isToggleFunction = + CeilingSensorHelper.toggleCodes.contains(selectedFunction); return Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ CpsFunctionsList(cpsFunctions: _cpsFunctions), if (state.selectedFunction != null) Expanded( - child: CpsDialogValueSelector( - operations: operations, - selectedFunction: selectedFunction ?? '', - selectedFunctionData: selectedFunctionData, - cpsFunctions: _cpsFunctions, - operationName: selectedOperationName ?? '', - device: widget.device, - ), + child: isToggleFunction + ? CpsDialogValueSelector( + operations: operations, + selectedFunction: selectedFunction ?? '', + selectedFunctionData: selectedFunctionData, + cpsFunctions: _cpsFunctions, + operationName: selectedOperationName ?? '', + device: widget.device, + ) + : CpsDialogSliderSelector( + operations: operations, + selectedFunction: selectedFunction ?? '', + selectedFunctionData: selectedFunctionData, + cpsFunctions: _cpsFunctions, + operationName: selectedOperationName ?? '', + device: widget.device, + dialogType: widget.dialogType, + ), ), ], ); 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 new file mode 100644 index 00000000..d826afb5 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart @@ -0,0 +1,105 @@ +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/models/ceiling_presence_sensor_functions.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; + +class CpsDialogSliderSelector extends StatelessWidget { + const CpsDialogSliderSelector({ + required this.operations, + required this.selectedFunction, + required this.selectedFunctionData, + required this.cpsFunctions, + required this.device, + required this.operationName, + required this.dialogType, + super.key, + }); + + final List operations; + final String selectedFunction; + final DeviceFunctionData selectedFunctionData; + final List cpsFunctions; + final AllDevicesModel? device; + final String operationName; + final String dialogType; + + @override + Widget build(BuildContext context) { + return SliderValueSelector( + selectedFunction: selectedFunction, + functionData: selectedFunctionData, + device: device, + dialogType: dialogType, + sliderRange: _sliderRange, + displayedValue: _displayText, + initialValue: selectedFunctionData.value ?? 0, + onConditionChanged: (condition) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: selectedFunctionData.operationName, + condition: condition, + value: selectedFunctionData.value, + ), + ), + ), + onSliderChanged: (value) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: selectedFunctionData.operationName, + value: value.toInt(), + condition: selectedFunctionData.condition, + ), + ), + ), + ); + } + + double get sliderStepper { + return 1; + } + + (double, double) get _sliderRange => switch (selectedFunctionData.functionCode) { + 'moving_speed' => (0, 32), + 'space_static_val' => (0, 255), + 'space_move_val' => (0, 255), + 'moving_max_dis' => (0, 10), + 'static_max_dis' => (0, 10), + 'moving_range' => (0, 25.5), + 'presence_range' => (0, 25.5), + 'presence_judgement_threshold' => (0, 255), + 'motion_amplitude_trigger_threshold' => (0, 255), + 'perceptual_boundary' => (0, 5), + 'moving_boundary' => (0, 5), + 'moving_rigger_time' => (0, 2), + 'moving_static_time' => (0, 50), + 'none_body_time' => (0, 300), + _ => (0, 300), + }; + + String get _displayText { + final value = selectedFunctionData.value; + final parsedValue = double.tryParse('$value') ?? value; + + return switch (selectedFunctionData.functionCode) { + 'moving_max_dis' || + 'static_max_dis' || + 'moving_range' || + 'presence_range' || + 'perceptual_boundary' || + 'moving_boundary' => + '${parsedValue?.toStringAsFixed(1) ?? '0'} M', + 'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'} s', + 'moving_static_time' || + 'none_body_time' => + '${parsedValue?.toStringAsFixed(0) ?? '0'} s', + _ => '${parsedValue ?? 0}', + }; + } +} diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index 6b408008..82c59533 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -46,20 +46,8 @@ class SliderValueSelector extends StatelessWidget { const SizedBox(height: 20), FunctionSlider( initialValue: initialValue, - range: sliderRange, onChanged: onSliderChanged, - // void _updateValue(BuildContext context, int value) { - // context.read().add( - // AddFunction( - // functionData: DeviceFunctionData( - // entityId: device?.uuid ?? '', - // functionCode: functionCode, - // operationName: functionData.operationName, - // value: value, - // condition: functionData.condition, - // ), - // ), - // ); - // } + range: sliderRange, + onChanged: onSliderChanged, ), ], ); From fadb23d631a6c9ce3bb5febbcb941e12f171db24 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 15:26:36 +0300 Subject: [PATCH 18/48] Refactor SliderValueSelector layout by removing unnecessary SizedBox and adjusting spacing for improved UI consistency. --- lib/pages/routines/widgets/slider_value_selector.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index 82c59533..dd26caf1 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -32,9 +32,9 @@ class SliderValueSelector extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + spacing: 16, mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(height: 20), ConditionToggle( currentCondition: functionData.condition, onChanged: onConditionChanged, @@ -43,7 +43,6 @@ class SliderValueSelector extends StatelessWidget { value: initialValue, label: displayedValue, ), - const SizedBox(height: 20), FunctionSlider( initialValue: initialValue, range: sliderRange, From 74046c5aede8d97ed0497daf2f69ce143087fd2b Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 16:22:02 +0300 Subject: [PATCH 19/48] Refactor SliderValueSelector and ValueDisplay to include unit handling and improve UI consistency across sensor dialogs. --- .../cps_dialog_slider_selector.dart | 30 ++-- .../wps_value_selector_widget.dart | 18 ++- .../widgets/slider_value_selector.dart | 132 +++++++++++++++--- lib/pages/routines/widgets/value_display.dart | 4 +- 4 files changed, 145 insertions(+), 39 deletions(-) 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 d826afb5..3c792eca 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 @@ -29,13 +29,12 @@ class CpsDialogSliderSelector extends StatelessWidget { @override Widget build(BuildContext context) { return SliderValueSelector( - selectedFunction: selectedFunction, - functionData: selectedFunctionData, - device: device, + currentCondition: selectedFunctionData.condition, dialogType: dialogType, sliderRange: _sliderRange, displayedValue: _displayText, initialValue: selectedFunctionData.value ?? 0, + unit: _unit, onConditionChanged: (condition) => context.read().add( AddFunction( functionData: DeviceFunctionData( @@ -58,13 +57,10 @@ class CpsDialogSliderSelector extends StatelessWidget { ), ), ), + ); } - double get sliderStepper { - return 1; - } - (double, double) get _sliderRange => switch (selectedFunctionData.functionCode) { 'moving_speed' => (0, 32), 'space_static_val' => (0, 255), @@ -94,12 +90,26 @@ class CpsDialogSliderSelector extends StatelessWidget { 'presence_range' || 'perceptual_boundary' || 'moving_boundary' => - '${parsedValue?.toStringAsFixed(1) ?? '0'} M', - 'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'} s', + '${parsedValue?.toStringAsFixed(1) ?? '0'}', + 'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'}', 'moving_static_time' || 'none_body_time' => - '${parsedValue?.toStringAsFixed(0) ?? '0'} s', + '${parsedValue?.toStringAsFixed(0) ?? '0'}', _ => '${parsedValue ?? 0}', }; } + + String get _unit { + return switch (selectedFunctionData.functionCode) { + 'moving_max_dis' || + 'static_max_dis' || + 'moving_range' || + 'presence_range' || + 'perceptual_boundary' || + 'moving_boundary' => + 'M', + 'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec', + _ => '', + }; + } } 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 5f38b240..ccd82e23 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 @@ -32,9 +32,7 @@ class WpsValueSelectorWidget extends StatelessWidget { if (_isSliderFunction(selectedFunction)) { return SliderValueSelector( - selectedFunction: selectedFunction, - functionData: functionData, - device: device, + currentCondition: functionData.condition, dialogType: dialogType, sliderRange: sliderRange, displayedValue: getDisplayText, @@ -61,6 +59,7 @@ class WpsValueSelectorWidget extends StatelessWidget { ), ), ), + unit: _unit, ); } @@ -86,10 +85,17 @@ class WpsValueSelectorWidget extends StatelessWidget { String get getDisplayText { final intValue = int.tryParse('${functionData.value ?? ''}'); return switch (functionData.functionCode) { - 'presence_time' => '${intValue ?? '0'} Min', - 'dis_current' => '${intValue ?? '250'} CM', - 'illuminance_value' => '${intValue ?? '0'} Lux', + 'presence_time' => '${intValue ?? '0'}', + 'dis_current' => '${intValue ?? '250'}', + 'illuminance_value' => '${intValue ?? '0'}', _ => '$intValue', }; } + + String get _unit => switch (functionData.functionCode) { + 'presence_time' => 'Min', + 'dis_current' => 'CM', + 'illuminance_value' => 'Lux', + _ => '', + }; } diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index dd26caf1..dfcfeac4 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -1,54 +1,142 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; -import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:flutter/services.dart'; import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart'; import 'package:syncrow_web/pages/routines/widgets/function_slider.dart'; import 'package:syncrow_web/pages/routines/widgets/value_display.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; class SliderValueSelector extends StatelessWidget { - final String selectedFunction; - final DeviceFunctionData functionData; - final AllDevicesModel? device; + final String? currentCondition; final String dialogType; final (double, double) sliderRange; final String displayedValue; final Object? initialValue; final void Function(String condition) onConditionChanged; final void Function(double value) onSliderChanged; + final String unit; const SliderValueSelector({ - required this.selectedFunction, - required this.functionData, - required this.device, required this.dialogType, required this.sliderRange, required this.displayedValue, required this.initialValue, required this.onConditionChanged, required this.onSliderChanged, + required this.currentCondition, + required this.unit, super.key, }); @override Widget build(BuildContext context) { - return Column( - spacing: 16, + if (dialogType == 'IF') { + return Column( + spacing: 16, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ConditionToggle( + currentCondition: currentCondition, + onChanged: onConditionChanged, + ), + ValueDisplay( + value: initialValue, + label: displayedValue, + unit: unit, + ), + FunctionSlider( + initialValue: initialValue, + range: sliderRange, + onChanged: onSliderChanged, + ), + ], + ); + } + + return Row( mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, children: [ - ConditionToggle( - currentCondition: functionData.condition, - onChanged: onConditionChanged, - ), - ValueDisplay( - value: initialValue, - label: displayedValue, - ), - FunctionSlider( - initialValue: initialValue, - range: sliderRange, - onChanged: onSliderChanged, + const Spacer(), + Expanded( + flex: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + onChanged: (value) => onSliderChanged(double.tryParse(value) ?? 0), + expands: false, + onTapOutside: (_) => FocusScope.of(context).unfocus(), + initialValue: displayedValue, + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + inputFormatters: [ + RangeInputFormatter(min: sliderRange.$1, max: sliderRange.$2), + ], + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(8), + ), + filled: true, + fillColor: ColorsManager.textFieldGreyColor.withOpacity(0.5), + suffixText: unit, + hintStyle: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ), + const SizedBox(height: 8), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Min: ${sliderRange.$1}', + style: context.textTheme.bodySmall!.copyWith( + color: ColorsManager.lightGrayColor, + ), + ), + const Spacer(), + Text( + 'Max: ${sliderRange.$2}', + style: context.textTheme.bodySmall!.copyWith( + color: ColorsManager.lightGrayColor, + ), + ), + ], + ), + ], + ), ), + const Spacer(), ], ); } } + +class RangeInputFormatter extends TextInputFormatter { + const RangeInputFormatter({required this.min, required this.max}); + + final double min; + final double max; + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + final text = newValue.text; + if (text.isEmpty) { + return newValue; + } + + final value = double.tryParse(text); + if (value == null || value < min || value > max) { + return oldValue; + } + + return newValue; + } +} diff --git a/lib/pages/routines/widgets/value_display.dart b/lib/pages/routines/widgets/value_display.dart index e9b76325..a47fb5b4 100644 --- a/lib/pages/routines/widgets/value_display.dart +++ b/lib/pages/routines/widgets/value_display.dart @@ -5,11 +5,13 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class ValueDisplay extends StatelessWidget { final dynamic value; final String label; + final String unit; const ValueDisplay({ required this.value, required this.label, super.key, + required this.unit, }); @override @@ -21,7 +23,7 @@ class ValueDisplay extends StatelessWidget { borderRadius: BorderRadius.circular(10), ), child: Text( - label, + '$label$unit', style: context.textTheme.headlineMedium!.copyWith( color: ColorsManager.primaryColorWithOpacity, ), From a5d26d04eb5d26f8949dea3b53a19f81f11303a0 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 16:35:19 +0300 Subject: [PATCH 20/48] Remove unused variables from CeilingSensorDialog to streamline state management --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 9 --------- 1 file changed, 9 deletions(-) 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 7b51d064..9190bf37 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 @@ -54,15 +54,6 @@ class _CeilingSensorDialogState extends State { content: BlocBuilder( builder: (context, state) { final selectedFunction = state.selectedFunction; - final selectedOperationName = state.selectedOperationName; - final selectedFunctionData = state.addedFunctions - .firstWhere((f) => f.functionCode == selectedFunction, - orElse: () => DeviceFunctionData( - entityId: '', - functionCode: selectedFunction ?? '', - operationName: '', - value: null, - )); return Container( width: selectedFunction != null ? 600 : 360, From 49439816d5c8ddca8f8e8f57cb1b19f62e59ce22 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 10 Apr 2025 16:43:33 +0300 Subject: [PATCH 21/48] Update SliderValueSelector to improve text styling for unit and range labels --- lib/pages/routines/widgets/slider_value_selector.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index dfcfeac4..db5c4675 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -83,7 +83,7 @@ class SliderValueSelector extends StatelessWidget { filled: true, fillColor: ColorsManager.textFieldGreyColor.withOpacity(0.5), suffixText: unit, - hintStyle: context.textTheme.headlineMedium!.copyWith( + suffixStyle: context.textTheme.headlineMedium!.copyWith( color: ColorsManager.primaryColorWithOpacity, ), ), @@ -94,14 +94,14 @@ class SliderValueSelector extends StatelessWidget { children: [ Text( 'Min: ${sliderRange.$1}', - style: context.textTheme.bodySmall!.copyWith( + style: context.textTheme.labelSmall!.copyWith( color: ColorsManager.lightGrayColor, ), ), const Spacer(), Text( 'Max: ${sliderRange.$2}', - style: context.textTheme.bodySmall!.copyWith( + style: context.textTheme.labelSmall!.copyWith( color: ColorsManager.lightGrayColor, ), ), From ebbecb9738854f8251b78f184dd860cd9fd108b0 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 09:18:09 +0300 Subject: [PATCH 22/48] Update toggleCodes in CeilingSensorHelper to replace 'self_test_result' and 'movement' with 'checking_result' and 'body_movement' for improved clarity. --- .../ceiling_sensor/ceiling_sensor_helper.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart index 291c2f81..940daf23 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart @@ -166,11 +166,11 @@ abstract final class CeilingSensorHelper { static const toggleCodes = { 'radar_switch', 'space_para_switch', - 'self_test_result', + 'checking_result', 'nobody_time', - 'movement', + 'body_movement', 'custom_mode', - 'space_type', + 'scene', 'presence_state', }; } From 828db5d5e4105e5ddccf0e0633dcfce447de3813 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 09:57:45 +0300 Subject: [PATCH 23/48] Refactor CpsDialogSliderSelector to improve value parsing and formatting in _displayText method --- .../ceiling_sensor/cps_dialog_slider_selector.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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 3c792eca..53f66b67 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 @@ -57,7 +57,6 @@ class CpsDialogSliderSelector extends StatelessWidget { ), ), ), - ); } @@ -81,7 +80,7 @@ class CpsDialogSliderSelector extends StatelessWidget { String get _displayText { final value = selectedFunctionData.value; - final parsedValue = double.tryParse('$value') ?? value; + final parsedValue = double.tryParse('$value'); return switch (selectedFunctionData.functionCode) { 'moving_max_dis' || @@ -90,12 +89,12 @@ class CpsDialogSliderSelector extends StatelessWidget { 'presence_range' || 'perceptual_boundary' || 'moving_boundary' => - '${parsedValue?.toStringAsFixed(1) ?? '0'}', - 'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'}', + parsedValue?.toStringAsFixed(1) ?? '0', + 'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0', 'moving_static_time' || 'none_body_time' => - '${parsedValue?.toStringAsFixed(0) ?? '0'}', - _ => '${parsedValue ?? 0}', + parsedValue?.toStringAsFixed(0) ?? '0', + _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', }; } From 60a1a9ad6f2c1853f0d92a5c335a1298bfe84862 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:09:39 +0300 Subject: [PATCH 24/48] Add dividendOfRange parameter to FunctionSlider and related components for improved range handling --- .../routines/widgets/function_slider.dart | 21 ++++++++++++----- .../cps_dialog_slider_selector.dart | 23 ++++++++++++++++++- .../wps_value_selector_widget.dart | 1 + .../widgets/slider_value_selector.dart | 3 +++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/pages/routines/widgets/function_slider.dart b/lib/pages/routines/widgets/function_slider.dart index b7fb0d53..50167a7b 100644 --- a/lib/pages/routines/widgets/function_slider.dart +++ b/lib/pages/routines/widgets/function_slider.dart @@ -2,25 +2,34 @@ import 'package:flutter/material.dart'; class FunctionSlider extends StatelessWidget { final dynamic initialValue; - final (double min, double max) range; final void Function(double value) onChanged; + final double dividendOfRange; const FunctionSlider({ required this.onChanged, required this.initialValue, required this.range, + required this.dividendOfRange, super.key, }); @override Widget build(BuildContext context) { + final (min, max) = range; + final bool isValidRange = max > min; + final double value = initialValue is int + ? (initialValue as int).toDouble() + : (initialValue as double); + + final int? divisions = isValidRange ? ((max - min) / dividendOfRange).round() : null; + return Slider( - value: initialValue is int ? initialValue.toDouble() : range.$1, - min: range.$1, - max: range.$2, - divisions: (range.$2 - range.$1).toInt(), - onChanged: onChanged, + value: value.clamp(min, max), + min: min, + max: max, + divisions: divisions, + onChanged: isValidRange ? onChanged : null, ); } } 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 53f66b67..2e399888 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 @@ -52,11 +52,12 @@ class CpsDialogSliderSelector extends StatelessWidget { entityId: device?.uuid ?? '', functionCode: selectedFunction, operationName: selectedFunctionData.operationName, - value: value.toInt(), + value: value, condition: selectedFunctionData.condition, ), ), ), + dividendOfRange: _dividendOfRange, ); } @@ -111,4 +112,24 @@ class CpsDialogSliderSelector extends StatelessWidget { _ => '', }; } + + double get _dividendOfRange => switch (selectedFunctionData.functionCode) { + 'sensitivity' => 1, + 'moving_speed' => 1, + 'space_static_val' => 1, + 'space_move_val' => 1, + 'presence_reference' => 5, + 'moving_reference' => 5, + 'moving_max_dis' => 0.5, + 'static_max_dis' => 0.5, + 'moving_range' => 0.1, + 'presence_range' => 0.1, + 'perceptual_boundary' => 0.5, + 'moving_boundary' => 0.5, + 'moving_rigger_time' => 0.1, + 'moving_static_time' => 1.0, + 'none_body_time' => 5.0, + 'sports_para' => 1.0, + _ => 1, + }; } 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 ccd82e23..fcdc991a 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 @@ -60,6 +60,7 @@ class WpsValueSelectorWidget extends StatelessWidget { ), ), unit: _unit, + dividendOfRange: 1, ); } diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index db5c4675..bc35e674 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -15,6 +15,7 @@ class SliderValueSelector extends StatelessWidget { final void Function(String condition) onConditionChanged; final void Function(double value) onSliderChanged; final String unit; + final double dividendOfRange; const SliderValueSelector({ required this.dialogType, @@ -25,6 +26,7 @@ class SliderValueSelector extends StatelessWidget { required this.onSliderChanged, required this.currentCondition, required this.unit, + required this.dividendOfRange, super.key, }); @@ -48,6 +50,7 @@ class SliderValueSelector extends StatelessWidget { initialValue: initialValue, range: sliderRange, onChanged: onSliderChanged, + dividendOfRange: dividendOfRange, ), ], ); From c62e8b3f1541fa38c2de8ba7456acf011391b08a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:14:35 +0300 Subject: [PATCH 25/48] Update _displayText method to format values with two decimal places for improved precision --- .../ceiling_sensor/cps_dialog_slider_selector.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e399888..d7f4b688 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 @@ -94,7 +94,7 @@ class CpsDialogSliderSelector extends StatelessWidget { 'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0', 'moving_static_time' || 'none_body_time' => - parsedValue?.toStringAsFixed(0) ?? '0', + parsedValue?.toStringAsFixed(2) ?? '0', _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', }; } From dcbca6481414c02c5c3c8481ede2a2db3f9f2276 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:16:20 +0300 Subject: [PATCH 26/48] Fix spacing in ValueDisplay text output for improved readability --- lib/pages/routines/widgets/value_display.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/routines/widgets/value_display.dart b/lib/pages/routines/widgets/value_display.dart index a47fb5b4..6a8bd949 100644 --- a/lib/pages/routines/widgets/value_display.dart +++ b/lib/pages/routines/widgets/value_display.dart @@ -23,7 +23,7 @@ class ValueDisplay extends StatelessWidget { borderRadius: BorderRadius.circular(10), ), child: Text( - '$label$unit', + '$label $unit ', style: context.textTheme.headlineMedium!.copyWith( color: ColorsManager.primaryColorWithOpacity, ), From fa8f29ff71dedd706e667a8415e1c0d9cb598ed4 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:41:23 +0300 Subject: [PATCH 27/48] Add sensitivity feature. --- assets/icons/sensitivity_feature_1.svg | 5 +++++ assets/icons/sensitivity_feature_2.svg | 5 +++++ assets/icons/sensitivity_feature_3.svg | 5 +++++ assets/icons/sensitivity_feature_4.svg | 5 +++++ assets/icons/sensitivity_feature_5.svg | 5 +++++ assets/icons/sensitivity_feature_6.svg | 5 +++++ assets/icons/sensitivity_feature_7.svg | 5 +++++ assets/icons/sensitivity_feature_8.svg | 5 +++++ assets/icons/sensitivity_feature_9.svg | 5 +++++ lib/utils/constants/assets.dart | 9 +++++++++ 10 files changed, 54 insertions(+) create mode 100644 assets/icons/sensitivity_feature_1.svg create mode 100644 assets/icons/sensitivity_feature_2.svg create mode 100644 assets/icons/sensitivity_feature_3.svg create mode 100644 assets/icons/sensitivity_feature_4.svg create mode 100644 assets/icons/sensitivity_feature_5.svg create mode 100644 assets/icons/sensitivity_feature_6.svg create mode 100644 assets/icons/sensitivity_feature_7.svg create mode 100644 assets/icons/sensitivity_feature_8.svg create mode 100644 assets/icons/sensitivity_feature_9.svg diff --git a/assets/icons/sensitivity_feature_1.svg b/assets/icons/sensitivity_feature_1.svg new file mode 100644 index 00000000..21bebd7a --- /dev/null +++ b/assets/icons/sensitivity_feature_1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_2.svg b/assets/icons/sensitivity_feature_2.svg new file mode 100644 index 00000000..7370ab6f --- /dev/null +++ b/assets/icons/sensitivity_feature_2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_3.svg b/assets/icons/sensitivity_feature_3.svg new file mode 100644 index 00000000..23b92c43 --- /dev/null +++ b/assets/icons/sensitivity_feature_3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_4.svg b/assets/icons/sensitivity_feature_4.svg new file mode 100644 index 00000000..7a92045f --- /dev/null +++ b/assets/icons/sensitivity_feature_4.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_5.svg b/assets/icons/sensitivity_feature_5.svg new file mode 100644 index 00000000..5f056602 --- /dev/null +++ b/assets/icons/sensitivity_feature_5.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_6.svg b/assets/icons/sensitivity_feature_6.svg new file mode 100644 index 00000000..288b172e --- /dev/null +++ b/assets/icons/sensitivity_feature_6.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_7.svg b/assets/icons/sensitivity_feature_7.svg new file mode 100644 index 00000000..5779dfd2 --- /dev/null +++ b/assets/icons/sensitivity_feature_7.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_8.svg b/assets/icons/sensitivity_feature_8.svg new file mode 100644 index 00000000..4816b1ba --- /dev/null +++ b/assets/icons/sensitivity_feature_8.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sensitivity_feature_9.svg b/assets/icons/sensitivity_feature_9.svg new file mode 100644 index 00000000..978145b4 --- /dev/null +++ b/assets/icons/sensitivity_feature_9.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 7bcd1484..6973492e 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -448,4 +448,13 @@ class Assets { static const String presenceJudgementThrshold = 'assets/icons/presence_judgement_threshold.svg'; static const String spaceType = 'assets/icons/space_type.svg'; static const String sportsPara = 'assets/icons/sports_para.svg'; + static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg'; + static const String sensitivityFeature2 = 'assets/icons/sensitivity_feature_2.svg'; + static const String sensitivityFeature3 = 'assets/icons/sensitivity_feature_3.svg'; + static const String sensitivityFeature4 = 'assets/icons/sensitivity_feature_4.svg'; + static const String sensitivityFeature5 = 'assets/icons/sensitivity_feature_5.svg'; + static const String sensitivityFeature6 = 'assets/icons/sensitivity_feature_6.svg'; + static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg'; + static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg'; + static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg'; } From c54fd780b7d9e5bf27ae50641e6e532e5e7bef90 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:41:40 +0300 Subject: [PATCH 28/48] Update sensitivity function to adjust max value and add image assets; modify dialog logic for sensitivity handling --- .../ceiling_presence_sensor_functions.dart | 16 +++++++++++++++- .../ceiling_sensor/ceiling_sensor_dialog.dart | 7 +++++-- .../cps_dialog_slider_selector.dart | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 6e181407..8f3eeb5e 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -98,13 +98,27 @@ final class CpsSensitivityFunction extends CpsFunctions { final int max; final int step; + static const _images = [ + Assets.sensitivityFeature1, + Assets.sensitivityFeature1, + Assets.sensitivityFeature2, + Assets.sensitivityFeature3, + Assets.sensitivityFeature4, + Assets.sensitivityFeature5, + Assets.sensitivityFeature6, + Assets.sensitivityFeature7, + Assets.sensitivityFeature8, + Assets.sensitivityFeature9, + Assets.sensitivityFeature9, + ]; + @override List getOperationalValues() { final values = []; for (var value = min; value <= max; value += step) { values.add( CpsOperationalValue( - icon: Assets.sensitivity, + icon: _images[value], description: '$value', value: value, ), 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 9190bf37..dc71a595 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 @@ -115,8 +115,11 @@ class _CeilingSensorDialogState extends State { ), ); final operations = selectedCpsFunctions.getOperationalValues(); - final isToggleFunction = - CeilingSensorHelper.toggleCodes.contains(selectedFunction); + final isSensitivityFunction = selectedFunction == 'sensitivity'; + final isToggleFunction = isSensitivityFunction + ? widget.dialogType == 'THEN' + : CeilingSensorHelper.toggleCodes.contains(selectedFunction); + return Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ 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 d7f4b688..59f48fbd 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 @@ -63,6 +63,7 @@ class CpsDialogSliderSelector extends StatelessWidget { (double, double) get _sliderRange => switch (selectedFunctionData.functionCode) { 'moving_speed' => (0, 32), + 'sensitivity' => (0, 10), 'space_static_val' => (0, 255), 'space_move_val' => (0, 255), 'moving_max_dis' => (0, 10), From 36e88d033be2a5270c556f2125e1dded88edc213 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 10:56:19 +0300 Subject: [PATCH 29/48] Refactor SliderValueSelector layout for improved readability and maintainability --- .../widgets/slider_value_selector.dart | 94 +++---------------- 1 file changed, 15 insertions(+), 79 deletions(-) diff --git a/lib/pages/routines/widgets/slider_value_selector.dart b/lib/pages/routines/widgets/slider_value_selector.dart index bc35e674..526e6fa0 100644 --- a/lib/pages/routines/widgets/slider_value_selector.dart +++ b/lib/pages/routines/widgets/slider_value_selector.dart @@ -3,8 +3,6 @@ import 'package:flutter/services.dart'; import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart'; import 'package:syncrow_web/pages/routines/widgets/function_slider.dart'; import 'package:syncrow_web/pages/routines/widgets/value_display.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; class SliderValueSelector extends StatelessWidget { final String? currentCondition; @@ -32,88 +30,26 @@ class SliderValueSelector extends StatelessWidget { @override Widget build(BuildContext context) { - if (dialogType == 'IF') { - return Column( - spacing: 16, - mainAxisAlignment: MainAxisAlignment.center, - children: [ + return Column( + spacing: 16, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (dialogType == 'IF') ConditionToggle( currentCondition: currentCondition, onChanged: onConditionChanged, ), - ValueDisplay( - value: initialValue, - label: displayedValue, - unit: unit, - ), - FunctionSlider( - initialValue: initialValue, - range: sliderRange, - onChanged: onSliderChanged, - dividendOfRange: dividendOfRange, - ), - ], - ); - } - - return Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - Expanded( - flex: 2, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - onChanged: (value) => onSliderChanged(double.tryParse(value) ?? 0), - expands: false, - onTapOutside: (_) => FocusScope.of(context).unfocus(), - initialValue: displayedValue, - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - inputFormatters: [ - RangeInputFormatter(min: sliderRange.$1, max: sliderRange.$2), - ], - decoration: InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(8), - ), - filled: true, - fillColor: ColorsManager.textFieldGreyColor.withOpacity(0.5), - suffixText: unit, - suffixStyle: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ), - const SizedBox(height: 8), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Min: ${sliderRange.$1}', - style: context.textTheme.labelSmall!.copyWith( - color: ColorsManager.lightGrayColor, - ), - ), - const Spacer(), - Text( - 'Max: ${sliderRange.$2}', - style: context.textTheme.labelSmall!.copyWith( - color: ColorsManager.lightGrayColor, - ), - ), - ], - ), - ], - ), + ValueDisplay( + value: initialValue, + label: displayedValue, + unit: unit, + ), + FunctionSlider( + initialValue: initialValue, + range: sliderRange, + onChanged: onSliderChanged, + dividendOfRange: dividendOfRange, ), - const Spacer(), ], ); } From 11feaf7c877227eeeda24010384c82b805499581 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 13 Apr 2025 11:10:51 +0300 Subject: [PATCH 30/48] passed correct operation name to `onSliderChanged` and onConditionChanged`. in `CpsDialogSliderSelector`. --- .../ceiling_sensor/cps_dialog_slider_selector.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 59f48fbd..49c3ac2e 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 @@ -40,7 +40,7 @@ class CpsDialogSliderSelector extends StatelessWidget { functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectedFunction, - operationName: selectedFunctionData.operationName, + operationName: operationName, condition: condition, value: selectedFunctionData.value, ), @@ -51,7 +51,7 @@ class CpsDialogSliderSelector extends StatelessWidget { functionData: DeviceFunctionData( entityId: device?.uuid ?? '', functionCode: selectedFunction, - operationName: selectedFunctionData.operationName, + operationName: operationName, value: value, condition: selectedFunctionData.condition, ), From 9a2687d4c58e3659ca6f06d9d0821734ed5c467b Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 11:13:27 +0300 Subject: [PATCH 31/48] added some comments for clarity about how we should send the data to BE. --- .../ceiling_presence_sensor_functions.dart | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index 8f3eeb5e..bdec76d3 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -1,6 +1,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +// TODO: functions in conditions / status in actions class CpsOperationalValue { final String icon; final String description; @@ -222,6 +223,7 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { } final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { + // confirm with BE CpsMaxDistanceOfDetectionFunction({ required super.deviceId, required super.deviceName, @@ -265,7 +267,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { max = 10.0, step = 0.5, super( - code: 'static_max_dis', + code: 'static_max_dis', // 0 / 500 operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @@ -300,7 +302,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { max = 25.5, step = 0.1, super( - code: 'moving_range', + code: 'moving_range', // just then operationName: 'Detection Range', icon: Assets.farDetection, ); @@ -370,7 +372,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'presence_reference', + code: 'presence_reference', // max 255 // change widget operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -401,7 +403,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'moving_reference', + code: 'moving_reference', // max 255 // change widget operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -432,7 +434,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { max = 5.00, step = 0.50, super( - code: 'perceptual_boundary', + code: 'perceptual_boundary', // 0 / 500 operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @@ -451,7 +453,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { return CpsOperationalValue( icon: Assets.boundary, description: '${value.toStringAsFixed(1)}M', - value: value, + value: value + 1200, ); }, ); @@ -467,7 +469,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { max = 5.0, step = 0.5, super( - code: 'moving_boundary', + code: 'moving_boundary', // 0 / 500 / step 50 operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @@ -502,7 +504,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { max = 2.0, step = 0.1, super( - code: 'moving_rigger_time', + code: 'moving_rigger_time', // 0 / 2000 steps 10 operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @@ -537,7 +539,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { max = 50.0, step = 1.0, super( - code: 'moving_static_time', + code: 'moving_static_time', // 0 / 6000 steps 100 operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @@ -572,7 +574,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { max = 300.0, step = 5.0, super( - code: 'none_body_time', + code: 'none_body_time', // 0 / 300000 / steps 500 operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); @@ -604,7 +606,7 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'checking_result', + code: 'checking_result', // just in action operationName: 'Self-Test Result', icon: Assets.selfTestResult, ); @@ -826,7 +828,7 @@ class CpsPresenceStatusFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'presence_state', + code: 'presence_state', // just in action operationName: 'Presence Status', icon: Assets.presenceSensor, ); @@ -862,7 +864,7 @@ final class CpsSportsParaFunction extends CpsFunctions { max = 100, step = 1, super( - code: 'sports_para', + code: 'sports_para', // just in action operationName: 'Sports Para', icon: Assets.sportsPara, ); From 9e3a78f6b734cf9f2919593f17d81c990832c617 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 12:10:42 +0300 Subject: [PATCH 32/48] Update dialog header to reflect sensor condition type dynamically --- .../routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 dc71a595..21aa7c51 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 @@ -66,7 +66,9 @@ class _CeilingSensorDialogState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const DialogHeader('Presence Sensor Condition'), + DialogHeader( + 'Presence Sensor ${widget.dialogType == "IF" ? 'Conditions' : 'Functions'}', + ), Expanded(child: _buildMainContent(context, state)), DialogFooter( onCancel: () => Navigator.pop(context), From 06383018b9e275edbce98cdc69e8c8c0e309e151 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 14:32:22 +0300 Subject: [PATCH 33/48] refactored helpers into a helper class to release some complexity out of the widget. --- .../cps_dialog_slider_selector.dart | 121 ++++++++++++------ 1 file changed, 79 insertions(+), 42 deletions(-) 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 49c3ac2e..42b5a29e 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 @@ -31,10 +31,13 @@ class CpsDialogSliderSelector extends StatelessWidget { return SliderValueSelector( currentCondition: selectedFunctionData.condition, dialogType: dialogType, - sliderRange: _sliderRange, - displayedValue: _displayText, + sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode), + displayedValue: CpsSliderHelpers.displayText( + value: selectedFunctionData.value, + functionCode: selectedFunctionData.functionCode, + ), initialValue: selectedFunctionData.value ?? 0, - unit: _unit, + unit: CpsSliderHelpers.unit(selectedFunctionData.functionCode), onConditionChanged: (condition) => context.read().add( AddFunction( functionData: DeviceFunctionData( @@ -57,11 +60,50 @@ class CpsDialogSliderSelector extends StatelessWidget { ), ), ), - dividendOfRange: _dividendOfRange, + dividendOfRange: CpsSliderHelpers.dividendOfRange( + selectedFunctionData.functionCode, + ), ); } - (double, double) get _sliderRange => switch (selectedFunctionData.functionCode) { + double reverseMappedValue(double value) { + final (inputMin, inputMax) = + CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode); + final (outputMin, outputMax, outputStep) = CpsSliderHelpers.mappedRange( + selectedFunctionData.functionCode, + ); + + final clampedValue = value.clamp(outputMin, outputMax); + + final stepsFromMin = ((clampedValue - outputMin) / outputStep).round(); + + final mappedValue = inputMin + + (stepsFromMin * + CpsSliderHelpers.dividendOfRange(selectedFunctionData.functionCode)); + + return mappedValue.clamp(inputMin, inputMax); + } +} + +abstract final class CpsSliderHelpers { + static (double min, double max, double step) mappedRange(String functionCode) { + final (defaultMin, defaultMax) = sliderRange(functionCode); + final defaultDivdidend = dividendOfRange(functionCode); + return switch (functionCode) { + 'static_max_dis' => (0, 500, 50), + 'presence_reference' => (0, 255, 50), + 'moving_reference' => (0, 255, 5), + 'perceptual_boundary' => (0, 500, 50), + 'moving_boundary' => (0, 500, 50), + 'moving_rigger_time' => (0, 2000, 100), + 'moving_static_time' => (0, 6000, 100), + 'none_body_time' => (0, 300000, 500), + _ => (defaultMin, defaultMax, defaultDivdidend), + }; + } + + static (double min, double max) sliderRange(String functionCode) => + switch (functionCode) { 'moving_speed' => (0, 32), 'sensitivity' => (0, 10), 'space_static_val' => (0, 255), @@ -80,11 +122,40 @@ class CpsDialogSliderSelector extends StatelessWidget { _ => (0, 300), }; - String get _displayText { - final value = selectedFunctionData.value; + static double dividendOfRange(String functionCode) => switch (functionCode) { + 'presence_reference' => 5, + 'moving_reference' => 5, + 'moving_max_dis' => 0.5, + 'static_max_dis' => 0.5, + 'moving_range' => 0.1, + 'presence_range' => 0.1, + 'perceptual_boundary' => 0.5, + 'moving_boundary' => 0.5, + 'moving_rigger_time' => 0.1, + 'moving_static_time' => 1.0, + 'none_body_time' => 5.0, + _ => 1, + }; + + static String unit(String functionCode) => switch (functionCode) { + 'moving_max_dis' || + 'static_max_dis' || + 'moving_range' || + 'presence_range' || + 'perceptual_boundary' || + 'moving_boundary' => + 'M', + 'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec', + _ => '', + }; + + static String displayText({ + required dynamic value, + required String functionCode, + }) { final parsedValue = double.tryParse('$value'); - return switch (selectedFunctionData.functionCode) { + return switch (functionCode) { 'moving_max_dis' || 'static_max_dis' || 'moving_range' || @@ -99,38 +170,4 @@ class CpsDialogSliderSelector extends StatelessWidget { _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', }; } - - String get _unit { - return switch (selectedFunctionData.functionCode) { - 'moving_max_dis' || - 'static_max_dis' || - 'moving_range' || - 'presence_range' || - 'perceptual_boundary' || - 'moving_boundary' => - 'M', - 'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec', - _ => '', - }; - } - - double get _dividendOfRange => switch (selectedFunctionData.functionCode) { - 'sensitivity' => 1, - 'moving_speed' => 1, - 'space_static_val' => 1, - 'space_move_val' => 1, - 'presence_reference' => 5, - 'moving_reference' => 5, - 'moving_max_dis' => 0.5, - 'static_max_dis' => 0.5, - 'moving_range' => 0.1, - 'presence_range' => 0.1, - 'perceptual_boundary' => 0.5, - 'moving_boundary' => 0.5, - 'moving_rigger_time' => 0.1, - 'moving_static_time' => 1.0, - 'none_body_time' => 5.0, - 'sports_para' => 1.0, - _ => 1, - }; } From f19cc616be9cff1155f867c99a022b8e74e7372e Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 14:32:49 +0300 Subject: [PATCH 34/48] moved `CpsSliderHelpers` to its own file. --- .../cps_dialog_slider_selector.dart | 88 +------------------ .../ceiling_sensor/cps_slider_helpers.dart | 86 ++++++++++++++++++ 2 files changed, 87 insertions(+), 87 deletions(-) create mode 100644 lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart 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 42b5a29e..2bdd1135 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/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart'; import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; class CpsDialogSliderSelector extends StatelessWidget { @@ -84,90 +85,3 @@ class CpsDialogSliderSelector extends StatelessWidget { return mappedValue.clamp(inputMin, inputMax); } } - -abstract final class CpsSliderHelpers { - static (double min, double max, double step) mappedRange(String functionCode) { - final (defaultMin, defaultMax) = sliderRange(functionCode); - final defaultDivdidend = dividendOfRange(functionCode); - return switch (functionCode) { - 'static_max_dis' => (0, 500, 50), - 'presence_reference' => (0, 255, 50), - 'moving_reference' => (0, 255, 5), - 'perceptual_boundary' => (0, 500, 50), - 'moving_boundary' => (0, 500, 50), - 'moving_rigger_time' => (0, 2000, 100), - 'moving_static_time' => (0, 6000, 100), - 'none_body_time' => (0, 300000, 500), - _ => (defaultMin, defaultMax, defaultDivdidend), - }; - } - - static (double min, double max) sliderRange(String functionCode) => - switch (functionCode) { - 'moving_speed' => (0, 32), - 'sensitivity' => (0, 10), - 'space_static_val' => (0, 255), - 'space_move_val' => (0, 255), - 'moving_max_dis' => (0, 10), - 'static_max_dis' => (0, 10), - 'moving_range' => (0, 25.5), - 'presence_range' => (0, 25.5), - 'presence_judgement_threshold' => (0, 255), - 'motion_amplitude_trigger_threshold' => (0, 255), - 'perceptual_boundary' => (0, 5), - 'moving_boundary' => (0, 5), - 'moving_rigger_time' => (0, 2), - 'moving_static_time' => (0, 50), - 'none_body_time' => (0, 300), - _ => (0, 300), - }; - - static double dividendOfRange(String functionCode) => switch (functionCode) { - 'presence_reference' => 5, - 'moving_reference' => 5, - 'moving_max_dis' => 0.5, - 'static_max_dis' => 0.5, - 'moving_range' => 0.1, - 'presence_range' => 0.1, - 'perceptual_boundary' => 0.5, - 'moving_boundary' => 0.5, - 'moving_rigger_time' => 0.1, - 'moving_static_time' => 1.0, - 'none_body_time' => 5.0, - _ => 1, - }; - - static String unit(String functionCode) => switch (functionCode) { - 'moving_max_dis' || - 'static_max_dis' || - 'moving_range' || - 'presence_range' || - 'perceptual_boundary' || - 'moving_boundary' => - 'M', - 'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec', - _ => '', - }; - - static String displayText({ - required dynamic value, - required String functionCode, - }) { - final parsedValue = double.tryParse('$value'); - - return switch (functionCode) { - 'moving_max_dis' || - 'static_max_dis' || - 'moving_range' || - 'presence_range' || - 'perceptual_boundary' || - 'moving_boundary' => - parsedValue?.toStringAsFixed(1) ?? '0', - 'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0', - 'moving_static_time' || - 'none_body_time' => - parsedValue?.toStringAsFixed(2) ?? '0', - _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', - }; - } -} diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart new file mode 100644 index 00000000..80192056 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart @@ -0,0 +1,86 @@ +abstract final class CpsSliderHelpers { + static (double min, double max, double step) mappedRange(String functionCode) { + final (defaultMin, defaultMax) = sliderRange(functionCode); + final defaultDivdidend = dividendOfRange(functionCode); + return switch (functionCode) { + 'static_max_dis' => (0, 500, 50), + 'presence_reference' => (0, 255, 50), + 'moving_reference' => (0, 255, 5), + 'perceptual_boundary' => (0, 500, 50), + 'moving_boundary' => (0, 500, 50), + 'moving_rigger_time' => (0, 2000, 100), + 'moving_static_time' => (0, 6000, 100), + 'none_body_time' => (0, 300000, 500), + _ => (defaultMin, defaultMax, defaultDivdidend), + }; + } + + static (double min, double max) sliderRange(String functionCode) => + switch (functionCode) { + 'moving_speed' => (0, 32), + 'sensitivity' => (0, 10), + 'space_static_val' => (0, 255), + 'space_move_val' => (0, 255), + 'moving_max_dis' => (0, 10), + 'static_max_dis' => (0, 10), + 'moving_range' => (0, 25.5), + 'presence_range' => (0, 25.5), + 'presence_judgement_threshold' => (0, 255), + 'motion_amplitude_trigger_threshold' => (0, 255), + 'perceptual_boundary' => (0, 5), + 'moving_boundary' => (0, 5), + 'moving_rigger_time' => (0, 2), + 'moving_static_time' => (0, 50), + 'none_body_time' => (0, 300), + _ => (0, 300), + }; + + static double dividendOfRange(String functionCode) => switch (functionCode) { + 'presence_reference' => 5, + 'moving_reference' => 5, + 'moving_max_dis' => 0.5, + 'static_max_dis' => 0.5, + 'moving_range' => 0.1, + 'presence_range' => 0.1, + 'perceptual_boundary' => 0.5, + 'moving_boundary' => 0.5, + 'moving_rigger_time' => 0.1, + 'moving_static_time' => 1.0, + 'none_body_time' => 5.0, + _ => 1, + }; + + static String unit(String functionCode) => switch (functionCode) { + 'moving_max_dis' || + 'static_max_dis' || + 'moving_range' || + 'presence_range' || + 'perceptual_boundary' || + 'moving_boundary' => + 'M', + 'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec', + _ => '', + }; + + static String displayText({ + required dynamic value, + required String functionCode, + }) { + final parsedValue = double.tryParse('$value'); + + return switch (functionCode) { + 'moving_max_dis' || + 'static_max_dis' || + 'moving_range' || + 'presence_range' || + 'perceptual_boundary' || + 'moving_boundary' => + parsedValue?.toStringAsFixed(1) ?? '0', + 'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0', + 'moving_static_time' || + 'none_body_time' => + parsedValue?.toStringAsFixed(2) ?? '0', + _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', + }; + } +} From 1493e35f6ad5175f2ac9827770a38ec1768437c4 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 14:35:24 +0300 Subject: [PATCH 35/48] removed unused method. --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 21aa7c51..1403b2ca 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 @@ -150,4 +150,15 @@ class _CeilingSensorDialogState extends State { ], ); } +List updateValuesForAddedFunctions(List addedFunctions) { + return addedFunctions.map((function) { + if (function.functionCode == 'sensitivity') { + return function.copyWith( + value: function.value, + condition: function.condition, + ); + } + return function; + }).toList(); + } } From ebcd89d2a5a85a64057913ada4bb8084872ddd84 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Apr 2025 16:24:50 +0300 Subject: [PATCH 36/48] Refactor ceiling sensor functions and update slider helper mappings for improved value handling --- .../ceiling_presence_sensor_functions.dart | 26 +++++---- .../ceiling_sensor/ceiling_sensor_dialog.dart | 53 +++++++++++++++++-- .../cps_dialog_slider_selector.dart | 18 ------- .../ceiling_sensor/cps_slider_helpers.dart | 10 ++-- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart index bdec76d3..6dbe5cf6 100644 --- a/lib/pages/routines/models/ceiling_presence_sensor_functions.dart +++ b/lib/pages/routines/models/ceiling_presence_sensor_functions.dart @@ -1,7 +1,6 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -// TODO: functions in conditions / status in actions class CpsOperationalValue { final String icon; final String description; @@ -223,7 +222,6 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions { } final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions { - // confirm with BE CpsMaxDistanceOfDetectionFunction({ required super.deviceId, required super.deviceName, @@ -267,7 +265,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions { max = 10.0, step = 0.5, super( - code: 'static_max_dis', // 0 / 500 + code: 'static_max_dis', operationName: 'Maximum Distance Of Static Detection', icon: Assets.currentDistanceIcon, ); @@ -302,7 +300,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions { max = 25.5, step = 0.1, super( - code: 'moving_range', // just then + code: 'moving_range', operationName: 'Detection Range', icon: Assets.farDetection, ); @@ -372,7 +370,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'presence_reference', // max 255 // change widget + code: 'presence_reference', operationName: 'Presence Judgement Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -403,7 +401,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions { max = 255, step = 5, super( - code: 'moving_reference', // max 255 // change widget + code: 'moving_reference', operationName: 'Motion Amplitude Trigger Threshold', icon: Assets.presenceJudgementThrshold, ); @@ -434,7 +432,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions { max = 5.00, step = 0.50, super( - code: 'perceptual_boundary', // 0 / 500 + code: 'perceptual_boundary', operationName: 'Perpetual Boundary', icon: Assets.boundary, ); @@ -469,7 +467,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions { max = 5.0, step = 0.5, super( - code: 'moving_boundary', // 0 / 500 / step 50 + code: 'moving_boundary', operationName: 'Motion Trigger Boundary', icon: Assets.motionMeter, ); @@ -504,7 +502,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions { max = 2.0, step = 0.1, super( - code: 'moving_rigger_time', // 0 / 2000 steps 10 + code: 'moving_rigger_time', operationName: 'Motion Trigger Time', icon: Assets.motionMeter, ); @@ -539,7 +537,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions { max = 50.0, step = 1.0, super( - code: 'moving_static_time', // 0 / 6000 steps 100 + code: 'moving_static_time', operationName: 'Motion To Static Time', icon: Assets.motionMeter, ); @@ -574,7 +572,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions { max = 300.0, step = 5.0, super( - code: 'none_body_time', // 0 / 300000 / steps 500 + code: 'none_body_time', operationName: 'Entering Nobody State Time', icon: Assets.motionMeter, ); @@ -606,7 +604,7 @@ final class CpsSelfTestResultFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'checking_result', // just in action + code: 'checking_result', operationName: 'Self-Test Result', icon: Assets.selfTestResult, ); @@ -828,7 +826,7 @@ class CpsPresenceStatusFunctions extends CpsFunctions { required super.deviceName, required super.type, }) : super( - code: 'presence_state', // just in action + code: 'presence_state', operationName: 'Presence Status', icon: Assets.presenceSensor, ); @@ -864,7 +862,7 @@ final class CpsSportsParaFunction extends CpsFunctions { max = 100, step = 1, super( - code: 'sports_para', // just in action + code: 'sports_para', operationName: 'Sports Para', icon: Assets.sportsPara, ); 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 1403b2ca..52665dfa 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 @@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_senso import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart'; class CeilingSensorDialog extends StatefulWidget { const CeilingSensorDialog({ @@ -76,7 +77,8 @@ class _CeilingSensorDialogState extends State { ? () { context.read().add( AddFunctionToRoutine( - state.addedFunctions, + _updateValuesForAddedFunctions( + state.addedFunctions), '${widget.uniqueCustomId}', ), ); @@ -150,15 +152,58 @@ class _CeilingSensorDialogState extends State { ], ); } -List updateValuesForAddedFunctions(List addedFunctions) { + + List _updateValuesForAddedFunctions( + List addedFunctions, + ) { + const mappableSteppedFunctions = { + 'static_max_dis', + 'presence_reference', + 'moving_reference', + 'perceptual_boundary', + 'moving_boundary', + 'moving_rigger_time', + 'moving_static_time', + 'none_body_time', + 'moving_max_dis', + }; return addedFunctions.map((function) { - if (function.functionCode == 'sensitivity') { - return function.copyWith( + if (mappableSteppedFunctions.contains(function.functionCode)) { + final mappedValue = mapSteppedValue( value: function.value, + inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode), + inputRange: CpsSliderHelpers.sliderRange(function.functionCode), + outputRange: CpsSliderHelpers.mappedRange(function.functionCode), + ); + return DeviceFunctionData( + value: mappedValue, + entityId: function.entityId, + functionCode: function.functionCode, + operationName: function.operationName, condition: function.condition, + actionExecutor: function.actionExecutor, + valueDescription: function.valueDescription, ); } return function; }).toList(); } + + int mapSteppedValue({ + required (double min, double max) inputRange, + required double inputStep, + required (double min, double max, double dividend) outputRange, + required double value, + }) { + final (inputMin, inputMax) = inputRange; + final (outputMin, outputMax, outputStep) = outputRange; + + final clampedValue = value.clamp(inputMin, inputMax); + + final stepsFromMin = ((clampedValue - inputMin) / inputStep).round(); + + final mappedValue = outputMin + (stepsFromMin * outputStep); + + return mappedValue.clamp(outputMin, outputMax).round(); + } } 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 2bdd1135..1c549d9f 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 @@ -66,22 +66,4 @@ class CpsDialogSliderSelector extends StatelessWidget { ), ); } - - double reverseMappedValue(double value) { - final (inputMin, inputMax) = - CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode); - final (outputMin, outputMax, outputStep) = CpsSliderHelpers.mappedRange( - selectedFunctionData.functionCode, - ); - - final clampedValue = value.clamp(outputMin, outputMax); - - final stepsFromMin = ((clampedValue - outputMin) / outputStep).round(); - - final mappedValue = inputMin + - (stepsFromMin * - CpsSliderHelpers.dividendOfRange(selectedFunctionData.functionCode)); - - return mappedValue.clamp(inputMin, inputMax); - } } diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart index 80192056..03b7b5af 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart @@ -4,13 +4,14 @@ abstract final class CpsSliderHelpers { final defaultDivdidend = dividendOfRange(functionCode); return switch (functionCode) { 'static_max_dis' => (0, 500, 50), - 'presence_reference' => (0, 255, 50), + 'presence_reference' => (0, 255, 5), 'moving_reference' => (0, 255, 5), 'perceptual_boundary' => (0, 500, 50), 'moving_boundary' => (0, 500, 50), 'moving_rigger_time' => (0, 2000, 100), - 'moving_static_time' => (0, 6000, 100), - 'none_body_time' => (0, 300000, 500), + 'moving_static_time' => (0, 60000, 1000), + 'none_body_time' => (0, 300000, 5000), + 'moving_max_dis' => (0, 500, 50), _ => (defaultMin, defaultMax, defaultDivdidend), }; } @@ -83,4 +84,7 @@ abstract final class CpsSliderHelpers { _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', }; } + // TODO: Sports Para causes 400 in IF and THEN / sports_para + // TODO: Detection range causes 400 in IF / moving_range + // TODO: Distance of moving objects causes 400 in IF / presence_range } From ee12980b473ad67c7b700f19900e3f60e1cf94c6 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 10:03:21 +0300 Subject: [PATCH 37/48] Fix value parsing in CpsDialogSliderSelector to ensure two decimal precision --- .../ceiling_sensor/cps_dialog_slider_selector.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1c549d9f..a2d11f79 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 @@ -56,7 +56,7 @@ class CpsDialogSliderSelector extends StatelessWidget { entityId: device?.uuid ?? '', functionCode: selectedFunction, operationName: operationName, - value: value, + value: double.parse(value.toStringAsFixed(2)), condition: selectedFunctionData.condition, ), ), From ceb1e1d23a203924ff5e11331cbf66e08a87b2fd Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 10:03:37 +0300 Subject: [PATCH 38/48] Remove TODO comments. --- .../routine_dialogs/ceiling_sensor/cps_slider_helpers.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart index 03b7b5af..7c7571f6 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart @@ -84,7 +84,4 @@ abstract final class CpsSliderHelpers { _ => '${parsedValue?.toStringAsFixed(0) ?? 0}', }; } - // TODO: Sports Para causes 400 in IF and THEN / sports_para - // TODO: Detection range causes 400 in IF / moving_range - // TODO: Distance of moving objects causes 400 in IF / presence_range } From e2ec986bb9ae8de3a618c5cbe725c6b1a3d26edb Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 10:03:47 +0300 Subject: [PATCH 39/48] Refactor mappable stepped functions to a static constant for improved readability and maintainability --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) 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 52665dfa..e54f348e 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 @@ -153,23 +153,27 @@ class _CeilingSensorDialogState extends State { ); } + static const _mappableSteppedFunctions = { + 'static_max_dis', + 'presence_reference', + 'moving_reference', + 'perceptual_boundary', + 'moving_boundary', + 'moving_rigger_time', + 'moving_static_time', + 'none_body_time', + 'moving_max_dis', + }; + List _updateValuesForAddedFunctions( List addedFunctions, ) { - const mappableSteppedFunctions = { - 'static_max_dis', - 'presence_reference', - 'moving_reference', - 'perceptual_boundary', - 'moving_boundary', - 'moving_rigger_time', - 'moving_static_time', - 'none_body_time', - 'moving_max_dis', - }; return addedFunctions.map((function) { - if (mappableSteppedFunctions.contains(function.functionCode)) { - final mappedValue = mapSteppedValue( + final shouldMapValue = _mappableSteppedFunctions.contains( + function.functionCode, + ); + if (shouldMapValue) { + final mappedValue = _mapSteppedValue( value: function.value, inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode), inputRange: CpsSliderHelpers.sliderRange(function.functionCode), @@ -189,7 +193,7 @@ class _CeilingSensorDialogState extends State { }).toList(); } - int mapSteppedValue({ + int _mapSteppedValue({ required (double min, double max) inputRange, required double inputStep, required (double min, double max, double dividend) outputRange, From 6bea4c2f4adbea34e666b440185280104994b038 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 10:34:05 +0300 Subject: [PATCH 40/48] Add mappings for moving_range and presence_range in slider helpers --- .../routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart | 2 ++ .../routine_dialogs/ceiling_sensor/cps_slider_helpers.dart | 2 ++ 2 files changed, 4 insertions(+) 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 e54f348e..7ce6a9b1 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 @@ -163,6 +163,8 @@ class _CeilingSensorDialogState extends State { 'moving_static_time', 'none_body_time', 'moving_max_dis', + 'moving_range', + 'presence_range', }; List _updateValuesForAddedFunctions( diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart index 7c7571f6..fcb363c9 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart @@ -12,6 +12,8 @@ abstract final class CpsSliderHelpers { 'moving_static_time' => (0, 60000, 1000), 'none_body_time' => (0, 300000, 5000), 'moving_max_dis' => (0, 500, 50), + 'moving_range' => (0, 255, 5), + 'presence_range' => (0, 255, 5), _ => (defaultMin, defaultMax, defaultDivdidend), }; } From 254e03e3c770da9f7661b88a20751f8e851023d8 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 11:19:58 +0300 Subject: [PATCH 41/48] Add mapping for 'sports_para' in slider range function --- .../routine_dialogs/ceiling_sensor/cps_slider_helpers.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart index fcb363c9..fd637c28 100644 --- a/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart +++ b/lib/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart @@ -35,6 +35,7 @@ abstract final class CpsSliderHelpers { 'moving_rigger_time' => (0, 2), 'moving_static_time' => (0, 50), 'none_body_time' => (0, 300), + 'sports_para' => (0, 100), _ => (0, 300), }; From be0533645eee2ec131a32dba7b496c9bbee06b7b Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 11:20:15 +0300 Subject: [PATCH 42/48] Refactor dialog header text assignment in `CeilingSensorDialog` for clarity and readability. --- .../ceiling_sensor/ceiling_sensor_dialog.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 7ce6a9b1..c18706f0 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 @@ -35,6 +35,7 @@ class CeilingSensorDialog extends StatefulWidget { class _CeilingSensorDialogState extends State { late final List _cpsFunctions; + late final String _dialogHeaderText; @override void initState() { @@ -46,6 +47,9 @@ class _CeilingSensorDialogState extends State { } return function.type == 'IF' || function.type == 'BOTH'; }).toList(); + + final isIfDialog = widget.dialogType == 'IF'; + _dialogHeaderText = isIfDialog ? 'Conditions' : 'Functions'; } @override @@ -67,18 +71,18 @@ class _CeilingSensorDialogState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - DialogHeader( - 'Presence Sensor ${widget.dialogType == "IF" ? 'Conditions' : 'Functions'}', - ), + DialogHeader('Presence Sensor $_dialogHeaderText'), Expanded(child: _buildMainContent(context, state)), DialogFooter( onCancel: () => Navigator.pop(context), onConfirm: state.addedFunctions.isNotEmpty ? () { + final functions = _updateValuesForAddedFunctions( + state.addedFunctions, + ); context.read().add( AddFunctionToRoutine( - _updateValuesForAddedFunctions( - state.addedFunctions), + functions, '${widget.uniqueCustomId}', ), ); From 616adccfdda7fd8611a4a5e1c2eb36d896439644 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 12:58:20 +0300 Subject: [PATCH 43/48] Applied the correct scenario of tapping add community icon button. --- .../widgets/sidebar_add_community_button.dart | 23 ++----------- .../all_spaces/widgets/sidebar_header.dart | 11 +++++-- .../all_spaces/widgets/sidebar_widget.dart | 32 ++++++++++++++++--- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/sidebar_add_community_button.dart b/lib/pages/spaces_management/all_spaces/widgets/sidebar_add_community_button.dart index 5c769d48..ae1eb2bf 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/sidebar_add_community_button.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/sidebar_add_community_button.dart @@ -1,19 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; -import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class SidebarAddCommunityButton extends StatelessWidget { const SidebarAddCommunityButton({ - required this.existingCommunityNames, + required this.onTap, super.key, }); - final List existingCommunityNames; + final void Function() onTap; @override Widget build(BuildContext context) { @@ -30,22 +26,9 @@ class SidebarAddCommunityButton extends StatelessWidget { ), ), ), - onPressed: () => _showCreateCommunityDialog(context), + onPressed: onTap, icon: SvgPicture.asset(Assets.addIcon), ), ); } - - void _showCreateCommunityDialog(BuildContext context) => showDialog( - context: context, - builder: (context) => CreateCommunityDialog( - isEditMode: false, - existingCommunityNames: existingCommunityNames, - onCreateCommunity: (name, description) { - context.read().add( - CreateCommunityEvent(name, description, context), - ); - }, - ), - ); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/sidebar_header.dart b/lib/pages/spaces_management/all_spaces/widgets/sidebar_header.dart index 135be109..1706d51a 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/sidebar_header.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/sidebar_header.dart @@ -5,9 +5,12 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; class SidebarHeader extends StatelessWidget { - const SidebarHeader({required this.existingCommunityNames, super.key}); + const SidebarHeader({ + required this.onAddCommunity, + super.key, + }); - final List existingCommunityNames; + final void Function() onAddCommunity; @override Widget build(BuildContext context) { @@ -23,7 +26,9 @@ class SidebarHeader extends StatelessWidget { color: ColorsManager.blackColor, ), ), - SidebarAddCommunityButton(existingCommunityNames: existingCommunityNames), + SidebarAddCommunityButton( + onTap: onAddCommunity, + ), ], ), ); diff --git a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart index 35bb8ad2..b30a8c96 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart @@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_header.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -94,10 +95,7 @@ class _SidebarWidgetState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SidebarHeader( - existingCommunityNames: - widget.communities.map((community) => community.name).toList(), - ), + SidebarHeader(onAddCommunity: _onAddCommunity), CustomSearchBar( onSearchChanged: (query) => setState(() => _searchQuery = query), ), @@ -179,4 +177,30 @@ class _SidebarWidgetState extends State { ), ); } + + void _onAddCommunity() => _selectedId?.isNotEmpty ?? true + ? _clearSelection() + : _showCreateCommunityDialog(); + + void _clearSelection() { + setState(() => _selectedId = ''); + context.read().add( + NewCommunityEvent(communities: widget.communities), + ); + } + + void _showCreateCommunityDialog() { + showDialog( + context: context, + builder: (context) => CreateCommunityDialog( + isEditMode: false, + existingCommunityNames: widget.communities.map((e) => e.name).toList(), + onCreateCommunity: (name, description) { + context.read().add( + CreateCommunityEvent(name, description, context), + ); + }, + ), + ); + } } From 7dcaa20da12ace15f71e1a9693a4bd9dc031f490 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 13:06:30 +0300 Subject: [PATCH 44/48] Enhanced the code and look of `DialogFooter` buttons. --- lib/pages/routines/widgets/dialog_footer.dart | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/lib/pages/routines/widgets/dialog_footer.dart b/lib/pages/routines/widgets/dialog_footer.dart index 15db9732..e5a548f7 100644 --- a/lib/pages/routines/widgets/dialog_footer.dart +++ b/lib/pages/routines/widgets/dialog_footer.dart @@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget { final int? dialogWidth; const DialogFooter({ - Key? key, + super.key, required this.onCancel, required this.onConfirm, required this.isConfirmEnabled, this.dialogWidth, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -28,21 +28,19 @@ class DialogFooter extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Expanded( - child: _buildFooterButton( - context, - 'Cancel', - onCancel, - ), + _buildFooterButton( + context: context, + text: 'Cancel', + onTap: onCancel, ), if (isConfirmEnabled) ...[ Container(width: 1, height: 50, color: ColorsManager.greyColor), - Expanded( - child: _buildFooterButton( - context, - 'Confirm', - onConfirm, - ), + _buildFooterButton( + context: context, + text: 'Confirm', + onTap: onConfirm, + textColor: + isConfirmEnabled ? ColorsManager.primaryColorWithOpacity : Colors.red, ), ], ], @@ -50,24 +48,24 @@ class DialogFooter extends StatelessWidget { ); } - Widget _buildFooterButton( - BuildContext context, - String text, - VoidCallback? onTap, - ) { - return GestureDetector( - onTap: onTap, - child: SizedBox( - height: 50, - child: Center( - child: Text( - text, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: text == 'Confirm' - ? ColorsManager.primaryColorWithOpacity - : ColorsManager.textGray, - ), - ), + Widget _buildFooterButton({ + required BuildContext context, + required String text, + required VoidCallback? onTap, + Color? textColor, + }) { + return Expanded( + child: TextButton( + style: TextButton.styleFrom( + foregroundColor: ColorsManager.primaryColorWithOpacity, + disabledForegroundColor: ColorsManager.primaryColor, + ), + onPressed: onTap, + child: Text( + text, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: textColor ?? ColorsManager.textGray, + ), ), ), ); From 1bfab8cc768557c5da701f7d997b837beaf0bc68 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 14:38:06 +0300 Subject: [PATCH 45/48] SP-1189-Fix tapping ok and nothing happening bug by taking the action out of the widget. --- .../widgets/loaded_space_widget.dart | 7 + .../all_spaces/widgets/sidebar_widget.dart | 8 +- .../views/create_subspace_model_dialog.dart | 231 ++++-------------- .../create_subspace_model_chips_box.dart | 64 +++++ .../create_subspace_model_footer_buttons.dart | 53 ++++ .../widgets/subspace_chip.dart | 58 +++++ .../widgets/subspaces_textfield.dart | 68 ++++++ 7 files changed, 297 insertions(+), 192 deletions(-) create mode 100644 lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart create mode 100644 lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart create mode 100644 lib/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart create mode 100644 lib/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart index 66b2d6da..5ef9c79b 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; @@ -107,6 +109,11 @@ class _LoadedSpaceViewState extends State { selectedSpaceUuid: widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '', + onCreateCommunity: (name, description) { + context.read().add( + CreateCommunityEvent(name, description, context), + ); + }, ), CommunityStructureArea( selectedCommunity: widget.selectedCommunity, diff --git a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart index b30a8c96..3eb1c001 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart @@ -16,9 +16,11 @@ import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { final List communities; final String? selectedSpaceUuid; + final void Function(String name, String description) onCreateCommunity; const SidebarWidget({ required this.communities, + required this.onCreateCommunity, this.selectedSpaceUuid, super.key, }); @@ -195,11 +197,7 @@ class _SidebarWidgetState extends State { builder: (context) => CreateCommunityDialog( isEditMode: false, existingCommunityNames: widget.communities.map((e) => e.name).toList(), - onCreateCommunity: (name, description) { - context.read().add( - CreateCommunityEvent(name, description, context), - ); - }, + onCreateCommunity: widget.onCreateCommunity, ), ); } diff --git a/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart index 7a39891b..66acdf3d 100644 --- a/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; -import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_state.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; class CreateSubSpaceModelDialog extends StatelessWidget { final bool isEdit; @@ -14,211 +15,67 @@ class CreateSubSpaceModelDialog extends StatelessWidget { final List? existingSubSpaces; final void Function(List newSubspaces)? onUpdate; - const CreateSubSpaceModelDialog( - {Key? key, - required this.isEdit, - required this.dialogTitle, - this.existingSubSpaces, - this.onUpdate}) - : super(key: key); + const CreateSubSpaceModelDialog({ + required this.isEdit, + required this.dialogTitle, + this.existingSubSpaces, + this.onUpdate, + super.key, + }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; - final textController = TextEditingController(); return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), child: BlocProvider( - create: (_) { + create: (context) { final bloc = SubSpaceModelBloc(); if (existingSubSpaces != null) { - for (var subSpace in existingSubSpaces!) { + for (final subSpace in existingSubSpaces ?? []) { bloc.add(AddSubSpaceModel(subSpace)); } } return bloc; }, child: BlocBuilder( - builder: (context, state) { - return Container( - color: ColorsManager.whiteColors, - child: SizedBox( - width: screenWidth * 0.3, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - dialogTitle, - style: Theme.of(context) - .textTheme - .headlineLarge - ?.copyWith(color: ColorsManager.blackColor), - ), - const SizedBox(height: 16), - Container( - width: screenWidth * 0.35, - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 16.0), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(10), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 8.0, - children: [ - ...state.subSpaces.asMap().entries.map( - (entry) { - final index = entry.key; - final subSpace = entry.value; - - final lowerName = - subSpace.subspaceName.toLowerCase(); - - final duplicateIndices = state.subSpaces - .asMap() - .entries - .where((e) => - e.value.subspaceName.toLowerCase() == - lowerName) - .map((e) => e.key) - .toList(); - final isDuplicate = - duplicateIndices.length > 1 && - duplicateIndices.indexOf(index) != 0; - - return Chip( - label: Text(subSpace.subspaceName, - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith( - color: ColorsManager.spaceColor, - )), - backgroundColor: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - side: BorderSide( - color: isDuplicate - ? ColorsManager.red - : ColorsManager.transparentColor, - width: 0, - ), - ), - deleteIcon: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.5, - ), - ), - child: const Icon( - Icons.close, - size: 16, - color: ColorsManager.lightGrayColor, - ), - ), - onDeleted: () => context - .read() - .add(RemoveSubSpaceModel(subSpace)), - ); - }, - ), - SizedBox( - width: 200, - child: TextField( - controller: textController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: state.subSpaces.isEmpty - ? 'Please enter the name' - : null, - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - color: ColorsManager - .lightGrayColor)), - onSubmitted: (value) { - if (value.trim().isNotEmpty) { - context.read().add( - AddSubSpaceModel( - SubspaceTemplateModel( - subspaceName: value.trim(), - disabled: false))); - textController.clear(); - } - }, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager.blackColor)), - ), - ], - ), - ), - if (state.errorMessage.isNotEmpty) - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: Text(state.errorMessage, - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith( - color: ColorsManager.red, - )), - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: CancelButton( - label: 'Cancel', - onPressed: () async { - Navigator.of(context).pop(); - }, - ), - ), - const SizedBox(width: 10), - Expanded( - child: DefaultButton( - onPressed: (state.errorMessage.isNotEmpty) - ? null - : () async { - final subSpaces = context - .read() - .state - .subSpaces; - Navigator.of(context).pop(); - if (onUpdate != null) { - onUpdate!(subSpaces); - } - }, - backgroundColor: ColorsManager.secondaryColor, - borderRadius: 10, - foregroundColor: state.errorMessage.isNotEmpty - ? ColorsManager.whiteColorsWithOpacity - : ColorsManager.whiteColors, - child: const Text('OK'), - ), - ), - ], - ), - ], + builder: (context, state) => Container( + color: ColorsManager.whiteColors, + width: screenWidth * 0.3, + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + dialogTitle, + style: context.textTheme.headlineLarge?.copyWith( + color: ColorsManager.blackColor, + ), + ), + const SizedBox(height: 16), + CreateSubspaceModelChipsBox(subSpaces: state.subSpaces), + if (state.errorMessage.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + state.errorMessage, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.red, + ), ), ), - )); - }, + const SizedBox(height: 16), + CreateSubspaceModelFooterButtons( + onUpdate: onUpdate, + errorMessage: state.errorMessage, + ), + ], + ), + ), ), ), ); diff --git a/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart b/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart new file mode 100644 index 00000000..a18fc8d8 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CreateSubspaceModelChipsBox extends StatelessWidget { + const CreateSubspaceModelChipsBox({ + required this.subSpaces, + super.key, + }); + + final List subSpaces; + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + + return Container( + width: screenWidth * 0.35, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(10), + ), + child: Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + ...subSpaces.asMap().entries.map( + (entry) { + final index = entry.key; + final subSpace = entry.value; + + final lowerName = subSpace.subspaceName.toLowerCase(); + + final duplicateIndices = subSpaces + .asMap() + .entries + .where((e) => e.value.subspaceName.toLowerCase() == lowerName) + .map((e) => e.key) + .toList(); + final isDuplicate = duplicateIndices.length > 1 && + duplicateIndices.indexOf(index) != 0; + + return SubspaceChip( + subSpace: subSpace, + isDuplicate: isDuplicate, + ); + }, + ), + SubspacesTextfield( + hintText: subSpaces.isEmpty ? 'Please enter the name' : null, + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart b/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart new file mode 100644 index 00000000..a8dcf89c --- /dev/null +++ b/lib/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CreateSubspaceModelFooterButtons extends StatelessWidget { + const CreateSubspaceModelFooterButtons({ + required this.onUpdate, + required this.errorMessage, + super.key, + }); + + final void Function(List newSubspaces)? onUpdate; + final String errorMessage; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: CancelButton( + label: 'Cancel', + onPressed: () => Navigator.of(context).pop(), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + onPressed: errorMessage.isEmpty + ? () { + Navigator.of(context).pop(); + if (onUpdate != null) { + final subSpaces = + context.read().state.subSpaces; + onUpdate!(subSpaces); + } + } + : null, + backgroundColor: ColorsManager.secondaryColor, + borderRadius: 10, + foregroundColor: errorMessage.isNotEmpty + ? ColorsManager.whiteColorsWithOpacity + : ColorsManager.whiteColors, + child: const Text('OK'), + ), + ), + ], + ); + } +} diff --git a/lib/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart b/lib/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart new file mode 100644 index 00000000..b54e0712 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class SubspaceChip extends StatelessWidget { + const SubspaceChip({ + required this.subSpace, + required this.isDuplicate, + super.key, + }); + + final SubspaceTemplateModel subSpace; + final bool isDuplicate; + + @override + Widget build(BuildContext context) { + return Chip( + label: Text( + subSpace.subspaceName, + style: context.textTheme.bodySmall?.copyWith( + color: isDuplicate ? ColorsManager.red : ColorsManager.spaceColor, + ), + ), + backgroundColor: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide( + color: isDuplicate ? ColorsManager.red : ColorsManager.transparentColor, + width: 0, + ), + ), + deleteIcon: Container( + padding: const EdgeInsetsDirectional.all(1), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1.5, + ), + ), + child: const FittedBox( + fit: BoxFit.scaleDown, + child: Icon( + Icons.close, + color: ColorsManager.lightGrayColor, + ), + ), + ), + onDeleted: () => context.read().add( + RemoveSubSpaceModel(subSpace), + ), + ); + } +} diff --git a/lib/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart b/lib/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart new file mode 100644 index 00000000..d654b960 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class SubspacesTextfield extends StatefulWidget { + const SubspacesTextfield({ + required this.hintText, + super.key, + }); + + final String? hintText; + + @override + State createState() => _SubspacesTextfieldState(); +} + +class _SubspacesTextfieldState extends State { + late final TextEditingController _controller; + @override + void initState() { + _controller = TextEditingController(); + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 100, + child: TextField( + controller: _controller, + decoration: InputDecoration( + border: InputBorder.none, + hintText: widget.hintText, + hintStyle: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.lightGrayColor, + ), + ), + onSubmitted: (value) { + final trimmedValue = value.trim(); + if (trimmedValue.isNotEmpty) { + context.read().add( + AddSubSpaceModel( + SubspaceTemplateModel( + subspaceName: trimmedValue, + disabled: false, + ), + ), + ); + _controller.clear(); + } + }, + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.blackColor, + ), + ), + ); + } +} From 86b87716945e41be605461fc80076d5cf84dd85c Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 16:12:01 +0300 Subject: [PATCH 46/48] bump-v of web deployment action. --- .../workflows/azure-static-web-apps-polite-smoke-017c65c10.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml index e28d1bb2..28cf00a2 100644 --- a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml +++ b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.22.2' # Specify the Flutter version you want to use + flutter-version: '3.27.3' # Specify the Flutter version you want to use - name: Install dependencies run: flutter pub get From 4989a0e95cdf1358110d9f8ef85845d4cd5192dc Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 16:24:46 +0300 Subject: [PATCH 47/48] removed the use of `Flexible` that was causing an exception. --- lib/pages/routines/widgets/dragable_card.dart | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/pages/routines/widgets/dragable_card.dart b/lib/pages/routines/widgets/dragable_card.dart index 7395d2d6..46f5ecfd 100644 --- a/lib/pages/routines/widgets/dragable_card.dart +++ b/lib/pages/routines/widgets/dragable_card.dart @@ -103,16 +103,14 @@ class DraggableCard extends StatelessWidget { const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 3), - child: Flexible( - child: Text( - deviceData['title'] ?? deviceData['name'] ?? title, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - maxLines: 2, - style: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - ), + child: Text( + deviceData['title'] ?? deviceData['name'] ?? title, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, ), ), ), From 9eefd522b7ee33da7dede28d74251c1036f72faf Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Apr 2025 16:25:00 +0300 Subject: [PATCH 48/48] bump flutter version on production github action. --- .../workflows/azure-static-web-apps-mango-bush-01e607f10.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml b/.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml index 95e9346d..f0379c95 100644 --- a/.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml +++ b/.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.22.2' # Specify the Flutter version you want to use + flutter-version: '3.27.3' # Specify the Flutter version you want to use - name: Install dependencies run: flutter pub get