From ce6e7700bc710b794deb516ff3065e1f2cadcb69 Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Sat, 30 Nov 2024 20:43:21 +0300 Subject: [PATCH] push changes --- .../bloc/routine_bloc/routine_bloc.dart | 649 +++++++++++++++--- .../bloc/routine_bloc/routine_event.dart | 16 +- .../bloc/routine_bloc/routine_state.dart | 7 +- .../routiens/helper/save_routine_helper.dart | 49 +- .../create_automation_model.dart | 4 +- .../models/routine_details_model.dart | 26 +- .../view/create_new_routine_view.dart | 1 + lib/pages/routiens/view/routines_view.dart | 13 +- lib/pages/routiens/widgets/dragable_card.dart | 13 +- .../fetch_routine_scenes_automation.dart | 29 +- .../routiens/widgets/routine_devices.dart | 122 ++-- .../routiens/widgets/then_container.dart | 126 ++-- lib/services/routines_api.dart | 99 ++- lib/utils/constants/api_const.dart | 3 + 14 files changed, 852 insertions(+), 305 deletions(-) diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart index 841027b3..15596a48 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart @@ -3,12 +3,17 @@ 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:uuid/uuid.dart'; part 'routine_event.dart'; part 'routine_state.dart'; @@ -34,11 +39,11 @@ class RoutineBloc extends Bloc { on(_onResetRoutineState); on(_onGetSceneDetails); on(_onGetAutomationDetails); - // on(_onInitializeRoutineState); on(_deleteScene); on(_deleteAutomation); - // on(_onRemoveFunction); - // on(_onClearFunctions); + on(_fetchDevices); + on(_onUpdateScene); + on(_onUpdateAutomation); } void _onAddToIfContainer(AddToIfContainer event, Emitter emit) { @@ -419,21 +424,254 @@ 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: event.isTabToRun, + isUpdate: true, + sceneId: event.sceneId, + isAutomation: false, + ifItems: [], + thenItems: [], + )); + final sceneDetails = await SceneApi.getSceneDetails(event.sceneId); - add(InitializeRoutineState(sceneDetails)); + + final List> thenItems; + final List> ifItems; + final Map> updatedFunctions = + Map>.from(state.selectedFunctions); + + if (event.isTabToRun) { + thenItems = sceneDetails.actions.map((action) { + late AllDevicesModel? matchingDevice; + for (var device in state.devices) { + if (device.uuid == action.entityId) { + matchingDevice = device; + break; + } + } + + final cardData = { + 'entityId': action.entityId, + 'uniqueCustomId': const Uuid().v4(), + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : action.type == 'automation' + ? action.name ?? 'Automation' + : (matchingDevice?.name ?? 'Device'), + 'productType': action.productType, + 'functions': matchingDevice?.functions, + 'imagePath': matchingDevice?.getDefaultIcon(action.productType), + 'device': matchingDevice, + }; + if (action.type == 'automation') { + updatedFunctions[cardData['uniqueCustomId'].toString()] = [ + DeviceFunctionData( + entityId: action.entityId, + functionCode: 'rule', + value: action.actionExecutor, + operationName: action.name ?? 'Automation', + ), + ]; + } else 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, + ), + ]; + } + + return cardData; + }).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, + } + ]; + } else { + final result = _createCardData( + sceneDetails.actions, + sceneDetails.conditions, + updatedFunctions, + false, + ); + thenItems = result.$1; + ifItems = result.$2; + } + + 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,100 +681,155 @@ 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( ifItems: [], @@ -565,10 +858,8 @@ class RoutineBloc extends Bloc { FutureOr _deleteScene(DeleteScene event, Emitter 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, @@ -580,11 +871,9 @@ class RoutineBloc extends Bloc { FutureOr _deleteAutomation( DeleteAutomation event, Emitter 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, @@ -592,4 +881,170 @@ class RoutineBloc extends Bloc { )); } } + + FutureOr _fetchDevices( + FetchDevicesInRoutine event, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + try { + 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; + } + + emit(state.copyWith(isLoading: true)); + + 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']) { + emit(_resetState()); + add(const LoadScenes(spaceId, communityId)); + } else { + emit(state.copyWith( + isLoading: false, + errorMessage: result['message'], + )); + } + } catch (e) { + emit(state.copyWith( + isLoading: false, + 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', + )); + } + } } diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart index 82e7875b..4c3285c2 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart @@ -105,12 +105,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, }); @@ -174,6 +172,20 @@ class DeleteAutomation extends RoutineEvent { List get props => [automationId]; } +class UpdateScene extends RoutineEvent { + const UpdateScene(); + @override + List get props => []; +} + +class UpdateAutomation extends RoutineEvent { + const UpdateAutomation(); + @override + List get props => []; +} + +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..3baff100 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart @@ -21,6 +21,7 @@ class RoutineState extends Equatable { final String? sceneId; final String? automationId; final bool? isUpdate; + final List devices; const RoutineState({ this.ifItems = const [], @@ -43,6 +44,7 @@ class RoutineState extends Equatable { this.sceneId, this.automationId, this.isUpdate, + this.devices = const [], }); RoutineState copyWith({ @@ -65,6 +67,7 @@ class RoutineState extends Equatable { String? sceneId, String? automationId, bool? isUpdate, + List? devices, }) { return RoutineState( ifItems: ifItems ?? this.ifItems, @@ -89,6 +92,7 @@ class RoutineState extends Equatable { sceneId: sceneId ?? this.sceneId, automationId: automationId ?? this.automationId, isUpdate: isUpdate ?? this.isUpdate, + devices: devices ?? this.devices, ); } @@ -112,6 +116,7 @@ class RoutineState extends Equatable { effectiveTime, sceneId, automationId, - isUpdate + isUpdate, + devices, ]; } diff --git a/lib/pages/routiens/helper/save_routine_helper.dart b/lib/pages/routiens/helper/save_routine_helper.dart index df9e8323..d506783e 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,9 +143,25 @@ 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()); + } } Navigator.pop(context, 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..d80ce5fb 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( diff --git a/lib/pages/routiens/view/routines_view.dart b/lib/pages/routiens/view/routines_view.dart index be5a5a3a..62f6f84f 100644 --- a/lib/pages/routiens/view/routines_view.dart +++ b/lib/pages/routiens/view/routines_view.dart @@ -7,9 +7,20 @@ 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( 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..746a0155 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'; @@ -70,7 +71,19 @@ class _FetchRoutineScenesState extends State child: Stack( children: [ RoutineViewCard( - onTap: () {}, + onTap: () { + BlocProvider.of(context) + .add( + const CreateNewRoutineViewEvent(true), + ); + context.read().add( + GetSceneDetails( + sceneId: state.scenes[index].id, + isTabToRun: true, + isUpdate: true, + ), + ); + }, textString: state.scenes[index].name, icon: state.scenes[index].icon ?? Assets.logoHorizontal, @@ -145,7 +158,19 @@ class _FetchRoutineScenesState extends State child: Stack( children: [ RoutineViewCard( - onTap: () {}, + 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/then_container.dart b/lib/pages/routiens/widgets/then_container.dart index 9491e8fd..d18ac81d 100644 --- a/lib/pages/routiens/widgets/then_container.dart +++ b/lib/pages/routiens/widgets/then_container.dart @@ -30,69 +30,75 @@ 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); + 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'])); - }, - ), - ))), + 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..5b5d7984 100644 --- a/lib/services/routines_api.dart +++ b/lib/services/routines_api.dart @@ -138,29 +138,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 +188,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 +213,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}'; }