diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart index 06adfd6c..6fb41713 100644 --- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart +++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart @@ -94,7 +94,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { return const RoutinesView(); } if (state is ShowCreateRoutineState && state.showCreateRoutine) { - return const CreateNewRoutineView(); + return CreateNewRoutineView(); } return BlocBuilder( diff --git a/lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart b/lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart index f4db836b..7f4ca22e 100644 --- a/lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart +++ b/lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart @@ -16,6 +16,7 @@ class EffectPeriodBloc extends Bloc { }; EffectPeriodBloc() : super(EffectPeriodState.initial()) { + on(_initialEvent); on(_onSetPeriod); on(_onToggleDay); on(_onSetCustomTime); @@ -24,6 +25,15 @@ class EffectPeriodBloc extends Bloc { on(_setAllDays); } + void _initialEvent(InitialEffectPeriodEvent event, Emitter emit) { + add(SetCustomTime(event.effectiveTime.start, event.effectiveTime.end)); + emit(state.copyWith( + selectedDaysBinary: event.effectiveTime.loops, + customStartTime: event.effectiveTime.start, + customEndTime: event.effectiveTime.end, + )); + } + void _onSetPeriod(SetPeriod event, Emitter emit) { String startTime = ''; String endTime = ''; diff --git a/lib/pages/routiens/bloc/effective_period/effect_period_event.dart b/lib/pages/routiens/bloc/effective_period/effect_period_event.dart index e1a86915..20f686d6 100644 --- a/lib/pages/routiens/bloc/effective_period/effect_period_event.dart +++ b/lib/pages/routiens/bloc/effective_period/effect_period_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; abstract class EffectPeriodEvent extends Equatable { @@ -8,6 +9,15 @@ abstract class EffectPeriodEvent extends Equatable { List get props => []; } +class InitialEffectPeriodEvent extends EffectPeriodEvent { + final EffectiveTime effectiveTime; + + const InitialEffectPeriodEvent(this.effectiveTime); + + @override + List get props => [effectiveTime]; +} + class SetPeriod extends EffectPeriodEvent { final EnumEffectivePeriodOptions period; diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart index 841027b3..5f25c247 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart @@ -3,12 +3,18 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_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/delay/delay_fucntions.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/services/devices_mang_api.dart'; import 'package:syncrow_web/services/routines_api.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/navigation_service.dart'; +import 'package:uuid/uuid.dart'; part 'routine_event.dart'; part 'routine_state.dart'; @@ -34,11 +40,12 @@ class RoutineBloc extends Bloc { on(_onResetRoutineState); on(_onGetSceneDetails); on(_onGetAutomationDetails); - // on(_onInitializeRoutineState); on(_deleteScene); - on(_deleteAutomation); - // on(_onRemoveFunction); - // on(_onClearFunctions); + // on(_deleteAutomation); + on(_fetchDevices); + on(_onUpdateScene); + on(_onUpdateAutomation); + on(_onSetAutomationActionExecutor); } void _onAddToIfContainer(AddToIfContainer event, Emitter emit) { @@ -194,6 +201,11 @@ class RoutineBloc extends Bloc { return actions.first['deviceId'] == 'delay'; } + bool _isLastActionDelay(List> actions) { + if (actions.isEmpty) return false; + return actions.last['deviceId'] == 'delay'; + } + Future _onCreateScene( CreateSceneEvent event, Emitter emit) async { try { @@ -206,7 +218,15 @@ class RoutineBloc extends Bloc { return; } - emit(state.copyWith(isLoading: true)); + if (_isLastActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the last action', + isLoading: false, + )); + return; + } + + emit(state.copyWith(isLoading: true, errorMessage: null)); final actions = state.thenItems.expand((item) { final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; @@ -254,8 +274,10 @@ class RoutineBloc extends Bloc { final result = await SceneApi.createScene(createSceneModel); if (result['success']) { - emit(_resetState()); + Navigator.of(NavigationService.navigatorKey.currentContext!).pop(); + add(ResetRoutineState()); add(const LoadScenes(spaceId, communityId)); + add(const LoadAutomation(spaceId)); } else { emit(state.copyWith( isLoading: false, @@ -279,8 +301,22 @@ class RoutineBloc extends Bloc { )); return; } + if (_isFirstActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the first action', + isLoading: false, + )); + return; + } - emit(state.copyWith(isLoading: true)); + if (_isLastActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the last action', + isLoading: false, + )); + return; + } + emit(state.copyWith(isLoading: true, errorMessage: null)); final conditions = state.ifItems.expand((item) { final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; @@ -355,8 +391,10 @@ class RoutineBloc extends Bloc { final result = await SceneApi.createAutomation(createAutomationModel); if (result['success']) { - emit(_resetState()); + Navigator.of(NavigationService.navigatorKey.currentContext!).pop(); + add(ResetRoutineState()); add(const LoadAutomation(spaceId)); + add(const LoadScenes(spaceId, communityId)); } else { emit(state.copyWith( isLoading: false, @@ -419,21 +457,285 @@ class RoutineBloc extends Bloc { emit(state.copyWith(routineName: event.name)); } + ( + List>, + List>, + Map> + ) _createCardData( + List actions, + List? conditions, + Map> currentFunctions, + bool isTabToRun, + ) { + final ifItems = isTabToRun + ? [ + { + 'entityId': 'tab_to_run', + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': 'tab_to_run', + 'title': 'Tab to run', + 'productType': 'tab_to_run', + 'imagePath': Assets.tabToRun, + } + ] + : conditions?.map((condition) { + final matchingDevice = state.devices.firstWhere( + (device) => device.uuid == condition.entityId, + orElse: () => AllDevicesModel( + uuid: condition.entityId, + name: condition.entityId, + productType: condition.entityType, + ), + ); + + final cardData = { + 'entityId': condition.entityId, + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': condition.entityId, + 'title': matchingDevice.name ?? condition.entityId, + 'productType': condition.entityType, + 'imagePath': + matchingDevice.getDefaultIcon(condition.entityType), + }; + + final functions = matchingDevice.functions; + + for (var function in functions) { + if (function.code == condition.expr.statusCode) { + currentFunctions[cardData['uniqueCustomId'] ?? ''] = [ + DeviceFunctionData( + entityId: condition.entityId, + functionCode: condition.expr.statusCode, + value: condition.expr.statusValue, + operationName: function.operationName, + ), + ]; + break; + } + } + + return cardData; + }).toList() ?? + []; + + final thenItems = actions.map((action) { + final matchingDevice = state.devices.firstWhere( + (device) => device.uuid == action.entityId, + orElse: () => AllDevicesModel( + uuid: action.entityId, + name: action.entityId, + productType: action.productType, + ), + ); + + final cardData = { + 'entityId': action.entityId, + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : (matchingDevice.name ?? 'Device'), + 'productType': action.productType, + 'imagePath': matchingDevice.getDefaultIcon(action.productType), + }; + + final functions = matchingDevice.functions; + + if (action.executorProperty != null && action.actionExecutor != 'delay') { + final functionCode = action.executorProperty!.functionCode; + for (var function in functions) { + if (function.code == functionCode) { + currentFunctions[cardData['uniqueCustomId'] ?? ''] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: functionCode ?? '', + value: action.executorProperty!.functionValue, + operationName: function.operationName, + ), + ]; + break; + } + } + } else if (action.actionExecutor == 'delay') { + final delayFunction = DelayFunction( + deviceId: action.entityId, + deviceName: 'Delay', + ); + currentFunctions[cardData['uniqueCustomId'] ?? ''] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'delay', + value: action.executorProperty?.delaySeconds ?? 0, + operationName: delayFunction.operationName, + ), + ]; + } + + return cardData; + }).toList(); + + return (thenItems, ifItems, currentFunctions); + } + Future _onGetSceneDetails( GetSceneDetails event, Emitter emit) async { try { emit(state.copyWith( - isLoading: true, - isTabToRun: event.isTabToRun, - isUpdate: true, - sceneId: event.sceneId, - isAutomation: false)); + isLoading: true, + isTabToRun: true, + isUpdate: true, + sceneId: event.sceneId, + routineName: null, + isAutomation: false, + selectedFunctions: {}, + ifItems: [], + thenItems: [], + errorMessage: null, + loadScenesErrorMessage: null, + loadAutomationErrorMessage: null, + searchText: '', + selectedIcon: null, + selectedAutomationOperator: 'or', + effectiveTime: null, + )); + final sceneDetails = await SceneApi.getSceneDetails(event.sceneId); - add(InitializeRoutineState(sceneDetails)); + + final List> thenItems; + final List> ifItems; + final Map> updatedFunctions = + Map>.from(state.selectedFunctions); + + final Map> deviceCards = {}; + + for (var action in sceneDetails.actions) { + AllDevicesModel? matchingDevice; + for (var device in state.devices) { + if (device.uuid == action.entityId) { + matchingDevice = device; + break; + } + } + + final deviceId = action.type == 'automation' + ? '${action.entityId}_automation' + : action.actionExecutor == 'delay' + ? '${action.entityId}_delay' + : action.entityId; + + if (!deviceCards.containsKey(deviceId)) { + deviceCards[deviceId] = { + 'entityId': action.entityId, + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'uniqueCustomId': + action.type == 'automation' || action.actionExecutor == 'delay' + ? const Uuid().v4() + : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : action.type == 'automation' + ? action.name ?? 'Automation' + : (matchingDevice?.name ?? 'Device'), + 'productType': action.productType, + 'functions': matchingDevice?.functions, + 'imagePath': action.type == 'automation' + ? Assets.automation + : action.actionExecutor == 'delay' + ? Assets.delay + : matchingDevice?.getDefaultIcon(action.productType), + 'device': matchingDevice, + 'name': action.name, + 'type': action.type, + }; + } + + final cardData = deviceCards[deviceId]!; + final uniqueCustomId = cardData['uniqueCustomId'].toString(); + + if (action.type == 'automation') { + if (!updatedFunctions.containsKey(uniqueCustomId)) { + updatedFunctions[uniqueCustomId] = []; + } + updatedFunctions[uniqueCustomId]!.add( + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'automation', + value: action.actionExecutor, + operationName: 'Automation', + ), + ); + emit(state.copyWith(automationActionExecutor: action.actionExecutor)); + } else if (action.executorProperty != null && + action.actionExecutor != 'delay') { + if (!updatedFunctions.containsKey(uniqueCustomId)) { + updatedFunctions[uniqueCustomId] = []; + } + final functions = matchingDevice?.functions; + final functionCode = action.executorProperty?.functionCode; + for (DeviceFunction function in functions ?? []) { + if (function.code == functionCode) { + updatedFunctions[uniqueCustomId]!.add( + DeviceFunctionData( + entityId: action.entityId, + functionCode: functionCode ?? '', + value: action.executorProperty?.functionValue, + operationName: function.operationName, + ), + ); + break; + } + } + } else if (action.actionExecutor == 'delay') { + if (!updatedFunctions.containsKey(uniqueCustomId)) { + updatedFunctions[uniqueCustomId] = []; + } + final delayFunction = DelayFunction( + deviceId: action.entityId, + deviceName: 'Delay', + ); + updatedFunctions[uniqueCustomId]!.add( + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'delay', + value: action.executorProperty?.delaySeconds ?? 0, + operationName: delayFunction.operationName, + ), + ); + } + } + + thenItems = deviceCards.values.toList(); + + ifItems = [ + { + 'entityId': 'tab_to_run', + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': 'tab_to_run', + 'title': 'Tab to run', + 'productType': 'tab_to_run', + 'imagePath': Assets.tabToRun, + } + ]; + + emit(state.copyWith( + isLoading: false, + routineName: sceneDetails.name, + selectedIcon: sceneDetails.iconId, + selectedAutomationOperator: sceneDetails.decisionExpr, + effectiveTime: sceneDetails.effectiveTime, + isAutomation: false, + isTabToRun: event.isTabToRun, + thenItems: thenItems, + ifItems: ifItems, + selectedFunctions: updatedFunctions, + sceneId: sceneDetails.sceneId, + )); } catch (e) { emit(state.copyWith( isLoading: false, - errorMessage: 'Failed to load scene details', + errorMessage: 'Failed to load scene details: $e', )); } } @@ -443,102 +745,158 @@ class RoutineBloc extends Bloc { try { emit(state.copyWith( isLoading: true, - isAutomation: event.isAutomation, - automationId: event.automationId, - isTabToRun: false, isUpdate: true, + isTabToRun: false, + automationId: event.automationId, + isAutomation: true, + ifItems: [], + thenItems: [], )); + final automationDetails = await SceneApi.getAutomationDetails(event.automationId); - add(InitializeRoutineState(automationDetails)); + + final List> thenItems; + final List> ifItems; + final Map> updatedFunctions = + Map>.from(state.selectedFunctions); + + ifItems = automationDetails.conditions?.map((condition) { + late AllDevicesModel? matchingDevice; + for (var device in state.devices) { + if (device.uuid == condition.entityId) { + matchingDevice = device; + break; + } + } + + final cardData = { + 'entityId': condition.entityId, + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': condition.expr.statusCode == 'delay' + ? 'delay' + : condition.entityId, + 'title': condition.expr.statusCode == 'delay' + ? 'Delay' + : (matchingDevice?.name ?? 'Device'), + 'productType': condition.productType, + 'functions': matchingDevice?.functions ?? [], + 'imagePath': + matchingDevice?.getDefaultIcon(condition.productType) ?? '', + 'device': matchingDevice, + }; + + final functions = matchingDevice?.functions ?? []; + for (var function in functions) { + if (function.code == condition.expr.statusCode) { + updatedFunctions[cardData['uniqueCustomId'].toString()] = [ + DeviceFunctionData( + entityId: condition.entityId, + functionCode: condition.expr.statusCode, + value: condition.expr.statusValue, + operationName: function.operationName, + ), + ]; + break; + } + } + + return cardData; + }).toList() ?? + []; + + // Create then items from actions + thenItems = automationDetails.actions.map((action) { + final matchingDevice = state.devices.firstWhere( + (device) => device.uuid == action.entityId, + orElse: () => AllDevicesModel( + uuid: action.entityId, + name: action.entityId, + productType: action.productType, + ), + ); + + final cardData = { + 'entityId': action.entityId, + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : (matchingDevice.name ?? 'Device'), + 'productType': action.productType, + 'functions': matchingDevice.functions, + 'imagePath': matchingDevice.getDefaultIcon(action.productType), + 'device': matchingDevice, + }; + + if (action.executorProperty != null && + action.actionExecutor != 'delay') { + final functions = matchingDevice.functions; + final functionCode = action.executorProperty!.functionCode; + for (var function in functions) { + if (function.code == functionCode) { + updatedFunctions[cardData['uniqueCustomId'].toString()] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: functionCode ?? '', + value: action.executorProperty!.functionValue, + operationName: function.operationName, + ), + ]; + break; + } + } + } else if (action.actionExecutor == 'delay') { + final delayFunction = DelayFunction( + deviceId: action.entityId, + deviceName: 'Delay', + ); + updatedFunctions[cardData['uniqueCustomId'].toString()] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'delay', + value: action.executorProperty?.delaySeconds ?? 0, + operationName: delayFunction.operationName, + ), + ]; + } else if (action.actionExecutor == 'rule_disable' || + action.actionExecutor == 'rule_enable') { + updatedFunctions[cardData['uniqueCustomId'].toString()] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'automation', + value: action.actionExecutor, + operationName: action.name ?? 'Automation', + ), + ]; + } + + return cardData; + }).toList(); + + emit(state.copyWith( + isLoading: false, + routineName: automationDetails.name, + selectedAutomationOperator: automationDetails.decisionExpr, + effectiveTime: automationDetails.effectiveTime, + isAutomation: true, + thenItems: thenItems, + ifItems: ifItems, + selectedFunctions: updatedFunctions, + automationId: automationDetails.automationId, + )); } catch (e) { emit(state.copyWith( isLoading: false, - errorMessage: 'Failed to load automation details', + errorMessage: 'Failed to load automation details: $e', )); } } - // void _onInitializeRoutineState( - // InitializeRoutineState event, Emitter emit) { - // final routineDetails = event.routineDetails; - - // // Convert actions to draggable cards for the THEN container - // final thenItems = routineDetails.actions.map((action) { - // final Map 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( - // 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 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( - // 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() { - return const RoutineState( + FutureOr _onResetRoutineState( + ResetRoutineState event, Emitter emit) { + emit(state.copyWith( ifItems: [], thenItems: [], selectedFunctions: {}, @@ -555,20 +913,22 @@ class RoutineBloc extends Bloc { selectedAutomationOperator: 'or', effectiveTime: null, routineName: null, - ); - } - - FutureOr _onResetRoutineState( - ResetRoutineState event, Emitter emit) { - emit(_resetState()); + )); } FutureOr _deleteScene(DeleteScene event, Emitter emit) { try { - // emit(state.copyWith(isLoading: true)); - SceneApi.deleteScene(unitUuid: spaceId, sceneId: event.sceneId); + emit(state.copyWith(isLoading: true)); + if (state.isTabToRun) { + SceneApi.deleteScene(unitUuid: spaceId, sceneId: state.sceneId ?? ''); + } else { + SceneApi.deleteAutomation( + unitUuid: spaceId, automationId: state.automationId ?? ''); + } + add(const LoadScenes(spaceId, communityId)); - //emit(_resetState()); + add(const LoadAutomation(spaceId)); + add(ResetRoutineState()); } catch (e) { emit(state.copyWith( isLoading: false, @@ -577,19 +937,200 @@ class RoutineBloc extends Bloc { } } - FutureOr _deleteAutomation( - DeleteAutomation event, Emitter emit) { + // FutureOr _deleteAutomation(DeleteAutomation event, Emitter emit) { + // try { + // emit(state.copyWith(isLoading: true)); + // add(const LoadAutomation(spaceId)); + // add(const LoadScenes(spaceId, communityId)); + // emit(_resetState()); + // } catch (e) { + // emit(state.copyWith( + // isLoading: false, + // errorMessage: 'Failed to delete automation', + // )); + // } + // } + + FutureOr _fetchDevices( + FetchDevicesInRoutine event, Emitter emit) async { + emit(state.copyWith(isLoading: true)); try { - //emit(state.copyWith(isLoading: true)); - SceneApi.deleteAutomation( - unitUuid: spaceId, automationId: event.automationId); - add(const LoadAutomation(spaceId)); - // emit(_resetState()); + final devices = await DevicesManagementApi().fetchDevices(); + + emit(state.copyWith(isLoading: false, devices: devices)); + } catch (e) { + emit(state.copyWith(isLoading: false)); + } + } + + FutureOr _onUpdateScene( + UpdateScene event, Emitter emit) async { + try { + // Check if first action is delay + if (_isFirstActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the first action', + isLoading: false, + )); + + return; + } + + if (_isLastActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the last action', + isLoading: false, + )); + return; + } + + emit(state.copyWith(isLoading: true, errorMessage: null)); + + final actions = state.thenItems.expand((item) { + final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; + return functions.map((function) { + if (function.functionCode == 'automation') { + return CreateSceneAction( + entityId: function.entityId, + actionExecutor: function.value, + executorProperty: null, + ); + } + + if (item['deviceId'] == 'delay') { + return CreateSceneAction( + entityId: function.entityId, + actionExecutor: 'delay', + executorProperty: CreateSceneExecutorProperty( + functionCode: '', + functionValue: '', + delaySeconds: int.tryParse(function.value.toString()) ?? 0, + ), + ); + } + + return CreateSceneAction( + entityId: function.entityId, + actionExecutor: 'device_issue', + executorProperty: CreateSceneExecutorProperty( + functionCode: function.functionCode, + functionValue: function.value, + delaySeconds: 0, + ), + ); + }); + }).toList(); + + final createSceneModel = CreateSceneModel( + spaceUuid: state.sceneId ?? '', + iconId: state.selectedIcon ?? '', + showInDevice: true, + sceneName: state.routineName ?? '', + decisionExpr: 'and', + actions: actions, + ); + + final result = + await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); + if (result['success']) { + Navigator.of(NavigationService.navigatorKey.currentContext!).pop(); + add(ResetRoutineState()); + add(const LoadScenes(spaceId, communityId)); + add(const LoadAutomation(spaceId)); + } else { + emit(state.copyWith( + isLoading: false, + errorMessage: result['message'], + )); + } } catch (e) { emit(state.copyWith( isLoading: false, - errorMessage: 'Failed to delete automation', + errorMessage: 'Something went wrong', )); } } + + FutureOr _onUpdateAutomation( + UpdateAutomation event, Emitter emit) async { + if (_isFirstActionDelay(state.thenItems)) { + emit(state.copyWith( + errorMessage: 'Cannot have delay as the first action', + isLoading: false, + )); + return; + } + emit(state.copyWith(isLoading: true)); + try { + final conditions = state.ifItems + .map((item) { + final functions = + state.selectedFunctions[item['uniqueCustomId']] ?? []; + if (functions.isEmpty) return null; + + final function = functions.first; + return Condition( + code: state.ifItems.indexOf(item) + 1, + entityId: function.entityId, + entityType: item['productType'], + expr: ConditionExpr( + statusCode: function.functionCode, + statusValue: function.value, + comparator: function.condition ?? '==', + ), + ); + }) + .whereType() + .toList(); + + final actions = state.thenItems + .map((item) { + final functions = + state.selectedFunctions[item['uniqueCustomId']] ?? []; + if (functions.isEmpty) return null; + + final function = functions.first; + return AutomationAction( + entityId: function.entityId, + actionExecutor: function.actionExecutor, + executorProperty: ExecutorProperty( + functionCode: function.functionCode, + functionValue: function.value, + delaySeconds: function.functionCode == 'delay' + ? (function.value as num).toInt() + : null, + ), + ); + }) + .whereType() + .toList(); + + final createAutomationModel = CreateAutomationModel( + spaceUuid: spaceId, + automationName: state.routineName ?? '', + decisionExpr: state.selectedAutomationOperator, + effectiveTime: + state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), + conditions: conditions, + actions: actions, + ); + + await SceneApi.updateAutomation( + createAutomationModel, state.automationId ?? ''); + + add(const LoadAutomation(spaceId)); + emit(state.copyWith(isLoading: false)); + } catch (e) { + emit(state.copyWith( + isLoading: false, + errorMessage: 'Failed to update automation: $e', + )); + } + } + + FutureOr _onSetAutomationActionExecutor( + SetAutomationActionExecutor event, Emitter emit) { + emit(state.copyWith( + automationActionExecutor: event.automationActionExecutor)); + } } diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart index 82e7875b..2e12bc2e 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart @@ -84,8 +84,7 @@ class RemoveDragCard extends RoutineEvent { final int index; final bool isFromThen; final String key; - const RemoveDragCard( - {required this.index, required this.isFromThen, required this.key}); + const RemoveDragCard({required this.index, required this.isFromThen, required this.key}); @override List get props => [index, isFromThen, key]; } @@ -105,12 +104,10 @@ class EffectiveTimePeriodEvent extends RoutineEvent { } class CreateAutomationEvent extends RoutineEvent { - // final CreateAutomationModel createAutomationModel; final String? automationId; final bool updateAutomation; const CreateAutomationEvent({ - //required this.createAutomationModel, this.automationId, this.updateAutomation = false, }); @@ -159,21 +156,39 @@ class InitializeRoutineState extends RoutineEvent { } class DeleteScene extends RoutineEvent { - final String sceneId; - final String unitUuid; - const DeleteScene({required this.sceneId, required this.unitUuid}); + const DeleteScene(); @override - List get props => [sceneId]; + List get props => []; } -class DeleteAutomation extends RoutineEvent { - final String automationId; - final String unitUuid; - const DeleteAutomation({required this.automationId, required this.unitUuid}); +// class DeleteAutomation extends RoutineEvent { +// final String automationId; +// const DeleteAutomation({required this.automationId}); +// @override +// List get props => [automationId]; +// } + +class UpdateScene extends RoutineEvent { + const UpdateScene(); @override - List get props => [automationId]; + List get props => []; } +class UpdateAutomation extends RoutineEvent { + const UpdateAutomation(); + @override + List get props => []; +} + +class SetAutomationActionExecutor extends RoutineEvent { + final String automationActionExecutor; + const SetAutomationActionExecutor({required this.automationActionExecutor}); + @override + List get props => [automationActionExecutor]; +} + +class FetchDevicesInRoutine extends RoutineEvent {} + class ResetRoutineState extends RoutineEvent {} class ClearFunctions extends RoutineEvent {} diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart index 8f8d9b5c..cd097d46 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart @@ -21,6 +21,8 @@ class RoutineState extends Equatable { final String? sceneId; final String? automationId; final bool? isUpdate; + final List devices; + final String? automationActionExecutor; const RoutineState({ this.ifItems = const [], @@ -43,6 +45,8 @@ class RoutineState extends Equatable { this.sceneId, this.automationId, this.isUpdate, + this.devices = const [], + this.automationActionExecutor, }); RoutineState copyWith({ @@ -65,6 +69,8 @@ class RoutineState extends Equatable { String? sceneId, String? automationId, bool? isUpdate, + List? devices, + String? automationActionExecutor, }) { return RoutineState( ifItems: ifItems ?? this.ifItems, @@ -89,6 +95,9 @@ class RoutineState extends Equatable { sceneId: sceneId ?? this.sceneId, automationId: automationId ?? this.automationId, isUpdate: isUpdate ?? this.isUpdate, + devices: devices ?? this.devices, + automationActionExecutor: + automationActionExecutor ?? this.automationActionExecutor, ); } @@ -112,6 +121,8 @@ class RoutineState extends Equatable { effectiveTime, sceneId, automationId, - isUpdate + isUpdate, + devices, + automationActionExecutor, ]; } diff --git a/lib/pages/routiens/helper/save_routine_helper.dart b/lib/pages/routiens/helper/save_routine_helper.dart index df9e8323..2f48eead 100644 --- a/lib/pages/routiens/helper/save_routine_helper.dart +++ b/lib/pages/routiens/helper/save_routine_helper.dart @@ -54,23 +54,27 @@ class SaveRoutineHelper { ), if (state.isAutomation) ...state.ifItems.map((item) { - final functions = - state.selectedFunctions[item['uniqueCustomId']] ?? []; + final functions = state.selectedFunctions[ + item['uniqueCustomId']] ?? + []; return ListTile( leading: SvgPicture.asset( item['imagePath'], width: 22, height: 22, ), - title: - Text(item['title'], style: const TextStyle(fontSize: 14)), + title: Text(item['title'], + style: const TextStyle(fontSize: 14)), subtitle: Wrap( children: functions .map((f) => Text( '${f.operationName}: ${f.value}, ', style: const TextStyle( - color: ColorsManager.grayColor, fontSize: 8), - overflow: TextOverflow.ellipsis, + color: ColorsManager + .grayColor, + fontSize: 8), + overflow: + TextOverflow.ellipsis, maxLines: 3, )) .toList(), @@ -95,22 +99,25 @@ class SaveRoutineHelper { ), const SizedBox(height: 8), ...state.thenItems.map((item) { - final functions = - state.selectedFunctions[item['uniqueCustomId']] ?? []; + final functions = state.selectedFunctions[ + item['uniqueCustomId']] ?? + []; return ListTile( leading: SvgPicture.asset( item['imagePath'], width: 22, height: 22, ), - title: - Text(item['title'], style: const TextStyle(fontSize: 14)), + title: Text(item['title'], + style: const TextStyle(fontSize: 14)), subtitle: Wrap( children: functions .map((f) => Text( '${f.operationName}: ${f.value}, ', style: const TextStyle( - color: ColorsManager.grayColor, fontSize: 8), + color: + ColorsManager.grayColor, + fontSize: 8), overflow: TextOverflow.ellipsis, maxLines: 3, )) @@ -136,12 +143,30 @@ class SaveRoutineHelper { onCancel: () => Navigator.pop(context, false), onConfirm: () { if (state.isAutomation) { - context.read().add(const CreateAutomationEvent()); + if (state.automationId != null) { + context + .read() + .add(const UpdateAutomation()); + } else { + context + .read() + .add(const CreateAutomationEvent()); + } } else { - context.read().add(const CreateSceneEvent()); + if (state.sceneId != null) { + context + .read() + .add(const UpdateScene()); + } else { + context + .read() + .add(const CreateSceneEvent()); + } + if (context.read().state.errorMessage == + null) { + Navigator.pop(context, true); + } } - - Navigator.pop(context, true); }, isConfirmEnabled: true, ), diff --git a/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart b/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart index c02e5dab..08ee75a1 100644 --- a/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart +++ b/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart @@ -17,7 +17,7 @@ class CreateAutomationModel { required this.actions, }); - Map toMap() { + Map toMap([String? automationId]) { return { 'spaceUuid': spaceUuid, 'automationName': automationName, @@ -41,7 +41,7 @@ class CreateAutomationModel { ); } - String toJson() => json.encode(toMap()); + String toJson(String? automationId) => json.encode(toMap(automationId)); factory CreateAutomationModel.fromJson(String source) => CreateAutomationModel.fromMap(json.decode(source)); diff --git a/lib/pages/routiens/models/routine_details_model.dart b/lib/pages/routiens/models/routine_details_model.dart index 8ad48c58..169bd237 100644 --- a/lib/pages/routiens/models/routine_details_model.dart +++ b/lib/pages/routiens/models/routine_details_model.dart @@ -13,6 +13,8 @@ class RoutineDetailsModel { final EffectiveTime? effectiveTime; final List? conditions; final String? type; + final String? sceneId; + final String? automationId; RoutineDetailsModel({ required this.spaceUuid, @@ -24,6 +26,8 @@ class RoutineDetailsModel { this.effectiveTime, this.conditions, this.type, + this.sceneId, + this.automationId, }); // Convert to CreateSceneModel @@ -57,12 +61,14 @@ class RoutineDetailsModel { 'name': name, 'decisionExpr': decisionExpr, 'actions': actions.map((x) => x.toMap()).toList(), - if (iconId != null) 'iconId': iconId, + if (iconId != null) 'iconUuid': 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, + if (sceneId != null) 'sceneId': sceneId, + if (automationId != null) 'automationId': automationId, }; } @@ -74,7 +80,7 @@ class RoutineDetailsModel { actions: List.from( map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [], ), - iconId: map['iconId'], + iconId: map['iconUuid'], showInDevice: map['showInDevice'], effectiveTime: map['effectiveTime'] != null ? EffectiveTime.fromMap(map['effectiveTime']) @@ -84,6 +90,8 @@ class RoutineDetailsModel { map['conditions'].map((x) => RoutineCondition.fromMap(x))) : null, type: map['type'], + sceneId: map['sceneId'], + automationId: map['automationId'], ); } @@ -96,12 +104,18 @@ class RoutineDetailsModel { class RoutineAction { final String entityId; final String actionExecutor; + final String? name; final RoutineExecutorProperty? executorProperty; + final String productType; + final String? type; RoutineAction({ required this.entityId, required this.actionExecutor, + required this.productType, this.executorProperty, + this.name, + this.type, }); CreateSceneAction toCreateSceneAction() { @@ -124,6 +138,8 @@ class RoutineAction { return { 'entityId': entityId, 'actionExecutor': actionExecutor, + if (type != null) 'type': type, + if (name != null) 'name': name, if (executorProperty != null) 'executorProperty': executorProperty!.toMap(), }; @@ -133,6 +149,9 @@ class RoutineAction { return RoutineAction( entityId: map['entityId'] ?? '', actionExecutor: map['actionExecutor'] ?? '', + productType: map['productType'] ?? '', + name: map['name'] ?? '', + type: map['type'] ?? '', executorProperty: map['executorProperty'] != null ? RoutineExecutorProperty.fromMap(map['executorProperty']) : null, @@ -189,12 +208,14 @@ class RoutineCondition { final String entityId; final String entityType; final RoutineConditionExpr expr; + final String productType; RoutineCondition({ required this.code, required this.entityId, required this.entityType, required this.expr, + required this.productType, }); Condition toCondition() { @@ -221,6 +242,7 @@ class RoutineCondition { entityId: map['entityId'] ?? '', entityType: map['entityType'] ?? '', expr: RoutineConditionExpr.fromMap(map['expr']), + productType: map['productType'] ?? '', ); } } diff --git a/lib/pages/routiens/view/create_new_routine_view.dart b/lib/pages/routiens/view/create_new_routine_view.dart index 320eb15d..dedfada8 100644 --- a/lib/pages/routiens/view/create_new_routine_view.dart +++ b/lib/pages/routiens/view/create_new_routine_view.dart @@ -16,6 +16,7 @@ class CreateNewRoutineView extends StatelessWidget { this.routineId, this.isScene = true, }); + @override Widget build(BuildContext context) { return Container( @@ -67,7 +68,7 @@ class CreateNewRoutineView extends StatelessWidget { width: double.infinity, color: ColorsManager.dialogBlueTitle, ), - + /// THEN Container Expanded( child: Card( diff --git a/lib/pages/routiens/view/routines_view.dart b/lib/pages/routiens/view/routines_view.dart index be5a5a3a..45b71c0b 100644 --- a/lib/pages/routiens/view/routines_view.dart +++ b/lib/pages/routiens/view/routines_view.dart @@ -7,15 +7,28 @@ import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routi import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class RoutinesView extends StatelessWidget { +class RoutinesView extends StatefulWidget { const RoutinesView({super.key}); + @override + State createState() => _RoutinesViewState(); +} + +class _RoutinesViewState extends State { + @override + void initState() { + super.initState(); + context.read().add(FetchDevicesInRoutine()); + } + @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is ShowCreateRoutineState && state.showCreateRoutine) { - return const CreateNewRoutineView(); + return const CreateNewRoutineView( + + ); } return Padding( padding: const EdgeInsets.all(16), diff --git a/lib/pages/routiens/widgets/delete_scene.dart b/lib/pages/routiens/widgets/delete_scene.dart new file mode 100644 index 00000000..7e06a615 --- /dev/null +++ b/lib/pages/routiens/widgets/delete_scene.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/common/custom_dialog.dart'; +import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class DeleteSceneWidget extends StatelessWidget { + const DeleteSceneWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + GestureDetector( + onTap: () async { + await showCustomDialog( + context: context, + message: 'Are you sure you want to delete this scene?', + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + alignment: AlignmentDirectional.center, + child: Text( + 'Cancel', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: ColorsManager.textGray, + ), + ), + ), + ), + Container( + width: 1, height: 50, color: ColorsManager.greyColor), + InkWell( + onTap: () { + context.read().add(const DeleteScene()); + Navigator.of(context).pop(); + Navigator.of(context).pop(true); + }, + child: Container( + alignment: AlignmentDirectional.center, + child: Text( + 'Confirm', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ), + ), + ], + ), + ]); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.delete, + color: ColorsManager.red, + ), + const SizedBox( + width: 2, + ), + Text( + 'Delete', + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: ColorsManager.red, + ), + ), + ], + ), + ), + const SizedBox( + height: 10, + ), + ], + ); + } +} diff --git a/lib/pages/routiens/widgets/dragable_card.dart b/lib/pages/routiens/widgets/dragable_card.dart index 5b4de81f..1eddf841 100644 --- a/lib/pages/routiens/widgets/dragable_card.dart +++ b/lib/pages/routiens/widgets/dragable_card.dart @@ -83,15 +83,19 @@ class DraggableCard extends StatelessWidget { ? SvgPicture.asset( imagePath, ) - : Image.memory( - base64Decode(imagePath), - ), + : imagePath.contains('.png') + ? Image.asset( + imagePath, + ) + : Image.memory( + base64Decode(imagePath), + ), ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 3), child: Text( - title, + deviceData['title'] ?? deviceData['name'] ?? title, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 2, @@ -104,7 +108,6 @@ class DraggableCard extends StatelessWidget { ], ), if (deviceFunctions.isNotEmpty) - // const Divider(height: 1), ...deviceFunctions.map((function) => Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart b/lib/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart index c9a5114f..4e41b3f6 100644 --- a/lib/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart +++ b/lib/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.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/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'; @@ -11,8 +12,7 @@ class FetchRoutineScenesAutomation extends StatefulWidget { const FetchRoutineScenesAutomation({super.key}); @override - State createState() => - _FetchRoutineScenesState(); + State createState() => _FetchRoutineScenesState(); } class _FetchRoutineScenesState extends State @@ -67,49 +67,23 @@ class _FetchRoutineScenesState extends State 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() - .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: RoutineViewCard( + onTap: () { + BlocProvider.of(context).add( + const CreateNewRoutineViewEvent(true), + ); + context.read().add( + GetSceneDetails( + sceneId: state.scenes[index].id, + isTabToRun: true, + isUpdate: true, ), - child: const Center( - child: Icon(Icons.delete, - size: 15, - color: ColorsManager.grayColor), - ), - ), - ), - ), - ], + ); + }, + textString: state.scenes[index].name, + icon: state.scenes[index].icon ?? Assets.logoHorizontal, + isFromScenes: true, + iconInBytes: state.scenes[index].iconInBytes, ), ), ), @@ -142,46 +116,20 @@ class _FetchRoutineScenesState extends State 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().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), - ), - ), - ), - ), - ], + child: RoutineViewCard( + onTap: () { + BlocProvider.of(context).add( + const CreateNewRoutineViewEvent(true), + ); + context.read().add( + GetAutomationDetails( + automationId: state.automations[index].id, + isAutomation: true, + isUpdate: true), + ); + }, + textString: state.automations[index].name, + icon: state.automations[index].icon ?? Assets.automation, ), ), ), diff --git a/lib/pages/routiens/widgets/routine_devices.dart b/lib/pages/routiens/widgets/routine_devices.dart index dbdf71f4..63386b72 100644 --- a/lib/pages/routiens/widgets/routine_devices.dart +++ b/lib/pages/routiens/widgets/routine_devices.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; @@ -10,68 +9,67 @@ class RoutineDevices extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => DeviceManagementBloc()..add(FetchDevices()), - child: BlocBuilder( - builder: (context, state) { - if (state is DeviceManagementLoaded) { - List deviceList = state.devices - .where((device) => - device.productType == 'AC' || - device.productType == '1G' || - device.productType == '2G' || - device.productType == '3G') - .toList(); - - // Provide the RoutineBloc to the child widgets - return BlocBuilder( - builder: (context, routineState) { - return Wrap( - spacing: 10, - runSpacing: 10, - children: deviceList.asMap().entries.map((entry) { - final device = entry.value; - if (routineState.searchText != null && routineState.searchText!.isNotEmpty) { - return device.name! - .toLowerCase() - .contains(routineState.searchText!.toLowerCase()) - ? DraggableCard( - imagePath: device.getDefaultIcon(device.productType), - title: device.name ?? '', - deviceData: { - 'device': device, - 'imagePath': device.getDefaultIcon(device.productType), - 'title': device.name ?? '', - 'deviceId': device.uuid, - 'productType': device.productType, - 'functions': device.functions, - 'uniqueCustomId': '', - }, - ) - : Container(); - } else { - return DraggableCard( - imagePath: device.getDefaultIcon(device.productType), - title: device.name ?? '', - deviceData: { - 'device': device, - 'imagePath': device.getDefaultIcon(device.productType), - 'title': device.name ?? '', - 'deviceId': device.uuid, - 'productType': device.productType, - 'functions': device.functions, - 'uniqueCustomId': '', - }, - ); - } - }).toList(), - ); - }, - ); - } + return BlocBuilder( + builder: (context, state) { + if (state.isLoading) { return const Center(child: CircularProgressIndicator()); - }, - ), + } + + Future.delayed(const Duration(seconds: 1), () { + if (state.devices.isEmpty) { + return const Center(child: Text('No devices found')); + } + }); + + List deviceList = state.devices + .where((device) => + device.productType == 'AC' || + device.productType == '1G' || + device.productType == '2G' || + device.productType == '3G') + .toList(); + + return Wrap( + spacing: 10, + runSpacing: 10, + children: deviceList.asMap().entries.map((entry) { + final device = entry.value; + if (state.searchText != null && state.searchText!.isNotEmpty) { + return device.name! + .toLowerCase() + .contains(state.searchText!.toLowerCase()) + ? DraggableCard( + imagePath: device.getDefaultIcon(device.productType), + title: device.name ?? '', + deviceData: { + 'device': device, + 'imagePath': device.getDefaultIcon(device.productType), + 'title': device.name ?? '', + 'deviceId': device.uuid, + 'productType': device.productType, + 'functions': device.functions, + 'uniqueCustomId': '', + }, + ) + : Container(); + } else { + return DraggableCard( + imagePath: device.getDefaultIcon(device.productType), + title: device.name ?? '', + deviceData: { + 'device': device, + 'imagePath': device.getDefaultIcon(device.productType), + 'title': device.name ?? '', + 'deviceId': device.uuid, + 'productType': device.productType, + 'functions': device.functions, + 'uniqueCustomId': '', + }, + ); + } + }).toList(), + ); + }, ); } } diff --git a/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart index 06995882..16a181b6 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart @@ -7,7 +7,7 @@ import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class AutomationDialog extends StatefulWidget { +class AutomationDialog extends StatelessWidget { final String automationName; final String automationId; final String uniqueCustomId; @@ -19,77 +19,86 @@ class AutomationDialog extends StatefulWidget { required this.uniqueCustomId, }); - @override - _AutomationDialogState createState() => _AutomationDialogState(); -} - -class _AutomationDialogState extends State { - bool _isEnabled = true; - @override Widget build(BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - child: Container( - width: 400, - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - DialogHeader(widget.automationName), - const SizedBox(height: 16), - ListTile( - leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24), - title: const Text('Enable'), - trailing: Radio( - value: true, - groupValue: _isEnabled, - onChanged: (bool? value) { - setState(() { - _isEnabled = value!; - }); - }, - ), - ), - ListTile( - leading: - SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24), - title: const Text('Disable'), - trailing: Radio( - value: false, - groupValue: _isEnabled, - onChanged: (bool? value) { - setState(() { - _isEnabled = value!; - }); - }, - ), - ), - const SizedBox(height: 16), - DialogFooter( - onConfirm: () { - context.read().add( - AddFunctionToRoutine( - [ - DeviceFunctionData( - entityId: widget.automationId, - functionCode: 'automation', - value: _isEnabled ? 'rule_enable' : 'rule_disable', - operationName: 'Automation', + return BlocBuilder( + builder: (context, state) { + final isEnabled = state.automationActionExecutor == 'rule_enable'; + + return Dialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + width: 400, + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DialogHeader(automationName), + const SizedBox(height: 16), + ListTile( + leading: + SvgPicture.asset(Assets.acPower, width: 24, height: 24), + title: const Text('Enable'), + trailing: Radio( + value: true, + groupValue: isEnabled, + onChanged: (bool? value) { + if (value == true) { + context.read().add( + const SetAutomationActionExecutor( + automationActionExecutor: 'rule_enable', + ), + ); + } + }, + ), + ), + ListTile( + leading: SvgPicture.asset(Assets.acPowerOff, + width: 24, height: 24), + title: const Text('Disable'), + trailing: Radio( + value: false, + groupValue: isEnabled, + onChanged: (bool? value) { + if (value == false) { + context.read().add( + const SetAutomationActionExecutor( + automationActionExecutor: 'rule_disable', + ), + ); + } + }, + ), + ), + const SizedBox(height: 16), + DialogFooter( + onConfirm: () { + context.read().add( + AddFunctionToRoutine( + [ + DeviceFunctionData( + entityId: automationId, + functionCode: 'automation', + value: state.automationActionExecutor, + operationName: 'Automation', + ), + ], + uniqueCustomId, ), - ], - widget.uniqueCustomId, - ), - ); - Navigator.of(context).pop(true); - }, - onCancel: () => Navigator.of(context).pop(false), - isConfirmEnabled: true, - dialogWidth: 400, + ); + Navigator.of(context).pop(true); + }, + onCancel: () => Navigator.of(context).pop(false), + isConfirmEnabled: true, + dialogWidth: 400, + ), + ], ), - ], - ), - ), + ), + ); + }, ); } } diff --git a/lib/pages/routiens/widgets/routine_dialogs/setting_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/setting_dialog.dart index 7fc723af..aa4a407b 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/setting_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/setting_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart'; @@ -9,6 +10,7 @@ import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart' import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart'; +import 'package:syncrow_web/pages/routiens/widgets/delete_scene.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:flutter/cupertino.dart'; @@ -22,11 +24,18 @@ class SettingHelper { context: context, builder: (BuildContext context) { final isAutomation = context.read().state.isAutomation; + final effectiveTime = context.read().state.effectiveTime; + return MultiBlocProvider( providers: [ - BlocProvider( - create: (_) => EffectPeriodBloc(), - ), + if (effectiveTime != null) + BlocProvider( + create: (_) => EffectPeriodBloc()..add(InitialEffectPeriodEvent(effectiveTime)), + ), + if (effectiveTime == null) + BlocProvider( + create: (_) => EffectPeriodBloc(), + ), BlocProvider( create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? ''))), ], @@ -44,7 +53,7 @@ class SettingHelper { } return Container( width: context.read().isExpanded ? 800 : 400, - height: context.read().isExpanded && isAutomation ? 500 : 300, + height: context.read().isExpanded && isAutomation ? 500 : 350, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), @@ -167,6 +176,9 @@ class SettingHelper { fontSize: 14)), ], ), + if (context.read().state.isUpdate ?? + false) + const DeleteSceneWidget() ], )), ], @@ -284,6 +296,9 @@ class SettingHelper { fontSize: 14)), ], ), + if (context.read().state.isUpdate ?? + false) + const DeleteSceneWidget() ], )), ], diff --git a/lib/pages/routiens/widgets/routine_search_and_buttons.dart b/lib/pages/routiens/widgets/routine_search_and_buttons.dart index cdc560f6..c094b506 100644 --- a/lib/pages/routiens/widgets/routine_search_and_buttons.dart +++ b/lib/pages/routiens/widgets/routine_search_and_buttons.dart @@ -10,14 +10,41 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; -class RoutineSearchAndButtons extends StatelessWidget { +class RoutineSearchAndButtons extends StatefulWidget { const RoutineSearchAndButtons({ super.key, }); + @override + State createState() => + _RoutineSearchAndButtonsState(); +} + +class _RoutineSearchAndButtonsState extends State { + late TextEditingController _nameController; + + @override + void initState() { + super.initState(); + _nameController = TextEditingController(); + } + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocConsumer( + listenWhen: (previous, current) => + previous.routineName != current.routineName, + listener: (context, state) { + if (state.routineName != _nameController.text) { + _nameController.text = state.routineName ?? ''; + } + }, builder: (context, state) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { @@ -35,26 +62,9 @@ class RoutineSearchAndButtons extends StatelessWidget { children: [ ConstrainedBox( constraints: BoxConstraints( - maxWidth: - constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32), - // child: StatefulTextField( - // title: 'Routine Name', - // initialValue: state.routineName ?? '', - // height: 40, - // // controller: TextEditingController(), - // hintText: 'Please enter the name', - // boxDecoration: containerWhiteDecoration, - // elevation: 0, - // borderRadius: 15, - // isRequired: true, - // width: 450, - // onSubmitted: (value) { - // // context.read().add(SetRoutineName(value)); - // }, - // onChanged: (value) { - // context.read().add(SetRoutineName(value)); - // }, - // ), + maxWidth: constraints.maxWidth > 700 + ? 450 + : constraints.maxWidth - 32), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -63,10 +73,13 @@ class RoutineSearchAndButtons extends StatelessWidget { children: [ Text('* ', style: context.textTheme.bodyMedium! - .copyWith(color: ColorsManager.red, fontSize: 13)), + .copyWith( + color: ColorsManager.red, + fontSize: 13)), Text( 'Routine Name', - style: context.textTheme.bodyMedium!.copyWith( + style: context.textTheme.bodyMedium! + .copyWith( fontSize: 13, fontWeight: FontWeight.w600, color: ColorsManager.blackColor, @@ -80,18 +93,24 @@ class RoutineSearchAndButtons extends StatelessWidget { decoration: containerWhiteDecoration, child: TextFormField( style: context.textTheme.bodyMedium! - .copyWith(color: ColorsManager.blackColor), - initialValue: state.routineName, + .copyWith( + color: ColorsManager.blackColor), + controller: _nameController, decoration: InputDecoration( hintText: 'Please enter the name', hintStyle: context.textTheme.bodyMedium! - .copyWith(fontSize: 12, color: ColorsManager.grayColor), + .copyWith( + fontSize: 12, + color: ColorsManager.grayColor), contentPadding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + const EdgeInsets.symmetric( + horizontal: 12, vertical: 10), border: InputBorder.none, ), onChanged: (value) { - context.read().add(SetRoutineName(value)); + context + .read() + .add(SetRoutineName(value)); }, validator: (value) { if (value == null || value.isEmpty) { @@ -111,15 +130,18 @@ class RoutineSearchAndButtons extends StatelessWidget { width: 200, child: Center( child: DefaultButton( - onPressed: state.isAutomation || state.isTabToRun + onPressed: state.isAutomation || + state.isTabToRun ? () async { - final result = await SettingHelper.showSettingDialog( + final result = await SettingHelper + .showSettingDialog( context: context, + iconId: + state.selectedIcon ?? '', ); if (result != null) { - context - .read() - .add(AddSelectedIcon(result)); + context.read().add( + AddSelectedIcon(result)); } } : null, @@ -175,10 +197,12 @@ class RoutineSearchAndButtons extends StatelessWidget { child: Center( child: DefaultButton( onPressed: () async { - if (state.routineName == null || state.routineName!.isEmpty) { + if (state.routineName == null || + state.routineName!.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: const Text('Please enter the routine name'), + content: const Text( + 'Please enter the routine name'), duration: const Duration(seconds: 2), backgroundColor: ColorsManager.red, action: SnackBarAction( @@ -192,10 +216,12 @@ class RoutineSearchAndButtons extends StatelessWidget { return; } - if (state.ifItems.isEmpty || state.thenItems.isEmpty) { + if (state.ifItems.isEmpty || + state.thenItems.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: const Text('Please add if and then condition'), + content: const Text( + 'Please add if and then condition'), duration: const Duration(seconds: 2), backgroundColor: ColorsManager.red, action: SnackBarAction( @@ -208,13 +234,15 @@ class RoutineSearchAndButtons extends StatelessWidget { ); return; } - final result = - await SaveRoutineHelper.showSaveRoutineDialog(context); + final result = await SaveRoutineHelper + .showSaveRoutineDialog(context); if (result != null && result) { - BlocProvider.of(context).add( + BlocProvider.of(context) + .add( const CreateNewRoutineViewEvent(false), ); - BlocProvider.of(context).add( + BlocProvider.of(context) + .add( const TriggerSwitchTabsEvent(true), ); } @@ -248,11 +276,14 @@ class RoutineSearchAndButtons extends StatelessWidget { child: DefaultButton( onPressed: state.isAutomation || state.isTabToRun ? () async { - final result = await SettingHelper.showSettingDialog( - context: context, - ); + final result = + await SettingHelper.showSettingDialog( + context: context, + iconId: state.selectedIcon ?? ''); if (result != null) { - context.read().add(AddSelectedIcon(result)); + context + .read() + .add(AddSelectedIcon(result)); } } : null, @@ -301,11 +332,13 @@ class RoutineSearchAndButtons extends StatelessWidget { width: 200, child: Center( child: DefaultButton( - onPressed: () { - if (state.routineName == null || state.routineName!.isEmpty) { + onPressed: () async { + if (state.routineName == null || + state.routineName!.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: const Text('Please enter the routine name'), + content: const Text( + 'Please enter the routine name'), duration: const Duration(seconds: 2), backgroundColor: ColorsManager.red, action: SnackBarAction( @@ -319,10 +352,12 @@ class RoutineSearchAndButtons extends StatelessWidget { return; } - if (state.ifItems.isEmpty || state.thenItems.isEmpty) { + if (state.ifItems.isEmpty || + state.thenItems.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: const Text('Please add if and then condition'), + content: const Text( + 'Please add if and then condition'), duration: const Duration(seconds: 2), backgroundColor: ColorsManager.red, action: SnackBarAction( @@ -335,7 +370,17 @@ class RoutineSearchAndButtons extends StatelessWidget { ); return; } - SaveRoutineHelper.showSaveRoutineDialog(context); + final result = + await SaveRoutineHelper.showSaveRoutineDialog( + context); + if (result != null && result) { + BlocProvider.of(context).add( + const CreateNewRoutineViewEvent(false), + ); + BlocProvider.of(context).add( + const TriggerSwitchTabsEvent(true), + ); + } }, borderRadius: 15, elevation: 0, diff --git a/lib/pages/routiens/widgets/then_container.dart b/lib/pages/routiens/widgets/then_container.dart index 9491e8fd..d959c372 100644 --- a/lib/pages/routiens/widgets/then_container.dart +++ b/lib/pages/routiens/widgets/then_container.dart @@ -30,69 +30,109 @@ class ThenContainer extends StatelessWidget { style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), - Wrap( - spacing: 8, - runSpacing: 8, - children: List.generate( - state.thenItems.length, - (index) => GestureDetector( - onTap: () async { - if (state.thenItems[index]['deviceId'] == - 'delay') { - final result = await DelayHelper - .showDelayPickerDialog( - context, state.thenItems[index]); + state.isLoading && state.isUpdate == true + ? const Center( + child: CircularProgressIndicator(), + ) + : Wrap( + spacing: 8, + runSpacing: 8, + children: List.generate( + state.thenItems.length, + (index) => GestureDetector( + onTap: () async { + if (state.thenItems[index] + ['deviceId'] == + 'delay') { + final result = await DelayHelper + .showDelayPickerDialog(context, + state.thenItems[index]); - if (result != null) { - context - .read() - .add(AddToThenContainer({ - ...state.thenItems[index], - 'imagePath': Assets.delay, - 'title': 'Delay', - })); - } - return; - } + if (result != null) { + context + .read() + .add(AddToThenContainer({ + ...state.thenItems[index], + 'imagePath': Assets.delay, + 'title': 'Delay', + })); + } + return; + } - final result = await DeviceDialogHelper - .showDeviceDialog( - context, state.thenItems[index], - removeComparetors: true); + if (state.thenItems[index]['type'] == + 'automation') { + final result = await showDialog( + context: context, + builder: (BuildContext context) => + AutomationDialog( + automationName: + state.thenItems[index] + ['name'] ?? + 'Automation', + automationId: + state.thenItems[index] + ['deviceId'] ?? + '', + uniqueCustomId: + state.thenItems[index] + ['uniqueCustomId'], + ), + ); - if (result != null) { - context.read().add( - AddToThenContainer( - state.thenItems[index])); - } else if (!['AC', '1G', '2G', '3G'] - .contains(state.thenItems[index] - ['productType'])) { - context.read().add( - AddToThenContainer( - state.thenItems[index])); - } - }, - child: DraggableCard( - imagePath: state.thenItems[index] - ['imagePath'] ?? - '', - title: - state.thenItems[index]['title'] ?? '', - deviceData: state.thenItems[index], - padding: const EdgeInsets.symmetric( - horizontal: 4, vertical: 8), - isFromThen: true, - isFromIf: false, - onRemove: () { - context.read().add( - RemoveDragCard( - index: index, - isFromThen: true, - key: state.thenItems[index] - ['uniqueCustomId'])); - }, - ), - ))), + if (result != null) { + context + .read() + .add(AddToThenContainer({ + ...state.thenItems[index], + 'imagePath': + Assets.automation, + 'title': state + .thenItems[index]['name'], + })); + } + return; + } + + final result = await DeviceDialogHelper + .showDeviceDialog( + context, state.thenItems[index], + removeComparetors: true); + + if (result != null) { + context.read().add( + AddToThenContainer( + state.thenItems[index])); + } else if (!['AC', '1G', '2G', '3G'] + .contains(state.thenItems[index] + ['productType'])) { + context.read().add( + AddToThenContainer( + state.thenItems[index])); + } + }, + child: DraggableCard( + imagePath: state.thenItems[index] + ['imagePath'] ?? + '', + title: state.thenItems[index] + ['title'] ?? + '', + deviceData: state.thenItems[index], + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), + isFromThen: true, + isFromIf: false, + onRemove: () { + context.read().add( + RemoveDragCard( + index: index, + isFromThen: true, + key: state.thenItems[index] + ['uniqueCustomId'])); + }, + ), + ))), ], ), ), diff --git a/lib/services/routines_api.dart b/lib/services/routines_api.dart index 333156ba..ffc8a8c7 100644 --- a/lib/services/routines_api.dart +++ b/lib/services/routines_api.dart @@ -36,7 +36,6 @@ class SceneApi { static Future> createAutomation( CreateAutomationModel createAutomationModel) async { try { - debugPrint("automation body ${createAutomationModel.toMap()}"); final response = await _httpService.post( path: ApiEndpoints.createAutomation, body: createAutomationModel.toMap(), @@ -138,29 +137,49 @@ class SceneApi { path: ApiEndpoints.getAutomationDetails .replaceAll('{automationId}', automationId), showServerMessage: false, - expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), + expectedResponseModel: (json) => RoutineDetailsModel.fromMap(json), + ); + return response; + } catch (e) { + rethrow; + } + } + + //update Scene + static updateScene(CreateSceneModel createSceneModel, String sceneId) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId), + body: createSceneModel + .toJson(sceneId.isNotEmpty == true ? sceneId : null), + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + + //update automation + static updateAutomation( + CreateAutomationModel createAutomationModel, String automationId) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.updateAutomation + .replaceAll('{automationId}', automationId), + body: createAutomationModel + .toJson(automationId.isNotEmpty == true ? automationId : null), + expectedResponseModel: (json) { + return json; + }, ); return response; } catch (e) { rethrow; } } -// -// //updateAutomationStatus -// static Future updateAutomationStatus(String automationId, -// AutomationStatusUpdate createAutomationEnable) async { -// try { -// final response = await _httpService.put( -// path: ApiEndpoints.updateAutomationStatus -// .replaceAll('{automationId}', automationId), -// body: createAutomationEnable.toMap(), -// expectedResponseModel: (json) => json['success'], -// ); -// return response; -// } catch (e) { -// rethrow; -// } -// } //getScene static Future getSceneDetails(String sceneId) async { @@ -168,52 +187,15 @@ class SceneApi { final response = await _httpService.get( path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), showServerMessage: false, - expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), + expectedResponseModel: (json) => + RoutineDetailsModel.fromMap(json['data']), ); return response; } catch (e) { rethrow; } } - // - // //update Scene - // static updateScene(CreateSceneModel createSceneModel, String sceneId) async { - // try { - // final response = await _httpService.put( - // path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId), - // body: createSceneModel - // .toJson(sceneId.isNotEmpty == true ? sceneId : null), - // expectedResponseModel: (json) { - // return json; - // }, - // ); - // return response; - // } catch (e) { - // rethrow; - // } - // } - // - // //update automation - // static updateAutomation( - // CreateAutomationModel createAutomationModel, String automationId) async { - // try { - // final response = await _httpService.put( - // path: ApiEndpoints.updateAutomation - // .replaceAll('{automationId}', automationId), - // body: createAutomationModel - // .toJson(automationId.isNotEmpty == true ? automationId : null), - // expectedResponseModel: (json) { - // return json; - // }, - // ); - // return response; - // } catch (e) { - // rethrow; - // } - // } - // - - + //delete Scene static Future deleteScene( {required String unitUuid, required String sceneId}) async { @@ -230,7 +212,7 @@ class SceneApi { rethrow; } } - + // delete automation static Future deleteAutomation( {required String unitUuid, required String automationId}) async { diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index bb37fd60..007b488d 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -74,4 +74,7 @@ abstract class ApiEndpoints { static const String deleteScene = '/scene/tap-to-run/{sceneId}'; static const String deleteAutomation = '/automation/{automationId}'; + static const String updateScene = '/scene/tap-to-run/{sceneId}'; + + static const String updateAutomation = '/automation/{automationId}'; }