push ac functions and gang switches functions

This commit is contained in:
ashrafzarkanisala
2024-11-21 00:50:06 +03:00
parent 57b8f6b03e
commit 4441878bdd
27 changed files with 1739 additions and 188 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"cSpell.words": [
"automations"
]
}

View File

@ -4,6 +4,9 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart'
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart'; import 'package:syncrow_web/utils/enum/device_types.dart';
@ -71,7 +74,6 @@ class AllDevicesModel {
int? batteryLevel; int? batteryLevel;
String? productName; String? productName;
List<DeviceSpaceModel>? spaces; List<DeviceSpaceModel>? spaces;
List<DeviceFunction>? _deviceFunctions;
AllDevicesModel({ AllDevicesModel({
this.room, this.room,
@ -104,8 +106,12 @@ class AllDevicesModel {
this.spaces, this.spaces,
}); });
AllDevicesModel.fromJson(Map<String, dynamic> json) { AllDevicesModel.fromJson(Map<String, dynamic> json) {
room = (json['room'] != null && (json['room'] is Map)) ? DevicesModelRoom.fromJson(json['room']) : null; room = (json['room'] != null && (json['room'] is Map))
unit = (json['unit'] != null && (json['unit'] is Map)) ? DevicesModelUnit.fromJson(json['unit']) : null; ? DevicesModelRoom.fromJson(json['room'])
: null;
unit = (json['unit'] != null && (json['unit'] is Map))
? DevicesModelUnit.fromJson(json['unit'])
: null;
community = (json['community'] != null && (json['community'] is Map)) community = (json['community'] != null && (json['community'] is Map))
? DeviceCommunityModel.fromJson(json['community']) ? DeviceCommunityModel.fromJson(json['community'])
: null; : null;
@ -134,7 +140,9 @@ class AllDevicesModel {
batteryLevel = int.tryParse(json['battery']?.toString() ?? ''); batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
productName = json['productName']?.toString(); productName = json['productName']?.toString();
if (json['spaces'] != null && json['spaces'] is List) { if (json['spaces'] != null && json['spaces'] is List) {
spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList(); spaces = (json['spaces'] as List)
.map((space) => DeviceSpaceModel.fromJson(space))
.toList();
} }
} }
@ -182,7 +190,8 @@ SOS
String tempIcon = ''; String tempIcon = '';
if (type == DeviceType.LightBulb) { if (type == DeviceType.LightBulb) {
tempIcon = Assets.lightBulb; tempIcon = Assets.lightBulb;
} else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) { } else if (type == DeviceType.CeilingSensor ||
type == DeviceType.WallSensor) {
tempIcon = Assets.sensors; tempIcon = Assets.sensors;
} else if (type == DeviceType.AC) { } else if (type == DeviceType.AC) {
tempIcon = Assets.ac; tempIcon = Assets.ac;
@ -218,11 +227,11 @@ SOS
return tempIcon; return tempIcon;
} }
List<DeviceFunction> get deviceFunctions { List<DeviceFunction> get functions {
_deviceFunctions ??= _getDeviceFunctions(); return _getDeviceFunctions();
return _deviceFunctions!;
} }
//! Functions for Devices Types
List<DeviceFunction> _getDeviceFunctions() { List<DeviceFunction> _getDeviceFunctions() {
switch (productType) { switch (productType) {
case 'AC': case 'AC':
@ -234,7 +243,38 @@ SOS
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''), ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
// other product types case '1G':
return [
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction(
deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '2G':
return [
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '3G':
return [
ThreeGangSwitch1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
];
default: default:
return []; return [];
} }

View File

@ -1,154 +1,39 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
mixin ACHelper { class ACHelper {
Future<Map<String, dynamic>?> showACFunctionsDialog(BuildContext context, List<DeviceFunction<dynamic>> functions) { static Future<Map<String, dynamic>?> showACFunctionsDialog(
BuildContext context,
List<DeviceFunction<dynamic>> functions,
) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList(); List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
String? selectedFunction; String? selectedFunction;
dynamic selectedValue; dynamic selectedValue = 20;
String? selectedCondition = "==";
List<bool> _selectedConditions = [false, true, false];
return showDialog( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return StatefulBuilder( return StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
return AlertDialog( return AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: Container( content: _buildDialogContent(
width: selectedFunction != null ? 600 : 360, context,
decoration: BoxDecoration( setState,
color: Colors.white, acFunctions,
borderRadius: BorderRadius.circular(20), selectedFunction,
), selectedValue,
padding: const EdgeInsets.only(top: 20), selectedCondition,
child: Column( _selectedConditions,
mainAxisSize: MainAxisSize.min, (fn) => selectedFunction = fn,
children: [ (val) => selectedValue = val,
Text( (cond) => selectedCondition = cond,
'AC Functions',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Flexible(
child: Row(
children: [
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: acFunctions.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
final function = acFunctions[index];
return ListTile(
leading: Image.asset(function.icon, width: 24, height: 24),
title: Text(function.operationName),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
setState(() {
selectedFunction = function.code;
selectedValue = null;
});
},
);
},
),
),
if (selectedFunction != null)
Container(
width: 1,
color: ColorsManager.greyColor,
),
if (selectedFunction != null)
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: acFunctions
.firstWhere((f) => f.code == selectedFunction)
.getOperationalValues()
.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
final operationalValue = acFunctions.firstWhere((f) => f.code == selectedFunction)
..getOperationalValues()[index];
return ListTile(
leading: Image.asset(operationalValue.icon, width: 24, height: 24),
title: Text(operationalValue.getOperationalValues()[index].description),
trailing: Radio<dynamic>(
value: operationalValue.getOperationalValues()[index].value,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value;
});
},
),
);
},
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: ColorsManager.greyColor),
),
),
),
Container(
height: 50,
width: 1,
color: ColorsManager.greyColor,
),
GestureDetector(
onTap: () {
// Handle the confirmation action here
Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
});
},
child: Center(
child: Text(
'Confirm',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
),
),
],
),
],
),
), ),
); );
}, },
@ -157,34 +42,370 @@ mixin ACHelper {
); );
} }
void handleACDeviceDrop(BuildContext context, Map<String, dynamic> data) { /// Build dialog content for AC functions dialog
final device = data['device'] as AllDevicesModel; static Widget _buildDialogContent(
final acFunctions = device.deviceFunctions; BuildContext context,
StateSetter setState,
showACFunctionsDialog(context, acFunctions).then((result) { List<ACFunction> acFunctions,
if (result != null) { String? selectedFunction,
_addACDeviceToRoutine(context, data, result); dynamic selectedValue,
} String? selectedCondition,
}); List<bool> selectedConditions,
Function(String?) onFunctionSelected,
Function(dynamic) onValueSelected,
Function(String?) onConditionSelected,
) {
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: [
_buildDialogHeader(context),
Flexible(
child: Row(
children: [
_buildFunctionsList(
context,
setState,
acFunctions,
selectedFunction,
onFunctionSelected,
),
if (selectedFunction != null)
_buildValueSelector(
context,
setState,
selectedFunction,
selectedValue,
selectedCondition,
selectedConditions,
onValueSelected,
onConditionSelected,
acFunctions,
),
],
),
),
_buildDialogFooter(
context,
selectedFunction,
selectedValue,
selectedCondition,
),
],
),
);
} }
void handleNonACDeviceDrop(BuildContext context, Map<String, dynamic> data) { /// Build header for AC functions dialog
context.read<RoutineBloc>().add(AddToThenContainer(data)); static Widget _buildDialogHeader(BuildContext context) {
return Column(
children: [
Text(
'AC Condition',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
],
);
} }
void _addACDeviceToRoutine(BuildContext context, Map<String, dynamic> deviceData, Map<String, dynamic> functionData) { /// Build functions list for AC functions dialog
final updatedData = { static Widget _buildFunctionsList(
...deviceData, BuildContext context,
'function': functionData['function'], StateSetter setState,
'value': functionData['value'], List<ACFunction> acFunctions,
}; String? selectedFunction,
Function(String?) onFunctionSelected,
context.read<RoutineBloc>().add(AddToThenContainer(updatedData)); ) {
return Expanded(
_logACFunctionSelection(functionData); child: ListView.separated(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: acFunctions.length,
separatorBuilder: (context, index) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = acFunctions[index];
return ListTile(
leading: SvgPicture.asset(
function.icon,
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: () => setState(() => onFunctionSelected(function.code)),
);
},
),
);
} }
void _logACFunctionSelection(Map<String, dynamic> functionData) { /// Build value selector for AC functions dialog
print('Selected AC function: ${functionData['function']}, Value: ${functionData['value']}'); static Widget _buildValueSelector(
BuildContext context,
StateSetter setState,
String selectedFunction,
dynamic selectedValue,
String? selectedCondition,
List<bool> selectedConditions,
Function(dynamic) onValueSelected,
Function(String?) onConditionSelected,
List<ACFunction> acFunctions,
) {
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
return Expanded(
child: _buildTemperatureSelector(
context,
setState,
selectedValue,
selectedCondition,
selectedConditions,
onValueSelected,
onConditionSelected,
),
);
}
final selectedFn =
acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return Expanded(
child: _buildOperationalValuesList(
context,
setState,
values,
selectedValue,
onValueSelected,
),
);
}
/// Build temperature selector for AC functions dialog
static Widget _buildTemperatureSelector(
BuildContext context,
StateSetter setState,
dynamic selectedValue,
String? selectedCondition,
List<bool> selectedConditions,
Function(dynamic) onValueSelected,
Function(String?) onConditionSelected,
) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildConditionToggle(
context,
setState,
selectedConditions,
onConditionSelected,
),
const SizedBox(height: 20),
_buildTemperatureDisplay(context, selectedValue),
const SizedBox(height: 20),
_buildTemperatureSlider(
context,
setState,
selectedValue,
onValueSelected,
),
],
);
}
/// Build condition toggle for AC functions dialog
static Widget _buildConditionToggle(
BuildContext context,
StateSetter setState,
List<bool> selectedConditions,
Function(String?) onConditionSelected,
) {
return ToggleButtons(
onPressed: (int index) {
setState(() {
for (int i = 0; i < selectedConditions.length; i++) {
selectedConditions[i] = i == index;
}
onConditionSelected(index == 0
? "<"
: index == 1
? "=="
: ">");
});
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected: selectedConditions,
children: const [Text("<"), Text("="), Text(">")],
);
}
/// Build temperature display for AC functions dialog
static Widget _buildTemperatureDisplay(
BuildContext context, dynamic selectedValue) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'${selectedValue ?? 20}°C',
style: context.textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
);
}
static Widget _buildTemperatureSlider(
BuildContext context,
StateSetter setState,
dynamic selectedValue,
Function(dynamic) onValueSelected,
) {
final currentValue = selectedValue is int ? selectedValue.toDouble() : 20.0;
return Slider(
value: currentValue,
min: 16,
max: 30,
divisions: 14,
label: '${currentValue.toInt()}°C',
onChanged: (value) {
setState(() => onValueSelected(value.toInt()));
},
);
}
static Widget _buildOperationalValuesList(
BuildContext context,
StateSetter setState,
List<dynamic> values,
dynamic selectedValue,
Function(dynamic) onValueSelected,
) {
return ListView.builder(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
setState(() => onValueSelected(newValue));
},
),
);
},
);
}
static Widget _buildDialogFooter(
BuildContext context,
String? selectedFunction,
dynamic selectedValue,
String? selectedCondition,
) {
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: ColorsManager.greyColor,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildFooterButton(
context,
'Cancel',
selectedFunction != null ? 299 : 179,
() => Navigator.pop(context),
),
_buildFooterButton(
context,
'Confirm',
selectedFunction != null ? 299 : 179,
selectedFunction != null && selectedValue != null
? () => Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
'condition': selectedCondition ?? "==",
})
: null,
),
],
),
);
}
static Widget _buildFooterButton(
BuildContext context,
String text,
double width,
VoidCallback? onTap,
) {
return GestureDetector(
onTap: onTap,
child: SizedBox(
height: 50,
width: width,
child: Center(
child: Text(
text,
style: context.textTheme.bodyMedium!.copyWith(
color: onTap != null
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
),
),
),
);
} }
} }

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/helper/ac_helper.dart';
import 'package:syncrow_web/pages/routiens/helper/one_gang_switch_helper.dart';
import 'package:syncrow_web/pages/routiens/helper/three_gang_switch_helper.dart';
import 'package:syncrow_web/pages/routiens/helper/two_gang_switch_helper.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context,
Map<String, dynamic> data,
) async {
final functions = data['functions'] as List<DeviceFunction>;
try {
final result = await _getDialogForDeviceType(
context,
data['productType'],
functions,
);
if (result != null) {
return {...data, ...result};
}
} catch (e) {
debugPrint('Error: $e');
}
return null;
}
static Future<Map<String, dynamic>?> _getDialogForDeviceType(
BuildContext context,
String productType,
List<DeviceFunction> functions,
) async {
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(context, functions);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
default:
return null;
}
}
}

View File

@ -0,0 +1,206 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class OneGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where(
(f) => f is OneGangSwitchFunction || f is OneGangCountdownFunction)
.toList();
String? selectedFunction;
dynamic selectedValue;
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: 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: [
Text(
'1 Gang Light Switch Condition',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Flexible(
child: Row(
children: [
Expanded(
child: ListView.separated(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: switchFunctions.length,
separatorBuilder: (context, index) =>
const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ListTile(
leading: SvgPicture.asset(
function.icon,
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: () {
setState(() {
selectedFunction = function.code;
selectedValue = null;
});
},
);
},
),
),
if (selectedFunction != null)
Expanded(
child: Builder(
builder: (context) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction)
as BaseSwitchFunction;
final values =
selectedFn.getOperationalValues();
return ListView.builder(
shrinkWrap: false,
physics:
const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
setState(() {
selectedValue = newValue;
});
},
),
);
},
);
},
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
height: 50,
width: selectedFunction != null ? 299 : 179,
decoration: const BoxDecoration(
border: Border(
right:
BorderSide(color: ColorsManager.greyColor),
),
),
child: Center(
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: ColorsManager.greyColor),
),
),
),
),
GestureDetector(
onTap: () {
if (selectedFunction != null &&
selectedValue != null) {
Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
});
}
},
child: SizedBox(
height: 50,
width: selectedFunction != null ? 299 : 179,
child: Center(
child: Text(
'Confirm',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.primaryColorWithOpacity,
),
),
),
),
),
],
),
],
),
),
);
},
);
},
);
}
}

View File

@ -0,0 +1,211 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class ThreeGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where((f) =>
f is ThreeGangSwitch1Function ||
f is ThreeGangSwitch2Function ||
f is ThreeGangSwitch3Function ||
f is ThreeGangCountdown1Function ||
f is ThreeGangCountdown2Function ||
f is ThreeGangCountdown3Function)
.toList();
String? selectedFunction;
dynamic selectedValue;
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: 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: [
Text(
'3 Gangs Light Switch Condition',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Flexible(
child: Row(
children: [
Expanded(
child: ListView.separated(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: switchFunctions.length,
separatorBuilder: (context, index) =>
const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ListTile(
leading: SvgPicture.asset(
function.icon,
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: () {
setState(() {
selectedFunction = function.code;
selectedValue = null;
});
},
);
},
),
),
if (selectedFunction != null)
Expanded(
child: Builder(
builder: (context) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction)
as BaseSwitchFunction;
final values =
selectedFn.getOperationalValues();
return ListView.builder(
shrinkWrap: false,
physics:
const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
setState(() {
selectedValue = newValue;
});
},
),
);
},
);
},
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
height: 50,
width: selectedFunction != null ? 299 : 179,
decoration: const BoxDecoration(
border: Border(
right:
BorderSide(color: ColorsManager.greyColor),
),
),
child: Center(
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: ColorsManager.greyColor),
),
),
),
),
GestureDetector(
onTap: () {
if (selectedFunction != null &&
selectedValue != null) {
Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
});
}
},
child: SizedBox(
height: 50,
width: selectedFunction != null ? 299 : 179,
child: Center(
child: Text(
'Confirm',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.primaryColorWithOpacity,
),
),
),
),
),
],
),
],
),
),
);
},
);
},
);
}
}

View File

@ -0,0 +1,209 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class TwoGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where((f) =>
f is TwoGangSwitch1Function ||
f is TwoGangSwitch2Function ||
f is TwoGangCountdown1Function ||
f is TwoGangCountdown2Function)
.toList();
String? selectedFunction;
dynamic selectedValue;
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: 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: [
Text(
'2 Gangs Light Switch Condition',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Flexible(
child: Row(
children: [
Expanded(
child: ListView.separated(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: switchFunctions.length,
separatorBuilder: (context, index) =>
const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ListTile(
leading: SvgPicture.asset(
function.icon,
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: () {
setState(() {
selectedFunction = function.code;
selectedValue = null;
});
},
);
},
),
),
if (selectedFunction != null)
Expanded(
child: Builder(
builder: (context) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction)
as BaseSwitchFunction;
final values =
selectedFn.getOperationalValues();
return ListView.builder(
shrinkWrap: false,
physics:
const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
setState(() {
selectedValue = newValue;
});
},
),
);
},
);
},
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
height: 50,
width: selectedFunction != null ? 299 : 179,
decoration: const BoxDecoration(
border: Border(
right:
BorderSide(color: ColorsManager.greyColor),
),
),
child: Center(
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: ColorsManager.greyColor),
),
),
),
),
GestureDetector(
onTap: () {
if (selectedFunction != null &&
selectedValue != null) {
Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
});
}
},
child: SizedBox(
height: 50,
width: selectedFunction != null ? 299 : 179,
child: Center(
child: Text(
'Confirm',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.primaryColorWithOpacity,
),
),
),
),
),
],
),
],
),
),
);
},
);
},
);
}
}

View File

@ -0,0 +1,17 @@
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
abstract class BaseSwitchFunction extends DeviceFunction<bool> {
BaseSwitchFunction({
required super.deviceId,
required super.deviceName,
required super.code,
required super.operationName,
required super.icon,
});
@override
bool execute(bool currentStatus, dynamic newValue);
List<SwitchOperationalValue> getOperationalValues();
}

View File

@ -0,0 +1,57 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class OneGangSwitchFunction extends BaseSwitchFunction {
OneGangSwitchFunction({required super.deviceId, required super.deviceName})
: super(
code: 'switch_1',
operationName: 'Light Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class OneGangCountdownFunction extends BaseSwitchFunction {
OneGangCountdownFunction({required super.deviceId, required super.deviceName})
: super(
code: 'countdown_1',
operationName: 'Light Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}

View File

@ -0,0 +1,17 @@
class SwitchOperationalValue {
final String icon;
final String description;
final dynamic value;
final double? minValue;
final double? maxValue;
final double? stepValue;
SwitchOperationalValue({
required this.icon,
required this.value,
this.description = '',
this.minValue,
this.maxValue,
this.stepValue,
});
}

View File

@ -0,0 +1,168 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class ThreeGangSwitch1Function extends BaseSwitchFunction {
ThreeGangSwitch1Function({required super.deviceId, required super.deviceName})
: super(
code: 'switch_1',
operationName: 'Light 1 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class ThreeGangCountdown1Function extends BaseSwitchFunction {
ThreeGangCountdown1Function(
{required super.deviceId, required super.deviceName})
: super(
code: 'countdown_1',
operationName: 'Light 1 Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}
class ThreeGangSwitch2Function extends BaseSwitchFunction {
ThreeGangSwitch2Function({required super.deviceId, required super.deviceName})
: super(
code: 'switch_2',
operationName: 'Light 2 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class ThreeGangCountdown2Function extends BaseSwitchFunction {
ThreeGangCountdown2Function(
{required super.deviceId, required super.deviceName})
: super(
code: 'countdown_2',
operationName: 'Light 2 Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}
class ThreeGangSwitch3Function extends BaseSwitchFunction {
ThreeGangSwitch3Function({required super.deviceId, required super.deviceName})
: super(
code: 'switch_3',
operationName: 'Light 3 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class ThreeGangCountdown3Function extends BaseSwitchFunction {
ThreeGangCountdown3Function(
{required super.deviceId, required super.deviceName})
: super(
code: 'countdown_3',
operationName: 'Light 3 Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}

View File

@ -0,0 +1,115 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class TwoGangSwitch1Function extends BaseSwitchFunction {
TwoGangSwitch1Function({required super.deviceId, required super.deviceName})
: super(
code: 'switch_1',
operationName: 'Light 1 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class TwoGangSwitch2Function extends BaseSwitchFunction {
TwoGangSwitch2Function({required String deviceId, required String deviceName})
: super(
deviceId: deviceId,
deviceName: deviceName,
code: 'switch_2',
operationName: 'Light 2 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SwitchOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class TwoGangCountdown1Function extends BaseSwitchFunction {
TwoGangCountdown1Function(
{required super.deviceId, required super.deviceName})
: super(
code: 'countdown_1',
operationName: 'Light 1 Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}
class TwoGangCountdown2Function extends BaseSwitchFunction {
TwoGangCountdown2Function(
{required super.deviceId, required super.deviceName})
: super(
code: 'countdown_2',
operationName: 'Light 2 Countdown',
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
];
}

View File

@ -11,6 +11,7 @@ class DraggableCard extends StatelessWidget {
this.titleColor, this.titleColor,
this.isDragged = false, this.isDragged = false,
this.isDisabled = false, this.isDisabled = false,
this.deviceData,
}); });
final String imagePath; final String imagePath;
@ -18,11 +19,17 @@ class DraggableCard extends StatelessWidget {
final Color? titleColor; final Color? titleColor;
final bool isDragged; final bool isDragged;
final bool isDisabled; final bool isDisabled;
final Map<String, dynamic>? deviceData;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget card = Draggable<Map<String, String>>( Widget card = Draggable<Map<String, dynamic>>(
data: {'key': UniqueKey().toString(), 'imagePath': imagePath, 'title': title}, data: deviceData ??
{
'key': UniqueKey().toString(),
'imagePath': imagePath,
'title': title,
},
feedback: Transform.rotate( feedback: Transform.rotate(
angle: -0.1, angle: -0.1,
child: _buildCardContent(context), child: _buildCardContent(context),

View File

@ -1,18 +1,17 @@
// lib/pages/routiens/widgets/if_container.dart
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/routiens/bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
class IfContainer extends StatelessWidget { class IfContainer extends StatelessWidget {
const IfContainer({Key? key}) : super(key: key); const IfContainer({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
return DragTarget<Map<String, String>>( return DragTarget<Map<String, dynamic>>(
builder: (context, candidateData, rejectedData) { builder: (context, candidateData, rejectedData) {
return Container( return Container(
width: double.infinity, width: double.infinity,
@ -20,7 +19,9 @@ class IfContainer extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const Text('IF',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
Wrap( Wrap(
spacing: 8, spacing: 8,
@ -37,8 +38,16 @@ class IfContainer extends StatelessWidget {
), ),
); );
}, },
onAccept: (data) { onWillAccept: (data) => data != null,
context.read<RoutineBloc>().add(AddToIfContainer(data)); onAccept: (data) async {
final result =
await DeviceDialogHelper.showDeviceDialog(context, data);
if (result != null) {
context.read<RoutineBloc>().add(AddToIfContainer(result));
} else if (!['AC', '1G', '2G', '3G']
.contains(data['productType'])) {
context.read<RoutineBloc>().add(AddToIfContainer(data));
}
}, },
); );
}, },

View File

@ -29,11 +29,18 @@ class RoutineDevices extends StatelessWidget {
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
children: deviceList.asMap().entries.map((entry) { children: deviceList.asMap().entries.map((entry) {
final index = entry.key;
final device = entry.value; final device = entry.value;
return DraggableCard( return DraggableCard(
imagePath: device.getDefaultIcon(device.productType), imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '', title: device.name ?? '',
deviceData: {
'key': UniqueKey().toString(),
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
},
); );
}).toList(), }).toList(),
); );

View File

@ -14,10 +14,10 @@ class ScenesAndAutomations extends StatelessWidget {
return BlocProvider( return BlocProvider(
create: (context) => RoutineBloc() create: (context) => RoutineBloc()
..add( ..add(
LoadScenes(spaceId), const LoadScenes(spaceId),
) )
..add( ..add(
LoadAutomation(spaceId), const LoadAutomation(spaceId),
), ),
child: BlocBuilder<RoutineBloc, RoutineState>( child: BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
@ -27,11 +27,10 @@ class ScenesAndAutomations extends StatelessWidget {
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
children: scenes.asMap().entries.map((entry) { children: scenes.asMap().entries.map((entry) {
final index = entry.key;
final scene = entry.value; final scene = entry.value;
return DraggableCard( return DraggableCard(
imagePath: Assets.logo, imagePath: Assets.logo,
title: scene.name ?? '', title: scene.name,
); );
}).toList(), }).toList(),
); );

View File

@ -3,16 +3,17 @@
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/routiens/bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
class ThenContainer extends StatelessWidget { class ThenContainer extends StatelessWidget {
const ThenContainer({Key? key}) : super(key: key); const ThenContainer({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
return DragTarget<Map<String, String>>( return DragTarget<Map<String, dynamic>>(
builder: (context, candidateData, rejectedData) { builder: (context, candidateData, rejectedData) {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@ -20,7 +21,9 @@ class ThenContainer extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const Text('THEN',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
Wrap( Wrap(
spacing: 8, spacing: 8,
@ -37,8 +40,16 @@ class ThenContainer extends StatelessWidget {
), ),
); );
}, },
onAccept: (data) { onWillAccept: (data) => data != null,
context.read<RoutineBloc>().add(AddToThenContainer(data)); onAccept: (data) async {
final result =
await DeviceDialogHelper.showDeviceDialog(context, data);
if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(result));
} else if (!['AC', '1G', '2G', '3G']
.contains(data['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(data));
}
}, },
); );
}, },

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict>
<key>DEC061F9-9521-4D0C-959C-43A07F62CC12</key>
<dict>
<key>className</key>
<string>IDECommandLineBuildLog</string>
<key>documentTypeString</key>
<string>&lt;nil&gt;</string>
<key>domainType</key>
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
<key>fileName</key>
<string>DEC061F9-9521-4D0C-959C-43A07F62CC12.xcactivitylog</string>
<key>hasPrimaryLog</key>
<true/>
<key>primaryObservable</key>
<dict>
<key>highLevelStatus</key>
<string>S</string>
<key>totalNumberOfAnalyzerIssues</key>
<integer>0</integer>
<key>totalNumberOfErrors</key>
<integer>0</integer>
<key>totalNumberOfTestFailures</key>
<integer>0</integer>
<key>totalNumberOfWarnings</key>
<integer>0</integer>
</dict>
<key>schemeIdentifier-containerName</key>
<string>Runner project</string>
<key>schemeIdentifier-schemeName</key>
<string>Flutter Assemble</string>
<key>schemeIdentifier-sharedScheme</key>
<integer>1</integer>
<key>signature</key>
<string>Cleaning workspace Runner with scheme Flutter Assemble</string>
<key>timeStartedRecording</key>
<real>752000674.27645695</real>
<key>timeStoppedRecording</key>
<real>752000674.42918503</real>
<key>title</key>
<string>Cleaning workspace Runner with scheme Flutter Assemble</string>
<key>uniqueIdentifier</key>
<string>DEC061F9-9521-4D0C-959C-43A07F62CC12</string>
</dict>
<key>FB42CDDD-C79D-4D4B-891A-12C476DFCB10</key>
<dict>
<key>className</key>
<string>IDECommandLineBuildLog</string>
<key>documentTypeString</key>
<string>&lt;nil&gt;</string>
<key>domainType</key>
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
<key>fileName</key>
<string>FB42CDDD-C79D-4D4B-891A-12C476DFCB10.xcactivitylog</string>
<key>hasPrimaryLog</key>
<true/>
<key>primaryObservable</key>
<dict>
<key>highLevelStatus</key>
<string>S</string>
<key>totalNumberOfAnalyzerIssues</key>
<integer>0</integer>
<key>totalNumberOfErrors</key>
<integer>0</integer>
<key>totalNumberOfTestFailures</key>
<integer>0</integer>
<key>totalNumberOfWarnings</key>
<integer>0</integer>
</dict>
<key>schemeIdentifier-containerName</key>
<string>Runner project</string>
<key>schemeIdentifier-schemeName</key>
<string>Runner</string>
<key>schemeIdentifier-sharedScheme</key>
<integer>1</integer>
<key>signature</key>
<string>Cleaning workspace Runner with scheme Runner</string>
<key>timeStartedRecording</key>
<real>752000674.90370798</real>
<key>timeStoppedRecording</key>
<real>752000675.05962098</real>
<key>title</key>
<string>Cleaning workspace Runner with scheme Runner</string>
<key>uniqueIdentifier</key>
<string>FB42CDDD-C79D-4D4B-891A-12C476DFCB10</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict>
<key>DEC061F9-9521-4D0C-959C-43A07F62CC12</key>
<dict>
<key>className</key>
<string>IDECommandLineBuildLog</string>
<key>documentTypeString</key>
<string>&lt;nil&gt;</string>
<key>domainType</key>
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
<key>fileName</key>
<string>DEC061F9-9521-4D0C-959C-43A07F62CC12.xcactivitylog</string>
<key>hasPrimaryLog</key>
<true/>
<key>primaryObservable</key>
<dict>
<key>highLevelStatus</key>
<string>S</string>
<key>totalNumberOfAnalyzerIssues</key>
<integer>0</integer>
<key>totalNumberOfErrors</key>
<integer>0</integer>
<key>totalNumberOfTestFailures</key>
<integer>0</integer>
<key>totalNumberOfWarnings</key>
<integer>0</integer>
</dict>
<key>schemeIdentifier-containerName</key>
<string>Runner project</string>
<key>schemeIdentifier-schemeName</key>
<string>Flutter Assemble</string>
<key>schemeIdentifier-sharedScheme</key>
<integer>1</integer>
<key>signature</key>
<string>Cleaning workspace Runner with scheme Flutter Assemble</string>
<key>timeStartedRecording</key>
<real>752000674.27645695</real>
<key>timeStoppedRecording</key>
<real>752000674.42918503</real>
<key>title</key>
<string>Cleaning workspace Runner with scheme Flutter Assemble</string>
<key>uniqueIdentifier</key>
<string>DEC061F9-9521-4D0C-959C-43A07F62CC12</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict/>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict/>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict/>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>logFormatVersion</key>
<integer>11</integer>
<key>logs</key>
<dict/>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LastAccessedDate</key>
<date>2024-10-30T17:04:35Z</date>
<key>WorkspacePath</key>
<string>/Users/akmz/Developer/web/syncrow-web/web/macos/Runner.xcworkspace</string>
</dict>
</plist>

View File

@ -76,6 +76,7 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/icons/automation_functions/ - assets/icons/automation_functions/
- assets/icons/functions_icons/
- assets/icons/routine/ - assets/icons/routine/
- assets/icons/ - assets/icons/
- assets/images/ - assets/images/