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 1fac903e..f029677e 100644 --- a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart @@ -3,7 +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/gateway/gateway_dialog.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_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'; @@ -105,6 +105,7 @@ class DeviceDialogHelper { functions: functions, uniqueCustomId: data['uniqueCustomId'], deviceSelectedFunctions: deviceSelectedFunctions, + device: data['device'], ); default: diff --git a/lib/pages/routines/widgets/routine_dialog_function_list_tile.dart b/lib/pages/routines/widgets/routine_dialog_function_list_tile.dart new file mode 100644 index 00000000..3e98a06e --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialog_function_list_tile.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class RoutineDialogFunctionListTile extends StatelessWidget { + const RoutineDialogFunctionListTile({ + super.key, + required this.iconPath, + required this.operationName, + required this.onTap, + }); + + final String iconPath; + final String operationName; + final void Function() onTap; + + @override + Widget build(BuildContext context) { + return ListTile( + leading: SvgPicture.asset( + iconPath, + width: 24, + height: 24, + placeholderBuilder: (context) => const SizedBox( + width: 24, + height: 24, + ), + ), + title: Text( + operationName, + style: context.textTheme.bodyMedium, + ), + trailing: const Icon( + Icons.arrow_forward_ios, + size: 16, + color: ColorsManager.textGray, + ), + onTap: onTap, + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialog_selection_list_tile.dart b/lib/pages/routines/widgets/routine_dialog_selection_list_tile.dart new file mode 100644 index 00000000..b661e591 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialog_selection_list_tile.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class RoutineDialogSelectionListTile extends StatelessWidget { + const RoutineDialogSelectionListTile({ + required this.iconPath, + required this.description, + required this.isSelected, + required this.onTap, + super.key, + }); + + final bool isSelected; + final String iconPath; + final String description; + final void Function() onTap; + + @override + Widget build(BuildContext context) { + return ListTile( + leading: SvgPicture.asset( + iconPath, + width: 24, + height: 24, + placeholderBuilder: (context) => Container( + width: 24, + height: 24, + color: Colors.transparent, + ), + ), + title: Text( + 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: onTap, + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart index b9638cd9..fc7189f2 100644 --- a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart @@ -1,28 +1,133 @@ 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/routine_dialogs/gateway/gateway_if_dialog.dart'; +import 'package:syncrow_web/pages/routines/models/gateway.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/gateway/gateway_dialog_value_selector.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_functions_list.dart'; -final class GatewayHelper { - static Future?> showGatewayFunctionsDialog({ - required BuildContext context, - required List functions, - required String? uniqueCustomId, - required List deviceSelectedFunctions, - }) async { - return showDialog( - context: context, - builder: (context) => BlocProvider( - create: (context) => FunctionBloc() - ..add( - InitializeFunctions(deviceSelectedFunctions), - ), - child: GatewayIfDialog( - uniqueCustomId: uniqueCustomId, - functions: functions, - deviceSelectedFunctions: deviceSelectedFunctions), +class GatewayDialog extends StatefulWidget { + const GatewayDialog({ + required this.uniqueCustomId, + required this.functions, + required this.deviceSelectedFunctions, + required this.device, + super.key, + }); + + final String? uniqueCustomId; + final List functions; + final List deviceSelectedFunctions; + final AllDevicesModel? device; + + @override + State createState() => _GatewayDialogState(); +} + +class _GatewayDialogState extends State { + late final List _gatewayFunctions; + + @override + void initState() { + super.initState(); + _gatewayFunctions = widget.functions.whereType().toList(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: BlocBuilder( + builder: (context, state) { + final selectedFunction = state.selectedFunction; + 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('Gateway Conditions'), + Expanded(child: _buildMainContent(context, state)), + _buildDialogFooter(context, 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 selectedGatewayFunctions = _gatewayFunctions.firstWhere( + (f) => f.code == selectedFunction, + orElse: () => GatewaySwitchAlarmSound( + code: selectedFunction ?? '', + deviceId: '', + deviceName: '', + operationName: '', + icon: '', + type: '', + ), + ); + final operations = selectedGatewayFunctions.getOperationalValues(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + GatewayFunctionsList(gatewayFunctions: _gatewayFunctions), + if (state.selectedFunction != null) + Expanded( + child: GatewayDialogValueSelector( + operations: operations, + selectedFunction: selectedFunction ?? '', + selectedFunctionData: selectedFunctionData, + gatewayFunctions: _gatewayFunctions, + operationName: selectedOperationName ?? '', + device: widget.device, + ), + ), + ], + ); + } + + Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) { + return DialogFooter( + onCancel: () => Navigator.pop(context), + onConfirm: state.addedFunctions.isNotEmpty + ? () { + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + widget.uniqueCustomId ?? '-1', + ), + ); + Navigator.pop( + context, + {'deviceId': widget.functions.firstOrNull?.deviceId}, + ); + } + : null, + isConfirmEnabled: state.selectedFunction != null, + ); + } } diff --git a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog_value_selector.dart b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog_value_selector.dart new file mode 100644 index 00000000..392c3012 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_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/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/gateway.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart'; + +class GatewayDialogValueSelector extends StatelessWidget { + const GatewayDialogValueSelector({ + required this.operations, + required this.selectedFunction, + required this.selectedFunctionData, + required this.gatewayFunctions, + required this.device, + required this.operationName, + super.key, + }); + + final List operations; + final String selectedFunction; + final DeviceFunctionData? selectedFunctionData; + final List gatewayFunctions; + 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/gateway/gateway_functions_list.dart b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_functions_list.dart new file mode 100644 index 00000000..6d253dcb --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_functions_list.dart @@ -0,0 +1,43 @@ +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/gateway.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class GatewayFunctionsList extends StatelessWidget { + const GatewayFunctionsList({ + required this.gatewayFunctions, + super.key, + }); + + final List gatewayFunctions; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 360, + child: ListView.separated( + shrinkWrap: false, + itemCount: gatewayFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider(color: ColorsManager.dividerColor), + ), + itemBuilder: (context, index) { + final function = gatewayFunctions[index]; + return RoutineDialogFunctionListTile( + iconPath: function.icon, + operationName: function.operationName, + onTap: () => context.read().add( + SelectFunction( + functionCode: function.code, + operationName: function.operationName, + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart new file mode 100644 index 00000000..9a9351ca --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart @@ -0,0 +1,34 @@ +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/gateway/gateway_dialog.dart'; + +abstract final class GatewayHelper { + const GatewayHelper._(); + + static Future?> showGatewayFunctionsDialog({ + required BuildContext context, + required List functions, + required String? uniqueCustomId, + required List deviceSelectedFunctions, + required AllDevicesModel? device, + }) async { + return showDialog( + context: context, + builder: (context) => BlocProvider( + create: (context) => FunctionBloc() + ..add( + InitializeFunctions(deviceSelectedFunctions), + ), + child: GatewayDialog( + uniqueCustomId: uniqueCustomId, + functions: functions, + deviceSelectedFunctions: deviceSelectedFunctions, + device: device, + ), + ), + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_if_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_if_dialog.dart deleted file mode 100644 index 457110b7..00000000 --- a/lib/pages/routines/widgets/routine_dialogs/gateway/gateway_if_dialog.dart +++ /dev/null @@ -1,243 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; -import 'package:syncrow_web/pages/routines/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/gateway.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/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; - -class GatewayIfDialog extends StatefulWidget { - const GatewayIfDialog({ - required this.uniqueCustomId, - required this.functions, - required this.deviceSelectedFunctions, - super.key, - }); - - final String? uniqueCustomId; - final List functions; - final List deviceSelectedFunctions; - - @override - State createState() => _GatewayIfDialogState(); -} - -class _GatewayIfDialogState extends State { - late final List _gatewayFunctions; - - @override - void initState() { - super.initState(); - _gatewayFunctions = widget.functions.whereType().toList(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - contentPadding: EdgeInsets.zero, - content: BlocBuilder( - builder: (context, state) { - final selectedFunction = state.selectedFunction; - 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('Gateway Conditions'), - Expanded(child: _buildMainContent(context, state)), - _buildDialogFooter(context, state), - ], - ), - ); - }, - ), - ); - } - - Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) { - return DialogFooter( - onCancel: () => Navigator.pop(context), - onConfirm: state.addedFunctions.isNotEmpty - ? () { - context.read().add( - AddFunctionToRoutine( - state.addedFunctions, - widget.uniqueCustomId ?? '-1', - ), - ); - Navigator.pop( - context, - {'deviceId': widget.functions.firstOrNull?.deviceId}, - ); - } - : null, - isConfirmEnabled: state.selectedFunction != null, - ); - } - - 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, - ), - ); - - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _buildFunctionList(context), - if (state.selectedFunction != null) - Expanded( - child: _buildValueSelector( - context: context, - selectedFunction: selectedFunction ?? '', - selectedFunctionData: selectedFunctionData, - acFunctions: _gatewayFunctions, - operationName: selectedOperationName ?? '', - ), - ), - ], - ); - } - - Widget _buildFunctionList(BuildContext context) { - return SizedBox( - width: 360, - child: ListView.separated( - shrinkWrap: false, - itemCount: _gatewayFunctions.length, - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.symmetric(horizontal: 40.0), - child: Divider(color: ColorsManager.dividerColor), - ), - itemBuilder: (context, index) { - final function = _gatewayFunctions[index]; - return ListTile( - leading: SvgPicture.asset( - function.icon, - width: 24, - height: 24, - placeholderBuilder: (context) => const SizedBox( - width: 24, - height: 24, - ), - ), - title: Text( - function.operationName, - style: context.textTheme.bodyMedium, - ), - trailing: const Icon( - Icons.arrow_forward_ios, - size: 16, - color: ColorsManager.textGray, - ), - onTap: () => context.read().add( - SelectFunction( - functionCode: function.code, - operationName: function.operationName, - ), - ), - ); - }, - ), - ); - } - - static Widget _buildValueSelector({ - required BuildContext context, - required String selectedFunction, - required DeviceFunctionData? selectedFunctionData, - required List acFunctions, - AllDevicesModel? device, - required String operationName, - }) { - final selectedGatewayFunctions = acFunctions.firstWhere( - (f) => f.code == selectedFunction, - ); - final values = selectedGatewayFunctions.getOperationalValues(); - - return _buildOperationalValuesList( - context: context, - values: values, - selectedValue: selectedFunctionData?.value, - device: device, - operationName: operationName, - selectCode: selectedFunction, - selectedFunctionData: selectedFunctionData, - ); - } - - 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( - 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: (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, - ), - ), - ); - } - }, - ); - }, - ); - } -}