Merge pull request #155 from SyncrowIOT/SP-1441-rework-FE-On-routine-creation-page-When-the-user-drags-a-card-that-has-signs-and-selects-a-sign-without-a-number-then-confirms-the-value-appears-to-be-Null

Sp 1441 rework fe on routine creation page when the user drags a card that has signs and selects a sign without a number then confirms the value appears to be null
This commit is contained in:
Faris Armoush
2025-04-24 16:25:39 +03:00
committed by GitHub
8 changed files with 212 additions and 87 deletions

View File

@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -74,11 +75,26 @@ class ACHelper {
child: _buildFunctionsList(
context: context,
acFunctions: acFunctions,
onFunctionSelected: (functionCode, operationName) =>
context.read<FunctionBloc>().add(SelectFunction(
functionCode: functionCode,
operationName: operationName,
)),
device: device,
onFunctionSelected: (functionCode, operationName) {
RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: functionCode,
functionOperationName: operationName,
functionValueDescription:
selectedFunctionData.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'temp_set',
'temp_current',
],
defaultValue: functionCode == 'temp_set'
? 200
: functionCode == 'temp_current'
? -100
: 0,
);
},
),
),
// Value selector
@ -137,6 +153,7 @@ class ACHelper {
required BuildContext context,
required List<ACFunction> acFunctions,
required Function(String, String) onFunctionSelected,
required AllDevicesModel? device,
}) {
return ListView.separated(
shrinkWrap: false,

View File

@ -131,7 +131,12 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CpsFunctionsList(cpsFunctions: _cpsFunctions),
CpsFunctionsList(
cpsFunctions: _cpsFunctions,
device: widget.device,
selectedFunctionData: selectedFunctionData,
dialogType: widget.dialogType,
),
if (state.selectedFunction != null)
Expanded(
child: isToggleFunction

View File

@ -1,14 +1,24 @@
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/device_managment/all_devices/models/devices_model.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_function_list_tile.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CpsFunctionsList extends StatelessWidget {
const CpsFunctionsList({required this.cpsFunctions, super.key});
const CpsFunctionsList({
required this.cpsFunctions,
required this.device,
required this.selectedFunctionData,
required this.dialogType,
super.key,
});
final List<CpsFunctions> cpsFunctions;
final AllDevicesModel? device;
final DeviceFunctionData? selectedFunctionData;
final String dialogType;
@override
Widget build(BuildContext context) {
@ -26,12 +36,27 @@ class CpsFunctionsList extends StatelessWidget {
return RoutineDialogFunctionListTile(
iconPath: function.icon,
operationName: function.operationName,
onTap: () => context.read<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
onTap: () => RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: function.code,
functionOperationName: function.operationName,
functionValueDescription: selectedFunctionData?.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'static_max_dis',
'presence_reference',
'moving_reference',
'perceptual_boundary',
'moving_boundary',
'moving_rigger_time',
'moving_static_time',
'none_body_time',
'moving_max_dis',
'moving_range',
'presence_range',
if (dialogType == "IF") 'sensitivity',
],
),
);
},
),

View File

@ -0,0 +1,46 @@
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/device_functions.dart';
abstract final class RoutineTapFunctionHelper {
const RoutineTapFunctionHelper._();
static void onTapFunction(
BuildContext context, {
required String functionCode,
required String functionOperationName,
required String? functionValueDescription,
required String? deviceUuid,
required List<String> codesToAddIntoFunctionsWithDefaultValue,
int defaultValue = 0,
}) {
final functionsBloc = context.read<FunctionBloc>();
functionsBloc.add(
SelectFunction(
functionCode: functionCode,
operationName: functionOperationName,
),
);
final addedFunctions = functionsBloc.state.addedFunctions;
final isFunctionAlreadyAdded = addedFunctions.any(
(e) => e.functionCode == functionCode,
);
final shouldAddFunction =
codesToAddIntoFunctionsWithDefaultValue.contains(functionCode);
if (!isFunctionAlreadyAdded && shouldAddFunction) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: deviceUuid ?? '',
functionCode: functionCode,
operationName: functionOperationName,
value: defaultValue,
condition: '==',
valueDescription: functionValueDescription,
),
),
);
}
}
}

View File

@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_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/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -24,21 +25,23 @@ class OneGangSwitchHelper {
required String uniqueCustomId,
required bool removeComparetors,
}) async {
List<BaseSwitchFunction> oneGangFunctions = functions.whereType<BaseSwitchFunction>().toList();
List<BaseSwitchFunction> oneGangFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return BlocProvider(
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData =
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
@ -84,12 +87,19 @@ class OneGangSwitchHelper {
size: 16,
color: ColorsManager.textGray,
),
onTap: () {
context.read<FunctionBloc>().add(SelectFunction(
functionCode: function.code,
operationName: function.operationName,
));
},
onTap: () =>
RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: function.code,
functionOperationName:
function.operationName,
functionValueDescription:
selectedFunctionData.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'countdown_1',
],
),
);
},
),
@ -220,11 +230,11 @@ class OneGangSwitchHelper {
selectedFunctionData,
),
const SizedBox(height: 20),
_buildCountDownDisplay(
context, initialValue, device, operationName, selectedFunctionData, selectCode),
_buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildCountDownSlider(
context, initialValue, device, operationName, selectedFunctionData, selectCode),
_buildCountDownSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
}
@ -314,9 +324,10 @@ class OneGangSwitchHelper {
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(),
divisions:
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
@ -368,7 +379,9 @@ class OneGangSwitchHelper {
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_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/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -85,15 +86,21 @@ class ThreeGangSwitchHelper {
size: 16,
color: ColorsManager.textGray,
),
onTap: () {
context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
));
},
onTap: () =>
RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: function.code,
functionOperationName:
function.operationName,
functionValueDescription:
selectedFunctionData.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'countdown_1',
'countdown_2',
'countdown_3',
],
),
);
},
),
@ -183,8 +190,7 @@ class ThreeGangSwitchHelper {
);
}
final selectedFn =
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
@ -266,8 +272,7 @@ class ThreeGangSwitchHelper {
minHeight: 40.0,
minWidth: 40.0,
),
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
}
@ -316,10 +321,10 @@ class ThreeGangSwitchHelper {
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(),
divisions:
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
@ -369,9 +374,7 @@ class ThreeGangSwitchHelper {
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
@ -387,8 +390,7 @@ class ThreeGangSwitchHelper {
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_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/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -85,15 +86,20 @@ class TwoGangSwitchHelper {
size: 16,
color: ColorsManager.textGray,
),
onTap: () {
context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
));
},
onTap: () =>
RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: function.code,
functionOperationName:
function.operationName,
functionValueDescription:
selectedFunctionData.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'countdown_1',
'countdown_2',
],
),
);
},
),
@ -167,8 +173,7 @@ class TwoGangSwitchHelper {
required String operationName,
required bool removeComparetors,
}) {
if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2') {
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
final initialValue = selectedFunctionData?.value ?? 0;
return _buildTemperatureSelector(
context: context,
@ -182,8 +187,7 @@ class TwoGangSwitchHelper {
);
}
final selectedFn =
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
@ -265,8 +269,7 @@ class TwoGangSwitchHelper {
minHeight: 40.0,
minWidth: 40.0,
),
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
}
@ -315,10 +318,10 @@ class TwoGangSwitchHelper {
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(),
divisions:
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
@ -368,9 +371,7 @@ class TwoGangSwitchHelper {
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
@ -386,8 +387,7 @@ class TwoGangSwitchHelper {
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);

View File

@ -8,6 +8,7 @@ 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/dialog_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
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';
@ -111,13 +112,23 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildFunctionList(context),
_buildFunctionList(context, state),
if (state.selectedFunction != null) _buildValueSelector(context, state),
],
);
}
Widget _buildFunctionList(BuildContext context) {
Widget _buildFunctionList(BuildContext context, FunctionBlocState state) {
final selectedFunction = state.selectedFunction;
final selectedFunctionData = state.addedFunctions.firstWhere(
(f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
),
);
return SizedBox(
width: 360,
child: ListView.separated(
@ -149,12 +160,18 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
size: 16,
color: ColorsManager.textGray,
),
onTap: () => context.read<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
onTap: () => RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: function.code,
functionOperationName: function.operationName,
functionValueDescription: selectedFunctionData.valueDescription,
deviceUuid: widget.device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'dis_current',
'presence_time',
'illuminance_value',
],
),
);
},
),