push changes

This commit is contained in:
ashrafzarkanisala
2024-11-30 20:43:21 +03:00
parent 8d388ac482
commit ce6e7700bc
14 changed files with 852 additions and 305 deletions

View File

@ -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<RoutineEvent, RoutineState> {
on<ResetRoutineState>(_onResetRoutineState);
on<GetSceneDetails>(_onGetSceneDetails);
on<GetAutomationDetails>(_onGetAutomationDetails);
// on<InitializeRoutineState>(_onInitializeRoutineState);
on<DeleteScene>(_deleteScene);
on<DeleteAutomation>(_deleteAutomation);
// on<RemoveFunction>(_onRemoveFunction);
// on<ClearFunctions>(_onClearFunctions);
on<FetchDevicesInRoutine>(_fetchDevices);
on<UpdateScene>(_onUpdateScene);
on<UpdateAutomation>(_onUpdateAutomation);
}
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
@ -419,21 +424,254 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(routineName: event.name));
}
(
List<Map<String, dynamic>>,
List<Map<String, dynamic>>,
Map<String, List<DeviceFunctionData>>
) _createCardData(
List<RoutineAction> actions,
List<RoutineCondition>? conditions,
Map<String, List<DeviceFunctionData>> 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<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> 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<Map<String, dynamic>> thenItems;
final List<Map<String, dynamic>> ifItems;
final Map<String, List<DeviceFunctionData>> updatedFunctions =
Map<String, List<DeviceFunctionData>>.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<RoutineEvent, RoutineState> {
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<Map<String, dynamic>> thenItems;
final List<Map<String, dynamic>> ifItems;
final Map<String, List<DeviceFunctionData>> updatedFunctions =
Map<String, List<DeviceFunctionData>>.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<RoutineState> emit) {
// final routineDetails = event.routineDetails;
// // Convert actions to draggable cards for the THEN container
// final thenItems = routineDetails.actions.map((action) {
// final Map<String, dynamic> cardData = {
// 'entityId': action.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId':
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
// 'title': action.actionExecutor == 'delay' ? 'Delay' : 'Device',
// // fix this
// 'imagePath':
// action.actionExecutor == 'delay' ? Assets.delay : Assets.logo,
// };
// // Add functions to selectedFunctions
// if (action.executorProperty != null) {
// final functions = <DeviceFunctionData>[
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: action.executorProperty!.functionCode ?? '',
// value: action.executorProperty!.functionValue,
// /// fix this
// operationName: action.executorProperty?.functionCode ?? ''),
// ];
// state.selectedFunctions[cardData['uniqueCustomId']] = functions;
// }
// return cardData;
// }).toList();
// // Convert conditions to draggable cards for the IF container
// final ifItems = routineDetails.conditions?.map((condition) {
// final Map<String, dynamic> cardData = {
// 'entityId': condition.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': condition.entityId,
// /// fix this
// 'title': 'Device',
// /// fix this
// 'imagePath': Assets.logo,
// };
// // Add functions to selectedFunctions
// final functions = <DeviceFunctionData>[
// DeviceFunctionData(
// entityId: condition.entityId,
// functionCode: condition.expr.statusCode,
// value: condition.expr.statusValue,
// condition: condition.expr.comparator,
// operationName: condition.expr.comparator,
// ),
// ];
// state.selectedFunctions[cardData['uniqueCustomId']] = functions;
// return cardData;
// }).toList() ??
// [];
// emit(state.copyWith(
// isLoading: false,
// routineName: routineDetails.name,
// selectedIcon: routineDetails.iconId,
// selectedAutomationOperator: routineDetails.decisionExpr,
// effectiveTime: routineDetails.effectiveTime,
// isAutomation: routineDetails.conditions != null,
// isTabToRun: routineDetails.conditions == null,
// thenItems: thenItems,
// ifItems: ifItems,
// selectedFunctions: Map.from(state.selectedFunctions),
// ));
// }
RoutineState _resetState() {
return const RoutineState(
ifItems: [],
@ -565,10 +858,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
try {
// emit(state.copyWith(isLoading: true));
SceneApi.deleteScene(unitUuid: spaceId, sceneId: event.sceneId);
add(const LoadScenes(spaceId, communityId));
//emit(_resetState());
} catch (e) {
emit(state.copyWith(
isLoading: false,
@ -580,11 +871,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
FutureOr<void> _deleteAutomation(
DeleteAutomation event, Emitter<RoutineState> emit) {
try {
//emit(state.copyWith(isLoading: true));
SceneApi.deleteAutomation(
unitUuid: spaceId, automationId: event.automationId);
add(const LoadAutomation(spaceId));
// emit(_resetState());
} catch (e) {
emit(state.copyWith(
isLoading: false,
@ -592,4 +881,170 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
FutureOr<void> _fetchDevices(
FetchDevicesInRoutine event, Emitter<RoutineState> 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<void> _onUpdateScene(
UpdateScene event, Emitter<RoutineState> 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<void> _onUpdateAutomation(
UpdateAutomation event, Emitter<RoutineState> 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<Condition>()
.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<AutomationAction>()
.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',
));
}
}
}

View File

@ -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<Object> get props => [automationId];
}
class UpdateScene extends RoutineEvent {
const UpdateScene();
@override
List<Object> get props => [];
}
class UpdateAutomation extends RoutineEvent {
const UpdateAutomation();
@override
List<Object> get props => [];
}
class FetchDevicesInRoutine extends RoutineEvent {}
class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {}

View File

@ -21,6 +21,7 @@ class RoutineState extends Equatable {
final String? sceneId;
final String? automationId;
final bool? isUpdate;
final List<AllDevicesModel> 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<AllDevicesModel>? 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,
];
}

View File

@ -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<RoutineBloc>().add(const CreateAutomationEvent());
if (state.automationId != null) {
context
.read<RoutineBloc>()
.add(const UpdateAutomation());
} else {
context
.read<RoutineBloc>()
.add(const CreateAutomationEvent());
}
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
if (state.sceneId != null) {
context
.read<RoutineBloc>()
.add(const UpdateScene());
} else {
context
.read<RoutineBloc>()
.add(const CreateSceneEvent());
}
}
Navigator.pop(context, true);

View File

@ -17,7 +17,7 @@ class CreateAutomationModel {
required this.actions,
});
Map<String, dynamic> toMap() {
Map<String, dynamic> 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));

View File

@ -13,6 +13,8 @@ class RoutineDetailsModel {
final EffectiveTime? effectiveTime;
final List<RoutineCondition>? 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<RoutineAction>.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'] ?? '',
);
}
}

View File

@ -16,6 +16,7 @@ class CreateNewRoutineView extends StatelessWidget {
this.routineId,
this.isScene = true,
});
@override
Widget build(BuildContext context) {
return Container(

View File

@ -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<RoutinesView> createState() => _RoutinesViewState();
}
class _RoutinesViewState extends State<RoutinesView> {
@override
void initState() {
super.initState();
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
@override
Widget build(BuildContext context) {
return BlocBuilder<SwitchTabsBloc, SwitchTabsState>(

View File

@ -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: [

View File

@ -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<FetchRoutineScenesAutomation>
child: Stack(
children: [
RoutineViewCard(
onTap: () {},
onTap: () {
BlocProvider.of<SwitchTabsBloc>(context)
.add(
const CreateNewRoutineViewEvent(true),
);
context.read<RoutineBloc>().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<FetchRoutineScenesAutomation>
child: Stack(
children: [
RoutineViewCard(
onTap: () {},
onTap: () {
BlocProvider.of<SwitchTabsBloc>(context)
.add(
const CreateNewRoutineViewEvent(true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId:
state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ??
Assets.automation,

View File

@ -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<DeviceManagementBloc, DeviceManagementState>(
builder: (context, state) {
if (state is DeviceManagementLoaded) {
List<AllDevicesModel> 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<RoutineBloc, RoutineState>(
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<RoutineBloc, RoutineState>(
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<AllDevicesModel> 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(),
);
},
);
}
}

View File

@ -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<RoutineBloc>()
.add(AddToThenContainer({
...state.thenItems[index],
'imagePath': Assets.delay,
'title': 'Delay',
}));
}
return;
}
if (result != null) {
context
.read<RoutineBloc>()
.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<RoutineBloc>().add(
AddToThenContainer(
state.thenItems[index]));
} else if (!['AC', '1G', '2G', '3G']
.contains(state.thenItems[index]
['productType'])) {
context.read<RoutineBloc>().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<RoutineBloc>().add(
RemoveDragCard(
index: index,
isFromThen: true,
key: state.thenItems[index]
['uniqueCustomId']));
},
),
))),
if (result != null) {
context.read<RoutineBloc>().add(
AddToThenContainer(
state.thenItems[index]));
} else if (!['AC', '1G', '2G', '3G']
.contains(state.thenItems[index]
['productType'])) {
context.read<RoutineBloc>().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<RoutineBloc>().add(
RemoveDragCard(
index: index,
isFromThen: true,
key: state.thenItems[index]
['uniqueCustomId']));
},
),
))),
],
),
),

View File

@ -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<bool> 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<RoutineDetailsModel> getSceneDetails(String sceneId) async {
@ -168,51 +188,14 @@ 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<bool> deleteScene(

View File

@ -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}';
}