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 ffc57131..06adfd6c 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 @@ -18,13 +18,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { Widget build(BuildContext context) { return MultiBlocProvider( providers: [ - BlocProvider( - create: (context) => DeviceManagementBloc()..add(FetchDevices()), - ), BlocProvider( create: (context) => SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)), ), + BlocProvider( + create: (context) => DeviceManagementBloc()..add(FetchDevices()), + ), ], child: WebScaffold( appBarTitle: FittedBox( @@ -101,12 +101,11 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { builder: (context, deviceState) { if (deviceState is DeviceManagementLoading) { return const Center(child: CircularProgressIndicator()); - } else if (deviceState is DeviceManagementLoaded || - deviceState is DeviceManagementFiltered) { - final devices = (deviceState as dynamic).devices ?? - (deviceState as DeviceManagementFiltered).filteredDevices; - - return DeviceManagementBody(devices: devices); + } else if (deviceState is DeviceManagementLoaded) { + return DeviceManagementBody(devices: deviceState.devices); + } else if (deviceState is DeviceManagementFiltered) { + return DeviceManagementBody( + devices: deviceState.filteredDevices); } else { return const Center(child: Text('Error fetching Devices')); } diff --git a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart index e3bec220..b9e36c25 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart @@ -18,14 +18,6 @@ class _DeviceSearchFiltersState extends State final TextEditingController unitNameController = TextEditingController(); final TextEditingController productNameController = TextEditingController(); - @override - void dispose() { - communityController.dispose(); - unitNameController.dispose(); - productNameController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return isExtraLargeScreenSize(context) diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart index 6eb03160..841027b3 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.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/routines_api.dart'; @@ -13,6 +14,7 @@ part 'routine_event.dart'; part 'routine_state.dart'; const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; +const communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; class RoutineBloc extends Bloc { RoutineBloc() : super(const RoutineState()) { @@ -30,6 +32,11 @@ class RoutineBloc extends Bloc { on(_onCreateAutomation); on(_onSetRoutineName); on(_onResetRoutineState); + on(_onGetSceneDetails); + on(_onGetAutomationDetails); + // on(_onInitializeRoutineState); + on(_deleteScene); + on(_deleteAutomation); // on(_onRemoveFunction); // on(_onClearFunctions); } @@ -38,8 +45,8 @@ class RoutineBloc extends Bloc { final updatedIfItems = List>.from(state.ifItems); // Find the index of the item in teh current itemsList - int index = - updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); + int index = updatedIfItems.indexWhere( + (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); // Replace the map if the index is valid if (index != -1) { updatedIfItems[index] = event.item; @@ -48,18 +55,21 @@ class RoutineBloc extends Bloc { } if (event.isTabToRun) { - emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); + emit(state.copyWith( + ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); } else { - emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); + emit(state.copyWith( + ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); } } - void _onAddToThenContainer(AddToThenContainer event, Emitter emit) { + void _onAddToThenContainer( + AddToThenContainer event, Emitter emit) { final currentItems = List>.from(state.thenItems); // Find the index of the item in teh current itemsList - int index = - currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); + int index = currentItems.indexWhere( + (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); // Replace the map if the index is valid if (index != -1) { currentItems[index] = event.item; @@ -70,22 +80,26 @@ class RoutineBloc extends Bloc { emit(state.copyWith(thenItems: currentItems)); } - void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter emit) { + void _onAddFunctionsToRoutine( + AddFunctionToRoutine event, Emitter emit) { try { if (event.functions.isEmpty) return; - List selectedFunction = List.from(event.functions); + List selectedFunction = + List.from(event.functions); Map> currentSelectedFunctions = Map>.from(state.selectedFunctions); if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { List currentFunctions = - List.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); + List.from( + currentSelectedFunctions[event.uniqueCustomId] ?? []); List functionCode = []; for (int i = 0; i < selectedFunction.length; i++) { for (int j = 0; j < currentFunctions.length; j++) { - if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) { + if (selectedFunction[i].functionCode == + currentFunctions[j].functionCode) { currentFunctions[j] = selectedFunction[i]; if (!functionCode.contains(currentFunctions[j].functionCode)) { functionCode.add(currentFunctions[j].functionCode); @@ -95,13 +109,15 @@ class RoutineBloc extends Bloc { } for (int i = 0; i < functionCode.length; i++) { - selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); + selectedFunction + .removeWhere((code) => code.functionCode == functionCode[i]); } - currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) - ..addAll(selectedFunction); + currentSelectedFunctions[event.uniqueCustomId] = + List.from(currentFunctions)..addAll(selectedFunction); } else { - currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions); + currentSelectedFunctions[event.uniqueCustomId] = + List.from(event.functions); } emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); @@ -110,11 +126,13 @@ class RoutineBloc extends Bloc { } } - Future _onLoadScenes(LoadScenes event, Emitter emit) async { + Future _onLoadScenes( + LoadScenes event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { - final scenes = await SceneApi.getScenesByUnitId(event.unitId); + final scenes = + await SceneApi.getScenesByUnitId(event.unitId, event.communityId); emit(state.copyWith( scenes: scenes, isLoading: false, @@ -129,7 +147,8 @@ class RoutineBloc extends Bloc { } } - Future _onLoadAutomation(LoadAutomation event, Emitter emit) async { + Future _onLoadAutomation( + LoadAutomation event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { @@ -157,14 +176,16 @@ class RoutineBloc extends Bloc { } } - FutureOr _onSearchRoutines(SearchRoutines event, Emitter emit) async { + FutureOr _onSearchRoutines( + SearchRoutines event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); await Future.delayed(const Duration(seconds: 1)); emit(state.copyWith(isLoading: false, errorMessage: null)); emit(state.copyWith(searchText: event.query)); } - FutureOr _onAddSelectedIcon(AddSelectedIcon event, Emitter emit) { + FutureOr _onAddSelectedIcon( + AddSelectedIcon event, Emitter emit) { emit(state.copyWith(selectedIcon: event.icon)); } @@ -173,7 +194,8 @@ class RoutineBloc extends Bloc { return actions.first['deviceId'] == 'delay'; } - Future _onCreateScene(CreateSceneEvent event, Emitter emit) async { + Future _onCreateScene( + CreateSceneEvent event, Emitter emit) async { try { // Check if first action is delay if (_isFirstActionDelay(state.thenItems)) { @@ -233,7 +255,7 @@ class RoutineBloc extends Bloc { final result = await SceneApi.createScene(createSceneModel); if (result['success']) { emit(_resetState()); - add(const LoadScenes(spaceId)); + add(const LoadScenes(spaceId, communityId)); } else { emit(state.copyWith( isLoading: false, @@ -248,7 +270,8 @@ class RoutineBloc extends Bloc { } } - Future _onCreateAutomation(CreateAutomationEvent event, Emitter emit) async { + Future _onCreateAutomation( + CreateAutomationEvent event, Emitter emit) async { try { if (state.routineName == null || state.routineName!.isEmpty) { emit(state.copyWith( @@ -348,21 +371,34 @@ class RoutineBloc extends Bloc { } } - FutureOr _onRemoveDragCard(RemoveDragCard event, Emitter emit) { + FutureOr _onRemoveDragCard( + RemoveDragCard event, Emitter emit) { if (event.isFromThen) { final thenItems = List>.from(state.thenItems); - final selectedFunctions = Map>.from(state.selectedFunctions); + final selectedFunctions = + Map>.from(state.selectedFunctions); thenItems.removeAt(event.index); selectedFunctions.remove(event.key); - emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); + emit(state.copyWith( + thenItems: thenItems, selectedFunctions: selectedFunctions)); } else { final ifItems = List>.from(state.ifItems); - final selectedFunctions = Map>.from(state.selectedFunctions); + final selectedFunctions = + Map>.from(state.selectedFunctions); ifItems.removeAt(event.index); selectedFunctions.remove(event.key); - emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); + if (ifItems.isEmpty && state.thenItems.isEmpty) { + emit(state.copyWith( + ifItems: ifItems, + selectedFunctions: selectedFunctions, + isAutomation: false, + isTabToRun: false)); + } else { + emit(state.copyWith( + ifItems: ifItems, selectedFunctions: selectedFunctions)); + } } } @@ -373,14 +409,134 @@ class RoutineBloc extends Bloc { )); } - FutureOr _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter emit) { + FutureOr _onEffectiveTimeEvent( + EffectiveTimePeriodEvent event, Emitter emit) { emit(state.copyWith(effectiveTime: event.effectiveTime)); } - FutureOr _onSetRoutineName(SetRoutineName event, Emitter emit) { + FutureOr _onSetRoutineName( + SetRoutineName event, Emitter emit) { emit(state.copyWith(routineName: event.name)); } + Future _onGetSceneDetails( + GetSceneDetails event, Emitter emit) async { + try { + emit(state.copyWith( + isLoading: true, + isTabToRun: event.isTabToRun, + isUpdate: true, + sceneId: event.sceneId, + isAutomation: false)); + final sceneDetails = await SceneApi.getSceneDetails(event.sceneId); + add(InitializeRoutineState(sceneDetails)); + } catch (e) { + emit(state.copyWith( + isLoading: false, + errorMessage: 'Failed to load scene details', + )); + } + } + + Future _onGetAutomationDetails( + GetAutomationDetails event, Emitter emit) async { + try { + emit(state.copyWith( + isLoading: true, + isAutomation: event.isAutomation, + automationId: event.automationId, + isTabToRun: false, + isUpdate: true, + )); + final automationDetails = + await SceneApi.getAutomationDetails(event.automationId); + add(InitializeRoutineState(automationDetails)); + } catch (e) { + emit(state.copyWith( + isLoading: false, + errorMessage: 'Failed to load automation details', + )); + } + } + + // void _onInitializeRoutineState( + // InitializeRoutineState event, Emitter 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: [], @@ -396,13 +552,44 @@ class RoutineBloc extends Bloc { selectedIcon: null, isTabToRun: false, isAutomation: false, - selectedAutomationOperator: 'AND', + selectedAutomationOperator: 'or', effectiveTime: null, routineName: null, ); } - FutureOr _onResetRoutineState(ResetRoutineState event, Emitter emit) { + 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); + add(const LoadScenes(spaceId, communityId)); + //emit(_resetState()); + } catch (e) { + emit(state.copyWith( + isLoading: false, + errorMessage: 'Failed to delete scene', + )); + } + } + + 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, + errorMessage: 'Failed to delete automation', + )); + } + } } diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart index 575f199d..82e7875b 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_event.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_event.dart @@ -28,11 +28,12 @@ class AddToThenContainer extends RoutineEvent { class LoadScenes extends RoutineEvent { final String unitId; + final String communityId; - const LoadScenes(this.unitId); + const LoadScenes(this.unitId, this.communityId); @override - List get props => [unitId]; + List get props => [unitId, communityId]; } class LoadAutomation extends RoutineEvent { @@ -124,6 +125,55 @@ class SetRoutineName extends RoutineEvent { List get props => [name]; } +class GetSceneDetails extends RoutineEvent { + final String sceneId; + final bool isUpdate; + final bool isTabToRun; + const GetSceneDetails({ + required this.sceneId, + required this.isUpdate, + required this.isTabToRun, + }); + @override + List get props => [sceneId]; +} + +class GetAutomationDetails extends RoutineEvent { + final String automationId; + final bool isUpdate; + final bool isAutomation; + const GetAutomationDetails({ + required this.automationId, + this.isUpdate = false, + this.isAutomation = false, + }); + @override + List get props => [automationId]; +} + +class InitializeRoutineState extends RoutineEvent { + final RoutineDetailsModel routineDetails; + const InitializeRoutineState(this.routineDetails); + @override + List get props => [routineDetails]; +} + +class DeleteScene extends RoutineEvent { + final String sceneId; + final String unitUuid; + const DeleteScene({required this.sceneId, required this.unitUuid}); + @override + List get props => [sceneId]; +} + +class DeleteAutomation extends RoutineEvent { + final String automationId; + final String unitUuid; + const DeleteAutomation({required this.automationId, required this.unitUuid}); + @override + List get props => [automationId]; +} + 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 42f584ba..8f8d9b5c 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_state.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_state.dart @@ -18,6 +18,9 @@ class RoutineState extends Equatable { final bool isAutomation; final String selectedAutomationOperator; final EffectiveTime? effectiveTime; + final String? sceneId; + final String? automationId; + final bool? isUpdate; const RoutineState({ this.ifItems = const [], @@ -37,6 +40,9 @@ class RoutineState extends Equatable { this.isAutomation = false, this.selectedAutomationOperator = 'or', this.effectiveTime, + this.sceneId, + this.automationId, + this.isUpdate, }); RoutineState copyWith({ @@ -56,6 +62,9 @@ class RoutineState extends Equatable { bool? isAutomation, String? selectedAutomationOperator, EffectiveTime? effectiveTime, + String? sceneId, + String? automationId, + bool? isUpdate, }) { return RoutineState( ifItems: ifItems ?? this.ifItems, @@ -77,6 +86,9 @@ class RoutineState extends Equatable { selectedAutomationOperator: selectedAutomationOperator ?? this.selectedAutomationOperator, effectiveTime: effectiveTime ?? this.effectiveTime, + sceneId: sceneId ?? this.sceneId, + automationId: automationId ?? this.automationId, + isUpdate: isUpdate ?? this.isUpdate, ); } @@ -97,6 +109,9 @@ class RoutineState extends Equatable { isTabToRun, isAutomation, selectedAutomationOperator, - effectiveTime + effectiveTime, + sceneId, + automationId, + isUpdate ]; } diff --git a/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart b/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart index 33cfffee..1dd84c19 100644 --- a/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routiens/helper/dialog_helper/device_dialog_helper.dart @@ -10,8 +10,9 @@ import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; class DeviceDialogHelper { static Future?> showDeviceDialog( BuildContext context, - Map data, - ) async { + Map data, { + required bool removeComparetors, + }) async { final functions = data['functions'] as List; try { @@ -20,6 +21,7 @@ class DeviceDialogHelper { data['productType'], data, functions, + removeComparetors: removeComparetors, ); if (result != null) { @@ -33,34 +35,49 @@ class DeviceDialogHelper { } static Future?> _getDialogForDeviceType( - BuildContext context, - String productType, - Map data, - List functions, - ) async { + BuildContext context, + String productType, + Map data, + List functions, + {required bool removeComparetors}) async { final routineBloc = context.read(); final deviceSelectedFunctions = routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; switch (productType) { case 'AC': - return ACHelper.showACFunctionsDialog(context, functions, - data['device'], deviceSelectedFunctions, data['uniqueCustomId']); + return ACHelper.showACFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '1G': - return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, - data['device'], deviceSelectedFunctions, data['uniqueCustomId']); + return OneGangSwitchHelper.showSwitchFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '2G': - return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, - data['device'], deviceSelectedFunctions, data['uniqueCustomId']); + return TwoGangSwitchHelper.showSwitchFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '3G': return ThreeGangSwitchHelper.showSwitchFunctionsDialog( - context, - functions, - data['device'], - deviceSelectedFunctions, - data['uniqueCustomId'], - ); + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); default: return null; } diff --git a/lib/pages/routiens/helper/save_routine_helper.dart b/lib/pages/routiens/helper/save_routine_helper.dart index 2a57baa5..df9e8323 100644 --- a/lib/pages/routiens/helper/save_routine_helper.dart +++ b/lib/pages/routiens/helper/save_routine_helper.dart @@ -8,8 +8,8 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class SaveRoutineHelper { - static Future showSaveRoutineDialog(BuildContext context) async { - return showDialog( + static Future showSaveRoutineDialog(BuildContext context) async { + return showDialog( context: context, builder: (BuildContext context) { return BlocBuilder( @@ -54,27 +54,23 @@ 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(), @@ -99,25 +95,22 @@ 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, )) @@ -140,19 +133,15 @@ class SaveRoutineHelper { ), ), DialogFooter( - onCancel: () => Navigator.pop(context), + onCancel: () => Navigator.pop(context, false), onConfirm: () { if (state.isAutomation) { - context - .read() - .add(const CreateAutomationEvent()); + context.read().add(const CreateAutomationEvent()); } else { - context - .read() - .add(const CreateSceneEvent()); + context.read().add(const CreateSceneEvent()); } - Navigator.pop(context); + 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 cd9a9c60..c02e5dab 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 @@ -1,177 +1,5 @@ import 'dart:convert'; -import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; - -// class CreateAutomationModel { -// String unitUuid; -// String automationName; -// String decisionExpr; -// EffectiveTime effectiveTime; -// List conditions; -// List actions; - -// CreateAutomationModel({ -// required this.unitUuid, -// required this.automationName, -// required this.decisionExpr, -// required this.effectiveTime, -// required this.conditions, -// required this.actions, -// }); - -// CreateAutomationModel copyWith({ -// String? unitUuid, -// String? automationName, -// String? decisionExpr, -// EffectiveTime? effectiveTime, -// List? conditions, -// List? actions, -// }) { -// return CreateAutomationModel( -// unitUuid: unitUuid ?? this.unitUuid, -// automationName: automationName ?? this.automationName, -// decisionExpr: decisionExpr ?? this.decisionExpr, -// effectiveTime: effectiveTime ?? this.effectiveTime, -// conditions: conditions ?? this.conditions, -// actions: actions ?? this.actions, -// ); -// } - -// Map toMap([String? automationId]) { -// return { -// if (automationId == null) 'spaceUuid': unitUuid, -// 'automationName': automationName, -// 'decisionExpr': decisionExpr, -// 'effectiveTime': effectiveTime.toMap(), -// 'conditions': conditions.map((x) => x.toMap()).toList(), -// 'actions': actions.map((x) => x.toMap()).toList(), -// }; -// } - -// factory CreateAutomationModel.fromMap(Map map) { -// return CreateAutomationModel( -// unitUuid: map['spaceUuid'] ?? '', -// automationName: map['automationName'] ?? '', -// decisionExpr: map['decisionExpr'] ?? '', -// effectiveTime: EffectiveTime.fromMap(map['effectiveTime']), -// conditions: List.from( -// map['conditions']?.map((x) => CreateCondition.fromMap(x))), -// actions: List.from( -// map['actions']?.map((x) => CreateSceneAction.fromMap(x))), -// ); -// } - -// String toJson([String? automationId]) => json.encode(toMap(automationId)); - -// factory CreateAutomationModel.fromJson(String source) => -// CreateAutomationModel.fromMap(json.decode(source)); - -// @override -// String toString() { -// return 'CreateAutomationModel(unitUuid: $unitUuid, automationName: $automationName, decisionExpr: $decisionExpr, effectiveTime: $effectiveTime, conditions: $conditions, actions: $actions)'; -// } -// } - -// class EffectiveTime { -// String start; -// String end; -// String loops; - -// EffectiveTime({ -// required this.start, -// required this.end, -// required this.loops, -// }); - -// Map toMap() { -// return { -// 'start': start, -// 'end': end, -// 'loops': loops, -// }; -// } - -// factory EffectiveTime.fromMap(Map map) { -// return EffectiveTime( -// start: map['start'] ?? '', -// end: map['end'] ?? '', -// loops: map['loops'] ?? '', -// ); -// } - -// @override -// String toString() => 'EffectiveTime(start: $start, end: $end, loops: $loops)'; -// } - -// class CreateCondition { -// int code; -// String entityId; -// String entityType; -// ConditionExpr expr; - -// CreateCondition({ -// required this.code, -// required this.entityId, -// required this.entityType, -// required this.expr, -// }); - -// Map toMap() { -// return { -// 'code': code, -// 'entityId': entityId, -// 'entityType': entityType, -// 'expr': expr.toMap(), -// }; -// } - -// factory CreateCondition.fromMap(Map map) { -// return CreateCondition( -// code: map['code'] ?? 0, -// entityId: map['entityId'] ?? '', -// entityType: map['entityType'] ?? '', -// expr: ConditionExpr.fromMap(map['expr']), -// ); -// } - -// @override -// String toString() => -// 'CreateCondition(code: $code, entityId: $entityId, entityType: $entityType, expr: $expr)'; -// } - -// class ConditionExpr { -// String statusCode; -// String comparator; -// dynamic statusValue; - -// ConditionExpr({ -// required this.statusCode, -// required this.comparator, -// required this.statusValue, -// }); - -// Map toMap() { -// return { -// 'statusCode': statusCode, -// 'comparator': comparator, -// 'statusValue': statusValue, -// }; -// } - -// factory ConditionExpr.fromMap(Map map) { -// return ConditionExpr( -// statusCode: map['statusCode'] ?? '', -// comparator: map['comparator'] ?? '', -// statusValue: map['statusValue'], -// ); -// } - -// @override -// String toString() => -// 'ConditionExpr(statusCode: $statusCode, comparator: $comparator, statusValue: $statusValue)'; -// } -import 'dart:convert'; - class CreateAutomationModel { String spaceUuid; String automationName; diff --git a/lib/pages/routiens/models/routine_details_model.dart b/lib/pages/routiens/models/routine_details_model.dart new file mode 100644 index 00000000..8ad48c58 --- /dev/null +++ b/lib/pages/routiens/models/routine_details_model.dart @@ -0,0 +1,262 @@ +import 'dart:convert'; + +import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; +import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; + +class RoutineDetailsModel { + final String spaceUuid; + final String name; + final String decisionExpr; + final List actions; + final String? iconId; + final bool? showInDevice; + final EffectiveTime? effectiveTime; + final List? conditions; + final String? type; + + RoutineDetailsModel({ + required this.spaceUuid, + required this.name, + required this.decisionExpr, + required this.actions, + this.iconId, + this.showInDevice, + this.effectiveTime, + this.conditions, + this.type, + }); + + // Convert to CreateSceneModel + CreateSceneModel toCreateSceneModel() { + return CreateSceneModel( + spaceUuid: spaceUuid, + iconId: iconId ?? '', + showInDevice: showInDevice ?? false, + sceneName: name, + decisionExpr: decisionExpr, + actions: actions.map((a) => a.toCreateSceneAction()).toList(), + ); + } + + // Convert to CreateAutomationModel + CreateAutomationModel toCreateAutomationModel() { + return CreateAutomationModel( + spaceUuid: spaceUuid, + automationName: name, + decisionExpr: decisionExpr, + effectiveTime: + effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), + conditions: conditions?.map((c) => c.toCondition()).toList() ?? [], + actions: actions.map((a) => a.toAutomationAction()).toList(), + ); + } + + Map toMap() { + return { + 'spaceUuid': spaceUuid, + 'name': name, + 'decisionExpr': decisionExpr, + 'actions': actions.map((x) => x.toMap()).toList(), + if (iconId != null) 'iconId': iconId, + if (showInDevice != null) 'showInDevice': showInDevice, + if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(), + if (conditions != null) + 'conditions': conditions!.map((x) => x.toMap()).toList(), + if (type != null) 'type': type, + }; + } + + factory RoutineDetailsModel.fromMap(Map map) { + return RoutineDetailsModel( + spaceUuid: map['spaceUuid'] ?? '', + name: map['name'] ?? '', + decisionExpr: map['decisionExpr'] ?? '', + actions: List.from( + map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [], + ), + iconId: map['iconId'], + showInDevice: map['showInDevice'], + effectiveTime: map['effectiveTime'] != null + ? EffectiveTime.fromMap(map['effectiveTime']) + : null, + conditions: map['conditions'] != null + ? List.from( + map['conditions'].map((x) => RoutineCondition.fromMap(x))) + : null, + type: map['type'], + ); + } + + String toJson() => json.encode(toMap()); + + factory RoutineDetailsModel.fromJson(String source) => + RoutineDetailsModel.fromMap(json.decode(source)); +} + +class RoutineAction { + final String entityId; + final String actionExecutor; + final RoutineExecutorProperty? executorProperty; + + RoutineAction({ + required this.entityId, + required this.actionExecutor, + this.executorProperty, + }); + + CreateSceneAction toCreateSceneAction() { + return CreateSceneAction( + entityId: entityId, + actionExecutor: actionExecutor, + executorProperty: executorProperty?.toCreateSceneExecutorProperty(), + ); + } + + AutomationAction toAutomationAction() { + return AutomationAction( + entityId: entityId, + actionExecutor: actionExecutor, + executorProperty: executorProperty?.toExecutorProperty(), + ); + } + + Map toMap() { + return { + 'entityId': entityId, + 'actionExecutor': actionExecutor, + if (executorProperty != null) + 'executorProperty': executorProperty!.toMap(), + }; + } + + factory RoutineAction.fromMap(Map map) { + return RoutineAction( + entityId: map['entityId'] ?? '', + actionExecutor: map['actionExecutor'] ?? '', + executorProperty: map['executorProperty'] != null + ? RoutineExecutorProperty.fromMap(map['executorProperty']) + : null, + ); + } +} + +class RoutineExecutorProperty { + final String? functionCode; + final dynamic functionValue; + final int? delaySeconds; + + RoutineExecutorProperty({ + this.functionCode, + this.functionValue, + this.delaySeconds, + }); + + CreateSceneExecutorProperty toCreateSceneExecutorProperty() { + return CreateSceneExecutorProperty( + functionCode: functionCode ?? '', + functionValue: functionValue, + delaySeconds: delaySeconds ?? 0, + ); + } + + ExecutorProperty toExecutorProperty() { + return ExecutorProperty( + functionCode: functionCode, + functionValue: functionValue, + delaySeconds: delaySeconds, + ); + } + + Map toMap() { + return { + if (functionCode != null) 'functionCode': functionCode, + if (functionValue != null) 'functionValue': functionValue, + if (delaySeconds != null) 'delaySeconds': delaySeconds, + }; + } + + factory RoutineExecutorProperty.fromMap(Map map) { + return RoutineExecutorProperty( + functionCode: map['functionCode'], + functionValue: map['functionValue'], + delaySeconds: map['delaySeconds']?.toInt(), + ); + } +} + +class RoutineCondition { + final int code; + final String entityId; + final String entityType; + final RoutineConditionExpr expr; + + RoutineCondition({ + required this.code, + required this.entityId, + required this.entityType, + required this.expr, + }); + + Condition toCondition() { + return Condition( + code: code, + entityId: entityId, + entityType: entityType, + expr: expr.toConditionExpr(), + ); + } + + Map toMap() { + return { + 'code': code, + 'entityId': entityId, + 'entityType': entityType, + 'expr': expr.toMap(), + }; + } + + factory RoutineCondition.fromMap(Map map) { + return RoutineCondition( + code: map['code']?.toInt() ?? 0, + entityId: map['entityId'] ?? '', + entityType: map['entityType'] ?? '', + expr: RoutineConditionExpr.fromMap(map['expr']), + ); + } +} + +class RoutineConditionExpr { + final String statusCode; + final String comparator; + final dynamic statusValue; + + RoutineConditionExpr({ + required this.statusCode, + required this.comparator, + required this.statusValue, + }); + + ConditionExpr toConditionExpr() { + return ConditionExpr( + statusCode: statusCode, + comparator: comparator, + statusValue: statusValue, + ); + } + + Map toMap() { + return { + 'statusCode': statusCode, + 'comparator': comparator, + 'statusValue': statusValue, + }; + } + + factory RoutineConditionExpr.fromMap(Map map) { + return RoutineConditionExpr( + statusCode: map['statusCode'] ?? '', + comparator: map['comparator'] ?? '', + statusValue: map['statusValue'], + ); + } +} diff --git a/lib/pages/routiens/models/routine_model.dart b/lib/pages/routiens/models/routine_model.dart index e2075579..bb3e117b 100644 --- a/lib/pages/routiens/models/routine_model.dart +++ b/lib/pages/routiens/models/routine_model.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; +import 'dart:typed_data'; import 'package:syncrow_web/utils/constants/assets.dart'; class ScenesModel { final String id; + final String? sceneTuyaId; final String name; final String status; final String type; @@ -9,26 +12,43 @@ class ScenesModel { ScenesModel({ required this.id, + this.sceneTuyaId, required this.name, required this.status, required this.type, this.icon, }); + factory ScenesModel.fromRawJson(String str) => + ScenesModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + Uint8List? get iconInBytes { + if (icon == null || icon?.isEmpty == true) return null; + try { + return base64Decode(icon!); + } catch (e) { + return null; + } + } + factory ScenesModel.fromJson(Map json, - {bool? isAutomation}) => - ScenesModel( - id: json["id"], - name: json["name"] ?? '', - status: json["status"] ?? '', - type: json["type"] ?? '', - icon: (isAutomation ?? false) - ? Assets.automation - : json["icon"] as String?, - ); + {bool? isAutomation}) { + return ScenesModel( + id: json["id"] ?? json["uuid"] ?? '', + sceneTuyaId: json["sceneTuyaId"] as String?, + name: json["name"] ?? '', + status: json["status"] ?? '', + type: json["type"] ?? '', + icon: + isAutomation == true ? Assets.automation : (json["icon"] as String?), + ); + } Map toJson() => { "id": id, + "sceneTuyaId": sceneTuyaId ?? '', "name": name, "status": status, "type": type, diff --git a/lib/pages/routiens/view/create_new_routine_view.dart b/lib/pages/routiens/view/create_new_routine_view.dart index 38750991..320eb15d 100644 --- a/lib/pages/routiens/view/create_new_routine_view.dart +++ b/lib/pages/routiens/view/create_new_routine_view.dart @@ -6,8 +6,16 @@ import 'package:syncrow_web/pages/routiens/widgets/then_container.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CreateNewRoutineView extends StatelessWidget { - const CreateNewRoutineView({super.key}); + final bool isUpdate; + final String? routineId; + final bool isScene; + const CreateNewRoutineView({ + super.key, + this.isUpdate = false, + this.routineId, + this.isScene = true, + }); @override 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 27690a9c..be5a5a3a 100644 --- a/lib/pages/routiens/view/routines_view.dart +++ b/lib/pages/routiens/view/routines_view.dart @@ -1,7 +1,10 @@ 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/view/create_new_routine_view.dart'; +import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart'; +import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class RoutinesView extends StatelessWidget { @@ -23,49 +26,30 @@ class RoutinesView extends StatelessWidget { children: [ Text( "Create New Routines", - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, + style: Theme.of(context).textTheme.titleLarge?.copyWith( color: ColorsManager.grayColor, + fontWeight: FontWeight.bold, ), ), - SizedBox( - height: 200, - width: 150, - child: GestureDetector( - onTap: () { - BlocProvider.of(context).add( - const CreateNewRoutineViewEvent(true), - ); - }, - child: Card( - elevation: 3, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - color: ColorsManager.whiteColors, - child: Center( - child: Container( - decoration: BoxDecoration( - color: ColorsManager.graysColor, - borderRadius: BorderRadius.circular(120), - border: Border.all( - color: ColorsManager.greyColor, - width: 2.0, - ), - ), - height: 70, - width: 70, - child: Icon( - Icons.add, - color: ColorsManager.dialogBlueTitle, - size: 40, - ), - ), - ), - ), - ), + const SizedBox( + height: 10, ), - const Spacer(), + RoutineViewCard( + onTap: () { + BlocProvider.of(context).add( + const CreateNewRoutineViewEvent(true), + ); + context.read().add( + (ResetRoutineState()), + ); + }, + icon: Icons.add, + textString: '', + ), + const SizedBox( + height: 15, + ), + const Expanded(child: FetchRoutineScenesAutomation()), ], ), ); diff --git a/lib/pages/routiens/widgets/dragable_card.dart b/lib/pages/routiens/widgets/dragable_card.dart index db8ad1c9..5b4de81f 100644 --- a/lib/pages/routiens/widgets/dragable_card.dart +++ b/lib/pages/routiens/widgets/dragable_card.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -81,7 +83,9 @@ class DraggableCard extends StatelessWidget { ? SvgPicture.asset( imagePath, ) - : Image.network(imagePath), + : Image.memory( + base64Decode(imagePath), + ), ), const SizedBox(height: 8), Padding( diff --git a/lib/pages/routiens/widgets/if_container.dart b/lib/pages/routiens/widgets/if_container.dart index d0273adc..9c357a17 100644 --- a/lib/pages/routiens/widgets/if_container.dart +++ b/lib/pages/routiens/widgets/if_container.dart @@ -26,8 +26,10 @@ class IfContainer extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - if (state.isAutomation) + const Text('IF', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), + if (state.isAutomation && state.ifItems.isNotEmpty) AutomationOperatorSelector( selectedOperator: state.selectedAutomationOperator), ], @@ -53,33 +55,44 @@ class IfContainer extends StatelessWidget { (index) => GestureDetector( onTap: () async { if (!state.isTabToRun) { - final result = await DeviceDialogHelper.showDeviceDialog( - context, state.ifItems[index]); + final result = await DeviceDialogHelper + .showDeviceDialog( + context, state.ifItems[index], + removeComparetors: false); if (result != null) { - context - .read() - .add(AddToIfContainer(state.ifItems[index], false)); - } else if (!['AC', '1G', '2G', '3G'] - .contains(state.ifItems[index]['productType'])) { - context - .read() - .add(AddToIfContainer(state.ifItems[index], false)); + context.read().add( + AddToIfContainer( + state.ifItems[index], false)); + } else if (![ + 'AC', + '1G', + '2G', + '3G' + ].contains( + state.ifItems[index]['productType'])) { + context.read().add( + AddToIfContainer( + state.ifItems[index], false)); } } }, child: DraggableCard( - imagePath: state.ifItems[index]['imagePath'] ?? '', + imagePath: + state.ifItems[index]['imagePath'] ?? '', title: state.ifItems[index]['title'] ?? '', deviceData: state.ifItems[index], - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), isFromThen: false, isFromIf: true, onRemove: () { - context.read().add(RemoveDragCard( - index: index, - isFromThen: false, - key: state.ifItems[index]['uniqueCustomId'])); + context.read().add( + RemoveDragCard( + index: index, + isFromThen: false, + key: state.ifItems[index] + ['uniqueCustomId'])); }, ), )), @@ -88,23 +101,35 @@ class IfContainer extends StatelessWidget { ), ); }, - onWillAccept: (data) => data != null, - onAccept: (data) async { + onAcceptWithDetails: (data) async { final uniqueCustomId = const Uuid().v4(); - final mutableData = Map.from(data); + final mutableData = Map.from(data.data); mutableData['uniqueCustomId'] = uniqueCustomId; + if (state.isAutomation && mutableData['deviceId'] == 'tab_to_run') { + return; + } + if (!state.isTabToRun) { if (mutableData['deviceId'] == 'tab_to_run') { - context.read().add(AddToIfContainer(mutableData, true)); + context + .read() + .add(AddToIfContainer(mutableData, true)); } else { - final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData); + final result = await DeviceDialogHelper.showDeviceDialog( + context, mutableData, + removeComparetors: false); if (result != null) { - context.read().add(AddToIfContainer(mutableData, false)); - } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { - context.read().add(AddToIfContainer(mutableData, false)); + context + .read() + .add(AddToIfContainer(mutableData, false)); + } else if (!['AC', '1G', '2G', '3G'] + .contains(mutableData['productType'])) { + context + .read() + .add(AddToIfContainer(mutableData, false)); } } } @@ -134,7 +159,7 @@ class AutomationOperatorSelector extends StatelessWidget { children: [ TextButton( style: TextButton.styleFrom( - backgroundColor: selectedOperator == 'or' + backgroundColor: selectedOperator.toLowerCase() == 'or' ? ColorsManager.dialogBlueTitle : ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -144,12 +169,15 @@ class AutomationOperatorSelector extends StatelessWidget { child: Text( 'Any condition is met', style: context.textTheme.bodyMedium?.copyWith( - color: - selectedOperator == 'or' ? ColorsManager.whiteColors : ColorsManager.blackColor, + color: selectedOperator.toLowerCase() == 'or' + ? ColorsManager.whiteColors + : ColorsManager.blackColor, ), ), onPressed: () { - context.read().add(const ChangeAutomationOperator(operator: 'or')); + context + .read() + .add(const ChangeAutomationOperator(operator: 'or')); }, ), Container( @@ -159,7 +187,7 @@ class AutomationOperatorSelector extends StatelessWidget { ), TextButton( style: TextButton.styleFrom( - backgroundColor: selectedOperator == 'and' + backgroundColor: selectedOperator.toLowerCase() == 'and' ? ColorsManager.dialogBlueTitle : ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -169,13 +197,15 @@ class AutomationOperatorSelector extends StatelessWidget { child: Text( 'All condition is met', style: context.textTheme.bodyMedium?.copyWith( - color: selectedOperator == 'and' + color: selectedOperator.toLowerCase() == 'and' ? ColorsManager.whiteColors : ColorsManager.blackColor, ), ), onPressed: () { - context.read().add(const ChangeAutomationOperator(operator: 'and')); + context + .read() + .add(const ChangeAutomationOperator(operator: 'and')); }, ), ], 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 new file mode 100644 index 00000000..c9a5114f --- /dev/null +++ b/lib/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class FetchRoutineScenesAutomation extends StatefulWidget { + const FetchRoutineScenesAutomation({super.key}); + + @override + State createState() => + _FetchRoutineScenesState(); +} + +class _FetchRoutineScenesState extends State + with HelperResponsiveLayout { + @override + void initState() { + super.initState(); + context.read() + ..add(const LoadScenes(spaceId, communityId)) + ..add(const LoadAutomation(spaceId)); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return state.isLoading + ? const Center( + child: CircularProgressIndicator(), + ) + : SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Scenes (Tab to Run)", + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + if (state.scenes.isEmpty) + Text( + "No scenes found", + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.grayColor, + ), + ), + if (state.scenes.isNotEmpty) + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: isSmallScreenSize(context) ? 160 : 170, + ), + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: state.scenes.length, + itemBuilder: (context, index) => Padding( + padding: EdgeInsets.only( + right: isSmallScreenSize(context) ? 4.0 : 8.0, + ), + child: Stack( + children: [ + RoutineViewCard( + onTap: () {}, + textString: state.scenes[index].name, + icon: state.scenes[index].icon ?? + Assets.logoHorizontal, + isFromScenes: true, + iconInBytes: + state.scenes[index].iconInBytes, + ), + Positioned( + top: 0, + right: 0, + child: InkWell( + onTap: () => context + .read() + .add( + DeleteScene( + sceneId: state.scenes[index].id, + unitUuid: spaceId, + ), + ), + child: Container( + height: 20, + width: 20, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.grayColor, + width: 2.0, + ), + ), + child: const Center( + child: Icon(Icons.delete, + size: 15, + color: ColorsManager.grayColor), + ), + ), + ), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 15), + Text( + "Automations", + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + if (state.automations.isEmpty) + Text( + "No automations found", + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.grayColor, + ), + ), + if (state.automations.isNotEmpty) + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: isSmallScreenSize(context) ? 160 : 170, + ), + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: state.automations.length, + itemBuilder: (context, index) => Padding( + padding: EdgeInsets.only( + right: isSmallScreenSize(context) ? 4.0 : 8.0, + ), + child: Stack( + children: [ + RoutineViewCard( + onTap: () {}, + textString: state.automations[index].name, + icon: state.automations[index].icon ?? + Assets.automation, + ), + Positioned( + top: 0, + right: 0, + child: InkWell( + onTap: () => + context.read().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), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/pages/routiens/widgets/main_routine_view/routine_view_card.dart b/lib/pages/routiens/widgets/main_routine_view/routine_view_card.dart new file mode 100644 index 00000000..7e446507 --- /dev/null +++ b/lib/pages/routiens/widgets/main_routine_view/routine_view_card.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout { + const RoutineViewCard({ + super.key, + required this.onTap, + required this.icon, + required this.textString, + this.isFromScenes, + this.iconInBytes, + }); + + final Function() onTap; + final dynamic icon; + final String textString; + final bool? isFromScenes; + final Uint8List? iconInBytes; + + @override + Widget build(BuildContext context) { + final double cardWidth = isSmallScreenSize(context) + ? 120 + : isMediumScreenSize(context) + ? 135 + : 150; + + final double cardHeight = isSmallScreenSize(context) ? 160 : 170; + + final double iconSize = isSmallScreenSize(context) + ? 50 + : isMediumScreenSize(context) + ? 60 + : 70; + + return ConstrainedBox( + constraints: BoxConstraints( + maxWidth: cardWidth, + maxHeight: cardHeight, + ), + child: Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + color: ColorsManager.whiteColors, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(10), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Center( + child: Container( + decoration: BoxDecoration( + color: ColorsManager.graysColor, + borderRadius: BorderRadius.circular(120), + border: Border.all( + color: ColorsManager.greyColor, + width: 2.0, + ), + ), + height: iconSize, + width: iconSize, + child: (isFromScenes ?? false) + ? (iconInBytes != null && + iconInBytes?.isNotEmpty == true) + ? Image.memory( + iconInBytes!, + height: iconSize, + width: iconSize, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) => + Image.asset( + Assets.logo, + height: iconSize, + width: iconSize, + fit: BoxFit.contain, + ), + ) + : Image.asset( + Assets.logo, + height: iconSize, + width: iconSize, + fit: BoxFit.contain, + ) + : (icon is String && icon.endsWith('.svg')) + ? SvgPicture.asset( + icon, + fit: BoxFit.contain, + ) + : Icon( + icon, + color: ColorsManager.dialogBlueTitle, + size: isSmallScreenSize(context) ? 30 : 40, + ), + ), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Text( + textString, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontSize: isSmallScreenSize(context) ? 10 : 12, + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/routiens/widgets/period_option.dart b/lib/pages/routiens/widgets/period_option.dart index 5c1c2d51..1871ebda 100644 --- a/lib/pages/routiens/widgets/period_option.dart +++ b/lib/pages/routiens/widgets/period_option.dart @@ -75,7 +75,11 @@ class PeriodOptions extends StatelessWidget { onTap: () { context.read().add(SetPeriod(value)); }, - title: Text(EffectPeriodHelper.formatEnumValue(value)), + title: Text( + EffectPeriodHelper.formatEnumValue(value), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 12), + ), subtitle: Text( subtitle, style: Theme.of(context).textTheme.bodyMedium!.copyWith( diff --git a/lib/pages/routiens/widgets/routine_dialogs/ac_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/ac_dialog.dart index e8b87a16..39e342ff 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/ac_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/ac_dialog.dart @@ -19,6 +19,7 @@ class ACHelper { AllDevicesModel? device, List? deviceSelectedFunctions, String uniqueCustomId, + bool? removeComparetors, ) async { List acFunctions = functions.whereType().toList(); @@ -84,6 +85,7 @@ class ACHelper { acFunctions: acFunctions, device: device, operationName: selectedOperationName ?? '', + removeComparators: removeComparetors, ), ), ], @@ -179,6 +181,7 @@ class ACHelper { required List acFunctions, AllDevicesModel? device, required String operationName, + bool? removeComparators, }) { if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') { final initialValue = selectedFunctionData?.value ?? 200; @@ -190,6 +193,7 @@ class ACHelper { device: device, operationName: operationName, selectedFunctionData: selectedFunctionData, + removeComparators: removeComparators, ); } @@ -217,18 +221,20 @@ class ACHelper { AllDevicesModel? device, required String operationName, DeviceFunctionData? selectedFunctionData, + bool? removeComparators, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), + if (removeComparators != true) + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), const SizedBox(height: 20), _buildTemperatureDisplay( context, diff --git a/lib/pages/routiens/widgets/routine_dialogs/discard_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/discard_dialog.dart index 8b22a671..4c8e4688 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/discard_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/discard_dialog.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart index 00f936d6..e7d7209f 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart @@ -20,6 +20,7 @@ class OneGangSwitchHelper { AllDevicesModel? device, List? deviceSelectedFunctions, String uniqueCustomId, + bool removeComparetors, ) async { List acFunctions = functions.whereType().toList(); @@ -106,6 +107,7 @@ class OneGangSwitchHelper { acFunctions: acFunctions, device: device, operationName: selectedOperationName ?? '', + removeComparetors: removeComparetors, ), ), ], @@ -162,6 +164,7 @@ class OneGangSwitchHelper { required List acFunctions, AllDevicesModel? device, required String operationName, + required bool removeComparetors, }) { if (selectedFunction == 'countdown_1') { final initialValue = selectedFunctionData?.value ?? 200; @@ -173,6 +176,7 @@ class OneGangSwitchHelper { device: device, operationName: operationName, selectedFunctionData: selectedFunctionData, + removeComparetors: removeComparetors, ); } @@ -199,18 +203,20 @@ class OneGangSwitchHelper { AllDevicesModel? device, required String operationName, DeviceFunctionData? selectedFunctionData, + required bool removeComparetors, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), + if (removeComparetors != true) + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), const SizedBox(height: 20), _buildCountDownDisplay(context, initialValue, device, operationName, selectedFunctionData, selectCode), diff --git a/lib/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart index ae704ded..bb23ece4 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart @@ -20,6 +20,7 @@ class ThreeGangSwitchHelper { AllDevicesModel? device, List? deviceSelectedFunctions, String uniqueCustomId, + bool removeComparetors, ) async { List switchFunctions = functions.whereType().toList(); @@ -106,6 +107,7 @@ class ThreeGangSwitchHelper { switchFunctions: switchFunctions, device: device, operationName: selectedOperationName ?? '', + removeComparetors: removeComparetors, ), ), ], @@ -162,6 +164,7 @@ class ThreeGangSwitchHelper { required List switchFunctions, AllDevicesModel? device, required String operationName, + required bool removeComparetors, }) { if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2' || @@ -175,6 +178,7 @@ class ThreeGangSwitchHelper { device: device, operationName: operationName, selectedFunctionData: selectedFunctionData, + removeComparetors: removeComparetors, ); } @@ -201,18 +205,20 @@ class ThreeGangSwitchHelper { AllDevicesModel? device, required String operationName, DeviceFunctionData? selectedFunctionData, + bool? removeComparetors, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), + if (removeComparetors != true) + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), const SizedBox(height: 20), _buildCountDownDisplay(context, initialValue, device, operationName, selectedFunctionData, selectCode), diff --git a/lib/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart index d017103e..f2a0ebd0 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart @@ -20,6 +20,7 @@ class TwoGangSwitchHelper { AllDevicesModel? device, List? deviceSelectedFunctions, String uniqueCustomId, + bool removeComparetors, ) async { List switchFunctions = functions.whereType().toList(); @@ -106,6 +107,7 @@ class TwoGangSwitchHelper { switchFunctions: switchFunctions, device: device, operationName: selectedOperationName ?? '', + removeComparetors: removeComparetors, ), ), ], @@ -162,6 +164,7 @@ class TwoGangSwitchHelper { required List switchFunctions, AllDevicesModel? device, required String operationName, + required bool removeComparetors, }) { if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') { @@ -174,6 +177,7 @@ class TwoGangSwitchHelper { device: device, operationName: operationName, selectedFunctionData: selectedFunctionData, + removeComparetors: removeComparetors, ); } @@ -200,18 +204,20 @@ class TwoGangSwitchHelper { AllDevicesModel? device, required String operationName, DeviceFunctionData? selectedFunctionData, + bool? removeComparetors, }) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildConditionToggle( - context, - currentCondition, - selectCode, - device, - operationName, - selectedFunctionData, - ), + if (removeComparetors != true) + _buildConditionToggle( + context, + currentCondition, + selectCode, + device, + operationName, + selectedFunctionData, + ), const SizedBox(height: 20), _buildCountDownDisplay(context, initialValue, device, operationName, selectedFunctionData, selectCode), diff --git a/lib/pages/routiens/widgets/routine_search_and_buttons.dart b/lib/pages/routiens/widgets/routine_search_and_buttons.dart index 6696225f..cdc560f6 100644 --- a/lib/pages/routiens/widgets/routine_search_and_buttons.dart +++ b/lib/pages/routiens/widgets/routine_search_and_buttons.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart'; import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart'; @@ -173,7 +174,7 @@ class RoutineSearchAndButtons extends StatelessWidget { width: 200, child: Center( child: DefaultButton( - onPressed: () { + onPressed: () async { if (state.routineName == null || state.routineName!.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -207,7 +208,16 @@ 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/scenes_and_automations.dart b/lib/pages/routiens/widgets/scenes_and_automations.dart index 36ee7db2..a0bd1ed6 100644 --- a/lib/pages/routiens/widgets/scenes_and_automations.dart +++ b/lib/pages/routiens/widgets/scenes_and_automations.dart @@ -18,7 +18,7 @@ class _ScenesAndAutomationsState extends State { void initState() { super.initState(); context.read() - ..add(const LoadScenes(spaceId)) + ..add(const LoadScenes(spaceId, communityId)) ..add(const LoadAutomation(spaceId)); } @@ -34,7 +34,9 @@ class _ScenesAndAutomationsState extends State { children: scenes.asMap().entries.map((entry) { final scene = entry.value; if (state.searchText != null && state.searchText!.isNotEmpty) { - return scene.name.toLowerCase().contains(state.searchText!.toLowerCase()) + return scene.name + .toLowerCase() + .contains(state.searchText!.toLowerCase()) ? DraggableCard( imagePath: scene.icon ?? Assets.loginLogo, title: scene.name, diff --git a/lib/pages/routiens/widgets/then_container.dart b/lib/pages/routiens/widgets/then_container.dart index 52749851..9491e8fd 100644 --- a/lib/pages/routiens/widgets/then_container.dart +++ b/lib/pages/routiens/widgets/then_container.dart @@ -26,7 +26,9 @@ class ThenContainer extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const Text('THEN', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), Wrap( spacing: 8, @@ -35,12 +37,16 @@ class ThenContainer extends StatelessWidget { state.thenItems.length, (index) => GestureDetector( onTap: () async { - if (state.thenItems[index]['deviceId'] == 'delay') { - final result = await DelayHelper.showDelayPickerDialog( - context, state.thenItems[index]); + if (state.thenItems[index]['deviceId'] == + 'delay') { + final result = await DelayHelper + .showDelayPickerDialog( + context, state.thenItems[index]); if (result != null) { - context.read().add(AddToThenContainer({ + context + .read() + .add(AddToThenContainer({ ...state.thenItems[index], 'imagePath': Assets.delay, 'title': 'Delay', @@ -49,32 +55,41 @@ class ThenContainer extends StatelessWidget { return; } - final result = await DeviceDialogHelper.showDeviceDialog( - context, state.thenItems[index]); + final result = await DeviceDialogHelper + .showDeviceDialog( + context, state.thenItems[index], + removeComparetors: true); if (result != null) { - context - .read() - .add(AddToThenContainer(state.thenItems[index])); + 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])); + .contains(state.thenItems[index] + ['productType'])) { + context.read().add( + AddToThenContainer( + state.thenItems[index])); } }, child: DraggableCard( - imagePath: state.thenItems[index]['imagePath'] ?? '', - title: state.thenItems[index]['title'] ?? '', + imagePath: state.thenItems[index] + ['imagePath'] ?? + '', + title: + state.thenItems[index]['title'] ?? '', deviceData: state.thenItems[index], - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + 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'])); + context.read().add( + RemoveDragCard( + index: index, + isFromThen: true, + key: state.thenItems[index] + ['uniqueCustomId'])); }, ), ))), @@ -83,21 +98,6 @@ class ThenContainer extends StatelessWidget { ), ); }, - // onWillAcceptWithDetails: (data) { - // if (data == null) return false; - // return data.data; - - // // if (state.isTabToRun) { - // // return data.data['type'] == 'automation'; - // // } - - // // if (state.isAutomation) { - // // return data.data['type'] == 'scene' || - // // data.data['type'] == 'automation'; - // // } - - // // return data.data['deviceId'] != null; - // }, onAcceptWithDetails: (data) async { final uniqueCustomId = const Uuid().v4(); final mutableData = Map.from(data.data); @@ -128,8 +128,23 @@ class ThenContainer extends StatelessWidget { return; } + if (mutableData['type'] == 'tap_to_run' && state.isAutomation) { + context.read().add(AddToThenContainer({ + ...mutableData, + 'imagePath': Assets.logo, + 'title': mutableData['name'], + })); + + return; + } + + if (mutableData['type'] == 'tap_to_run' && !state.isAutomation) { + return; + } + if (mutableData['deviceId'] == 'delay') { - final result = await DelayHelper.showDelayPickerDialog(context, mutableData); + final result = + await DelayHelper.showDelayPickerDialog(context, mutableData); if (result != null) { context.read().add(AddToThenContainer({ @@ -141,11 +156,13 @@ class ThenContainer extends StatelessWidget { return; } - final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData); - + final result = await DeviceDialogHelper.showDeviceDialog( + context, mutableData, + removeComparetors: true); if (result != null) { context.read().add(AddToThenContainer(mutableData)); - } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { + } else if (!['AC', '1G', '2G', '3G'] + .contains(mutableData['productType'])) { context.read().add(AddToThenContainer(mutableData)); } }, diff --git a/lib/services/routines_api.dart b/lib/services/routines_api.dart index 756d533e..333156ba 100644 --- a/lib/services/routines_api.dart +++ b/lib/services/routines_api.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; +import 'package:syncrow_web/pages/routiens/models/routine_details_model.dart'; import 'package:syncrow_web/pages/routiens/models/routine_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; @@ -67,17 +68,24 @@ class SceneApi { return response; } - //get scene by unit id + //get scenes by community id and space id - static Future> getScenesByUnitId(String unitId) async { + static Future> getScenesByUnitId( + String unitId, String communityId, + {showInDevice = false}) async { try { final response = await _httpService.get( - path: ApiEndpoints.getSpaceScenes.replaceAll('{unitUuid}', unitId), + path: ApiEndpoints.getUnitScenes + .replaceAll('{spaceUuid}', unitId) + .replaceAll('{communityUuid}', communityId), + queryParameters: {'showInHomePage': showInDevice}, showServerMessage: false, expectedResponseModel: (json) { + final scenesJson = json['data'] as List; + List scenes = []; - for (var scene in json) { - scenes.add(ScenesModel.fromJson(scene)); + for (var scene in scenesJson) { + scenes.add(ScenesModel.fromJson(scene, isAutomation: false)); } return scenes; }, @@ -122,21 +130,21 @@ class SceneApi { // } // } -// //automation details -// static Future getAutomationDetails( -// String automationId) async { -// try { -// final response = await _httpService.get( -// path: ApiEndpoints.getAutomationDetails -// .replaceAll('{automationId}', automationId), -// showServerMessage: false, -// expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), -// ); -// return response; -// } catch (e) { -// rethrow; -// } -// } +//automation details + static Future getAutomationDetails( + String automationId) async { + try { + final response = await _httpService.get( + path: ApiEndpoints.getAutomationDetails + .replaceAll('{automationId}', automationId), + showServerMessage: false, + expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), + ); + return response; + } catch (e) { + rethrow; + } + } // // //updateAutomationStatus // static Future updateAutomationStatus(String automationId, @@ -154,20 +162,19 @@ class SceneApi { // } // } - // //getScene - // - // static Future getSceneDetails(String sceneId) async { - // try { - // final response = await _httpService.get( - // path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), - // showServerMessage: false, - // expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), - // ); - // return response; - // } catch (e) { - // rethrow; - // } - // } + //getScene + static Future getSceneDetails(String sceneId) async { + try { + final response = await _httpService.get( + path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), + showServerMessage: false, + expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), + ); + return response; + } catch (e) { + rethrow; + } + } // // //update Scene // static updateScene(CreateSceneModel createSceneModel, String sceneId) async { @@ -205,38 +212,39 @@ class SceneApi { // } // } // - // //delete Scene - // - // static Future deleteScene( - // {required String unitUuid, required String sceneId}) async { - // try { - // final response = await _httpService.delete( - // path: ApiEndpoints.deleteScene - // .replaceAll('{sceneId}', sceneId) - // .replaceAll('{unitUuid}', unitUuid), - // showServerMessage: false, - // expectedResponseModel: (json) => json['statusCode'] == 200, - // ); - // return response; - // } catch (e) { - // rethrow; - // } - // } - // - // // delete automation - // static Future deleteAutomation( - // {required String unitUuid, required String automationId}) async { - // try { - // final response = await _httpService.delete( - // path: ApiEndpoints.deleteAutomation - // .replaceAll('{automationId}', automationId) - // .replaceAll('{unitUuid}', unitUuid), - // showServerMessage: false, - // expectedResponseModel: (json) => json['statusCode'] == 200, - // ); - // return response; - // } catch (e) { - // rethrow; - // } - // } + + + //delete Scene + static Future deleteScene( + {required String unitUuid, required String sceneId}) async { + try { + final response = await _httpService.delete( + path: ApiEndpoints.deleteScene + .replaceAll('{sceneId}', sceneId) + .replaceAll('{unitUuid}', unitUuid), + showServerMessage: false, + expectedResponseModel: (json) => json['statusCode'] == 200, + ); + return response; + } catch (e) { + rethrow; + } + } + + // delete automation + static Future deleteAutomation( + {required String unitUuid, required String automationId}) async { + try { + final response = await _httpService.delete( + path: ApiEndpoints.deleteAutomation + .replaceAll('{automationId}', automationId) + .replaceAll('{unitUuid}', unitUuid), + showServerMessage: false, + expectedResponseModel: (json) => json['statusCode'] == 200, + ); + return response; + } catch (e) { + rethrow; + } + } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 4bc0e752..dcf6b367 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -46,7 +46,6 @@ abstract class ApiEndpoints { '/schedule/{deviceUuid}/{scheduleUuid}'; static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}'; - static const String factoryReset = '/device/factory/reset/{deviceUuid}'; static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; @@ -55,4 +54,12 @@ abstract class ApiEndpoints { static const String getIconScene = '/scene/icon'; static const String createScene = '/scene/tap-to-run'; static const String createAutomation = '/automation'; + static const String getUnitScenes = + '/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; + static const String getAutomationDetails = + '/automation/details/{automationId}'; + static const String getScene = '/scene/tap-to-run/{sceneId}'; + static const String deleteScene = '/scene/tap-to-run/{sceneId}'; + + static const String deleteAutomation = '/automation/{automationId}'; }