Merge pull request #53 from SyncrowIOT/routines

Routines
This commit is contained in:
Abdullah
2024-11-28 11:53:43 +03:00
committed by GitHub
27 changed files with 1281 additions and 503 deletions

View File

@ -18,13 +18,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
),
BlocProvider( BlocProvider(
create: (context) => create: (context) =>
SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)), SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
), ),
BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
),
], ],
child: WebScaffold( child: WebScaffold(
appBarTitle: FittedBox( appBarTitle: FittedBox(
@ -101,12 +101,11 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
builder: (context, deviceState) { builder: (context, deviceState) {
if (deviceState is DeviceManagementLoading) { if (deviceState is DeviceManagementLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (deviceState is DeviceManagementLoaded || } else if (deviceState is DeviceManagementLoaded) {
deviceState is DeviceManagementFiltered) { return DeviceManagementBody(devices: deviceState.devices);
final devices = (deviceState as dynamic).devices ?? } else if (deviceState is DeviceManagementFiltered) {
(deviceState as DeviceManagementFiltered).filteredDevices; return DeviceManagementBody(
devices: deviceState.filteredDevices);
return DeviceManagementBody(devices: devices);
} else { } else {
return const Center(child: Text('Error fetching Devices')); return const Center(child: Text('Error fetching Devices'));
} }

View File

@ -18,14 +18,6 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
final TextEditingController unitNameController = TextEditingController(); final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController(); final TextEditingController productNameController = TextEditingController();
@override
void dispose() {
communityController.dispose();
unitNameController.dispose();
productNameController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return isExtraLargeScreenSize(context) return isExtraLargeScreenSize(context)

View File

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.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/routine_details_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart'; import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
@ -13,6 +14,7 @@ part 'routine_event.dart';
part 'routine_state.dart'; part 'routine_state.dart';
const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
const communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> { class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
RoutineBloc() : super(const RoutineState()) { RoutineBloc() : super(const RoutineState()) {
@ -30,6 +32,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<CreateAutomationEvent>(_onCreateAutomation); on<CreateAutomationEvent>(_onCreateAutomation);
on<SetRoutineName>(_onSetRoutineName); on<SetRoutineName>(_onSetRoutineName);
on<ResetRoutineState>(_onResetRoutineState); on<ResetRoutineState>(_onResetRoutineState);
on<GetSceneDetails>(_onGetSceneDetails);
on<GetAutomationDetails>(_onGetAutomationDetails);
// on<InitializeRoutineState>(_onInitializeRoutineState);
on<DeleteScene>(_deleteScene);
on<DeleteAutomation>(_deleteAutomation);
// on<RemoveFunction>(_onRemoveFunction); // on<RemoveFunction>(_onRemoveFunction);
// on<ClearFunctions>(_onClearFunctions); // on<ClearFunctions>(_onClearFunctions);
} }
@ -38,8 +45,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems); final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = updatedIfItems.indexWhere(
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
updatedIfItems[index] = event.item; updatedIfItems[index] = event.item;
@ -48,18 +55,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
if (event.isTabToRun) { if (event.isTabToRun) {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else { } else {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
} }
} }
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) { void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems); final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = currentItems.indexWhere(
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
currentItems[index] = event.item; currentItems[index] = event.item;
@ -70,22 +80,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems)); emit(state.copyWith(thenItems: currentItems));
} }
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) { void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try { try {
if (event.functions.isEmpty) return; if (event.functions.isEmpty) return;
List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions); List<DeviceFunctionData> selectedFunction =
List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions = Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
List<DeviceFunctionData> currentFunctions = List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); List<DeviceFunctionData>.from(
currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = []; List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) { for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) { for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) { if (selectedFunction[i].functionCode ==
currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i]; currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) { if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode); functionCode.add(currentFunctions[j].functionCode);
@ -95,13 +109,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
for (int i = 0; i < functionCode.length; i++) { for (int i = 0; i < functionCode.length; i++) {
selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); selectedFunction
.removeWhere((code) => code.functionCode == functionCode[i]);
} }
currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) currentSelectedFunctions[event.uniqueCustomId] =
..addAll(selectedFunction); List.from(currentFunctions)..addAll(selectedFunction);
} else { } else {
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions); currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
} }
emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
@ -110,11 +126,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async { Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
final scenes = await SceneApi.getScenesByUnitId(event.unitId); final scenes =
await SceneApi.getScenesByUnitId(event.unitId, event.communityId);
emit(state.copyWith( emit(state.copyWith(
scenes: scenes, scenes: scenes,
isLoading: false, isLoading: false,
@ -129,7 +147,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async { Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
@ -157,14 +176,16 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async { FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null)); emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query)); emit(state.copyWith(searchText: event.query));
} }
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) { FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon)); emit(state.copyWith(selectedIcon: event.icon));
} }
@ -173,7 +194,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.first['deviceId'] == 'delay'; return actions.first['deviceId'] == 'delay';
} }
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
try { try {
// Check if first action is delay // Check if first action is delay
if (_isFirstActionDelay(state.thenItems)) { if (_isFirstActionDelay(state.thenItems)) {
@ -233,7 +255,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createScene(createSceneModel); final result = await SceneApi.createScene(createSceneModel);
if (result['success']) { if (result['success']) {
emit(_resetState()); emit(_resetState());
add(const LoadScenes(spaceId)); add(const LoadScenes(spaceId, communityId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -248,7 +270,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try { try {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith( emit(state.copyWith(
@ -348,21 +371,34 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) { FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) { if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems); final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index); thenItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); emit(state.copyWith(
thenItems: thenItems, selectedFunctions: selectedFunctions));
} else { } else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems); final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index); ifItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); if (ifItems.isEmpty && state.thenItems.isEmpty) {
emit(state.copyWith(
ifItems: ifItems,
selectedFunctions: selectedFunctions,
isAutomation: false,
isTabToRun: false));
} else {
emit(state.copyWith(
ifItems: ifItems, selectedFunctions: selectedFunctions));
}
} }
} }
@ -373,14 +409,134 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
)); ));
} }
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) { FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime)); emit(state.copyWith(effectiveTime: event.effectiveTime));
} }
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) { FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith(routineName: event.name)); emit(state.copyWith(routineName: event.name));
} }
Future<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> emit) async {
try {
emit(state.copyWith(
isLoading: true,
isTabToRun: event.isTabToRun,
isUpdate: true,
sceneId: event.sceneId,
isAutomation: false));
final sceneDetails = await SceneApi.getSceneDetails(event.sceneId);
add(InitializeRoutineState(sceneDetails));
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Failed to load scene details',
));
}
}
Future<void> _onGetAutomationDetails(
GetAutomationDetails event, Emitter<RoutineState> emit) async {
try {
emit(state.copyWith(
isLoading: true,
isAutomation: event.isAutomation,
automationId: event.automationId,
isTabToRun: false,
isUpdate: true,
));
final automationDetails =
await SceneApi.getAutomationDetails(event.automationId);
add(InitializeRoutineState(automationDetails));
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Failed to load automation details',
));
}
}
// void _onInitializeRoutineState(
// InitializeRoutineState event, Emitter<RoutineState> emit) {
// final routineDetails = event.routineDetails;
// // Convert actions to draggable cards for the THEN container
// final thenItems = routineDetails.actions.map((action) {
// final Map<String, dynamic> cardData = {
// 'entityId': action.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId':
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
// 'title': action.actionExecutor == 'delay' ? 'Delay' : 'Device',
// // fix this
// 'imagePath':
// action.actionExecutor == 'delay' ? Assets.delay : Assets.logo,
// };
// // Add functions to selectedFunctions
// if (action.executorProperty != null) {
// final functions = <DeviceFunctionData>[
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: action.executorProperty!.functionCode ?? '',
// value: action.executorProperty!.functionValue,
// /// fix this
// operationName: action.executorProperty?.functionCode ?? ''),
// ];
// state.selectedFunctions[cardData['uniqueCustomId']] = functions;
// }
// return cardData;
// }).toList();
// // Convert conditions to draggable cards for the IF container
// final ifItems = routineDetails.conditions?.map((condition) {
// final Map<String, dynamic> cardData = {
// 'entityId': condition.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': condition.entityId,
// /// fix this
// 'title': 'Device',
// /// fix this
// 'imagePath': Assets.logo,
// };
// // Add functions to selectedFunctions
// final functions = <DeviceFunctionData>[
// DeviceFunctionData(
// entityId: condition.entityId,
// functionCode: condition.expr.statusCode,
// value: condition.expr.statusValue,
// condition: condition.expr.comparator,
// operationName: condition.expr.comparator,
// ),
// ];
// state.selectedFunctions[cardData['uniqueCustomId']] = functions;
// return cardData;
// }).toList() ??
// [];
// emit(state.copyWith(
// isLoading: false,
// routineName: routineDetails.name,
// selectedIcon: routineDetails.iconId,
// selectedAutomationOperator: routineDetails.decisionExpr,
// effectiveTime: routineDetails.effectiveTime,
// isAutomation: routineDetails.conditions != null,
// isTabToRun: routineDetails.conditions == null,
// thenItems: thenItems,
// ifItems: ifItems,
// selectedFunctions: Map.from(state.selectedFunctions),
// ));
// }
RoutineState _resetState() { RoutineState _resetState() {
return const RoutineState( return const RoutineState(
ifItems: [], ifItems: [],
@ -396,13 +552,44 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
selectedIcon: null, selectedIcon: null,
isTabToRun: false, isTabToRun: false,
isAutomation: false, isAutomation: false,
selectedAutomationOperator: 'AND', selectedAutomationOperator: 'or',
effectiveTime: null, effectiveTime: null,
routineName: null, routineName: null,
); );
} }
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) { FutureOr<void> _onResetRoutineState(
ResetRoutineState event, Emitter<RoutineState> emit) {
emit(_resetState()); emit(_resetState());
} }
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
try {
// emit(state.copyWith(isLoading: true));
SceneApi.deleteScene(unitUuid: spaceId, sceneId: event.sceneId);
add(const LoadScenes(spaceId, communityId));
//emit(_resetState());
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Failed to delete scene',
));
}
}
FutureOr<void> _deleteAutomation(
DeleteAutomation event, Emitter<RoutineState> emit) {
try {
//emit(state.copyWith(isLoading: true));
SceneApi.deleteAutomation(
unitUuid: spaceId, automationId: event.automationId);
add(const LoadAutomation(spaceId));
// emit(_resetState());
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Failed to delete automation',
));
}
}
} }

View File

@ -28,11 +28,12 @@ class AddToThenContainer extends RoutineEvent {
class LoadScenes extends RoutineEvent { class LoadScenes extends RoutineEvent {
final String unitId; final String unitId;
final String communityId;
const LoadScenes(this.unitId); const LoadScenes(this.unitId, this.communityId);
@override @override
List<Object> get props => [unitId]; List<Object> get props => [unitId, communityId];
} }
class LoadAutomation extends RoutineEvent { class LoadAutomation extends RoutineEvent {
@ -124,6 +125,55 @@ class SetRoutineName extends RoutineEvent {
List<Object> get props => [name]; List<Object> get props => [name];
} }
class GetSceneDetails extends RoutineEvent {
final String sceneId;
final bool isUpdate;
final bool isTabToRun;
const GetSceneDetails({
required this.sceneId,
required this.isUpdate,
required this.isTabToRun,
});
@override
List<Object> get props => [sceneId];
}
class GetAutomationDetails extends RoutineEvent {
final String automationId;
final bool isUpdate;
final bool isAutomation;
const GetAutomationDetails({
required this.automationId,
this.isUpdate = false,
this.isAutomation = false,
});
@override
List<Object> get props => [automationId];
}
class InitializeRoutineState extends RoutineEvent {
final RoutineDetailsModel routineDetails;
const InitializeRoutineState(this.routineDetails);
@override
List<Object> get props => [routineDetails];
}
class DeleteScene extends RoutineEvent {
final String sceneId;
final String unitUuid;
const DeleteScene({required this.sceneId, required this.unitUuid});
@override
List<Object> get props => [sceneId];
}
class DeleteAutomation extends RoutineEvent {
final String automationId;
final String unitUuid;
const DeleteAutomation({required this.automationId, required this.unitUuid});
@override
List<Object> get props => [automationId];
}
class ResetRoutineState extends RoutineEvent {} class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {} class ClearFunctions extends RoutineEvent {}

View File

@ -18,6 +18,9 @@ class RoutineState extends Equatable {
final bool isAutomation; final bool isAutomation;
final String selectedAutomationOperator; final String selectedAutomationOperator;
final EffectiveTime? effectiveTime; final EffectiveTime? effectiveTime;
final String? sceneId;
final String? automationId;
final bool? isUpdate;
const RoutineState({ const RoutineState({
this.ifItems = const [], this.ifItems = const [],
@ -37,6 +40,9 @@ class RoutineState extends Equatable {
this.isAutomation = false, this.isAutomation = false,
this.selectedAutomationOperator = 'or', this.selectedAutomationOperator = 'or',
this.effectiveTime, this.effectiveTime,
this.sceneId,
this.automationId,
this.isUpdate,
}); });
RoutineState copyWith({ RoutineState copyWith({
@ -56,6 +62,9 @@ class RoutineState extends Equatable {
bool? isAutomation, bool? isAutomation,
String? selectedAutomationOperator, String? selectedAutomationOperator,
EffectiveTime? effectiveTime, EffectiveTime? effectiveTime,
String? sceneId,
String? automationId,
bool? isUpdate,
}) { }) {
return RoutineState( return RoutineState(
ifItems: ifItems ?? this.ifItems, ifItems: ifItems ?? this.ifItems,
@ -77,6 +86,9 @@ class RoutineState extends Equatable {
selectedAutomationOperator: selectedAutomationOperator:
selectedAutomationOperator ?? this.selectedAutomationOperator, selectedAutomationOperator ?? this.selectedAutomationOperator,
effectiveTime: effectiveTime ?? this.effectiveTime, effectiveTime: effectiveTime ?? this.effectiveTime,
sceneId: sceneId ?? this.sceneId,
automationId: automationId ?? this.automationId,
isUpdate: isUpdate ?? this.isUpdate,
); );
} }
@ -97,6 +109,9 @@ class RoutineState extends Equatable {
isTabToRun, isTabToRun,
isAutomation, isAutomation,
selectedAutomationOperator, selectedAutomationOperator,
effectiveTime effectiveTime,
sceneId,
automationId,
isUpdate
]; ];
} }

View File

@ -10,8 +10,9 @@ import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
class DeviceDialogHelper { class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog( static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context, BuildContext context,
Map<String, dynamic> data, Map<String, dynamic> data, {
) async { required bool removeComparetors,
}) async {
final functions = data['functions'] as List<DeviceFunction>; final functions = data['functions'] as List<DeviceFunction>;
try { try {
@ -20,6 +21,7 @@ class DeviceDialogHelper {
data['productType'], data['productType'],
data, data,
functions, functions,
removeComparetors: removeComparetors,
); );
if (result != null) { if (result != null) {
@ -33,34 +35,49 @@ class DeviceDialogHelper {
} }
static Future<Map<String, dynamic>?> _getDialogForDeviceType( static Future<Map<String, dynamic>?> _getDialogForDeviceType(
BuildContext context, BuildContext context,
String productType, String productType,
Map<String, dynamic> data, Map<String, dynamic> data,
List<DeviceFunction> functions, List<DeviceFunction> functions,
) async { {required bool removeComparetors}) async {
final routineBloc = context.read<RoutineBloc>(); final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions = final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) { switch (productType) {
case 'AC': case 'AC':
return ACHelper.showACFunctionsDialog(context, functions, return ACHelper.showACFunctionsDialog(
data['device'], deviceSelectedFunctions, data['uniqueCustomId']); context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '1G': case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, return OneGangSwitchHelper.showSwitchFunctionsDialog(
data['device'], deviceSelectedFunctions, data['uniqueCustomId']); context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '2G': case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, return TwoGangSwitchHelper.showSwitchFunctionsDialog(
data['device'], deviceSelectedFunctions, data['uniqueCustomId']); context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '3G': case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog( return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context, context,
functions, functions,
data['device'], data['device'],
deviceSelectedFunctions, deviceSelectedFunctions,
data['uniqueCustomId'], data['uniqueCustomId'],
); removeComparetors);
default: default:
return null; return null;
} }

View File

@ -8,8 +8,8 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class SaveRoutineHelper { class SaveRoutineHelper {
static Future<void> showSaveRoutineDialog(BuildContext context) async { static Future<bool?> showSaveRoutineDialog(BuildContext context) async {
return showDialog<void>( return showDialog<bool?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
@ -54,27 +54,23 @@ class SaveRoutineHelper {
), ),
if (state.isAutomation) if (state.isAutomation)
...state.ifItems.map((item) { ...state.ifItems.map((item) {
final functions = state.selectedFunctions[ final functions =
item['uniqueCustomId']] ?? state.selectedFunctions[item['uniqueCustomId']] ?? [];
[];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(
item['imagePath'], item['imagePath'],
width: 22, width: 22,
height: 22, height: 22,
), ),
title: Text(item['title'], title:
style: const TextStyle(fontSize: 14)), Text(item['title'], style: const TextStyle(fontSize: 14)),
subtitle: Wrap( subtitle: Wrap(
children: functions children: functions
.map((f) => Text( .map((f) => Text(
'${f.operationName}: ${f.value}, ', '${f.operationName}: ${f.value}, ',
style: const TextStyle( style: const TextStyle(
color: ColorsManager color: ColorsManager.grayColor, fontSize: 8),
.grayColor, overflow: TextOverflow.ellipsis,
fontSize: 8),
overflow:
TextOverflow.ellipsis,
maxLines: 3, maxLines: 3,
)) ))
.toList(), .toList(),
@ -99,25 +95,22 @@ class SaveRoutineHelper {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
...state.thenItems.map((item) { ...state.thenItems.map((item) {
final functions = state.selectedFunctions[ final functions =
item['uniqueCustomId']] ?? state.selectedFunctions[item['uniqueCustomId']] ?? [];
[];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(
item['imagePath'], item['imagePath'],
width: 22, width: 22,
height: 22, height: 22,
), ),
title: Text(item['title'], title:
style: const TextStyle(fontSize: 14)), Text(item['title'], style: const TextStyle(fontSize: 14)),
subtitle: Wrap( subtitle: Wrap(
children: functions children: functions
.map((f) => Text( .map((f) => Text(
'${f.operationName}: ${f.value}, ', '${f.operationName}: ${f.value}, ',
style: const TextStyle( style: const TextStyle(
color: color: ColorsManager.grayColor, fontSize: 8),
ColorsManager.grayColor,
fontSize: 8),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 3, maxLines: 3,
)) ))
@ -140,19 +133,15 @@ class SaveRoutineHelper {
), ),
), ),
DialogFooter( DialogFooter(
onCancel: () => Navigator.pop(context), onCancel: () => Navigator.pop(context, false),
onConfirm: () { onConfirm: () {
if (state.isAutomation) { if (state.isAutomation) {
context context.read<RoutineBloc>().add(const CreateAutomationEvent());
.read<RoutineBloc>()
.add(const CreateAutomationEvent());
} else { } else {
context context.read<RoutineBloc>().add(const CreateSceneEvent());
.read<RoutineBloc>()
.add(const CreateSceneEvent());
} }
Navigator.pop(context); Navigator.pop(context, true);
}, },
isConfirmEnabled: true, isConfirmEnabled: true,
), ),

View File

@ -1,177 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart';
// class CreateAutomationModel {
// String unitUuid;
// String automationName;
// String decisionExpr;
// EffectiveTime effectiveTime;
// List<CreateCondition> conditions;
// List<CreateSceneAction> actions;
// CreateAutomationModel({
// required this.unitUuid,
// required this.automationName,
// required this.decisionExpr,
// required this.effectiveTime,
// required this.conditions,
// required this.actions,
// });
// CreateAutomationModel copyWith({
// String? unitUuid,
// String? automationName,
// String? decisionExpr,
// EffectiveTime? effectiveTime,
// List<CreateCondition>? conditions,
// List<CreateSceneAction>? actions,
// }) {
// return CreateAutomationModel(
// unitUuid: unitUuid ?? this.unitUuid,
// automationName: automationName ?? this.automationName,
// decisionExpr: decisionExpr ?? this.decisionExpr,
// effectiveTime: effectiveTime ?? this.effectiveTime,
// conditions: conditions ?? this.conditions,
// actions: actions ?? this.actions,
// );
// }
// Map<String, dynamic> toMap([String? automationId]) {
// return {
// if (automationId == null) 'spaceUuid': unitUuid,
// 'automationName': automationName,
// 'decisionExpr': decisionExpr,
// 'effectiveTime': effectiveTime.toMap(),
// 'conditions': conditions.map((x) => x.toMap()).toList(),
// 'actions': actions.map((x) => x.toMap()).toList(),
// };
// }
// factory CreateAutomationModel.fromMap(Map<String, dynamic> map) {
// return CreateAutomationModel(
// unitUuid: map['spaceUuid'] ?? '',
// automationName: map['automationName'] ?? '',
// decisionExpr: map['decisionExpr'] ?? '',
// effectiveTime: EffectiveTime.fromMap(map['effectiveTime']),
// conditions: List<CreateCondition>.from(
// map['conditions']?.map((x) => CreateCondition.fromMap(x))),
// actions: List<CreateSceneAction>.from(
// map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
// );
// }
// String toJson([String? automationId]) => json.encode(toMap(automationId));
// factory CreateAutomationModel.fromJson(String source) =>
// CreateAutomationModel.fromMap(json.decode(source));
// @override
// String toString() {
// return 'CreateAutomationModel(unitUuid: $unitUuid, automationName: $automationName, decisionExpr: $decisionExpr, effectiveTime: $effectiveTime, conditions: $conditions, actions: $actions)';
// }
// }
// class EffectiveTime {
// String start;
// String end;
// String loops;
// EffectiveTime({
// required this.start,
// required this.end,
// required this.loops,
// });
// Map<String, dynamic> toMap() {
// return {
// 'start': start,
// 'end': end,
// 'loops': loops,
// };
// }
// factory EffectiveTime.fromMap(Map<String, dynamic> map) {
// return EffectiveTime(
// start: map['start'] ?? '',
// end: map['end'] ?? '',
// loops: map['loops'] ?? '',
// );
// }
// @override
// String toString() => 'EffectiveTime(start: $start, end: $end, loops: $loops)';
// }
// class CreateCondition {
// int code;
// String entityId;
// String entityType;
// ConditionExpr expr;
// CreateCondition({
// required this.code,
// required this.entityId,
// required this.entityType,
// required this.expr,
// });
// Map<String, dynamic> toMap() {
// return {
// 'code': code,
// 'entityId': entityId,
// 'entityType': entityType,
// 'expr': expr.toMap(),
// };
// }
// factory CreateCondition.fromMap(Map<String, dynamic> map) {
// return CreateCondition(
// code: map['code'] ?? 0,
// entityId: map['entityId'] ?? '',
// entityType: map['entityType'] ?? '',
// expr: ConditionExpr.fromMap(map['expr']),
// );
// }
// @override
// String toString() =>
// 'CreateCondition(code: $code, entityId: $entityId, entityType: $entityType, expr: $expr)';
// }
// class ConditionExpr {
// String statusCode;
// String comparator;
// dynamic statusValue;
// ConditionExpr({
// required this.statusCode,
// required this.comparator,
// required this.statusValue,
// });
// Map<String, dynamic> toMap() {
// return {
// 'statusCode': statusCode,
// 'comparator': comparator,
// 'statusValue': statusValue,
// };
// }
// factory ConditionExpr.fromMap(Map<String, dynamic> map) {
// return ConditionExpr(
// statusCode: map['statusCode'] ?? '',
// comparator: map['comparator'] ?? '',
// statusValue: map['statusValue'],
// );
// }
// @override
// String toString() =>
// 'ConditionExpr(statusCode: $statusCode, comparator: $comparator, statusValue: $statusValue)';
// }
import 'dart:convert';
class CreateAutomationModel { class CreateAutomationModel {
String spaceUuid; String spaceUuid;
String automationName; String automationName;

View File

@ -0,0 +1,262 @@
import 'dart:convert';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart';
class RoutineDetailsModel {
final String spaceUuid;
final String name;
final String decisionExpr;
final List<RoutineAction> actions;
final String? iconId;
final bool? showInDevice;
final EffectiveTime? effectiveTime;
final List<RoutineCondition>? conditions;
final String? type;
RoutineDetailsModel({
required this.spaceUuid,
required this.name,
required this.decisionExpr,
required this.actions,
this.iconId,
this.showInDevice,
this.effectiveTime,
this.conditions,
this.type,
});
// Convert to CreateSceneModel
CreateSceneModel toCreateSceneModel() {
return CreateSceneModel(
spaceUuid: spaceUuid,
iconId: iconId ?? '',
showInDevice: showInDevice ?? false,
sceneName: name,
decisionExpr: decisionExpr,
actions: actions.map((a) => a.toCreateSceneAction()).toList(),
);
}
// Convert to CreateAutomationModel
CreateAutomationModel toCreateAutomationModel() {
return CreateAutomationModel(
spaceUuid: spaceUuid,
automationName: name,
decisionExpr: decisionExpr,
effectiveTime:
effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
conditions: conditions?.map((c) => c.toCondition()).toList() ?? [],
actions: actions.map((a) => a.toAutomationAction()).toList(),
);
}
Map<String, dynamic> toMap() {
return {
'spaceUuid': spaceUuid,
'name': name,
'decisionExpr': decisionExpr,
'actions': actions.map((x) => x.toMap()).toList(),
if (iconId != null) 'iconId': iconId,
if (showInDevice != null) 'showInDevice': showInDevice,
if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(),
if (conditions != null)
'conditions': conditions!.map((x) => x.toMap()).toList(),
if (type != null) 'type': type,
};
}
factory RoutineDetailsModel.fromMap(Map<String, dynamic> map) {
return RoutineDetailsModel(
spaceUuid: map['spaceUuid'] ?? '',
name: map['name'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
actions: List<RoutineAction>.from(
map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [],
),
iconId: map['iconId'],
showInDevice: map['showInDevice'],
effectiveTime: map['effectiveTime'] != null
? EffectiveTime.fromMap(map['effectiveTime'])
: null,
conditions: map['conditions'] != null
? List<RoutineCondition>.from(
map['conditions'].map((x) => RoutineCondition.fromMap(x)))
: null,
type: map['type'],
);
}
String toJson() => json.encode(toMap());
factory RoutineDetailsModel.fromJson(String source) =>
RoutineDetailsModel.fromMap(json.decode(source));
}
class RoutineAction {
final String entityId;
final String actionExecutor;
final RoutineExecutorProperty? executorProperty;
RoutineAction({
required this.entityId,
required this.actionExecutor,
this.executorProperty,
});
CreateSceneAction toCreateSceneAction() {
return CreateSceneAction(
entityId: entityId,
actionExecutor: actionExecutor,
executorProperty: executorProperty?.toCreateSceneExecutorProperty(),
);
}
AutomationAction toAutomationAction() {
return AutomationAction(
entityId: entityId,
actionExecutor: actionExecutor,
executorProperty: executorProperty?.toExecutorProperty(),
);
}
Map<String, dynamic> toMap() {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
if (executorProperty != null)
'executorProperty': executorProperty!.toMap(),
};
}
factory RoutineAction.fromMap(Map<String, dynamic> map) {
return RoutineAction(
entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '',
executorProperty: map['executorProperty'] != null
? RoutineExecutorProperty.fromMap(map['executorProperty'])
: null,
);
}
}
class RoutineExecutorProperty {
final String? functionCode;
final dynamic functionValue;
final int? delaySeconds;
RoutineExecutorProperty({
this.functionCode,
this.functionValue,
this.delaySeconds,
});
CreateSceneExecutorProperty toCreateSceneExecutorProperty() {
return CreateSceneExecutorProperty(
functionCode: functionCode ?? '',
functionValue: functionValue,
delaySeconds: delaySeconds ?? 0,
);
}
ExecutorProperty toExecutorProperty() {
return ExecutorProperty(
functionCode: functionCode,
functionValue: functionValue,
delaySeconds: delaySeconds,
);
}
Map<String, dynamic> toMap() {
return {
if (functionCode != null) 'functionCode': functionCode,
if (functionValue != null) 'functionValue': functionValue,
if (delaySeconds != null) 'delaySeconds': delaySeconds,
};
}
factory RoutineExecutorProperty.fromMap(Map<String, dynamic> map) {
return RoutineExecutorProperty(
functionCode: map['functionCode'],
functionValue: map['functionValue'],
delaySeconds: map['delaySeconds']?.toInt(),
);
}
}
class RoutineCondition {
final int code;
final String entityId;
final String entityType;
final RoutineConditionExpr expr;
RoutineCondition({
required this.code,
required this.entityId,
required this.entityType,
required this.expr,
});
Condition toCondition() {
return Condition(
code: code,
entityId: entityId,
entityType: entityType,
expr: expr.toConditionExpr(),
);
}
Map<String, dynamic> toMap() {
return {
'code': code,
'entityId': entityId,
'entityType': entityType,
'expr': expr.toMap(),
};
}
factory RoutineCondition.fromMap(Map<String, dynamic> map) {
return RoutineCondition(
code: map['code']?.toInt() ?? 0,
entityId: map['entityId'] ?? '',
entityType: map['entityType'] ?? '',
expr: RoutineConditionExpr.fromMap(map['expr']),
);
}
}
class RoutineConditionExpr {
final String statusCode;
final String comparator;
final dynamic statusValue;
RoutineConditionExpr({
required this.statusCode,
required this.comparator,
required this.statusValue,
});
ConditionExpr toConditionExpr() {
return ConditionExpr(
statusCode: statusCode,
comparator: comparator,
statusValue: statusValue,
);
}
Map<String, dynamic> toMap() {
return {
'statusCode': statusCode,
'comparator': comparator,
'statusValue': statusValue,
};
}
factory RoutineConditionExpr.fromMap(Map<String, dynamic> map) {
return RoutineConditionExpr(
statusCode: map['statusCode'] ?? '',
comparator: map['comparator'] ?? '',
statusValue: map['statusValue'],
);
}
}

View File

@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ScenesModel { class ScenesModel {
final String id; final String id;
final String? sceneTuyaId;
final String name; final String name;
final String status; final String status;
final String type; final String type;
@ -9,26 +12,43 @@ class ScenesModel {
ScenesModel({ ScenesModel({
required this.id, required this.id,
this.sceneTuyaId,
required this.name, required this.name,
required this.status, required this.status,
required this.type, required this.type,
this.icon, this.icon,
}); });
factory ScenesModel.fromRawJson(String str) =>
ScenesModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
Uint8List? get iconInBytes {
if (icon == null || icon?.isEmpty == true) return null;
try {
return base64Decode(icon!);
} catch (e) {
return null;
}
}
factory ScenesModel.fromJson(Map<String, dynamic> json, factory ScenesModel.fromJson(Map<String, dynamic> json,
{bool? isAutomation}) => {bool? isAutomation}) {
ScenesModel( return ScenesModel(
id: json["id"], id: json["id"] ?? json["uuid"] ?? '',
name: json["name"] ?? '', sceneTuyaId: json["sceneTuyaId"] as String?,
status: json["status"] ?? '', name: json["name"] ?? '',
type: json["type"] ?? '', status: json["status"] ?? '',
icon: (isAutomation ?? false) type: json["type"] ?? '',
? Assets.automation icon:
: json["icon"] as String?, isAutomation == true ? Assets.automation : (json["icon"] as String?),
); );
}
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"id": id, "id": id,
"sceneTuyaId": sceneTuyaId ?? '',
"name": name, "name": name,
"status": status, "status": status,
"type": type, "type": type,

View File

@ -6,8 +6,16 @@ import 'package:syncrow_web/pages/routiens/widgets/then_container.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class CreateNewRoutineView extends StatelessWidget { class CreateNewRoutineView extends StatelessWidget {
const CreateNewRoutineView({super.key}); final bool isUpdate;
final String? routineId;
final bool isScene;
const CreateNewRoutineView({
super.key,
this.isUpdate = false,
this.routineId,
this.isScene = true,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(

View File

@ -1,7 +1,10 @@
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/bloc/switch_tabs/switch_tabs_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class RoutinesView extends StatelessWidget { class RoutinesView extends StatelessWidget {
@ -23,49 +26,30 @@ class RoutinesView extends StatelessWidget {
children: [ children: [
Text( Text(
"Create New Routines", "Create New Routines",
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: ColorsManager.grayColor, color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
), ),
), ),
SizedBox( const SizedBox(
height: 200, height: 10,
width: 150,
child: GestureDetector(
onTap: () {
BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(true),
);
},
child: Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
color: ColorsManager.whiteColors,
child: Center(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(120),
border: Border.all(
color: ColorsManager.greyColor,
width: 2.0,
),
),
height: 70,
width: 70,
child: Icon(
Icons.add,
color: ColorsManager.dialogBlueTitle,
size: 40,
),
),
),
),
),
), ),
const Spacer(), RoutineViewCard(
onTap: () {
BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(true),
);
context.read<RoutineBloc>().add(
(ResetRoutineState()),
);
},
icon: Icons.add,
textString: '',
),
const SizedBox(
height: 15,
),
const Expanded(child: FetchRoutineScenesAutomation()),
], ],
), ),
); );

View File

@ -1,3 +1,5 @@
import 'dart:convert';
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:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@ -81,7 +83,9 @@ class DraggableCard extends StatelessWidget {
? SvgPicture.asset( ? SvgPicture.asset(
imagePath, imagePath,
) )
: Image.network(imagePath), : Image.memory(
base64Decode(imagePath),
),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Padding( Padding(

View File

@ -26,8 +26,10 @@ class IfContainer extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const Text('IF',
if (state.isAutomation) style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation && state.ifItems.isNotEmpty)
AutomationOperatorSelector( AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator), selectedOperator: state.selectedAutomationOperator),
], ],
@ -53,33 +55,44 @@ class IfContainer extends StatelessWidget {
(index) => GestureDetector( (index) => GestureDetector(
onTap: () async { onTap: () async {
if (!state.isTabToRun) { if (!state.isTabToRun) {
final result = await DeviceDialogHelper.showDeviceDialog( final result = await DeviceDialogHelper
context, state.ifItems[index]); .showDeviceDialog(
context, state.ifItems[index],
removeComparetors: false);
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(
.read<RoutineBloc>() AddToIfContainer(
.add(AddToIfContainer(state.ifItems[index], false)); state.ifItems[index], false));
} else if (!['AC', '1G', '2G', '3G'] } else if (![
.contains(state.ifItems[index]['productType'])) { 'AC',
context '1G',
.read<RoutineBloc>() '2G',
.add(AddToIfContainer(state.ifItems[index], false)); '3G'
].contains(
state.ifItems[index]['productType'])) {
context.read<RoutineBloc>().add(
AddToIfContainer(
state.ifItems[index], false));
} }
} }
}, },
child: DraggableCard( child: DraggableCard(
imagePath: state.ifItems[index]['imagePath'] ?? '', imagePath:
state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '', title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index], deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 8),
isFromThen: false, isFromThen: false,
isFromIf: true, isFromIf: true,
onRemove: () { onRemove: () {
context.read<RoutineBloc>().add(RemoveDragCard( context.read<RoutineBloc>().add(
index: index, RemoveDragCard(
isFromThen: false, index: index,
key: state.ifItems[index]['uniqueCustomId'])); isFromThen: false,
key: state.ifItems[index]
['uniqueCustomId']));
}, },
), ),
)), )),
@ -88,23 +101,35 @@ class IfContainer extends StatelessWidget {
), ),
); );
}, },
onWillAccept: (data) => data != null, onAcceptWithDetails: (data) async {
onAccept: (data) async {
final uniqueCustomId = const Uuid().v4(); final uniqueCustomId = const Uuid().v4();
final mutableData = Map<String, dynamic>.from(data); final mutableData = Map<String, dynamic>.from(data.data);
mutableData['uniqueCustomId'] = uniqueCustomId; mutableData['uniqueCustomId'] = uniqueCustomId;
if (state.isAutomation && mutableData['deviceId'] == 'tab_to_run') {
return;
}
if (!state.isTabToRun) { if (!state.isTabToRun) {
if (mutableData['deviceId'] == 'tab_to_run') { if (mutableData['deviceId'] == 'tab_to_run') {
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, true)); context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, true));
} else { } else {
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData); final result = await DeviceDialogHelper.showDeviceDialog(
context, mutableData,
removeComparetors: false);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false)); context
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { .read<RoutineBloc>()
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false)); .add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
} }
} }
} }
@ -134,7 +159,7 @@ class AutomationOperatorSelector extends StatelessWidget {
children: [ children: [
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: selectedOperator == 'or' backgroundColor: selectedOperator.toLowerCase() == 'or'
? ColorsManager.dialogBlueTitle ? ColorsManager.dialogBlueTitle
: ColorsManager.whiteColors, : ColorsManager.whiteColors,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -144,12 +169,15 @@ class AutomationOperatorSelector extends StatelessWidget {
child: Text( child: Text(
'Any condition is met', 'Any condition is met',
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
color: color: selectedOperator.toLowerCase() == 'or'
selectedOperator == 'or' ? ColorsManager.whiteColors : ColorsManager.blackColor, ? ColorsManager.whiteColors
: ColorsManager.blackColor,
), ),
), ),
onPressed: () { onPressed: () {
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or')); context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'or'));
}, },
), ),
Container( Container(
@ -159,7 +187,7 @@ class AutomationOperatorSelector extends StatelessWidget {
), ),
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: selectedOperator == 'and' backgroundColor: selectedOperator.toLowerCase() == 'and'
? ColorsManager.dialogBlueTitle ? ColorsManager.dialogBlueTitle
: ColorsManager.whiteColors, : ColorsManager.whiteColors,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -169,13 +197,15 @@ class AutomationOperatorSelector extends StatelessWidget {
child: Text( child: Text(
'All condition is met', 'All condition is met',
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
color: selectedOperator == 'and' color: selectedOperator.toLowerCase() == 'and'
? ColorsManager.whiteColors ? ColorsManager.whiteColors
: ColorsManager.blackColor, : ColorsManager.blackColor,
), ),
), ),
onPressed: () { onPressed: () {
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and')); context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'and'));
}, },
), ),
], ],

View File

@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() =>
_FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
with HelperResponsiveLayout {
@override
void initState() {
super.initState();
context.read<RoutineBloc>()
..add(const LoadScenes(spaceId, communityId))
..add(const LoadAutomation(spaceId));
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Scenes (Tab to Run)",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.scenes.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: Stack(
children: [
RoutineViewCard(
onTap: () {},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ??
Assets.logoHorizontal,
isFromScenes: true,
iconInBytes:
state.scenes[index].iconInBytes,
),
Positioned(
top: 0,
right: 0,
child: InkWell(
onTap: () => context
.read<RoutineBloc>()
.add(
DeleteScene(
sceneId: state.scenes[index].id,
unitUuid: spaceId,
),
),
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.grayColor,
width: 2.0,
),
),
child: const Center(
child: Icon(Icons.delete,
size: 15,
color: ColorsManager.grayColor),
),
),
),
),
],
),
),
),
),
const SizedBox(height: 15),
Text(
"Automations",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.automations.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: Stack(
children: [
RoutineViewCard(
onTap: () {},
textString: state.automations[index].name,
icon: state.automations[index].icon ??
Assets.automation,
),
Positioned(
top: 0,
right: 0,
child: InkWell(
onTap: () =>
context.read<RoutineBloc>().add(
DeleteAutomation(
automationId: state
.automations[index].id,
unitUuid: spaceId,
),
),
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.grayColor,
width: 2.0,
),
),
child: const Center(
child: Icon(Icons.delete,
size: 15,
color: ColorsManager.grayColor),
),
),
),
),
],
),
),
),
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
const RoutineViewCard({
super.key,
required this.onTap,
required this.icon,
required this.textString,
this.isFromScenes,
this.iconInBytes,
});
final Function() onTap;
final dynamic icon;
final String textString;
final bool? isFromScenes;
final Uint8List? iconInBytes;
@override
Widget build(BuildContext context) {
final double cardWidth = isSmallScreenSize(context)
? 120
: isMediumScreenSize(context)
? 135
: 150;
final double cardHeight = isSmallScreenSize(context) ? 160 : 170;
final double iconSize = isSmallScreenSize(context)
? 50
: isMediumScreenSize(context)
? 60
: 70;
return ConstrainedBox(
constraints: BoxConstraints(
maxWidth: cardWidth,
maxHeight: cardHeight,
),
child: Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
color: ColorsManager.whiteColors,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(120),
border: Border.all(
color: ColorsManager.greyColor,
width: 2.0,
),
),
height: iconSize,
width: iconSize,
child: (isFromScenes ?? false)
? (iconInBytes != null &&
iconInBytes?.isNotEmpty == true)
? Image.memory(
iconInBytes!,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) =>
Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
),
)
: Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
)
: (icon is String && icon.endsWith('.svg'))
? SvgPicture.asset(
icon,
fit: BoxFit.contain,
)
: Icon(
icon,
color: ColorsManager.dialogBlueTitle,
size: isSmallScreenSize(context) ? 30 : 40,
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text(
textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: isSmallScreenSize(context) ? 10 : 12,
),
),
),
],
),
),
),
),
);
}
}

View File

@ -75,7 +75,11 @@ class PeriodOptions extends StatelessWidget {
onTap: () { onTap: () {
context.read<EffectPeriodBloc>().add(SetPeriod(value)); context.read<EffectPeriodBloc>().add(SetPeriod(value));
}, },
title: Text(EffectPeriodHelper.formatEnumValue(value)), title: Text(
EffectPeriodHelper.formatEnumValue(value),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 12),
),
subtitle: Text( subtitle: Text(
subtitle, subtitle,
style: Theme.of(context).textTheme.bodyMedium!.copyWith( style: Theme.of(context).textTheme.bodyMedium!.copyWith(

View File

@ -19,6 +19,7 @@ class ACHelper {
AllDevicesModel? device, AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, String uniqueCustomId,
bool? removeComparetors,
) async { ) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList(); List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
@ -84,6 +85,7 @@ class ACHelper {
acFunctions: acFunctions, acFunctions: acFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparators: removeComparetors,
), ),
), ),
], ],
@ -179,6 +181,7 @@ class ACHelper {
required List<ACFunction> acFunctions, required List<ACFunction> acFunctions,
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
bool? removeComparators,
}) { }) {
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') { if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
final initialValue = selectedFunctionData?.value ?? 200; final initialValue = selectedFunctionData?.value ?? 200;
@ -190,6 +193,7 @@ class ACHelper {
device: device, device: device,
operationName: operationName, operationName: operationName,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
removeComparators: removeComparators,
); );
} }
@ -217,18 +221,20 @@ class ACHelper {
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
DeviceFunctionData? selectedFunctionData, DeviceFunctionData? selectedFunctionData,
bool? removeComparators,
}) { }) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildConditionToggle( if (removeComparators != true)
context, _buildConditionToggle(
currentCondition, context,
selectCode, currentCondition,
device, selectCode,
operationName, device,
selectedFunctionData, operationName,
), selectedFunctionData,
),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildTemperatureDisplay( _buildTemperatureDisplay(
context, context,

View File

@ -1,8 +1,6 @@
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/common/custom_table.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.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'; import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -20,6 +20,7 @@ class OneGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> acFunctions = List<BaseSwitchFunction> acFunctions =
functions.whereType<BaseSwitchFunction>().toList(); functions.whereType<BaseSwitchFunction>().toList();
@ -106,6 +107,7 @@ class OneGangSwitchHelper {
acFunctions: acFunctions, acFunctions: acFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparetors: removeComparetors,
), ),
), ),
], ],
@ -162,6 +164,7 @@ class OneGangSwitchHelper {
required List<BaseSwitchFunction> acFunctions, required List<BaseSwitchFunction> acFunctions,
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
required bool removeComparetors,
}) { }) {
if (selectedFunction == 'countdown_1') { if (selectedFunction == 'countdown_1') {
final initialValue = selectedFunctionData?.value ?? 200; final initialValue = selectedFunctionData?.value ?? 200;
@ -173,6 +176,7 @@ class OneGangSwitchHelper {
device: device, device: device,
operationName: operationName, operationName: operationName,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
removeComparetors: removeComparetors,
); );
} }
@ -199,18 +203,20 @@ class OneGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
DeviceFunctionData? selectedFunctionData, DeviceFunctionData? selectedFunctionData,
required bool removeComparetors,
}) { }) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildConditionToggle( if (removeComparetors != true)
context, _buildConditionToggle(
currentCondition, context,
selectCode, currentCondition,
device, selectCode,
operationName, device,
selectedFunctionData, operationName,
), selectedFunctionData,
),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode), selectedFunctionData, selectCode),

View File

@ -20,6 +20,7 @@ class ThreeGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList(); functions.whereType<BaseSwitchFunction>().toList();
@ -106,6 +107,7 @@ class ThreeGangSwitchHelper {
switchFunctions: switchFunctions, switchFunctions: switchFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparetors: removeComparetors,
), ),
), ),
], ],
@ -162,6 +164,7 @@ class ThreeGangSwitchHelper {
required List<BaseSwitchFunction> switchFunctions, required List<BaseSwitchFunction> switchFunctions,
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
required bool removeComparetors,
}) { }) {
if (selectedFunction == 'countdown_1' || if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2' || selectedFunction == 'countdown_2' ||
@ -175,6 +178,7 @@ class ThreeGangSwitchHelper {
device: device, device: device,
operationName: operationName, operationName: operationName,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
removeComparetors: removeComparetors,
); );
} }
@ -201,18 +205,20 @@ class ThreeGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
DeviceFunctionData? selectedFunctionData, DeviceFunctionData? selectedFunctionData,
bool? removeComparetors,
}) { }) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildConditionToggle( if (removeComparetors != true)
context, _buildConditionToggle(
currentCondition, context,
selectCode, currentCondition,
device, selectCode,
operationName, device,
selectedFunctionData, operationName,
), selectedFunctionData,
),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode), selectedFunctionData, selectCode),

View File

@ -20,6 +20,7 @@ class TwoGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList(); functions.whereType<BaseSwitchFunction>().toList();
@ -106,6 +107,7 @@ class TwoGangSwitchHelper {
switchFunctions: switchFunctions, switchFunctions: switchFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparetors: removeComparetors,
), ),
), ),
], ],
@ -162,6 +164,7 @@ class TwoGangSwitchHelper {
required List<BaseSwitchFunction> switchFunctions, required List<BaseSwitchFunction> switchFunctions,
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
required bool removeComparetors,
}) { }) {
if (selectedFunction == 'countdown_1' || if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2') { selectedFunction == 'countdown_2') {
@ -174,6 +177,7 @@ class TwoGangSwitchHelper {
device: device, device: device,
operationName: operationName, operationName: operationName,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
removeComparetors: removeComparetors,
); );
} }
@ -200,18 +204,20 @@ class TwoGangSwitchHelper {
AllDevicesModel? device, AllDevicesModel? device,
required String operationName, required String operationName,
DeviceFunctionData? selectedFunctionData, DeviceFunctionData? selectedFunctionData,
bool? removeComparetors,
}) { }) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildConditionToggle( if (removeComparetors != true)
context, _buildConditionToggle(
currentCondition, context,
selectCode, currentCondition,
device, selectCode,
operationName, device,
selectedFunctionData, operationName,
), selectedFunctionData,
),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode), selectedFunctionData, selectCode),

View File

@ -1,6 +1,7 @@
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/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart'; import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart'; import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart';
@ -173,7 +174,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
width: 200, width: 200,
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () async {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
@ -207,7 +208,16 @@ class RoutineSearchAndButtons extends StatelessWidget {
); );
return; return;
} }
SaveRoutineHelper.showSaveRoutineDialog(context); final result =
await SaveRoutineHelper.showSaveRoutineDialog(context);
if (result != null && result) {
BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(false),
);
BlocProvider.of<SwitchTabsBloc>(context).add(
const TriggerSwitchTabsEvent(true),
);
}
}, },
borderRadius: 15, borderRadius: 15,
elevation: 0, elevation: 0,

View File

@ -18,7 +18,7 @@ class _ScenesAndAutomationsState extends State<ScenesAndAutomations> {
void initState() { void initState() {
super.initState(); super.initState();
context.read<RoutineBloc>() context.read<RoutineBloc>()
..add(const LoadScenes(spaceId)) ..add(const LoadScenes(spaceId, communityId))
..add(const LoadAutomation(spaceId)); ..add(const LoadAutomation(spaceId));
} }
@ -34,7 +34,9 @@ class _ScenesAndAutomationsState extends State<ScenesAndAutomations> {
children: scenes.asMap().entries.map((entry) { children: scenes.asMap().entries.map((entry) {
final scene = entry.value; final scene = entry.value;
if (state.searchText != null && state.searchText!.isNotEmpty) { if (state.searchText != null && state.searchText!.isNotEmpty) {
return scene.name.toLowerCase().contains(state.searchText!.toLowerCase()) return scene.name
.toLowerCase()
.contains(state.searchText!.toLowerCase())
? DraggableCard( ? DraggableCard(
imagePath: scene.icon ?? Assets.loginLogo, imagePath: scene.icon ?? Assets.loginLogo,
title: scene.name, title: scene.name,

View File

@ -26,7 +26,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,
@ -35,12 +37,16 @@ class ThenContainer extends StatelessWidget {
state.thenItems.length, state.thenItems.length,
(index) => GestureDetector( (index) => GestureDetector(
onTap: () async { onTap: () async {
if (state.thenItems[index]['deviceId'] == 'delay') { if (state.thenItems[index]['deviceId'] ==
final result = await DelayHelper.showDelayPickerDialog( 'delay') {
context, state.thenItems[index]); final result = await DelayHelper
.showDelayPickerDialog(
context, state.thenItems[index]);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context
.read<RoutineBloc>()
.add(AddToThenContainer({
...state.thenItems[index], ...state.thenItems[index],
'imagePath': Assets.delay, 'imagePath': Assets.delay,
'title': 'Delay', 'title': 'Delay',
@ -49,32 +55,41 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
final result = await DeviceDialogHelper.showDeviceDialog( final result = await DeviceDialogHelper
context, state.thenItems[index]); .showDeviceDialog(
context, state.thenItems[index],
removeComparetors: true);
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(
.read<RoutineBloc>() AddToThenContainer(
.add(AddToThenContainer(state.thenItems[index])); state.thenItems[index]));
} else if (!['AC', '1G', '2G', '3G'] } else if (!['AC', '1G', '2G', '3G']
.contains(state.thenItems[index]['productType'])) { .contains(state.thenItems[index]
context ['productType'])) {
.read<RoutineBloc>() context.read<RoutineBloc>().add(
.add(AddToThenContainer(state.thenItems[index])); AddToThenContainer(
state.thenItems[index]));
} }
}, },
child: DraggableCard( child: DraggableCard(
imagePath: state.thenItems[index]['imagePath'] ?? '', imagePath: state.thenItems[index]
title: state.thenItems[index]['title'] ?? '', ['imagePath'] ??
'',
title:
state.thenItems[index]['title'] ?? '',
deviceData: state.thenItems[index], deviceData: state.thenItems[index],
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 8),
isFromThen: true, isFromThen: true,
isFromIf: false, isFromIf: false,
onRemove: () { onRemove: () {
context.read<RoutineBloc>().add(RemoveDragCard( context.read<RoutineBloc>().add(
index: index, RemoveDragCard(
isFromThen: true, index: index,
key: state.thenItems[index]['uniqueCustomId'])); isFromThen: true,
key: state.thenItems[index]
['uniqueCustomId']));
}, },
), ),
))), ))),
@ -83,21 +98,6 @@ class ThenContainer extends StatelessWidget {
), ),
); );
}, },
// onWillAcceptWithDetails: (data) {
// if (data == null) return false;
// return data.data;
// // if (state.isTabToRun) {
// // return data.data['type'] == 'automation';
// // }
// // if (state.isAutomation) {
// // return data.data['type'] == 'scene' ||
// // data.data['type'] == 'automation';
// // }
// // return data.data['deviceId'] != null;
// },
onAcceptWithDetails: (data) async { onAcceptWithDetails: (data) async {
final uniqueCustomId = const Uuid().v4(); final uniqueCustomId = const Uuid().v4();
final mutableData = Map<String, dynamic>.from(data.data); final mutableData = Map<String, dynamic>.from(data.data);
@ -128,8 +128,23 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
context.read<RoutineBloc>().add(AddToThenContainer({
...mutableData,
'imagePath': Assets.logo,
'title': mutableData['name'],
}));
return;
}
if (mutableData['type'] == 'tap_to_run' && !state.isAutomation) {
return;
}
if (mutableData['deviceId'] == 'delay') { if (mutableData['deviceId'] == 'delay') {
final result = await DelayHelper.showDelayPickerDialog(context, mutableData); final result =
await DelayHelper.showDelayPickerDialog(context, mutableData);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context.read<RoutineBloc>().add(AddToThenContainer({
@ -141,11 +156,13 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData); final result = await DeviceDialogHelper.showDeviceDialog(
context, mutableData,
removeComparetors: true);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { } else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} }
}, },

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart'; import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
@ -67,17 +68,24 @@ class SceneApi {
return response; return response;
} }
//get scene by unit id //get scenes by community id and space id
static Future<List<ScenesModel>> getScenesByUnitId(String unitId) async { static Future<List<ScenesModel>> getScenesByUnitId(
String unitId, String communityId,
{showInDevice = false}) async {
try { try {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getSpaceScenes.replaceAll('{unitUuid}', unitId), path: ApiEndpoints.getUnitScenes
.replaceAll('{spaceUuid}', unitId)
.replaceAll('{communityUuid}', communityId),
queryParameters: {'showInHomePage': showInDevice},
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
final scenesJson = json['data'] as List;
List<ScenesModel> scenes = []; List<ScenesModel> scenes = [];
for (var scene in json) { for (var scene in scenesJson) {
scenes.add(ScenesModel.fromJson(scene)); scenes.add(ScenesModel.fromJson(scene, isAutomation: false));
} }
return scenes; return scenes;
}, },
@ -122,21 +130,21 @@ class SceneApi {
// } // }
// } // }
// //automation details //automation details
// static Future<SceneDetailsModel> getAutomationDetails( static Future<RoutineDetailsModel> getAutomationDetails(
// String automationId) async { String automationId) async {
// try { try {
// final response = await _httpService.get( final response = await _httpService.get(
// path: ApiEndpoints.getAutomationDetails path: ApiEndpoints.getAutomationDetails
// .replaceAll('{automationId}', automationId), .replaceAll('{automationId}', automationId),
// showServerMessage: false, showServerMessage: false,
// expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json),
// ); );
// return response; return response;
// } catch (e) { } catch (e) {
// rethrow; rethrow;
// } }
// } }
// //
// //updateAutomationStatus // //updateAutomationStatus
// static Future<bool> updateAutomationStatus(String automationId, // static Future<bool> updateAutomationStatus(String automationId,
@ -154,20 +162,19 @@ class SceneApi {
// } // }
// } // }
// //getScene //getScene
// static Future<RoutineDetailsModel> getSceneDetails(String sceneId) async {
// static Future<SceneDetailsModel> getSceneDetails(String sceneId) async { try {
// try { final response = await _httpService.get(
// final response = await _httpService.get( path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
// path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), showServerMessage: false,
// showServerMessage: false, expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json),
// expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), );
// ); return response;
// return response; } catch (e) {
// } catch (e) { rethrow;
// rethrow; }
// } }
// }
// //
// //update Scene // //update Scene
// static updateScene(CreateSceneModel createSceneModel, String sceneId) async { // static updateScene(CreateSceneModel createSceneModel, String sceneId) async {
@ -205,38 +212,39 @@ class SceneApi {
// } // }
// } // }
// //
// //delete Scene
//
// static Future<bool> deleteScene( //delete Scene
// {required String unitUuid, required String sceneId}) async { static Future<bool> deleteScene(
// try { {required String unitUuid, required String sceneId}) async {
// final response = await _httpService.delete( try {
// path: ApiEndpoints.deleteScene final response = await _httpService.delete(
// .replaceAll('{sceneId}', sceneId) path: ApiEndpoints.deleteScene
// .replaceAll('{unitUuid}', unitUuid), .replaceAll('{sceneId}', sceneId)
// showServerMessage: false, .replaceAll('{unitUuid}', unitUuid),
// expectedResponseModel: (json) => json['statusCode'] == 200, showServerMessage: false,
// ); expectedResponseModel: (json) => json['statusCode'] == 200,
// return response; );
// } catch (e) { return response;
// rethrow; } catch (e) {
// } rethrow;
// } }
// }
// // delete automation
// static Future<bool> deleteAutomation( // delete automation
// {required String unitUuid, required String automationId}) async { static Future<bool> deleteAutomation(
// try { {required String unitUuid, required String automationId}) async {
// final response = await _httpService.delete( try {
// path: ApiEndpoints.deleteAutomation final response = await _httpService.delete(
// .replaceAll('{automationId}', automationId) path: ApiEndpoints.deleteAutomation
// .replaceAll('{unitUuid}', unitUuid), .replaceAll('{automationId}', automationId)
// showServerMessage: false, .replaceAll('{unitUuid}', unitUuid),
// expectedResponseModel: (json) => json['statusCode'] == 200, showServerMessage: false,
// ); expectedResponseModel: (json) => json['statusCode'] == 200,
// return response; );
// } catch (e) { return response;
// rethrow; } catch (e) {
// } rethrow;
// } }
}
} }

View File

@ -46,7 +46,6 @@ abstract class ApiEndpoints {
'/schedule/{deviceUuid}/{scheduleUuid}'; '/schedule/{deviceUuid}/{scheduleUuid}';
static const String updateScheduleByDeviceId = static const String updateScheduleByDeviceId =
'/schedule/enable/{deviceUuid}'; '/schedule/enable/{deviceUuid}';
static const String factoryReset = '/device/factory/reset/{deviceUuid}'; static const String factoryReset = '/device/factory/reset/{deviceUuid}';
static const String powerClamp = static const String powerClamp =
'/device/{powerClampUuid}/power-clamp/status'; '/device/{powerClampUuid}/power-clamp/status';
@ -55,4 +54,12 @@ abstract class ApiEndpoints {
static const String getIconScene = '/scene/icon'; static const String getIconScene = '/scene/icon';
static const String createScene = '/scene/tap-to-run'; static const String createScene = '/scene/tap-to-run';
static const String createAutomation = '/automation'; static const String createAutomation = '/automation';
static const String getUnitScenes =
'/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
static const String getAutomationDetails =
'/automation/details/{automationId}';
static const String getScene = '/scene/tap-to-run/{sceneId}';
static const String deleteScene = '/scene/tap-to-run/{sceneId}';
static const String deleteAutomation = '/automation/{automationId}';
} }