Merge pull request #52 from SyncrowIOT/routines

Routines
This commit is contained in:
Abdullah
2024-11-27 13:43:18 +03:00
committed by GitHub
17 changed files with 1322 additions and 951 deletions

View File

@ -88,32 +88,31 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
);
}),
rightBody: const NavigateHomeGridView(),
scaffoldBody: CreateNewRoutineView(),
// BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
// builder: (context, state) {
// if (state is SelectedTabState && state.selectedTab) {
// return const RoutinesView();
// }
// if (state is ShowCreateRoutineState && state.showCreateRoutine) {
// return const CreateNewRoutineView();
// }
scaffoldBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
builder: (context, state) {
if (state is SelectedTabState && state.selectedTab) {
return const RoutinesView();
}
if (state is ShowCreateRoutineState && state.showCreateRoutine) {
return const CreateNewRoutineView();
}
// return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
// 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 BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
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 {
// return const Center(child: Text('Error fetching Devices'));
// }
// },
// );
// }),
return DeviceManagementBody(devices: devices);
} else {
return const Center(child: Text('Error fetching Devices'));
}
},
);
}),
),
);
}

View File

@ -2,10 +2,7 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
final daysMap = {
@ -52,10 +49,6 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
break;
}
BlocProvider.of<RoutineBloc>(NavigationService.navigatorKey.currentContext!).add(
EffectiveTimePeriodEvent(
EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
emit(state.copyWith(
selectedPeriod: event.period, customStartTime: startTime, customEndTime: endTime));
}
@ -70,12 +63,6 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
}
final newDaysBinary = daysList.join();
emit(state.copyWith(selectedDaysBinary: newDaysBinary));
BlocProvider.of<RoutineBloc>(NavigationService.navigatorKey.currentContext!).add(
EffectiveTimePeriodEvent(EffectiveTime(
start: state.customStartTime ?? '00:00',
end: state.customEndTime ?? '23:59',
loops: newDaysBinary)));
}
void _onSetCustomTime(SetCustomTime event, Emitter<EffectPeriodState> emit) {
@ -96,10 +83,6 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
emit(
state.copyWith(customStartTime: startTime, customEndTime: endTime, selectedPeriod: period));
BlocProvider.of<RoutineBloc>(NavigationService.navigatorKey.currentContext!).add(
EffectiveTimePeriodEvent(
EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
}
void _onResetEffectivePeriod(ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
@ -108,9 +91,6 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
customStartTime: '00:00',
customEndTime: '23:59',
selectedDaysBinary: '1111111'));
BlocProvider.of<RoutineBloc>(NavigationService.navigatorKey.currentContext!).add(
EffectiveTimePeriodEvent(EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
}
void _onResetDays(ResetDays event, Emitter<EffectPeriodState> emit) {

View File

@ -15,9 +15,6 @@ part 'routine_state.dart';
const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
// bool isAutomation = false;
// bool isTabToRun = false;
RoutineBloc() : super(const RoutineState()) {
on<AddToIfContainer>(_onAddToIfContainer);
on<AddToThenContainer>(_onAddToThenContainer);
@ -32,29 +29,37 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<EffectiveTimePeriodEvent>(_onEffectiveTimeEvent);
on<CreateAutomationEvent>(_onCreateAutomation);
on<SetRoutineName>(_onSetRoutineName);
on<ResetRoutineState>(_onResetRoutineState);
// on<RemoveFunction>(_onRemoveFunction);
// on<ClearFunctions>(_onClearFunctions);
}
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems)
..add(event.item);
if (event.isTabToRun) {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList
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;
} else {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
updatedIfItems.add(event.item);
}
if (event.isTabToRun) {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
}
}
void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// 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;
@ -65,21 +70,38 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems));
}
void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try {
if (event.functions.isEmpty) return;
final currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
currentSelectedFunctions[event.uniqueCustomId] =
List.from(currentSelectedFunctions[event.uniqueCustomId]!)
..addAll(event.functions);
List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode);
}
}
}
}
for (int i = 0; i < functionCode.length; i++) {
selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
}
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));
@ -88,8 +110,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
try {
@ -108,16 +129,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
try {
final automations = await SceneApi.getAutomationByUnitId(event.unitId);
emit(state.copyWith(
automations: automations,
isLoading: false,
));
if (automations.isNotEmpty) {
emit(state.copyWith(
automations: automations,
isLoading: false,
));
} else {
emit(state.copyWith(
isLoading: false,
loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '',
loadScenesErrorMessage: '',
));
}
} catch (e) {
emit(state.copyWith(
isLoading: false,
@ -128,15 +157,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query));
}
FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon));
}
@ -145,8 +173,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.first['deviceId'] == 'delay';
}
Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
if (_isFirstActionDelay(state.thenItems)) {
@ -159,50 +186,54 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(isLoading: true));
final actions = state.thenItems
.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null;
final function = functions.first;
if (item['deviceId'] == 'delay') {
return CreateSceneAction(
entityId: function.entityId,
actionExecutor: 'delay',
executorProperty: CreateSceneExecutorProperty(
functionCode: '',
functionValue: '',
delaySeconds: function.value,
),
);
}
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: 'device_issue',
actionExecutor: function.value,
executorProperty: null,
);
}
if (item['deviceId'] == 'delay') {
return CreateSceneAction(
entityId: function.entityId,
actionExecutor: 'delay',
executorProperty: CreateSceneExecutorProperty(
functionCode: function.functionCode.toString(),
functionValue: function.value,
delaySeconds: 0,
functionCode: '',
functionValue: '',
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
),
);
})
.whereType<CreateSceneAction>()
.toList();
}
return CreateSceneAction(
entityId: function.entityId,
actionExecutor: 'device_issue',
executorProperty: CreateSceneExecutorProperty(
functionCode: function.functionCode,
functionValue: function.value,
delaySeconds: 0,
),
);
});
}).toList();
final createSceneModel = CreateSceneModel(
spaceUuid: spaceId,
iconId: state.selectedIcon ?? '',
showInDevice: true,
sceneName: state.routineName ?? '',
sceneName: state.routineName!,
decisionExpr: 'and',
actions: actions,
);
final result = await SceneApi.createScene(createSceneModel);
if (result['success']) {
emit(const RoutineState());
emit(_resetState());
add(const LoadScenes(spaceId));
} else {
emit(state.copyWith(
isLoading: false,
@ -212,13 +243,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: e.toString(),
errorMessage: 'Something went wrong',
));
}
}
Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try {
if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith(
@ -229,26 +259,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(isLoading: true));
final conditions = state.ifItems
.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null;
final function = functions.first;
return CreateCondition(
code: state.ifItems.indexOf(item) + 1,
entityId: function.entityId,
entityType: 'device_report',
expr: ConditionExpr(
statusCode: function.functionCode,
comparator: function.condition ?? '==',
statusValue: function.value,
),
);
})
.whereType<CreateCondition>()
.toList();
final conditions = state.ifItems.expand((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functions.map((function) {
return Condition(
code: state.selectedFunctions[item['uniqueCustomId']]!.indexOf(
function,
) +
1,
entityId: function.entityId,
entityType: 'device_report',
expr: ConditionExpr(
statusCode: function.functionCode,
comparator: function.condition ?? '==',
statusValue: function.value,
),
);
});
}).toList();
if (conditions.isEmpty) {
emit(state.copyWith(
@ -258,61 +286,54 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return;
}
final actions = state.thenItems.expand((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functions.map((function) {
if (function.functionCode == 'automation') {
return AutomationAction(
entityId: function.entityId,
actionExecutor: function.value,
);
}
if (item['deviceId'] == 'delay') {
return AutomationAction(
entityId: function.entityId,
actionExecutor: 'delay',
executorProperty: ExecutorProperty(
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
),
);
}
return AutomationAction(
entityId: function.entityId,
actionExecutor: 'device_issue',
executorProperty: ExecutorProperty(
functionCode: function.functionCode,
functionValue: function.value,
),
);
});
}).toList();
final createAutomationModel = CreateAutomationModel(
unitUuid: spaceId,
spaceUuid: spaceId,
automationName: state.routineName!,
decisionExpr: state.selectedAutomationOperator,
effectiveTime: state.effectiveTime ??
EffectiveTime(
start: '00:00',
end: '23:59',
loops: '1111111',
),
effectiveTime: EffectiveTime(
start: state.effectiveTime?.start ?? '00:00',
end: state.effectiveTime?.end ?? '23:59',
loops: state.effectiveTime?.loops ?? '1111111',
),
conditions: conditions,
actions: state.thenItems
.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null;
final function = functions.first;
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: function.value,
),
);
}
return CreateSceneAction(
entityId: function.entityId,
actionExecutor: 'device_issue',
executorProperty: CreateSceneExecutorProperty(
functionCode: function.functionCode,
functionValue: function.value,
delaySeconds: 0,
),
);
})
.whereType<CreateSceneAction>()
.toList(),
actions: actions,
);
final result = await SceneApi.createAutomation(createAutomationModel);
if (result['success']) {
emit(const RoutineState());
emit(_resetState());
add(const LoadAutomation(spaceId));
} else {
emit(state.copyWith(
isLoading: false,
@ -322,21 +343,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: e.toString(),
errorMessage: 'Something went wrong',
));
}
}
FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index);
emit(state.copyWith(thenItems: thenItems));
selectedFunctions.remove(event.key);
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
} else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index);
emit(state.copyWith(ifItems: ifItems));
selectedFunctions.remove(event.key);
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
}
}
@ -347,13 +373,36 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime));
}
FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith(routineName: event.name));
}
RoutineState _resetState() {
return const RoutineState(
ifItems: [],
thenItems: [],
selectedFunctions: {},
scenes: [],
automations: [],
isLoading: false,
errorMessage: null,
loadScenesErrorMessage: null,
loadAutomationErrorMessage: null,
searchText: '',
selectedIcon: null,
isTabToRun: false,
isAutomation: false,
selectedAutomationOperator: 'AND',
effectiveTime: null,
routineName: null,
);
}
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
emit(_resetState());
}
}

View File

@ -82,9 +82,11 @@ class CreateSceneEvent extends RoutineEvent {
class RemoveDragCard extends RoutineEvent {
final int index;
final bool isFromThen;
const RemoveDragCard({required this.index, required this.isFromThen});
final String key;
const RemoveDragCard(
{required this.index, required this.isFromThen, required this.key});
@override
List<Object> get props => [index];
List<Object> get props => [index, isFromThen, key];
}
class ChangeAutomationOperator extends RoutineEvent {
@ -121,4 +123,7 @@ class SetRoutineName extends RoutineEvent {
@override
List<Object> get props => [name];
}
class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {}

View File

@ -142,9 +142,16 @@ class SaveRoutineHelper {
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: () {
context
.read<RoutineBloc>()
.add(const CreateSceneEvent());
if (state.isAutomation) {
context
.read<RoutineBloc>()
.add(const CreateAutomationEvent());
} else {
context
.read<RoutineBloc>()
.add(const CreateSceneEvent());
}
Navigator.pop(context);
},
isConfirmEnabled: true,

View File

@ -1,18 +1,187 @@
import 'dart:convert';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart';
// class CreateAutomationModel {
// String unitUuid;
// String automationName;
// String decisionExpr;
// EffectiveTime effectiveTime;
// List<CreateCondition> conditions;
// List<CreateSceneAction> actions;
// CreateAutomationModel({
// required this.unitUuid,
// required this.automationName,
// required this.decisionExpr,
// required this.effectiveTime,
// required this.conditions,
// required this.actions,
// });
// CreateAutomationModel copyWith({
// String? unitUuid,
// String? automationName,
// String? decisionExpr,
// EffectiveTime? effectiveTime,
// List<CreateCondition>? conditions,
// List<CreateSceneAction>? actions,
// }) {
// return CreateAutomationModel(
// unitUuid: unitUuid ?? this.unitUuid,
// automationName: automationName ?? this.automationName,
// decisionExpr: decisionExpr ?? this.decisionExpr,
// effectiveTime: effectiveTime ?? this.effectiveTime,
// conditions: conditions ?? this.conditions,
// actions: actions ?? this.actions,
// );
// }
// Map<String, dynamic> toMap([String? automationId]) {
// return {
// if (automationId == null) 'spaceUuid': unitUuid,
// 'automationName': automationName,
// 'decisionExpr': decisionExpr,
// 'effectiveTime': effectiveTime.toMap(),
// 'conditions': conditions.map((x) => x.toMap()).toList(),
// 'actions': actions.map((x) => x.toMap()).toList(),
// };
// }
// factory CreateAutomationModel.fromMap(Map<String, dynamic> map) {
// return CreateAutomationModel(
// unitUuid: map['spaceUuid'] ?? '',
// automationName: map['automationName'] ?? '',
// decisionExpr: map['decisionExpr'] ?? '',
// effectiveTime: EffectiveTime.fromMap(map['effectiveTime']),
// conditions: List<CreateCondition>.from(
// map['conditions']?.map((x) => CreateCondition.fromMap(x))),
// actions: List<CreateSceneAction>.from(
// map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
// );
// }
// String toJson([String? automationId]) => json.encode(toMap(automationId));
// factory CreateAutomationModel.fromJson(String source) =>
// CreateAutomationModel.fromMap(json.decode(source));
// @override
// String toString() {
// return 'CreateAutomationModel(unitUuid: $unitUuid, automationName: $automationName, decisionExpr: $decisionExpr, effectiveTime: $effectiveTime, conditions: $conditions, actions: $actions)';
// }
// }
// class EffectiveTime {
// String start;
// String end;
// String loops;
// EffectiveTime({
// required this.start,
// required this.end,
// required this.loops,
// });
// Map<String, dynamic> toMap() {
// return {
// 'start': start,
// 'end': end,
// 'loops': loops,
// };
// }
// factory EffectiveTime.fromMap(Map<String, dynamic> map) {
// return EffectiveTime(
// start: map['start'] ?? '',
// end: map['end'] ?? '',
// loops: map['loops'] ?? '',
// );
// }
// @override
// String toString() => 'EffectiveTime(start: $start, end: $end, loops: $loops)';
// }
// class CreateCondition {
// int code;
// String entityId;
// String entityType;
// ConditionExpr expr;
// CreateCondition({
// required this.code,
// required this.entityId,
// required this.entityType,
// required this.expr,
// });
// Map<String, dynamic> toMap() {
// return {
// 'code': code,
// 'entityId': entityId,
// 'entityType': entityType,
// 'expr': expr.toMap(),
// };
// }
// factory CreateCondition.fromMap(Map<String, dynamic> map) {
// return CreateCondition(
// code: map['code'] ?? 0,
// entityId: map['entityId'] ?? '',
// entityType: map['entityType'] ?? '',
// expr: ConditionExpr.fromMap(map['expr']),
// );
// }
// @override
// String toString() =>
// 'CreateCondition(code: $code, entityId: $entityId, entityType: $entityType, expr: $expr)';
// }
// class ConditionExpr {
// String statusCode;
// String comparator;
// dynamic statusValue;
// ConditionExpr({
// required this.statusCode,
// required this.comparator,
// required this.statusValue,
// });
// Map<String, dynamic> toMap() {
// return {
// 'statusCode': statusCode,
// 'comparator': comparator,
// 'statusValue': statusValue,
// };
// }
// factory ConditionExpr.fromMap(Map<String, dynamic> map) {
// return ConditionExpr(
// statusCode: map['statusCode'] ?? '',
// comparator: map['comparator'] ?? '',
// statusValue: map['statusValue'],
// );
// }
// @override
// String toString() =>
// 'ConditionExpr(statusCode: $statusCode, comparator: $comparator, statusValue: $statusValue)';
// }
import 'dart:convert';
class CreateAutomationModel {
String unitUuid;
String spaceUuid;
String automationName;
String decisionExpr;
EffectiveTime effectiveTime;
List<CreateCondition> conditions;
List<CreateSceneAction> actions;
List<Condition> conditions;
List<AutomationAction> actions;
CreateAutomationModel({
required this.unitUuid,
required this.spaceUuid,
required this.automationName,
required this.decisionExpr,
required this.effectiveTime,
@ -20,27 +189,9 @@ class CreateAutomationModel {
required this.actions,
});
CreateAutomationModel copyWith({
String? unitUuid,
String? automationName,
String? decisionExpr,
EffectiveTime? effectiveTime,
List<CreateCondition>? conditions,
List<CreateSceneAction>? actions,
}) {
return CreateAutomationModel(
unitUuid: unitUuid ?? this.unitUuid,
automationName: automationName ?? this.automationName,
decisionExpr: decisionExpr ?? this.decisionExpr,
effectiveTime: effectiveTime ?? this.effectiveTime,
conditions: conditions ?? this.conditions,
actions: actions ?? this.actions,
);
}
Map<String, dynamic> toMap([String? automationId]) {
Map<String, dynamic> toMap() {
return {
if (automationId == null) 'spaceUuid': unitUuid,
'spaceUuid': spaceUuid,
'automationName': automationName,
'decisionExpr': decisionExpr,
'effectiveTime': effectiveTime.toMap(),
@ -51,26 +202,21 @@ class CreateAutomationModel {
factory CreateAutomationModel.fromMap(Map<String, dynamic> map) {
return CreateAutomationModel(
unitUuid: map['spaceUuid'] ?? '',
spaceUuid: map['spaceUuid'] ?? '',
automationName: map['automationName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
effectiveTime: EffectiveTime.fromMap(map['effectiveTime']),
conditions: List<CreateCondition>.from(
map['conditions']?.map((x) => CreateCondition.fromMap(x))),
actions: List<CreateSceneAction>.from(
map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
conditions: List<Condition>.from(
map['conditions']?.map((x) => Condition.fromMap(x)) ?? []),
actions: List<AutomationAction>.from(
map['actions']?.map((x) => AutomationAction.fromMap(x)) ?? []),
);
}
String toJson([String? automationId]) => json.encode(toMap(automationId));
String toJson() => json.encode(toMap());
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 {
@ -99,18 +245,15 @@ class EffectiveTime {
loops: map['loops'] ?? '',
);
}
@override
String toString() => 'EffectiveTime(start: $start, end: $end, loops: $loops)';
}
class CreateCondition {
class Condition {
int code;
String entityId;
String entityType;
ConditionExpr expr;
CreateCondition({
Condition({
required this.code,
required this.entityId,
required this.entityType,
@ -126,18 +269,14 @@ class CreateCondition {
};
}
factory CreateCondition.fromMap(Map<String, dynamic> map) {
return CreateCondition(
code: map['code'] ?? 0,
factory Condition.fromMap(Map<String, dynamic> map) {
return Condition(
code: map['code']?.toInt() ?? 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 {
@ -166,8 +305,62 @@ class ConditionExpr {
statusValue: map['statusValue'],
);
}
@override
String toString() =>
'ConditionExpr(statusCode: $statusCode, comparator: $comparator, statusValue: $statusValue)';
}
class AutomationAction {
String entityId;
String actionExecutor;
ExecutorProperty? executorProperty;
AutomationAction({
required this.entityId,
required this.actionExecutor,
this.executorProperty,
});
Map<String, dynamic> toMap() {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
'executorProperty': executorProperty?.toMap(),
};
}
factory AutomationAction.fromMap(Map<String, dynamic> map) {
return AutomationAction(
entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '',
executorProperty: map['executorProperty'] != null
? ExecutorProperty.fromMap(map['executorProperty'])
: null,
);
}
}
class ExecutorProperty {
String? functionCode;
dynamic functionValue;
int? delaySeconds;
ExecutorProperty({
this.functionCode,
this.functionValue,
this.delaySeconds,
});
Map<String, dynamic> toMap() {
return {
if (functionCode != null) 'functionCode': functionCode,
if (functionValue != null) 'functionValue': functionValue,
if (delaySeconds != null) 'delaySeconds': delaySeconds,
};
}
factory ExecutorProperty.fromMap(Map<String, dynamic> map) {
return ExecutorProperty(
functionCode: map['functionCode'],
functionValue: map['functionValue'],
delaySeconds: map['delaySeconds']?.toInt(),
);
}
}

View File

@ -1,6 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/effictive_period_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/period_option.dart';
import 'package:syncrow_web/pages/routiens/widgets/repeat_days.dart';
@ -11,39 +9,36 @@ class EffectivePeriodView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => EffectPeriodBloc(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Spacer(),
Expanded(
child: Text(
'Effective Period',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Spacer(),
Expanded(
child: Text(
'Effective Period',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
const Spacer(),
],
),
const Divider(
color: ColorsManager.backgroundColor,
),
const PeriodOptions(
showCustomTimePicker: EffectPeriodHelper.showCustomTimePicker,
),
const SizedBox(height: 16),
const RepeatDays(),
const SizedBox(height: 24),
],
),
),
const Spacer(),
],
),
const Divider(
color: ColorsManager.backgroundColor,
),
const PeriodOptions(
showCustomTimePicker: EffectPeriodHelper.showCustomTimePicker,
),
const SizedBox(height: 16),
const RepeatDays(),
const SizedBox(height: 24),
],
),
);
}

View File

@ -26,9 +26,7 @@ class IfContainer extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('IF',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation)
AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator),
@ -52,20 +50,38 @@ class IfContainer extends StatelessWidget {
runSpacing: 8,
children: List.generate(
state.ifItems.length,
(index) => DraggableCard(
imagePath:
state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 8),
isFromThen: false,
isFromIf: true,
onRemove: () {
context.read<RoutineBloc>().add(
RemoveDragCard(
index: index, isFromThen: false));
(index) => GestureDetector(
onTap: () async {
if (!state.isTabToRun) {
final result = await DeviceDialogHelper.showDeviceDialog(
context, state.ifItems[index]);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(state.ifItems[index], false));
} else if (!['AC', '1G', '2G', '3G']
.contains(state.ifItems[index]['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(state.ifItems[index], false));
}
}
},
child: DraggableCard(
imagePath: state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
isFromThen: false,
isFromIf: true,
onRemove: () {
context.read<RoutineBloc>().add(RemoveDragCard(
index: index,
isFromThen: false,
key: state.ifItems[index]['uniqueCustomId']));
},
),
)),
),
],
@ -81,22 +97,14 @@ class IfContainer extends StatelessWidget {
if (!state.isTabToRun) {
if (mutableData['deviceId'] == 'tab_to_run') {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, true));
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, true));
} else {
final result = await DeviceDialogHelper.showDeviceDialog(
context, mutableData);
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
}
}
}
@ -136,15 +144,12 @@ class AutomationOperatorSelector extends StatelessWidget {
child: Text(
'Any condition is met',
style: context.textTheme.bodyMedium?.copyWith(
color: selectedOperator == 'or'
? ColorsManager.whiteColors
: ColorsManager.blackColor,
color:
selectedOperator == 'or' ? ColorsManager.whiteColors : ColorsManager.blackColor,
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'or'));
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or'));
},
),
Container(
@ -170,9 +175,7 @@ class AutomationOperatorSelector extends StatelessWidget {
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'and'));
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and'));
},
),
],

View File

@ -21,12 +21,9 @@ class PeriodOptions extends StatelessWidget {
builder: (context, state) {
return Column(
children: [
_buildRadioOption(
context, EnumEffectivePeriodOptions.allDay, '24 Hours'),
_buildRadioOption(context, EnumEffectivePeriodOptions.daytime,
'Sunrise to Sunset'),
_buildRadioOption(
context, EnumEffectivePeriodOptions.night, 'Sunset to Sunrise'),
_buildRadioOption(context, EnumEffectivePeriodOptions.allDay, '24 Hours'),
_buildRadioOption(context, EnumEffectivePeriodOptions.daytime, 'Sunrise to Sunset'),
_buildRadioOption(context, EnumEffectivePeriodOptions.night, 'Sunset to Sunrise'),
ListTile(
contentPadding: EdgeInsets.zero,
onTap: () => showCustomTimePicker(context),
@ -37,8 +34,7 @@ class PeriodOptions extends StatelessWidget {
fontWeight: FontWeight.w400,
fontSize: 14),
),
subtitle: state.customStartTime != null &&
state.customEndTime != null
subtitle: state.customStartTime != null && state.customEndTime != null
? Text(
'${"${state.customStartTime}"} - ${"${state.customEndTime}"}',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
@ -83,9 +79,7 @@ class PeriodOptions extends StatelessWidget {
subtitle: Text(
subtitle,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 10),
color: ColorsManager.textPrimaryColor, fontWeight: FontWeight.w400, fontSize: 10),
),
trailing: Radio<EnumEffectivePeriodOptions>(
value: value,

View File

@ -74,7 +74,7 @@ class _AutomationDialogState extends State<AutomationDialog> {
DeviceFunctionData(
entityId: widget.automationId,
functionCode: 'automation',
value: _isEnabled,
value: _isEnabled ? 'rule_enable' : 'rule_disable',
operationName: 'Automation',
),
],

View File

@ -0,0 +1,62 @@
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';
class DiscardDialog {
static void show(BuildContext context) {
context.customAlertDialog(
alertBody: Container(
height: 150,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
'If you close, you will lose all the changes you have made.',
textAlign: TextAlign.center,
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.red,
fontWeight: FontWeight.w400,
fontSize: 14,
),
),
const SizedBox(
height: 20,
),
Text(
'Are you sure you wish to close?',
style: context.textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.grayColor,
),
)
],
)),
title: 'Discard',
titleStyle: context.textTheme.titleLarge!.copyWith(
color: ColorsManager.red,
fontWeight: FontWeight.bold,
),
onDismissText: "Dont Close",
onConfirmText: "Close",
onDismissColor: ColorsManager.grayColor,
onConfirmColor: ColorsManager.red.withOpacity(0.8),
onDismiss: () {
Navigator.pop(context);
},
onConfirm: () {
context.read<RoutineBloc>().add(ResetRoutineState());
Navigator.pop(context);
BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(false),
);
BlocProvider.of<SwitchTabsBloc>(context).add(
const TriggerSwitchTabsEvent(true),
);
});
}
}

View File

@ -1,9 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
@ -19,444 +22,392 @@ class SettingHelper {
context: context,
builder: (BuildContext context) {
final isAutomation = context.read<RoutineBloc>().state.isAutomation;
return BlocProvider(
create: (_) =>
SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),
return MultiBlocProvider(
providers: [
BlocProvider(
create: (_) => EffectPeriodBloc(),
),
BlocProvider(
create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? ''))),
],
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<SettingBloc, SettingState>(
builder: (context, state) {
String selectedIcon = '';
List<IconModel> list = [];
if (state is TabToRunSettingLoaded) {
selectedIcon = state.selectedIcon;
list = state.iconList;
}
return Container(
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
height: context.read<SettingBloc>().isExpanded && isAutomation
? 500
: 300,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DialogHeader('Settings'),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 400,
child: isAutomation
? Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Validity',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(
context)
.add(FetchIcons(
expanded: !context
.read<
SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Effective Period',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Executed by',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager
.textGray,
fontWeight:
FontWeight
.w400,
fontSize: 14)),
],
),
],
)),
],
)
: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(
context)
.add(FetchIcons(
expanded: !context
.read<
SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Icons',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Show on devices page',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
height: 30,
width: 1,
color: ColorsManager
.graysColor,
),
Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: (value) {},
applyTheme: true,
),
),
],
)
],
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Executed by',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager
.textGray,
fontWeight:
FontWeight
.w400,
fontSize: 14)),
],
),
],
)),
],
),
),
if (context.read<SettingBloc>().isExpanded &&
!isAutomation)
SizedBox(
width: 400,
height: 150,
child: state is LoadingState
? const Center(
child: CircularProgressIndicator())
: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
final iconModel = list[index];
return SizedBox(
width: 35,
height: 35,
child: InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(
context)
.add(SelectIcon(
iconId: iconModel.uuid,
));
selectedIcon = iconModel.uuid;
},
child: SizedBox(
child: ClipOval(
child: Container(
padding:
const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(
color: selectedIcon ==
iconModel.uuid
? ColorsManager
.primaryColorWithOpacity
: Colors
.transparent,
width: 2,
),
shape: BoxShape.circle,
),
child: Image.memory(
iconModel.iconBytes,
),
),
),
),
),
);
},
)),
if (context.read<SettingBloc>().isExpanded &&
isAutomation)
const SizedBox(
height: 350,
width: 400,
child: EffectivePeriodView())
],
),
Container(
width: MediaQuery.sizeOf(context).width,
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: ColorsManager.greyColor,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
content: BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
builder: (context, effectPeriodState) {
return BlocBuilder<SettingBloc, SettingState>(
builder: (context, settingState) {
String selectedIcon = '';
List<IconModel> list = [];
if (settingState is TabToRunSettingLoaded) {
selectedIcon = settingState.selectedIcon;
list = settingState.iconList;
}
return Container(
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 300,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DialogHeader('Settings'),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
),
),
),
),
),
Container(
width: 1,
height: 50,
color: ColorsManager.greyColor),
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop(selectedIcon);
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Confirm',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.primaryColorWithOpacity,
),
),
),
),
SizedBox(
width: 400,
child: isAutomation
? Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.only(
top: 10, left: 10, right: 10, bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'Validity',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context).add(
FetchIcons(
expanded: !context
.read<SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'Effective Period',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Executed by',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
fontWeight: FontWeight.w400,
fontSize: 14)),
],
),
],
)),
],
)
: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.only(
top: 10, left: 10, right: 10, bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context).add(
FetchIcons(
expanded: !context
.read<SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'Icons',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
size: 15,
)
],
),
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Show on devices page',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: 30,
width: 1,
color: ColorsManager.graysColor,
),
Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: (value) {},
applyTheme: true,
),
),
],
)
],
),
const SizedBox(
height: 5,
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Executed by',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
fontWeight: FontWeight.w400,
fontSize: 14)),
],
),
],
)),
],
),
),
if (context.read<SettingBloc>().isExpanded && !isAutomation)
SizedBox(
width: 400,
height: 150,
child: settingState is LoadingState
? const Center(child: CircularProgressIndicator())
: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
final iconModel = list[index];
return SizedBox(
width: 35,
height: 35,
child: InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context)
.add(SelectIcon(
iconId: iconModel.uuid,
));
selectedIcon = iconModel.uuid;
},
child: SizedBox(
child: ClipOval(
child: Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(
color: selectedIcon == iconModel.uuid
? ColorsManager
.primaryColorWithOpacity
: Colors.transparent,
width: 2,
),
shape: BoxShape.circle,
),
child: Image.memory(
iconModel.iconBytes,
),
),
),
),
),
);
},
)),
if (context.read<SettingBloc>().isExpanded && isAutomation)
const SizedBox(height: 350, width: 400, child: EffectivePeriodView())
],
),
)
],
),
);
},
),
Container(
width: MediaQuery.sizeOf(context).width,
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: ColorsManager.greyColor,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.textGray,
),
),
),
),
),
Container(width: 1, height: 50, color: ColorsManager.greyColor),
Expanded(
child: InkWell(
onTap: () {
if (isAutomation) {
BlocProvider.of<RoutineBloc>(context).add(
EffectiveTimePeriodEvent(EffectiveTime(
start: effectPeriodState.customStartTime!,
end: effectPeriodState.customEndTime!,
loops: effectPeriodState.selectedDaysBinary)));
Navigator.of(context).pop();
} else {
Navigator.of(context).pop(selectedIcon);
}
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Confirm',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
),
),
),
],
),
)
],
),
);
},
);
}),
),
);
},

View File

@ -1,11 +1,12 @@
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/common/text_field/custom_text_field.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';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/setting_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart';
class RoutineSearchAndButtons extends StatelessWidget {
@ -15,88 +16,260 @@ class RoutineSearchAndButtons extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Wrap(
runSpacing: 16,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Wrap(
runSpacing: 16,
children: [
Expanded(
child: Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.end,
children: [
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constraints.maxWidth > 700
? 450
: constraints.maxWidth - 32),
child: StatefulTextField(
title: 'Routine Name',
height: 40,
controller: TextEditingController(),
hintText: 'Please enter the name',
boxDecoration: containerWhiteDecoration,
elevation: 0,
borderRadius: 15,
isRequired: true,
width: 450,
onChanged: (value) {
context
.read<RoutineBloc>()
.add(SetRoutineName(value));
},
),
),
(constraints.maxWidth <= 1000)
? const SizedBox()
: SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () async {
final result =
await SettingHelper.showSettingDialog(
context: context,
);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddSelectedIcon(result));
}
},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Settings',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.primaryColor,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.end,
children: [
ConstrainedBox(
constraints: BoxConstraints(
maxWidth:
constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
// child: StatefulTextField(
// title: 'Routine Name',
// initialValue: state.routineName ?? '',
// height: 40,
// // controller: TextEditingController(),
// hintText: 'Please enter the name',
// boxDecoration: containerWhiteDecoration,
// elevation: 0,
// borderRadius: 15,
// isRequired: true,
// width: 450,
// onSubmitted: (value) {
// // context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// onChanged: (value) {
// context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// ),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('* ',
style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.red, fontSize: 13)),
Text(
'Routine Name',
style: context.textTheme.bodyMedium!.copyWith(
fontSize: 13,
fontWeight: FontWeight.w600,
color: ColorsManager.blackColor,
),
),
],
),
Container(
width: 450,
height: 40,
decoration: containerWhiteDecoration,
child: TextFormField(
style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.blackColor),
initialValue: state.routineName,
decoration: InputDecoration(
hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium!
.copyWith(fontSize: 12, color: ColorsManager.grayColor),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
border: InputBorder.none,
),
onChanged: (value) {
context.read<RoutineBloc>().add(SetRoutineName(value));
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'This field is required';
}
return null;
},
),
),
],
),
),
(constraints.maxWidth <= 1000)
? const SizedBox()
: SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: state.isAutomation || state.isTabToRun
? () async {
final result = await SettingHelper.showSettingDialog(
context: context,
);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddSelectedIcon(result));
}
}
: null,
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Settings',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.primaryColor,
),
),
),
),
),
],
),
),
if (constraints.maxWidth > 1000)
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {
DiscardDialog.show(context);
},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Cancel',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.blackColor,
),
),
),
),
],
),
),
const SizedBox(width: 12),
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {
if (state.routineName == null || state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Please enter the routine name'),
duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red,
action: SnackBarAction(
label: 'Dismiss',
onPressed: () {
// Optional action on Snackbar
},
),
),
);
return;
}
if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Please add if and then condition'),
duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red,
action: SnackBarAction(
label: 'Dismiss',
onPressed: () {
// Optional action on Snackbar
},
),
),
);
return;
}
SaveRoutineHelper.showSaveRoutineDialog(context);
},
borderRadius: 15,
elevation: 0,
backgroundColor: ColorsManager.primaryColor,
child: const Text(
'Save',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.whiteColors,
),
),
),
),
),
],
),
],
),
if (constraints.maxWidth > 1000)
Row(
mainAxisSize: MainAxisSize.min,
if (constraints.maxWidth <= 1000)
Wrap(
runSpacing: 12,
children: [
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
onPressed: state.isAutomation || state.isTabToRun
? () async {
final result = await SettingHelper.showSettingDialog(
context: context,
);
if (result != null) {
context.read<RoutineBloc>().add(AddSelectedIcon(result));
}
}
: null,
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Settings',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.primaryColor,
),
),
),
),
),
const SizedBox(width: 12),
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {
DiscardDialog.show(context);
},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
@ -119,6 +292,39 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: Center(
child: DefaultButton(
onPressed: () {
if (state.routineName == null || state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Please enter the routine name'),
duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red,
action: SnackBarAction(
label: 'Dismiss',
onPressed: () {
// Optional action on Snackbar
},
),
),
);
return;
}
if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Please add if and then condition'),
duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red,
action: SnackBarAction(
label: 'Dismiss',
onPressed: () {
// Optional action on Snackbar
},
),
),
);
return;
}
SaveRoutineHelper.showSaveRoutineDialog(context);
},
borderRadius: 15,
@ -138,78 +344,8 @@ class RoutineSearchAndButtons extends StatelessWidget {
],
),
],
),
if (constraints.maxWidth <= 1000)
Wrap(
runSpacing: 12,
children: [
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Settings',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.primaryColor,
),
),
),
),
),
const SizedBox(width: 12),
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,
backgroundColor: ColorsManager.boxColor,
child: const Text(
'Cancel',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.blackColor,
),
),
),
),
),
const SizedBox(width: 12),
SizedBox(
height: 40,
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
borderRadius: 15,
elevation: 0,
backgroundColor: ColorsManager.primaryColor,
child: const Text(
'Save',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: ColorsManager.whiteColors,
),
),
),
),
),
],
),
],
);
},
);
},
);

View File

@ -26,7 +26,7 @@ class _ScenesAndAutomationsState extends State<ScenesAndAutomations> {
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
if (state.scenes.isNotEmpty || state.automations.isNotEmpty) {
if (!state.isLoading) {
var scenes = [...state.scenes, ...state.automations];
return Wrap(
spacing: 10,

View File

@ -26,9 +26,7 @@ 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,
@ -37,16 +35,12 @@ 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<RoutineBloc>()
.add(AddToThenContainer({
context.read<RoutineBloc>().add(AddToThenContainer({
...state.thenItems[index],
'imagePath': Assets.delay,
'title': 'Delay',
@ -55,37 +49,32 @@ class ThenContainer extends StatelessWidget {
return;
}
final result = await DeviceDialogHelper
.showDeviceDialog(
context, state.thenItems[index]);
final result = await DeviceDialogHelper.showDeviceDialog(
context, state.thenItems[index]);
if (result != null) {
context.read<RoutineBloc>().add(
AddToThenContainer(
state.thenItems[index]));
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]));
.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'] ?? '',
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<RoutineBloc>().add(
RemoveDragCard(
index: index, isFromThen: true));
context.read<RoutineBloc>().add(RemoveDragCard(
index: index,
isFromThen: true,
key: state.thenItems[index]['uniqueCustomId']));
},
),
))),
@ -140,8 +129,7 @@ class ThenContainer extends StatelessWidget {
}
if (mutableData['deviceId'] == 'delay') {
final result =
await DelayHelper.showDelayPickerDialog(context, mutableData);
final result = await DelayHelper.showDelayPickerDialog(context, mutableData);
if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({
@ -153,13 +141,11 @@ class ThenContainer extends StatelessWidget {
return;
}
final result =
await DeviceDialogHelper.showDeviceDialog(context, mutableData);
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData);
if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
}
},

View File

@ -29,11 +29,13 @@ class SceneApi {
rethrow;
}
}
//
// create automation
static Future<Map<String, dynamic>> createAutomation(
CreateAutomationModel createAutomationModel) async {
try {
debugPrint("automation body ${createAutomationModel.toMap()}");
final response = await _httpService.post(
path: ApiEndpoints.createAutomation,
body: createAutomationModel.toMap(),
@ -42,8 +44,10 @@ class SceneApi {
return json;
},
);
debugPrint('create automation response: $response');
return response;
} catch (e) {
debugPrint(e.toString());
rethrow;
}
}

View File

@ -23,6 +23,11 @@ extension BuildContextExt on BuildContext {
VoidCallback? onDismiss,
bool? hideConfirmButton,
final double? dialogWidth,
TextStyle? titleStyle,
String? onDismissText,
String? onConfirmText,
Color? onDismissColor,
Color? onConfirmColor,
}) {
showDialog(
context: this,
@ -42,10 +47,11 @@ extension BuildContextExt on BuildContext {
/// header widget
Text(
title,
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
style: titleStyle ??
context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
@ -79,9 +85,10 @@ extension BuildContextExt on BuildContext {
},
child: Center(
child: Text(
'Cancel',
style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.greyColor),
onDismissText ?? 'Cancel',
style: context.textTheme.bodyMedium!.copyWith(
color: onDismissColor ??
ColorsManager.greyColor),
),
),
),
@ -94,9 +101,9 @@ extension BuildContextExt on BuildContext {
onTap: onConfirm,
child: Center(
child: Text(
'Confirm',
onConfirmText ?? 'Confirm',
style: context.textTheme.bodyMedium!.copyWith(
color:
color: onConfirmColor ??
ColorsManager.primaryColorWithOpacity),
),
),