From 5e94de5d78ef2035aa86f0f8a5101a0c69d573ee Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Sun, 24 Nov 2024 01:10:31 +0300 Subject: [PATCH] fixes unique id for card drag --- .../all_devices/models/devices_model.dart | 2 + .../bloc/routine_bloc/routine_bloc.dart | 45 +- .../bloc/routine_bloc/routine_event.dart | 7 +- .../bloc/routine_bloc/routine_state.dart | 6 +- lib/pages/routiens/helper/ac_helper.dart | 51 +- .../dialog_helper/device_dialog_helper.dart | 24 +- .../helper/one_gang_switch_helper.dart | 185 ++---- .../helper/three_gang_switch_helper.dart | 571 +++++++++++------- .../helper/two_gang_switch_helper.dart | 564 ++++++++++------- .../routiens/models/device_functions.dart | 5 +- lib/pages/routiens/models/routine_item.dart | 51 +- lib/pages/routiens/models/routine_model.dart | 14 +- lib/pages/routiens/widgets/dragable_card.dart | 7 +- .../routiens/widgets/routine_devices.dart | 1 + .../routiens/widgets/then_container.dart | 51 +- pubspec.lock | 32 + pubspec.yaml | 1 + 17 files changed, 905 insertions(+), 712 deletions(-) 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 367a4fe8..b4f72287 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -105,6 +105,7 @@ class AllDevicesModel { this.productName, this.spaces, }); + AllDevicesModel.fromJson(Map json) { room = (json['room'] != null && (json['room'] is Map)) ? DevicesModelRoom.fromJson(json['room']) @@ -138,6 +139,7 @@ class AllDevicesModel { updateTime = int.tryParse(json['updateTime']?.toString() ?? ''); uuid = json['uuid']?.toString(); batteryLevel = int.tryParse(json['battery']?.toString() ?? ''); + productName = json['productName']?.toString(); if (json['spaces'] != null && json['spaces'] is List) { spaces = (json['spaces'] as List) diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart index 2b711f39..bbd711a2 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart @@ -16,9 +16,9 @@ class RoutineBloc extends Bloc { on(_onAddToThenContainer); on(_onLoadScenes); on(_onLoadAutomation); - on(_onAddFunction); - on(_onRemoveFunction); - on(_onClearFunctions); + on(_onAddFunctionsToRoutine); + // on(_onRemoveFunction); + // on(_onClearFunctions); } void _onAddToIfContainer(AddToIfContainer event, Emitter emit) { @@ -38,24 +38,33 @@ class RoutineBloc extends Bloc { // } } - void _onAddFunction(AddFunctionToRoutine event, Emitter emit) { - final functions = List.from(state.selectedFunctions); - functions.add(event.function); - debugPrint("******" + functions.toString()); - emit(state.copyWith(selectedFunctions: functions)); + void _onAddFunctionsToRoutine( + AddFunctionToRoutine event, Emitter emit) { + debugPrint(event.uniqueCustomId.toString()); + debugPrint(event.functions.toString()); + final currentSelectedFunctions = + Map>.from(state.selectedFunctions); + + if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { + currentSelectedFunctions[event.uniqueCustomId]!.addAll(event.functions); + } else { + currentSelectedFunctions[event.uniqueCustomId] = event.functions; + } + + emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); } - void _onRemoveFunction(RemoveFunction event, Emitter emit) { - final functions = List.from(state.selectedFunctions) - ..removeWhere((f) => - f.functionCode == event.function.functionCode && - f.value == event.function.value); - emit(state.copyWith(selectedFunctions: functions)); - } + // void _onRemoveFunction(RemoveFunction event, Emitter emit) { + // final functions = List.from(state.selectedFunctions) + // ..removeWhere((f) => + // f.functionCode == event.function.functionCode && + // f.value == event.function.value); + // emit(state.copyWith(selectedFunctions: functions)); + // } - void _onClearFunctions(ClearFunctions event, Emitter emit) { - emit(state.copyWith(selectedFunctions: [])); - } + // void _onClearFunctions(ClearFunctions event, Emitter emit) { + // emit(state.copyWith(selectedFunctions: [])); + // } // bool _isDuplicate( // List> items, Map newItem) { diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart index 3321af5e..e3071b15 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart @@ -44,10 +44,11 @@ class LoadAutomation extends RoutineEvent { } class AddFunctionToRoutine extends RoutineEvent { - final DeviceFunctionData function; - const AddFunctionToRoutine(this.function); + final List functions; + final String uniqueCustomId; + const AddFunctionToRoutine(this.functions, this.uniqueCustomId); @override - List get props => [function]; + List get props => [functions]; } class RemoveFunction extends RoutineEvent { diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart index 95aab557..ce6fea15 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart @@ -6,7 +6,7 @@ class RoutineState extends Equatable { final List> availableCards; final List scenes; final List automations; - final List selectedFunctions; + final Map> selectedFunctions; final bool isLoading; final String? errorMessage; @@ -16,7 +16,7 @@ class RoutineState extends Equatable { this.availableCards = const [], this.scenes = const [], this.automations = const [], - this.selectedFunctions = const [], + this.selectedFunctions = const {}, this.isLoading = false, this.errorMessage, }); @@ -26,7 +26,7 @@ class RoutineState extends Equatable { List>? thenItems, List? scenes, List? automations, - List? selectedFunctions, + Map>? selectedFunctions, bool? isLoading, String? errorMessage, }) { diff --git a/lib/pages/routiens/helper/ac_helper.dart b/lib/pages/routiens/helper/ac_helper.dart index 9ba02633..8b555b7a 100644 --- a/lib/pages/routiens/helper/ac_helper.dart +++ b/lib/pages/routiens/helper/ac_helper.dart @@ -18,6 +18,7 @@ class ACHelper { List functions, AllDevicesModel? device, List? deviceSelectedFunctions, + String uniqueCustomId, ) async { List acFunctions = functions.whereType().toList(); @@ -95,13 +96,14 @@ class ACHelper { onConfirm: state.addedFunctions.isNotEmpty ? () { /// add the functions to the routine bloc - for (var function in state.addedFunctions) { - context.read().add( - AddFunctionToRoutine( - function, - ), - ); - } + // for (var function in state.addedFunctions) { + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + uniqueCustomId, + ), + ); + //} // Return the device data to be added to the container Navigator.pop(context, { 'deviceId': functions.first.deviceId, @@ -229,11 +231,23 @@ class ACHelper { selectedFunctionData, ), const SizedBox(height: 20), - _buildTemperatureDisplay(context, initialValue, device, operationName, - selectedFunctionData, selectCode), + _buildTemperatureDisplay( + context, + initialValue, + device, + operationName, + selectedFunctionData, + selectCode, + ), const SizedBox(height: 20), - _buildTemperatureSlider(context, initialValue, device, operationName, - selectedFunctionData, selectCode), + _buildTemperatureSlider( + context, + initialValue, + device, + operationName, + selectedFunctionData, + selectCode, + ), ], ); } @@ -246,6 +260,7 @@ class ACHelper { AllDevicesModel? device, String operationName, DeviceFunctionData? selectedFunctionData, + // Function(String) onConditionChanged, ) { final conditions = ["<", "==", ">"]; @@ -282,12 +297,13 @@ class ACHelper { /// Build temperature display for AC functions dialog static Widget _buildTemperatureDisplay( - BuildContext context, - dynamic initialValue, - AllDevicesModel? device, - String operationName, - DeviceFunctionData? selectedFunctionData, - String selectCode) { + BuildContext context, + dynamic initialValue, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + String selectCode, + ) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( @@ -342,6 +358,7 @@ class ACHelper { required String operationName, required String selectCode, DeviceFunctionData? selectedFunctionData, + // required Function(dynamic) onValueChanged, }) { return ListView.builder( diff --git a/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart b/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart index 714feda3..fcb8e740 100644 --- a/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart @@ -39,24 +39,28 @@ class DeviceDialogHelper { List functions, ) async { final routineBloc = context.read(); - final deviceSelectedFunctions = routineBloc.state.selectedFunctions - .where((f) => f.entityId == data['deviceId']) - .toList(); + final deviceSelectedFunctions = + routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; switch (productType) { case 'AC': - return ACHelper.showACFunctionsDialog( - context, functions, data['device'], deviceSelectedFunctions); + return ACHelper.showACFunctionsDialog(context, functions, + data['device'], deviceSelectedFunctions, data['uniqueCustomId']); case '1G': - return OneGangSwitchHelper.showSwitchFunctionsDialog( - context, functions, data['device'], deviceSelectedFunctions); + return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, + data['device'], deviceSelectedFunctions, data['uniqueCustomId']); case '2G': - return TwoGangSwitchHelper.showSwitchFunctionsDialog( - context, functions); + return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, + data['device'], deviceSelectedFunctions, data['uniqueCustomId']); case '3G': return ThreeGangSwitchHelper.showSwitchFunctionsDialog( - context, functions); + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + ); default: return null; } diff --git a/lib/pages/routiens/helper/one_gang_switch_helper.dart b/lib/pages/routiens/helper/one_gang_switch_helper.dart index 6cb28424..c7d5d35a 100644 --- a/lib/pages/routiens/helper/one_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/one_gang_switch_helper.dart @@ -4,11 +4,9 @@ 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/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; -import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; -import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart'; +import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -20,8 +18,10 @@ class OneGangSwitchHelper { List functions, AllDevicesModel? device, List? deviceSelectedFunctions, + String uniqueCustomId, ) async { - List acFunctions = functions.whereType().toList(); + List acFunctions = + functions.whereType().toList(); return showDialog?>( context: context, @@ -107,30 +107,6 @@ class OneGangSwitchHelper { operationName: selectedOperationName ?? '', ), ), - // ValueListenableBuilder( - // valueListenable: selectedFunctionNotifier, - // builder: (context, selectedFunction, _) { - // final selectedFn = - // switchFunctions.firstWhere( - // (f) => f.code == selectedFunction, - // ); - // return Expanded( - // child: selectedFn - // is OneGangCountdownFunction - // ? _buildCountDownSelector( - // context, - // selectedValueNotifier, - // selectedConditionNotifier, - // selectedConditionsNotifier, - // ) - // : _buildOperationalValuesList( - // context, - // selectedFn as BaseSwitchFunction, - // selectedValueNotifier, - // ), - // ); - // }, - // ), ], ), ), @@ -146,13 +122,20 @@ class OneGangSwitchHelper { onConfirm: state.addedFunctions.isNotEmpty ? () { /// add the functions to the routine bloc - for (var function in state.addedFunctions) { - context.read().add( - AddFunctionToRoutine( - function, - ), - ); - } + // for (var function in state.addedFunctions) { + // context.read().add( + // AddFunctionToRoutine( + // function, + // uniqueCustomId, + // ), + // ); + // } + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + uniqueCustomId, + ), + ); // Return the device data to be added to the container Navigator.pop(context, { 'deviceId': functions.first.deviceId, @@ -175,13 +158,13 @@ class OneGangSwitchHelper { required BuildContext context, required String selectedFunction, required DeviceFunctionData? selectedFunctionData, - required List acFunctions, + required List acFunctions, AllDevicesModel? device, required String operationName, }) { if (selectedFunction == 'countdown_1') { final initialValue = selectedFunctionData?.value ?? 200; - return _buildTemperatureSelector( + return _buildCountDownSelector( context: context, initialValue: initialValue, selectCode: selectedFunction, @@ -207,7 +190,7 @@ class OneGangSwitchHelper { ); } - static Widget _buildTemperatureSelector({ + static Widget _buildCountDownSelector({ required BuildContext context, required dynamic initialValue, required String? currentCondition, @@ -228,10 +211,10 @@ class OneGangSwitchHelper { selectedFunctionData, ), const SizedBox(height: 20), - _buildTemperatureDisplay(context, initialValue, device, operationName, + _buildCountDownDisplay(context, initialValue, device, operationName, selectedFunctionData, selectCode), const SizedBox(height: 20), - _buildTemperatureSlider(context, initialValue, device, operationName, + _buildCountDownSlider(context, initialValue, device, operationName, selectedFunctionData, selectCode), ], ); @@ -280,7 +263,7 @@ class OneGangSwitchHelper { } /// Build temperature display for AC functions dialog - static Widget _buildTemperatureDisplay( + static Widget _buildCountDownDisplay( BuildContext context, dynamic initialValue, AllDevicesModel? device, @@ -302,7 +285,7 @@ class OneGangSwitchHelper { ); } - static Widget _buildTemperatureSlider( + static Widget _buildCountDownSlider( BuildContext context, dynamic initialValue, AllDevicesModel? device, @@ -310,11 +293,22 @@ class OneGangSwitchHelper { DeviceFunctionData? selectedFunctionData, String selectCode, ) { + final operationalValues = SwitchOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ); return Slider( value: (initialValue ?? 0).toDouble(), - min: 0, - max: 300, - divisions: 300, + min: operationalValues.minValue?.toDouble() ?? 0.0, + max: operationalValues.maxValue?.toDouble() ?? 0.0, + divisions: (((operationalValues.maxValue ?? 0) - + (operationalValues.minValue ?? 0)) / + (operationalValues.stepValue ?? 1)) + .round(), onChanged: (value) { context.read().add( AddFunction( @@ -334,13 +328,12 @@ class OneGangSwitchHelper { static Widget _buildOperationalValuesList({ required BuildContext context, - required List values, + required List values, required dynamic selectedValue, AllDevicesModel? device, required String operationName, required String selectCode, DeviceFunctionData? selectedFunctionData, - // required Function(dynamic) onValueChanged, }) { return ListView.builder( shrinkWrap: false, @@ -394,102 +387,4 @@ class OneGangSwitchHelper { }, ); } - - // static Widget _buildCountDownSelector( - // BuildContext context, - // ValueNotifier valueNotifier, - // ValueNotifier conditionNotifier, - // ValueNotifier> conditionsNotifier, - // ) { - // return ValueListenableBuilder( - // valueListenable: valueNotifier, - // builder: (context, value, _) { - // return Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // ValueListenableBuilder>( - // valueListenable: conditionsNotifier, - // builder: (context, selectedConditions, _) { - // return ToggleButtons( - // onPressed: (int index) { - // final newConditions = List.filled(3, false); - // newConditions[index] = true; - // conditionsNotifier.value = newConditions; - // conditionNotifier.value = index == 0 - // ? "<" - // : index == 1 - // ? "==" - // : ">"; - // }, - // 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: selectedConditions, - // children: const [Text("<"), Text("="), Text(">")], - // ); - // }, - // ), - // const SizedBox(height: 20), - // Text( - // '${value ?? 0} sec', - // style: Theme.of(context).textTheme.headlineMedium, - // ), - // const SizedBox(height: 20), - // Slider( - // value: (value ?? 0).toDouble(), - // min: 0, - // max: 300, - // divisions: 300, - // onChanged: (newValue) { - // valueNotifier.value = newValue.toInt(); - // }, - // ), - // ], - // ); - // }, - // ); - // } - - // static Widget _buildOperationalValuesList( - // BuildContext context, - // BaseSwitchFunction function, - // ValueNotifier valueNotifier, - // ) { - // final values = function.getOperationalValues(); - // return ValueListenableBuilder( - // valueListenable: valueNotifier, - // builder: (context, selectedValue, _) { - // return ListView.builder( - // itemCount: values.length, - // itemBuilder: (context, index) { - // final value = values[index]; - // return ListTile( - // leading: SvgPicture.asset( - // value.icon, - // width: 24, - // height: 24, - // ), - // title: Text( - // value.description, - // style: context.textTheme.bodyMedium, - // ), - // trailing: Radio( - // value: value.value, - // groupValue: selectedValue, - // onChanged: (newValue) { - // valueNotifier.value = newValue; - // }, - // ), - // ); - // }, - // ); - // }, - // ); - // } } diff --git a/lib/pages/routiens/helper/three_gang_switch_helper.dart b/lib/pages/routiens/helper/three_gang_switch_helper.dart index 502b302e..4a4c5e02 100644 --- a/lib/pages/routiens/helper/three_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/three_gang_switch_helper.dart @@ -1,8 +1,12 @@ 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/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; +import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; -import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart'; +import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -10,64 +14,60 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class ThreeGangSwitchHelper { static Future?> showSwitchFunctionsDialog( - BuildContext context, List> functions) async { - List> switchFunctions = functions - .where((f) => - f is ThreeGangSwitch1Function || - f is ThreeGangSwitch2Function || - f is ThreeGangSwitch3Function || - f is ThreeGangCountdown1Function || - f is ThreeGangCountdown2Function || - f is ThreeGangCountdown3Function) - .toList(); + BuildContext context, + List functions, + AllDevicesModel? device, + List? deviceSelectedFunctions, + String uniqueCustomId, + ) async { + List switchFunctions = + functions.whereType().toList(); - final selectedFunctionNotifier = ValueNotifier(null); - final selectedValueNotifier = ValueNotifier(null); - final selectedConditionNotifier = ValueNotifier('<'); - final selectedConditionsNotifier = - ValueNotifier>([true, false, false]); - - await showDialog( + return showDialog?>( context: context, builder: (BuildContext context) { - return ValueListenableBuilder( - valueListenable: selectedFunctionNotifier, - builder: (context, selectedFunction, _) { - return AlertDialog( + return BlocProvider( + create: (_) => FunctionBloc() + ..add(InitializeFunctions(deviceSelectedFunctions ?? [])), + child: AlertDialog( contentPadding: EdgeInsets.zero, - content: Container( - width: selectedFunction != null ? 600 : 300, - 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('3 Gangs Light Switch Condition'), - Expanded( - child: Row( - children: [ - // Left side: Function list - Expanded( - child: ListView.separated( - itemCount: switchFunctions.length, - separatorBuilder: (_, __) => const Divider( - color: ColorsManager.dividerColor, - ), - itemBuilder: (context, index) { - final function = switchFunctions[index]; - return ValueListenableBuilder( - valueListenable: selectedFunctionNotifier, - builder: (context, selectedFunction, _) { - final isSelected = - selectedFunction == function.code; + 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('3 Gangs Light Switch Condition'), + Expanded( + child: Row( + children: [ + // Left side: Function list + Expanded( + child: ListView.separated( + itemCount: switchFunctions.length, + separatorBuilder: (_, __) => const Divider( + color: ColorsManager.dividerColor, + ), + itemBuilder: (context, index) { + final function = switchFunctions[index]; return ListTile( - tileColor: isSelected - ? Colors.grey.shade100 - : null, leading: SvgPicture.asset( function.icon, width: 24, @@ -83,196 +83,307 @@ class ThreeGangSwitchHelper { color: ColorsManager.textGray, ), onTap: () { - selectedFunctionNotifier.value = - function.code; - selectedValueNotifier.value = function - is ThreeGangCountdown1Function || - function - is ThreeGangCountdown2Function || - function - is ThreeGangCountdown3Function - ? 0 - : null; + context + .read() + .add(SelectFunction( + functionCode: function.code, + operationName: + function.operationName, + )); }, ); }, - ); - }, - ), - ), - // Right side: Value selector - if (selectedFunction != null) - Expanded( - child: ValueListenableBuilder( - valueListenable: selectedValueNotifier, - builder: (context, selectedValue, _) { - final selectedFn = switchFunctions.firstWhere( - (f) => f.code == selectedFunction, - ); - return selectedFn - is ThreeGangCountdown1Function || - selectedFn - is ThreeGangCountdown2Function || - selectedFn - is ThreeGangCountdown3Function - ? _buildCountDownSelector( - context, - selectedValueNotifier, - selectedConditionNotifier, - selectedConditionsNotifier, - ) - : _buildOperationalValuesList( - context, - selectedFn as BaseSwitchFunction, - selectedValueNotifier, - ); - }, + ), ), - ), - ], - ), + // Right side: Value selector + if (selectedFunction != null) + Expanded( + child: _buildValueSelector( + context: context, + selectedFunction: selectedFunction, + selectedFunctionData: selectedFunctionData, + switchFunctions: switchFunctions, + device: device, + operationName: selectedOperationName ?? '', + ), + ), + ], + ), + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + DialogFooter( + onCancel: () { + Navigator.pop(context); + }, + onConfirm: state.addedFunctions.isNotEmpty + ? () { + /// add the functions to the routine bloc + // for (var function in state.addedFunctions) { + // context.read().add( + // AddFunctionToRoutine( + // function, + // uniqueCustomId, + // ), + // ); + // } + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + uniqueCustomId, + ), + ); + // Return the device data to be added to the container + Navigator.pop(context, { + 'deviceId': functions.first.deviceId, + }); + } + : null, + isConfirmEnabled: selectedFunction != null, + ), + ], ), - DialogFooter( - onCancel: () => Navigator.pop(context), - onConfirm: selectedFunctionNotifier.value != null && - selectedValueNotifier.value != null - ? () { - final selectedFn = switchFunctions.firstWhere( - (f) => f.code == selectedFunctionNotifier.value, - ); - final value = selectedValueNotifier.value; - final functionData = DeviceFunctionData( - entityId: selectedFn.deviceId, - functionCode: selectedFn.code, - operationName: selectedFn.operationName, - value: value, - condition: selectedConditionNotifier.value, - valueDescription: selectedFn - is ThreeGangCountdown1Function || - selectedFn - is ThreeGangCountdown2Function || - selectedFn - is ThreeGangCountdown3Function - ? '${value} sec' - : ((selectedFn as BaseSwitchFunction) - .getOperationalValues() - .firstWhere((v) => v.value == value) - .description), - ); - Navigator.pop( - context, {selectedFn.code: functionData}); - } - : null, - isConfirmEnabled: selectedFunctionNotifier.value != null, - ), - ], - ), + ); + }, ), - ); - }, - ); - }, - ).then((value) { - selectedFunctionNotifier.dispose(); - selectedValueNotifier.dispose(); - selectedConditionNotifier.dispose(); - selectedConditionsNotifier.dispose(); - return value; - }); - } - - static Widget _buildCountDownSelector( - BuildContext context, - ValueNotifier valueNotifier, - ValueNotifier conditionNotifier, - ValueNotifier> conditionsNotifier, - ) { - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, value, _) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ValueListenableBuilder>( - valueListenable: conditionsNotifier, - builder: (context, selectedConditions, _) { - return ToggleButtons( - onPressed: (int index) { - final newConditions = List.filled(3, false); - newConditions[index] = true; - conditionsNotifier.value = newConditions; - conditionNotifier.value = index == 0 - ? "<" - : index == 1 - ? "==" - : ">"; - }, - 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: selectedConditions, - children: const [Text("<"), Text("="), Text(">")], - ); - }, - ), - const SizedBox(height: 20), - Text( - '${value ?? 0} sec', - style: Theme.of(context).textTheme.headlineMedium, - ), - const SizedBox(height: 20), - Slider( - value: (value ?? 0).toDouble(), - min: 0, - max: 300, - divisions: 300, - onChanged: (newValue) { - valueNotifier.value = newValue.toInt(); - }, - ), - ], - ); + )); }, ); } - static Widget _buildOperationalValuesList( + static Widget _buildValueSelector({ + required BuildContext context, + required String selectedFunction, + required DeviceFunctionData? selectedFunctionData, + required List switchFunctions, + AllDevicesModel? device, + required String operationName, + }) { + if (selectedFunction == 'countdown_1' || + selectedFunction == 'countdown_2' || + selectedFunction == 'countdown_3') { + final initialValue = selectedFunctionData?.value ?? 200; + return _buildTemperatureSelector( + context: context, + initialValue: initialValue, + selectCode: selectedFunction, + currentCondition: selectedFunctionData?.condition, + device: device, + operationName: operationName, + selectedFunctionData: selectedFunctionData, + ); + } + + final selectedFn = + switchFunctions.firstWhere((f) => f.code == selectedFunction); + final values = selectedFn.getOperationalValues(); + + return _buildOperationalValuesList( + context: context, + values: values, + selectedValue: selectedFunctionData?.value, + device: device, + operationName: operationName, + selectCode: selectedFunction, + selectedFunctionData: selectedFunctionData, + ); + } + + static Widget _buildTemperatureSelector({ + required BuildContext context, + required dynamic initialValue, + required String? currentCondition, + required String selectCode, + AllDevicesModel? device, + required String operationName, + DeviceFunctionData? selectedFunctionData, + }) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), + const SizedBox(height: 20), + _buildCountDownDisplay(context, initialValue, device, operationName, + selectedFunctionData, selectCode), + const SizedBox(height: 20), + _buildCountDownSlider(context, initialValue, device, operationName, + selectedFunctionData, selectCode), + ], + ); + } + + /// Build condition toggle for AC functions dialog + static Widget _buildConditionToggle( BuildContext context, - BaseSwitchFunction function, - ValueNotifier valueNotifier, + String? currentCondition, + String selectCode, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + // Function(String) onConditionChanged, ) { - final values = function.getOperationalValues(); - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, selectedValue, _) { - return ListView.builder( - itemCount: values.length, - itemBuilder: (context, index) { - final value = values[index]; - return ListTile( - leading: SvgPicture.asset( - value.icon, - width: 24, - height: 24, - ), - title: Text( - value.description, - style: context.textTheme.bodyMedium, - ), - trailing: Radio( - value: value.value, - groupValue: selectedValue, - onChanged: (newValue) { - valueNotifier.value = newValue; - }, + final conditions = ["<", "==", ">"]; + + return ToggleButtons( + onPressed: (int index) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + condition: conditions[index], + value: selectedFunctionData?.value, + valueDescription: selectedFunctionData?.valueDescription, + ), ), ); + }, + borderRadius: const BorderRadius.all(Radius.circular(8)), + selectedBorderColor: ColorsManager.primaryColorWithOpacity, + selectedColor: Colors.white, + fillColor: ColorsManager.primaryColorWithOpacity, + color: ColorsManager.primaryColorWithOpacity, + constraints: const BoxConstraints( + minHeight: 40.0, + minWidth: 40.0, + ), + isSelected: + conditions.map((c) => c == (currentCondition ?? "==")).toList(), + children: conditions.map((c) => Text(c)).toList(), + ); + } + + /// Build temperature display for AC functions dialog + static Widget _buildCountDownDisplay( + BuildContext context, + dynamic initialValue, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + String selectCode) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '${initialValue ?? 0} sec', + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ); + } + + static Widget _buildCountDownSlider( + BuildContext context, + dynamic initialValue, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + String selectCode, + ) { + final operationalValues = SwitchOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ); + return Slider( + value: (initialValue ?? 0).toDouble(), + min: operationalValues.minValue?.toDouble() ?? 0.0, + max: operationalValues.maxValue?.toDouble() ?? 0.0, + divisions: (((operationalValues.maxValue ?? 0) - + (operationalValues.minValue ?? 0)) / + (operationalValues.stepValue ?? 1)) + .round(), + onChanged: (value) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value, + condition: selectedFunctionData?.condition, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + ); + } + + static Widget _buildOperationalValuesList({ + required BuildContext context, + required List values, + required dynamic selectedValue, + AllDevicesModel? device, + required String operationName, + required String selectCode, + DeviceFunctionData? selectedFunctionData, + }) { + return ListView.builder( + shrinkWrap: false, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: values.length, + itemBuilder: (context, index) { + final value = values[index]; + final isSelected = selectedValue == value.value; + return ListTile( + leading: SvgPicture.asset( + value.icon, + width: 24, + height: 24, + placeholderBuilder: (BuildContext context) => Container( + width: 24, + height: 24, + color: Colors.transparent, + ), + ), + title: Text( + value.description, + style: context.textTheme.bodyMedium, + ), + trailing: Icon( + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, + size: 24, + color: isSelected + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.textGray, + ), + onTap: () { + if (!isSelected) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value.value, + condition: selectedFunctionData?.condition, + valueDescription: + selectedFunctionData?.valueDescription, + ), + ), + ); + } }, ); }, diff --git a/lib/pages/routiens/helper/two_gang_switch_helper.dart b/lib/pages/routiens/helper/two_gang_switch_helper.dart index 038ee345..3805882a 100644 --- a/lib/pages/routiens/helper/two_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/two_gang_switch_helper.dart @@ -1,8 +1,12 @@ 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/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; +import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; -import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart'; +import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -10,62 +14,60 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class TwoGangSwitchHelper { static Future?> showSwitchFunctionsDialog( - BuildContext context, List> functions) async { - List> switchFunctions = functions - .where((f) => - f is TwoGangSwitch1Function || - f is TwoGangSwitch2Function || - f is TwoGangCountdown1Function || - f is TwoGangCountdown2Function) - .toList(); + BuildContext context, + List functions, + AllDevicesModel? device, + List? deviceSelectedFunctions, + String uniqueCustomId, + ) async { + List switchFunctions = + functions.whereType().toList(); - final selectedFunctionNotifier = ValueNotifier(null); - final selectedValueNotifier = ValueNotifier(null); - final selectedConditionNotifier = ValueNotifier('<'); - final selectedConditionsNotifier = - ValueNotifier>([true, false, false]); - - await showDialog( + return showDialog?>( context: context, builder: (BuildContext context) { - return ValueListenableBuilder( - valueListenable: selectedFunctionNotifier, - builder: (context, selectedFunction, _) { - return AlertDialog( + return BlocProvider( + create: (_) => FunctionBloc() + ..add(InitializeFunctions(deviceSelectedFunctions ?? [])), + child: AlertDialog( contentPadding: EdgeInsets.zero, - content: Container( - width: selectedFunction != null ? 600 : 300, - 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('2 Gangs Light Switch Condition'), - Expanded( - child: Row( - children: [ - // Left side: Function list - Expanded( - child: ListView.separated( - itemCount: switchFunctions.length, - separatorBuilder: (_, __) => const Divider( - color: ColorsManager.dividerColor, - ), - itemBuilder: (context, index) { - final function = switchFunctions[index]; - return ValueListenableBuilder( - valueListenable: selectedFunctionNotifier, - builder: (context, selectedFunction, _) { - final isSelected = - selectedFunction == function.code; + 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('2 Gangs Light Switch Condition'), + Expanded( + child: Row( + children: [ + // Left side: Function list + Expanded( + child: ListView.separated( + itemCount: switchFunctions.length, + separatorBuilder: (_, __) => const Divider( + color: ColorsManager.dividerColor, + ), + itemBuilder: (context, index) { + final function = switchFunctions[index]; return ListTile( - tileColor: isSelected - ? Colors.grey.shade100 - : null, leading: SvgPicture.asset( function.icon, width: 24, @@ -81,191 +83,307 @@ class TwoGangSwitchHelper { color: ColorsManager.textGray, ), onTap: () { - selectedFunctionNotifier.value = - function.code; - selectedValueNotifier.value = function - is TwoGangCountdown1Function || - function - is TwoGangCountdown2Function - ? 0 - : null; + context + .read() + .add(SelectFunction( + functionCode: function.code, + operationName: + function.operationName, + )); }, ); }, - ); - }, - ), - ), - // Right side: Value selector - if (selectedFunction != null) - Expanded( - child: ValueListenableBuilder( - valueListenable: selectedValueNotifier, - builder: (context, selectedValue, _) { - final selectedFn = switchFunctions.firstWhere( - (f) => f.code == selectedFunction, - ); - - if (selectedFn is TwoGangCountdown1Function || - selectedFn is TwoGangCountdown2Function) { - return _buildCountDownSelector( - context, - selectedValueNotifier, - selectedConditionNotifier, - selectedConditionsNotifier, - ); - } - - return _buildOperationalValuesList( - context, - selectedFn as BaseSwitchFunction, - selectedValueNotifier, - ); - }, + ), ), - ), - ], - ), + // Right side: Value selector + if (selectedFunction != null) + Expanded( + child: _buildValueSelector( + context: context, + selectedFunction: selectedFunction, + selectedFunctionData: selectedFunctionData, + switchFunctions: switchFunctions, + device: device, + operationName: selectedOperationName ?? '', + ), + ), + ], + ), + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + DialogFooter( + onCancel: () { + Navigator.pop(context); + }, + onConfirm: state.addedFunctions.isNotEmpty + ? () { + /// add the functions to the routine bloc + // for (var function in state.addedFunctions) { + // context.read().add( + // AddFunctionToRoutine( + // function, + // uniqueCustomId + // ), + // ); + // } + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + uniqueCustomId, + ), + ); + // Return the device data to be added to the container + Navigator.pop(context, { + 'deviceId': functions.first.deviceId, + }); + } + : null, + isConfirmEnabled: selectedFunction != null, + ), + ], ), - DialogFooter( - onCancel: () => Navigator.pop(context), - onConfirm: selectedFunction != null && - selectedValueNotifier.value != null - ? () { - final selectedFn = switchFunctions.firstWhere( - (f) => f.code == selectedFunction, - ); - final value = selectedValueNotifier.value; - final functionData = DeviceFunctionData( - entityId: selectedFn.deviceId, - functionCode: selectedFn.code, - operationName: selectedFn.operationName, - value: value, - condition: selectedConditionNotifier.value, - valueDescription: selectedFn - is TwoGangCountdown1Function || - selectedFn is TwoGangCountdown2Function - ? '${value} sec' - : ((selectedFn as BaseSwitchFunction) - .getOperationalValues() - .firstWhere((v) => v.value == value) - .description), - ); - Navigator.pop( - context, {selectedFn.code: functionData}); - } - : null, - isConfirmEnabled: selectedFunction != null, - ), - ], - ), - ), - ); - }, - ); - }, - ).then((value) { - selectedFunctionNotifier.dispose(); - selectedValueNotifier.dispose(); - selectedConditionNotifier.dispose(); - selectedConditionsNotifier.dispose(); - return value; - }); - } - - static Widget _buildOperationalValuesList( - BuildContext context, - BaseSwitchFunction function, - ValueNotifier valueNotifier, - ) { - final values = function.getOperationalValues(); - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, selectedValue, _) { - return ListView.builder( - itemCount: values.length, - itemBuilder: (context, index) { - final value = values[index]; - return ListTile( - leading: SvgPicture.asset( - value.icon, - width: 24, - height: 24, - ), - title: Text( - value.description, - style: context.textTheme.bodyMedium, - ), - trailing: Radio( - value: value.value, - groupValue: selectedValue, - onChanged: (newValue) { - valueNotifier.value = newValue; + ); }, ), - ); - }, - ); + )); }, ); } - static Widget _buildCountDownSelector( + static Widget _buildValueSelector({ + required BuildContext context, + required String selectedFunction, + required DeviceFunctionData? selectedFunctionData, + required List switchFunctions, + AllDevicesModel? device, + required String operationName, + }) { + if (selectedFunction == 'countdown_1' || + selectedFunction == 'countdown_2') { + final initialValue = selectedFunctionData?.value ?? 200; + return _buildTemperatureSelector( + context: context, + initialValue: initialValue, + selectCode: selectedFunction, + currentCondition: selectedFunctionData?.condition, + device: device, + operationName: operationName, + selectedFunctionData: selectedFunctionData, + ); + } + + final selectedFn = + switchFunctions.firstWhere((f) => f.code == selectedFunction); + final values = selectedFn.getOperationalValues(); + + return _buildOperationalValuesList( + context: context, + values: values, + selectedValue: selectedFunctionData?.value, + device: device, + operationName: operationName, + selectCode: selectedFunction, + selectedFunctionData: selectedFunctionData, + ); + } + + static Widget _buildTemperatureSelector({ + required BuildContext context, + required dynamic initialValue, + required String? currentCondition, + required String selectCode, + AllDevicesModel? device, + required String operationName, + DeviceFunctionData? selectedFunctionData, + }) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), + const SizedBox(height: 20), + _buildCountDownDisplay(context, initialValue, device, operationName, + selectedFunctionData, selectCode), + const SizedBox(height: 20), + _buildCountDownSlider(context, initialValue, device, operationName, + selectedFunctionData, selectCode), + ], + ); + } + + /// Build condition toggle for AC functions dialog + static Widget _buildConditionToggle( BuildContext context, - ValueNotifier valueNotifier, - ValueNotifier conditionNotifier, - ValueNotifier> conditionsNotifier, + String? currentCondition, + String selectCode, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + // Function(String) onConditionChanged, ) { - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, value, _) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ValueListenableBuilder>( - valueListenable: conditionsNotifier, - builder: (context, selectedConditions, _) { - return ToggleButtons( - onPressed: (int index) { - final newConditions = List.filled(3, false); - newConditions[index] = true; - conditionsNotifier.value = newConditions; - conditionNotifier.value = index == 0 - ? "<" - : index == 1 - ? "==" - : ">"; - }, - 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: selectedConditions, - children: const [Text("<"), Text("="), Text(">")], - ); - }, + final conditions = ["<", "==", ">"]; + + return ToggleButtons( + onPressed: (int index) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + condition: conditions[index], + value: selectedFunctionData?.value, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + borderRadius: const BorderRadius.all(Radius.circular(8)), + selectedBorderColor: ColorsManager.primaryColorWithOpacity, + selectedColor: Colors.white, + fillColor: ColorsManager.primaryColorWithOpacity, + color: ColorsManager.primaryColorWithOpacity, + constraints: const BoxConstraints( + minHeight: 40.0, + minWidth: 40.0, + ), + isSelected: + conditions.map((c) => c == (currentCondition ?? "==")).toList(), + children: conditions.map((c) => Text(c)).toList(), + ); + } + + /// Build temperature display for AC functions dialog + static Widget _buildCountDownDisplay( + BuildContext context, + dynamic initialValue, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + String selectCode) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '${initialValue ?? 0} sec', + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ); + } + + static Widget _buildCountDownSlider( + BuildContext context, + dynamic initialValue, + AllDevicesModel? device, + String operationName, + DeviceFunctionData? selectedFunctionData, + String selectCode, + ) { + final operationalValues = SwitchOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ); + return Slider( + value: (initialValue ?? 0).toDouble(), + min: operationalValues.minValue?.toDouble() ?? 0.0, + max: operationalValues.maxValue?.toDouble() ?? 0.0, + divisions: (((operationalValues.maxValue ?? 0) - + (operationalValues.minValue ?? 0)) / + (operationalValues.stepValue ?? 1)) + .round(), + onChanged: (value) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value, + condition: selectedFunctionData?.condition, + valueDescription: selectedFunctionData?.valueDescription, + ), + ), + ); + }, + ); + } + + static Widget _buildOperationalValuesList({ + required BuildContext context, + required List values, + required dynamic selectedValue, + AllDevicesModel? device, + required String operationName, + required String selectCode, + DeviceFunctionData? selectedFunctionData, + }) { + return ListView.builder( + shrinkWrap: false, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: values.length, + itemBuilder: (context, index) { + final value = values[index]; + final isSelected = selectedValue == value.value; + return ListTile( + leading: SvgPicture.asset( + value.icon, + width: 24, + height: 24, + placeholderBuilder: (BuildContext context) => Container( + width: 24, + height: 24, + color: Colors.transparent, ), - const SizedBox(height: 20), - Text( - '${value ?? 0} sec', - style: Theme.of(context).textTheme.headlineMedium, - ), - const SizedBox(height: 20), - Slider( - value: (value ?? 0).toDouble(), - min: 0, - max: 300, - divisions: 300, - onChanged: (newValue) { - valueNotifier.value = newValue.toInt(); - }, - ), - ], + ), + title: Text( + value.description, + style: context.textTheme.bodyMedium, + ), + trailing: Icon( + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, + size: 24, + color: isSelected + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.textGray, + ), + onTap: () { + if (!isSelected) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value.value, + condition: selectedFunctionData?.condition, + valueDescription: + selectedFunctionData?.valueDescription, + ), + ), + ); + } + }, ); }, ); diff --git a/lib/pages/routiens/models/device_functions.dart b/lib/pages/routiens/models/device_functions.dart index a1f5725e..59b63a4f 100644 --- a/lib/pages/routiens/models/device_functions.dart +++ b/lib/pages/routiens/models/device_functions.dart @@ -1,5 +1,3 @@ -import 'package:flutter/material.dart'; - abstract class DeviceFunction { final String deviceId; final String deviceName; @@ -24,7 +22,6 @@ class DeviceFunctionData { final dynamic value; final String? condition; final String? valueDescription; - final UniqueKey uniqueKey; DeviceFunctionData({ required this.entityId, @@ -34,7 +31,7 @@ class DeviceFunctionData { required this.value, this.condition, this.valueDescription, - }) : uniqueKey = UniqueKey(); + }); Map toJson() { return { diff --git a/lib/pages/routiens/models/routine_item.dart b/lib/pages/routiens/models/routine_item.dart index 33920634..08263d53 100644 --- a/lib/pages/routiens/models/routine_item.dart +++ b/lib/pages/routiens/models/routine_item.dart @@ -1,29 +1,30 @@ -import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +// import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; -class RoutineItem { - final AllDevicesModel device; - final String? function; - final dynamic value; +// class RoutineItem { +// final AllDevicesModel device; +// final String? function; +// final dynamic value; - RoutineItem({ - required this.device, - this.function, - this.value, - }); +// RoutineItem({ +// required this.device, +// this.function, +// this.value, +// }); - Map toMap() { - return { - 'device': device, - 'function': function, - 'value': value, - }; - } +// Map toMap() { +// return { +// 'device': device, +// 'function': function, +// 'value': value, +// }; +// } - factory RoutineItem.fromMap(Map map) { - return RoutineItem( - device: map['device'] as AllDevicesModel, - function: map['function'], - value: map['value'], - ); - } -} +// factory RoutineItem.fromMap(Map map) { +// return RoutineItem( +// device: map['device'] as AllDevicesModel, +// function: map['function'], +// value: map['value'], +// ); +// } +// } +// : uniqueCustomId = uniqueCustomId ?? const Uuid().v4() diff --git a/lib/pages/routiens/models/routine_model.dart b/lib/pages/routiens/models/routine_model.dart index 2db2247f..8035b044 100644 --- a/lib/pages/routiens/models/routine_model.dart +++ b/lib/pages/routiens/models/routine_model.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - class ScenesModel { final String id; final String name; @@ -7,11 +5,13 @@ class ScenesModel { final String type; final String? icon; - ScenesModel({required this.id, required this.name, required this.status, required this.type, this.icon}); - - factory ScenesModel.fromRawJson(String str) => ScenesModel.fromJson(json.decode(str)); - - String toRawJson() => json.encode(toJson()); + ScenesModel({ + required this.id, + required this.name, + required this.status, + required this.type, + this.icon, + }); factory ScenesModel.fromJson(Map json) => ScenesModel( id: json["id"], diff --git a/lib/pages/routiens/widgets/dragable_card.dart b/lib/pages/routiens/widgets/dragable_card.dart index 197ea0d2..2b25b9c4 100644 --- a/lib/pages/routiens/widgets/dragable_card.dart +++ b/lib/pages/routiens/widgets/dragable_card.dart @@ -22,9 +22,8 @@ class DraggableCard extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - final deviceFunctions = state.selectedFunctions - .where((f) => f.entityId == deviceData['deviceId']) - .toList(); + final deviceFunctions = + state.selectedFunctions[deviceData['uniqueCustomId']] ?? []; return Draggable>( data: deviceData, @@ -45,7 +44,7 @@ class DraggableCard extends StatelessWidget { color: ColorsManager.whiteColors, child: Container( padding: const EdgeInsets.all(8), - width: 90, + width: 110, height: deviceFunctions.isEmpty ? 123 : null, child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/routiens/widgets/routine_devices.dart b/lib/pages/routiens/widgets/routine_devices.dart index 20d47007..67d1dc85 100644 --- a/lib/pages/routiens/widgets/routine_devices.dart +++ b/lib/pages/routiens/widgets/routine_devices.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; +import 'package:uuid/uuid.dart'; class RoutineDevices extends StatelessWidget { const RoutineDevices({super.key}); diff --git a/lib/pages/routiens/widgets/then_container.dart b/lib/pages/routiens/widgets/then_container.dart index 9c868f09..82a5feea 100644 --- a/lib/pages/routiens/widgets/then_container.dart +++ b/lib/pages/routiens/widgets/then_container.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; +import 'package:uuid/uuid.dart'; class ThenContainer extends StatelessWidget { const ThenContainer({super.key}); @@ -15,34 +16,38 @@ class ThenContainer extends StatelessWidget { builder: (context, state) { return DragTarget>( builder: (context, candidateData, rejectedData) { - return Container( - padding: const EdgeInsets.all(16), - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('THEN', - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - const SizedBox(height: 16), - Wrap( - spacing: 8, - runSpacing: 8, - children: state.thenItems - .map((item) => DraggableCard( - // key: Key(item['key']!), - imagePath: item['imagePath']!, - title: item['title']!, - deviceData: item, - )) - .toList(), - ), - ], + return SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(16), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('THEN', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 16), + Wrap( + spacing: 8, + runSpacing: 8, + children: state.thenItems + .map((item) => DraggableCard( + // key: Key(item['key']!), + imagePath: item['imagePath']!, + title: item['title']!, + deviceData: item, + )) + .toList(), + ), + ], + ), ), ); }, onWillAccept: (data) => data != null, onAccept: (data) async { + final uniqueCustomId = const Uuid().v4(); + data['uniqueCustomId'] = uniqueCustomId; final result = await DeviceDialogHelper.showDeviceDialog(context, data); // if (result != null) { diff --git a/pubspec.lock b/pubspec.lock index 3e12d6c5..cfd8b310 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -137,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" fl_chart: dependency: "direct main" description: @@ -533,6 +549,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -589,6 +613,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c8f42ea5..3aa81860 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: flutter_dotenv: ^5.1.0 fl_chart: ^0.69.0 time_picker_spinner: ^1.0.0 + uuid: ^4.4.0 dev_dependencies: flutter_test: