Compare commits

..

16 Commits

Author SHA1 Message Date
387ef99728 fix: handle nullable uuid in GatewayModel.fromJson 2025-04-09 12:33:20 +03:00
694a5a7dda Refactor GatewayModel.fromJson to use a loop for status parsing instead of firstWhere. 2025-04-09 12:32:44 +03:00
c90b9a1912 bugfix/ range index errorof automations in routines. 2025-04-09 12:24:59 +03:00
7860292276 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions 2025-04-09 12:19:53 +03:00
550acc4953 Merge pull request #137 from SyncrowIOT/fix-remove-option-from-then-dialog-routins
add dialogType to devices and add parameter in showSwitchFunctionsDialog
2025-04-09 12:18:33 +03:00
fd6b737556 Refactor one_gang_switch_dialog.dart to use one_gang_switch instead of ac in function names and variables 2025-04-09 12:17:26 +03:00
3d5825adca Merge branch 'SP-1366-FE-Add-Gateway-Device-Card-to-Devices-Section' of https://github.com/SyncrowIOT/web into SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions 2025-04-09 11:21:20 +03:00
9c97e2879a Refactor constructor syntax for Gateway functions to use initializer list 2025-04-09 10:57:22 +03:00
fda96025e9 uncommented code. 2025-04-09 10:41:38 +03:00
92aa574944 update GatewayMasterState.getOperationalValues to match what the api expects. 2025-04-09 10:21:49 +03:00
d08ab8caac add dialogType to devices and add parameter in showSwitchFunctionsDialog 2025-04-09 10:09:56 +03:00
774f21a55b Refactor RoutineDevices to consolidate device data preparation in a single map to remove code duplication. 2025-04-09 09:59:15 +03:00
9b69ec31e9 Refactor RoutineDevices to use a class-level constant for allowed product types. 2025-04-09 09:57:18 +03:00
a242377ea6 Fully refactored CreateRoutine/Gateway feature. 2025-04-09 09:46:45 +03:00
3a0c8edf86 SP-1365/ Gateway Functions Popup (for “Then” section). 2025-04-08 16:42:31 +03:00
970f7ed16f SP-1365 2025-04-08 16:42:12 +03:00
20 changed files with 624 additions and 460 deletions

View File

@ -248,12 +248,18 @@ SOS
switch (productType) { switch (productType) {
case 'AC': case 'AC':
return [ return [
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''), SwitchFunction(
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? ''), ModeFunction(
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? ''), TempSetFunction(
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
CurrentTempFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
LevelFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ChildLockFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
]; ];
case '1G': case '1G':
@ -276,17 +282,17 @@ SOS
case '3G': case '3G':
return [ return [
ThreeGangSwitch1Function( ThreeGangSwitch1Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch2Function( ThreeGangSwitch2Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch3Function( ThreeGangSwitch3Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown1Function( ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown2Function( ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown3Function( ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? ''), deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
]; ];
case 'WPS': case 'WPS':
return [ return [
@ -312,8 +318,6 @@ SOS
NoOneTimeFunction( NoOneTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
// FarDetectionSliderFunction(
// deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN')
]; ];
case 'GW': case 'GW':
return [ return [

View File

@ -14,27 +14,36 @@ class GatewayModel {
}); });
factory GatewayModel.fromJson(Map<String, dynamic> json) { factory GatewayModel.fromJson(Map<String, dynamic> json) {
final status = json['status'] as List<dynamic>; final status = json['status'] as List<dynamic>? ?? <dynamic>[];
final switchAlarmSound = status.firstWhere( bool? switchAlarmSound;
(item) => item['code'] == 'switch_alarm_sound', String? masterState;
)['value'] as bool; bool? factoryReset;
final masterState = status.firstWhere( String? alarmActive;
(item) => item['code'] == 'master_state',
)['value'] as String; for (final item in status) {
final factoryReset = status.firstWhere( switch (item['code']) {
(item) => item['code'] == 'factory_reset', case 'switch_alarm_sound':
)['value'] as bool; switchAlarmSound = item['value'] as bool;
final alarmActive = status.firstWhere( break;
(item) => item['code'] == 'alarm_active', case 'master_state':
)['value'] as String; masterState = item['value'] as String;
break;
case 'factory_reset':
factoryReset = item['value'] as bool;
break;
case 'alarm_active':
alarmActive = item['value'] as String;
break;
}
}
return GatewayModel( return GatewayModel(
uuid: json['uuid'] as String, uuid: json['uuid'] as String? ?? '',
switchAlarmSound: switchAlarmSound, switchAlarmSound: switchAlarmSound ?? false,
masterState: masterState, masterState: masterState ?? '',
factoryReset: factoryReset, factoryReset: factoryReset ?? false,
alarmActive: alarmActive, alarmActive: alarmActive ?? '',
); );
} }
} }

View File

@ -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/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.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/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/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/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
@ -50,46 +50,45 @@ class DeviceDialogHelper {
final deviceSelectedFunctions = final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
if (removeComparetors && data['productType'] != 'WPS') {
//remove the current temp function in the 'if container'
functions.removeAt(3);
}
switch (productType) { switch (productType) {
case 'AC': case 'AC':
return ACHelper.showACFunctionsDialog( return ACHelper.showACFunctionsDialog(
context, context: context,
functions, functions: functions,
data['device'], device: data['device'],
deviceSelectedFunctions, deviceSelectedFunctions: deviceSelectedFunctions,
data['uniqueCustomId'], uniqueCustomId: data['uniqueCustomId'],
removeComparetors, removeComparetors: removeComparetors,
dialogType: dialogType,
); );
case '1G': case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog( return OneGangSwitchHelper.showSwitchFunctionsDialog(
context, dialogType: dialogType,
functions, context: context,
data['device'], functions: functions,
deviceSelectedFunctions, device: data['device'],
data['uniqueCustomId'], deviceSelectedFunctions: deviceSelectedFunctions,
removeComparetors); uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case '2G': case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog( return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context, dialogType: dialogType,
functions, context: context,
data['device'], functions: functions,
deviceSelectedFunctions, device: data['device'],
data['uniqueCustomId'], deviceSelectedFunctions: deviceSelectedFunctions,
removeComparetors); uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case '3G': case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog( return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context, dialogType: dialogType,
functions, context: context,
data['device'], functions: functions,
deviceSelectedFunctions, device: data['device'],
data['uniqueCustomId'], deviceSelectedFunctions: deviceSelectedFunctions,
removeComparetors); uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case 'WPS': case 'WPS':
return WallPresenceSensor.showWPSFunctionsDialog( return WallPresenceSensor.showWPSFunctionsDialog(
dialogType: dialogType, dialogType: dialogType,
@ -101,11 +100,11 @@ class DeviceDialogHelper {
removeComparetors: removeComparetors); removeComparetors: removeComparetors);
case 'GW': case 'GW':
return GatewayHelper.showGatewayFunctionsDialog( return GatewayHelper.showGatewayFunctionsDialog(
dialogType: dialogType,
context: context, context: context,
functions: functions, functions: functions,
uniqueCustomId: data['uniqueCustomId'], uniqueCustomId: data['uniqueCustomId'],
deviceSelectedFunctions: deviceSelectedFunctions, deviceSelectedFunctions: deviceSelectedFunctions,
device: data['device'],
); );
default: default:

View File

@ -5,23 +5,28 @@ import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
abstract class ACFunction extends DeviceFunction<AcStatusModel> { abstract class ACFunction extends DeviceFunction<AcStatusModel> {
final String type;
ACFunction({ ACFunction({
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.code, required super.code,
required super.operationName, required super.operationName,
required super.icon, required super.icon,
required this.type,
}); });
List<ACOperationalValue> getOperationalValues(); List<ACOperationalValue> getOperationalValues();
} }
class SwitchFunction extends ACFunction { class SwitchFunction extends ACFunction {
SwitchFunction({required super.deviceId, required super.deviceName}) SwitchFunction(
{required super.deviceId, required super.deviceName, required type})
: super( : super(
code: 'switch', code: 'switch',
operationName: 'Power', operationName: 'Power',
icon: Assets.assetsAcPower, icon: Assets.assetsAcPower,
type: type,
); );
@override @override
@ -40,11 +45,13 @@ class SwitchFunction extends ACFunction {
} }
class ModeFunction extends ACFunction { class ModeFunction extends ACFunction {
ModeFunction({required super.deviceId, required super.deviceName}) ModeFunction(
{required super.deviceId, required super.deviceName, required type})
: super( : super(
code: 'mode', code: 'mode',
operationName: 'Mode', operationName: 'Mode',
icon: Assets.assetsFreezing, icon: Assets.assetsFreezing,
type: type,
); );
@override @override
@ -72,7 +79,8 @@ class TempSetFunction extends ACFunction {
final int max; final int max;
final int step; final int step;
TempSetFunction({required super.deviceId, required super.deviceName}) TempSetFunction(
{required super.deviceId, required super.deviceName, required type})
: min = 160, : min = 160,
max = 300, max = 300,
step = 1, step = 1,
@ -80,6 +88,7 @@ class TempSetFunction extends ACFunction {
code: 'temp_set', code: 'temp_set',
operationName: 'Set Temperature', operationName: 'Set Temperature',
icon: Assets.assetsTempreture, icon: Assets.assetsTempreture,
type: type,
); );
@override @override
@ -97,8 +106,10 @@ class TempSetFunction extends ACFunction {
} }
class LevelFunction extends ACFunction { class LevelFunction extends ACFunction {
LevelFunction({required super.deviceId, required super.deviceName}) LevelFunction(
{required super.deviceId, required super.deviceName, required type})
: super( : super(
type: type,
code: 'level', code: 'level',
operationName: 'Fan Speed', operationName: 'Fan Speed',
icon: Assets.assetsFanSpeed, icon: Assets.assetsFanSpeed,
@ -130,8 +141,10 @@ class LevelFunction extends ACFunction {
} }
class ChildLockFunction extends ACFunction { class ChildLockFunction extends ACFunction {
ChildLockFunction({required super.deviceId, required super.deviceName}) ChildLockFunction(
{required super.deviceId, required super.deviceName, required type})
: super( : super(
type: type,
code: 'child_lock', code: 'child_lock',
operationName: 'Child Lock', operationName: 'Child Lock',
icon: Assets.assetsChildLock, icon: Assets.assetsChildLock,
@ -157,11 +170,13 @@ class CurrentTempFunction extends ACFunction {
final int max; final int max;
final int step; final int step;
CurrentTempFunction({required super.deviceId, required super.deviceName}) CurrentTempFunction(
{required super.deviceId, required super.deviceName, required type})
: min = -100, : min = -100,
max = 990, max = 990,
step = 1, step = 1,
super( super(
type: type,
code: 'temp_current', code: 'temp_current',
operationName: 'Current Temperature', operationName: 'Current Temperature',
icon: Assets.currentTemp, icon: Assets.currentTemp,

View File

@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operation
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ThreeGangSwitch1Function extends BaseSwitchFunction { class ThreeGangSwitch1Function extends BaseSwitchFunction {
ThreeGangSwitch1Function({required super.deviceId, required super.deviceName}) ThreeGangSwitch1Function({required super.deviceId, required super.deviceName ,required type})
: super( : super(
code: 'switch_1', code: 'switch_1',
operationName: 'Light 1 Switch', operationName: 'Light 1 Switch',
@ -26,7 +26,7 @@ class ThreeGangSwitch1Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown1Function extends BaseSwitchFunction { class ThreeGangCountdown1Function extends BaseSwitchFunction {
ThreeGangCountdown1Function({required super.deviceId, required super.deviceName}) ThreeGangCountdown1Function({required super.deviceId, required super.deviceName ,required type})
: super( : super(
code: 'countdown_1', code: 'countdown_1',
operationName: 'Light 1 Countdown', operationName: 'Light 1 Countdown',
@ -47,7 +47,7 @@ class ThreeGangCountdown1Function extends BaseSwitchFunction {
} }
class ThreeGangSwitch2Function extends BaseSwitchFunction { class ThreeGangSwitch2Function extends BaseSwitchFunction {
ThreeGangSwitch2Function({required super.deviceId, required super.deviceName}) ThreeGangSwitch2Function({required super.deviceId, required super.deviceName, required type})
: super( : super(
code: 'switch_2', code: 'switch_2',
operationName: 'Light 2 Switch', operationName: 'Light 2 Switch',
@ -70,7 +70,7 @@ class ThreeGangSwitch2Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown2Function extends BaseSwitchFunction { class ThreeGangCountdown2Function extends BaseSwitchFunction {
ThreeGangCountdown2Function({required super.deviceId, required super.deviceName}) ThreeGangCountdown2Function({required super.deviceId, required super.deviceName ,required type})
: super( : super(
code: 'countdown_2', code: 'countdown_2',
operationName: 'Light 2 Countdown', operationName: 'Light 2 Countdown',
@ -91,7 +91,7 @@ class ThreeGangCountdown2Function extends BaseSwitchFunction {
} }
class ThreeGangSwitch3Function extends BaseSwitchFunction { class ThreeGangSwitch3Function extends BaseSwitchFunction {
ThreeGangSwitch3Function({required super.deviceId, required super.deviceName}) ThreeGangSwitch3Function({required super.deviceId, required super.deviceName ,required type})
: super( : super(
code: 'switch_3', code: 'switch_3',
operationName: 'Light 3 Switch', operationName: 'Light 3 Switch',
@ -114,7 +114,7 @@ class ThreeGangSwitch3Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown3Function extends BaseSwitchFunction { class ThreeGangCountdown3Function extends BaseSwitchFunction {
ThreeGangCountdown3Function({required super.deviceId, required super.deviceName}) ThreeGangCountdown3Function({required super.deviceId, required super.deviceName ,required type})
: super( : super(
code: 'countdown_3', code: 'countdown_3',
operationName: 'Light 3 Countdown', operationName: 'Light 3 Countdown',

View File

@ -33,10 +33,11 @@ final class GatewaySwitchAlarmSound extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
super.code = 'switch_alarm_sound', }) : super(
super.operationName = 'Switch Alarm Sound', code: 'switch_alarm_sound',
super.icon = Assets.activeBell, operationName: 'Switch Alarm Sound',
}); icon: Assets.activeBell,
);
@override @override
List<GatewayOperationalValue> getOperationalValues() => [ List<GatewayOperationalValue> getOperationalValues() => [
@ -58,10 +59,11 @@ final class GatewayMasterState extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
super.code = 'master_state', }) : super(
super.operationName = 'Master State', code: 'master_state',
super.icon = Assets.gear, operationName: 'Master State',
}); icon: Assets.gear,
);
@override @override
List<GatewayOperationalValue> getOperationalValues() { List<GatewayOperationalValue> getOperationalValues() {
@ -69,12 +71,12 @@ final class GatewayMasterState extends GatewayFunctions {
GatewayOperationalValue( GatewayOperationalValue(
icon: Assets.assetsAcPower, icon: Assets.assetsAcPower,
description: "Normal", description: "Normal",
value: 'Normal', value: 'normal',
), ),
GatewayOperationalValue( GatewayOperationalValue(
icon: Assets.assetsAcPowerOFF, icon: Assets.assetsAcPowerOFF,
description: "Alarm", description: "Alarm",
value: 'Alarm', value: 'alarm',
), ),
]; ];
} }
@ -85,10 +87,11 @@ final class GatewayFactoryReset extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
super.code = 'factory_reset', }) : super(
super.operationName = 'Factory Reset', code: 'factory_reset',
super.icon = Assets.factoryReset, operationName: 'Factory Reset',
}); icon: Assets.factoryReset,
);
@override @override
List<GatewayOperationalValue> getOperationalValues() { List<GatewayOperationalValue> getOperationalValues() {

View File

@ -183,7 +183,7 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
state.automations[index].id, state.automations[index].id,
cardType: 'automations', cardType: 'automations',
spaceName: spaceName:
state.scenes[index].spaceName, state.automations[index].spaceName,
onTap: () { onTap: () {
BlocProvider.of<RoutineBloc>(context) BlocProvider.of<RoutineBloc>(context)
.add( .add(

View File

@ -17,6 +17,8 @@ class _RoutineDevicesState extends State<RoutineDevices> {
context.read<RoutineBloc>().add(FetchDevicesInRoutine()); context.read<RoutineBloc>().add(FetchDevicesInRoutine());
} }
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
@ -31,47 +33,41 @@ class _RoutineDevicesState extends State<RoutineDevices> {
} }
}); });
final deviceList = state.devices.where((device) { final deviceList = state.devices
const allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'}; .where((device) => _allowedProductTypes.contains(device.productType))
return allowedProductTypes.contains(device.productType); .toList();
}).toList();
return Wrap( return Wrap(
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
children: deviceList.asMap().entries.map((entry) { children: deviceList.asMap().entries.map((entry) {
final device = entry.value; final device = entry.value;
final deviceData = {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
};
if (state.searchText != null && state.searchText!.isNotEmpty) { if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name! return device.name!
.toLowerCase() .toLowerCase()
.contains(state.searchText!.toLowerCase()) .contains(state.searchText!.toLowerCase())
? DraggableCard( ? DraggableCard(
imagePath: device.getDefaultIcon(device.productType), imagePath: deviceData['imagePath'] as String,
title: device.name ?? '', title: deviceData['title'] as String,
deviceData: { deviceData: deviceData,
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
) )
: const SizedBox.shrink(); : const SizedBox.shrink();
} else { } else {
return DraggableCard( return DraggableCard(
imagePath: device.getDefaultIcon(device.productType), imagePath: deviceData['imagePath'] as String,
title: device.name ?? '', title: deviceData['title'] as String,
deviceData: { deviceData: deviceData,
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
); );
} }
}).toList(), }).toList(),

View File

@ -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,
);
}
}

View File

@ -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,
);
}
}

View File

@ -13,29 +13,38 @@ 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/bloc/functions_bloc/functions_bloc_bloc.dart';
class ACHelper { class ACHelper {
static Future<Map<String, dynamic>?> showACFunctionsDialog( static Future<Map<String, dynamic>?> showACFunctionsDialog({
BuildContext context, required BuildContext context,
List<DeviceFunction> functions, required List<DeviceFunction> functions,
AllDevicesModel? device, required AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, required List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, required String uniqueCustomId,
bool? removeComparetors, required bool? removeComparetors,
) async { required String dialogType,
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList(); }) async {
List<ACFunction> acFunctions =
functions.whereType<ACFunction>().where((function) {
if (dialogType == 'THEN') {
return function.type == 'THEN' || function.type == 'BOTH';
}
return function.type == 'IF' || function.type == 'BOTH';
}).toList();
// List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])), create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = final selectedFunctionData = state.addedFunctions
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction, .firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -65,8 +74,10 @@ class ACHelper {
child: _buildFunctionsList( child: _buildFunctionsList(
context: context, context: context,
acFunctions: acFunctions, acFunctions: acFunctions,
onFunctionSelected: (functionCode, operationName) => onFunctionSelected:
context.read<FunctionBloc>().add(SelectFunction( (functionCode, operationName) => context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: functionCode, functionCode: functionCode,
operationName: operationName, operationName: operationName,
)), )),
@ -194,7 +205,8 @@ class ACHelper {
); );
} }
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction); final selectedFn =
acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -290,7 +302,8 @@ class ACHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 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(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -384,9 +397,13 @@ class ACHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray, color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -398,7 +415,8 @@ class ACHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription, valueDescription:
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -1,29 +1,130 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/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/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 { class GatewayDialog extends StatefulWidget {
static Future<Map<String, dynamic>?> showGatewayFunctionsDialog({ const GatewayDialog({
required String dialogType, required this.uniqueCustomId,
required BuildContext context, required this.functions,
required List<DeviceFunction> functions, required this.deviceSelectedFunctions,
required String? uniqueCustomId, required this.device,
required List<DeviceFunctionData> deviceSelectedFunctions, super.key,
}) async { });
return showDialog(
context: context, final String? uniqueCustomId;
builder: (context) => BlocProvider<FunctionBloc>( final List<DeviceFunction> functions;
create: (context) => FunctionBloc() final List<DeviceFunctionData> deviceSelectedFunctions;
..add( final AllDevicesModel? device;
InitializeFunctions(deviceSelectedFunctions),
), @override
child: GatewayIfDialog( State<GatewayDialog> createState() => _GatewayDialogState();
uniqueCustomId: uniqueCustomId, }
functions: functions,
deviceSelectedFunctions: deviceSelectedFunctions), class _GatewayDialogState extends State<GatewayDialog> {
late final List<GatewayFunctions> _gatewayFunctions;
@override
void initState() {
super.initState();
_gatewayFunctions = widget.functions.whereType<GatewayFunctions>().toList();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
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(
deviceId: '',
deviceName: '',
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<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
widget.uniqueCustomId ?? '-1',
),
);
Navigator.pop(
context,
{'deviceId': widget.functions.firstOrNull?.deviceId},
);
}
: null,
isConfirmEnabled: state.selectedFunction != null,
);
}
} }

View File

@ -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<GatewayOperationalValue> operations;
final String selectedFunction;
final DeviceFunctionData? selectedFunctionData;
final List<GatewayFunctions> 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<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectedFunction,
operationName: operationName,
value: operation.value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);
}
}

View File

@ -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> 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<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
);
},
),
);
}
}

View File

@ -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<Map<String, dynamic>?> showGatewayFunctionsDialog({
required BuildContext context,
required List<DeviceFunction> functions,
required String? uniqueCustomId,
required List<DeviceFunctionData> deviceSelectedFunctions,
required AllDevicesModel? device,
}) async {
return showDialog(
context: context,
builder: (context) => BlocProvider<FunctionBloc>(
create: (context) => FunctionBloc()
..add(
InitializeFunctions(deviceSelectedFunctions),
),
child: GatewayDialog(
uniqueCustomId: uniqueCustomId,
functions: functions,
deviceSelectedFunctions: deviceSelectedFunctions,
device: device,
),
),
);
}
}

View File

@ -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<DeviceFunction> functions;
final List<DeviceFunctionData> deviceSelectedFunctions;
@override
State<GatewayIfDialog> createState() => _GatewayIfDialogState();
}
class _GatewayIfDialogState extends State<GatewayIfDialog> {
late final List<GatewayFunctions> _gatewayFunctions;
@override
void initState() {
super.initState();
_gatewayFunctions = widget.functions.whereType<GatewayFunctions>().toList();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
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<RoutineBloc>().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<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
);
},
),
);
}
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<GatewayFunctions> 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<GatewayOperationalValue> 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<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);
}
}

View File

@ -5,8 +5,10 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/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/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/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_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
@ -14,29 +16,32 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
class OneGangSwitchHelper { class OneGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog( static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({
BuildContext context, required String dialogType,
List<DeviceFunction> functions, required BuildContext context,
AllDevicesModel? device, required List<DeviceFunction> functions,
List<DeviceFunctionData>? deviceSelectedFunctions, required AllDevicesModel? device,
String uniqueCustomId, required List<DeviceFunctionData>? deviceSelectedFunctions,
bool removeComparetors, required String uniqueCustomId,
) async { required bool removeComparetors,
List<BaseSwitchFunction> acFunctions = functions.whereType<BaseSwitchFunction>().toList(); }) async {
List<BaseSwitchFunction> oneGangFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])), create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = final selectedFunctionData = state.addedFunctions
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction, .firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -61,12 +66,12 @@ class OneGangSwitchHelper {
// Left side: Function list // Left side: Function list
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
itemCount: acFunctions.length, itemCount: oneGangFunctions.length,
separatorBuilder: (_, __) => const Divider( separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor, color: ColorsManager.dividerColor,
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final function = acFunctions[index]; final function = oneGangFunctions[index];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(
function.icon, function.icon,
@ -83,9 +88,12 @@ class OneGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context.read<FunctionBloc>().add(SelectFunction( context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: function.operationName, operationName:
function.operationName,
)); ));
}, },
); );
@ -99,7 +107,7 @@ class OneGangSwitchHelper {
context: context, context: context,
selectedFunction: selectedFunction, selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
acFunctions: acFunctions, acFunctions: oneGangFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparetors: removeComparetors, removeComparetors: removeComparetors,
@ -174,8 +182,14 @@ class OneGangSwitchHelper {
removeComparetors: removeComparetors, removeComparetors: removeComparetors,
); );
} }
final selectedFn = acFunctions.firstWhere(
(f) => f.code == selectedFunction,
orElse: () => OneGangSwitchFunction(
deviceId: '',
deviceName: '',
),
);
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -212,11 +226,11 @@ class OneGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay( _buildCountDownDisplay(context, initialValue, device, operationName,
context, initialValue, device, operationName, selectedFunctionData, selectCode), selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider( _buildCountDownSlider(context, initialValue, device, operationName,
context, initialValue, device, operationName, selectedFunctionData, selectCode), selectedFunctionData, selectCode),
], ],
); );
} }
@ -257,7 +271,8 @@ class OneGangSwitchHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 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(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -305,7 +320,8 @@ class OneGangSwitchHelper {
value: (initialValue ?? 0).toDouble(), value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0, min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / divisions: (((operationalValues.maxValue ?? 0) -
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -357,9 +373,13 @@ class OneGangSwitchHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray, color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -371,7 +391,8 @@ class OneGangSwitchHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription, valueDescription:
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -14,14 +14,15 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
class ThreeGangSwitchHelper { class ThreeGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog( static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({
BuildContext context, required BuildContext context,
List<DeviceFunction> functions, required List<DeviceFunction> functions,
AllDevicesModel? device, required AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, required List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, required String uniqueCustomId,
bool removeComparetors, required String dialogType,
) async { required bool removeComparetors,
}) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList(); functions.whereType<BaseSwitchFunction>().toList();

View File

@ -14,29 +14,32 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
class TwoGangSwitchHelper { class TwoGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog( static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({
BuildContext context, required BuildContext context,
List<DeviceFunction> functions, required List<DeviceFunction> functions,
AllDevicesModel? device, required AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, required List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, required String uniqueCustomId,
bool removeComparetors, required bool removeComparetors,
) async { required String dialogType,
List<BaseSwitchFunction> switchFunctions = functions.whereType<BaseSwitchFunction>().toList(); }) async {
List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])), create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = final selectedFunctionData = state.addedFunctions
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction, .firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -83,9 +86,12 @@ class TwoGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context.read<FunctionBloc>().add(SelectFunction( context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: function.operationName, operationName:
function.operationName,
)); ));
}, },
); );
@ -161,7 +167,8 @@ class TwoGangSwitchHelper {
required String operationName, required String operationName,
required bool removeComparetors, required bool removeComparetors,
}) { }) {
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') { if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2') {
final initialValue = selectedFunctionData?.value ?? 200; final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector( return _buildTemperatureSelector(
context: context, context: context,
@ -175,7 +182,8 @@ class TwoGangSwitchHelper {
); );
} }
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction); final selectedFn =
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -212,11 +220,11 @@ class TwoGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay( _buildCountDownDisplay(context, initialValue, device, operationName,
context, initialValue, device, operationName, selectedFunctionData, selectCode), selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider( _buildCountDownSlider(context, initialValue, device, operationName,
context, initialValue, device, operationName, selectedFunctionData, selectCode), selectedFunctionData, selectCode),
], ],
); );
} }
@ -257,7 +265,8 @@ class TwoGangSwitchHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 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(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -305,7 +314,8 @@ class TwoGangSwitchHelper {
value: (initialValue ?? 0).toDouble(), value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0, min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / divisions: (((operationalValues.maxValue ?? 0) -
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -357,9 +367,13 @@ class TwoGangSwitchHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray, color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -371,7 +385,8 @@ class TwoGangSwitchHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription, valueDescription:
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -113,7 +113,8 @@ class ThenContainer extends StatelessWidget {
'1G', '1G',
'2G', '2G',
'3G', '3G',
'WPS' 'WPS',
"GW",
].contains(state.thenItems[index] ].contains(state.thenItems[index]
['productType'])) { ['productType'])) {
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(
@ -229,7 +230,7 @@ class ThenContainer extends StatelessWidget {
dialogType: "THEN"); dialogType: "THEN");
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} else if (!['AC', '1G', '2G', '3G', 'WPS'] } else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
.contains(mutableData['productType'])) { .contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} }