push calling create scene

This commit is contained in:
ashrafzarkanisala
2024-11-24 11:17:54 +03:00
parent 5a9729fe10
commit 87c47a74ce
13 changed files with 759 additions and 131 deletions

View File

@ -18,6 +18,7 @@ class StatefulTextField extends StatefulWidget {
this.padding,
this.icon,
this.hintColor,
required this.onChanged,
});
final String title;
@ -32,6 +33,7 @@ class StatefulTextField extends StatefulWidget {
final double? padding;
final IconData? icon;
final Color? hintColor;
final Function(String)? onChanged;
@override
State<StatefulTextField> createState() => _StatefulTextFieldState();
@ -59,6 +61,7 @@ class _StatefulTextFieldState extends State<StatefulTextField> {
padding: widget.padding,
icon: widget.icon,
hintColor: widget.hintColor,
onChanged: widget.onChanged,
);
}
}
@ -78,6 +81,7 @@ class CustomTextField extends StatelessWidget {
this.padding,
this.icon,
this.hintColor,
required this.onChanged,
});
final String title;
@ -92,6 +96,7 @@ class CustomTextField extends StatelessWidget {
final double? padding;
final IconData? icon;
final Color? hintColor;
final Function(String)? onChanged;
@override
Widget build(BuildContext context) {
@ -120,14 +125,21 @@ class CustomTextField extends StatelessWidget {
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: hintText,
hintStyle: TextStyle(fontSize: 12, color: hintColor ?? ColorsManager.blackColor),
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: padding ?? 10),
hintStyle: TextStyle(
fontSize: 12, color: hintColor ?? ColorsManager.blackColor),
contentPadding: EdgeInsets.symmetric(
horizontal: 12, vertical: padding ?? 10),
border: InputBorder.none,
suffixIcon: icon != null ? Icon(icon, color: ColorsManager.greyColor) : null,
suffixIcon: icon != null
? Icon(icon, color: ColorsManager.greyColor)
: null,
),
onFieldSubmitted: (_) {
onSubmittedFun!();
},
onChanged: (value) {
onChanged!(value);
},
),
),
),

View File

@ -2,8 +2,10 @@ 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/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/view/routines_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -20,7 +22,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
),
BlocProvider(
create: (context) => SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
create: (context) =>
SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
),
],
child: WebScaffold(
@ -30,7 +33,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
style: Theme.of(context).textTheme.headlineLarge,
),
),
centerBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(builder: (context, state) {
centerBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
builder: (context, state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -39,16 +43,21 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null,
),
onPressed: () {
context.read<SwitchTabsBloc>().add(const TriggerSwitchTabsEvent(false));
context
.read<SwitchTabsBloc>()
.add(const TriggerSwitchTabsEvent(false));
},
child: Text(
'Devices',
style: context.textTheme.titleMedium?.copyWith(
color: state is SelectedTabState && state.selectedTab == false
color:
state is SelectedTabState && state.selectedTab == false
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
(state is SelectedTabState) && state.selectedTab == false ? FontWeight.w700 : FontWeight.w400,
fontWeight: (state is SelectedTabState) &&
state.selectedTab == false
? FontWeight.w700
: FontWeight.w400,
),
),
),
@ -57,16 +66,21 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null,
),
onPressed: () {
context.read<SwitchTabsBloc>().add(const TriggerSwitchTabsEvent(true));
context
.read<SwitchTabsBloc>()
.add(const TriggerSwitchTabsEvent(true));
},
child: Text(
'Routines',
style: context.textTheme.titleMedium?.copyWith(
color: (state is SelectedTabState) && state.selectedTab == true
color:
(state is SelectedTabState) && state.selectedTab == true
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
(state is SelectedTabState) && state.selectedTab == true ? FontWeight.w700 : FontWeight.w400,
(state is SelectedTabState) && state.selectedTab == true
? FontWeight.w700
: FontWeight.w400,
),
),
),
@ -74,30 +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();
// }
//
// 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'));
// }
// },
// );
// }),
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 DeviceManagementBody(devices: devices);
} else {
return const Center(child: Text('Error fetching Devices'));
}
},
);
}),
),
);
}

View File

@ -12,7 +12,8 @@ class DeviceSearchFilters extends StatefulWidget {
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
}
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout {
final TextEditingController communityController = TextEditingController();
final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController();
@ -34,7 +35,8 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperRe
const SizedBox(width: 20),
_buildSearchField("Space Name", unitNameController, 200),
const SizedBox(width: 20),
_buildSearchField("Device Name / Product Name", productNameController, 300),
_buildSearchField(
"Device Name / Product Name", productNameController, 300),
const SizedBox(width: 20),
_buildSearchResetButtons(),
],
@ -59,7 +61,8 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperRe
);
}
Widget _buildSearchField(String title, TextEditingController controller, double width) {
Widget _buildSearchField(
String title, TextEditingController controller, double width) {
return Container(
child: StatefulTextField(
title: title,
@ -73,6 +76,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperRe
community: communityController.text,
searchField: true));
},
onChanged: (p0) {},
),
);
}

View File

@ -1,6 +1,9 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene/create_scene_model.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/routines_api.dart';
@ -20,6 +23,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<LoadScenes>(_onLoadScenes);
on<LoadAutomation>(_onLoadAutomation);
on<AddFunctionToRoutine>(_onAddFunctionsToRoutine);
on<SearchRoutines>(_onSearchRoutines);
on<AddSelectedIcon>(_onAddSelectedIcon);
on<CreateSceneEvent>(_onCreateScene);
// on<RemoveFunction>(_onRemoveFunction);
// on<ClearFunctions>(_onClearFunctions);
}
@ -40,8 +46,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
debugPrint('Adding functions to routine: ${event.functions}');
debugPrint('Unique Custom ID: ${event.uniqueCustomId}');
final currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
@ -52,7 +56,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
debugPrint('Updated selected functions: $currentSelectedFunctions');
}
// void _onRemoveFunction(RemoveFunction event, Emitter<RoutineState> emit) {
@ -80,7 +83,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Something went wrong',
errorMessage: 'Failed to load scenes',
));
}
}
@ -98,7 +101,97 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: 'Something went wrong',
errorMessage: 'Failed to load automations',
));
}
}
FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) {
emit(state.copyWith(routineName: event.query));
}
FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon));
}
bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
if (actions.isEmpty) return false;
return actions.first['deviceId'] == 'delay';
}
Future<void> _onCreateScene(
CreateSceneEvent 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
.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,
),
);
}
return CreateSceneAction(
entityId: function.entityId,
actionExecutor: 'device_issue',
executorProperty: CreateSceneExecutorProperty(
functionCode: function.functionCode.toString(),
functionValue: function.value,
delaySeconds: 0,
),
);
})
.whereType<CreateSceneAction>()
.toList();
final createSceneModel = CreateSceneModel(
spaceUuid: spaceId,
iconId: state.selectedIcon ?? '',
showInDevice: true,
sceneName: state.routineName ?? '',
decisionExpr: 'and',
actions: actions,
);
final result = await SceneApi.createScene(createSceneModel);
if (result['success']) {
emit(state.copyWith(
isLoading: false,
errorMessage: null,
));
} else {
emit(state.copyWith(
isLoading: false,
errorMessage: result['message'],
));
}
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: e.toString(),
));
}
}

View File

@ -59,4 +59,24 @@ class RemoveFunction extends RoutineEvent {
List<Object> get props => [function];
}
class SearchRoutines extends RoutineEvent {
final String query;
const SearchRoutines(this.query);
@override
List<Object> get props => [query];
}
class AddSelectedIcon extends RoutineEvent {
final String icon;
const AddSelectedIcon(this.icon);
@override
List<Object> get props => [icon];
}
class CreateSceneEvent extends RoutineEvent {
const CreateSceneEvent();
@override
List<Object> get props => [];
}
class ClearFunctions extends RoutineEvent {}

View File

@ -9,6 +9,8 @@ class RoutineState extends Equatable {
final Map<String, List<DeviceFunctionData>> selectedFunctions;
final bool isLoading;
final String? errorMessage;
final String? routineName;
final String? selectedIcon;
const RoutineState({
this.ifItems = const [],
@ -19,6 +21,8 @@ class RoutineState extends Equatable {
this.selectedFunctions = const {},
this.isLoading = false,
this.errorMessage,
this.routineName,
this.selectedIcon,
});
RoutineState copyWith({
@ -29,6 +33,8 @@ class RoutineState extends Equatable {
Map<String, List<DeviceFunctionData>>? selectedFunctions,
bool? isLoading,
String? errorMessage,
String? routineName,
String? selectedIcon,
}) {
return RoutineState(
ifItems: ifItems ?? this.ifItems,
@ -38,6 +44,8 @@ class RoutineState extends Equatable {
selectedFunctions: selectedFunctions ?? this.selectedFunctions,
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage ?? this.errorMessage,
routineName: routineName ?? this.routineName,
selectedIcon: selectedIcon ?? this.selectedIcon,
);
}
@ -50,5 +58,7 @@ class RoutineState extends Equatable {
selectedFunctions,
isLoading,
errorMessage,
routineName,
selectedIcon,
];
}

View File

@ -0,0 +1,132 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class SaveRoutineHelper {
static Future<void> showSaveRoutineDialog(BuildContext context) async {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: 600,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DialogHeader('Create a scene: ${state.routineName ?? ""}'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Left side - IF
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'IF:',
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
if (context.read<RoutineBloc>().isTabToRun)
ListTile(
leading: SvgPicture.asset(
Assets.tabToRun,
width: 24,
height: 24,
),
title: const Text('Tab to run'),
),
],
),
),
const SizedBox(width: 16),
// Right side - THEN items
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'THEN:',
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
...state.thenItems.map((item) {
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)),
subtitle: Wrap(
children: functions
.map((f) => Text(
'${f.operationName}: ${f.value}, ',
style: const TextStyle(
color:
ColorsManager.grayColor,
fontSize: 8),
overflow: TextOverflow.ellipsis,
maxLines: 3,
))
.toList(),
),
);
}),
],
),
),
],
),
),
if (state.errorMessage != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
state.errorMessage!,
style: const TextStyle(color: Colors.red),
),
),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: () {
context
.read<RoutineBloc>()
.add(const CreateSceneEvent());
Navigator.pop(context);
},
isConfirmEnabled: true,
),
],
),
),
);
},
);
},
);
}
}

View File

@ -10,13 +10,16 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:flutter/cupertino.dart';
class SettingHelper {
static Future<Map<String, dynamic>?> showSettingDialog(
{required BuildContext context, String? iconId, required bool isAutomation}) async {
return showDialog<Map<String, dynamic>?>(
static Future<String?> showSettingDialog(
{required BuildContext context,
String? iconId,
required bool isAutomation}) async {
return showDialog<String>(
context: context,
builder: (BuildContext context) {
return BlocProvider(
create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),
create: (_) =>
SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<SettingBloc, SettingState>(
@ -29,7 +32,9 @@ class SettingHelper {
}
return Container(
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 300,
height: context.read<SettingBloc>().isExpanded && isAutomation
? 500
: 300,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
@ -52,13 +57,18 @@ class SettingHelper {
children: [
Container(
padding: const EdgeInsets.only(
top: 10, left: 10, right: 10, bottom: 10),
top: 10,
left: 10,
right: 10,
bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Validity',
@ -66,13 +76,18 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
@ -89,14 +104,18 @@ class SettingHelper {
),
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context).add(
FetchIcons(
BlocProvider.of<SettingBloc>(
context)
.add(FetchIcons(
expanded: !context
.read<SettingBloc>()
.read<
SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Effective Period',
@ -104,13 +123,18 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
@ -126,7 +150,9 @@ class SettingHelper {
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Executed by',
@ -134,8 +160,10 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
@ -143,8 +171,12 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
fontWeight: FontWeight.w400,
color:
ColorsManager
.textGray,
fontWeight:
FontWeight
.w400,
fontSize: 14)),
],
),
@ -157,19 +189,26 @@ class SettingHelper {
children: [
Container(
padding: const EdgeInsets.only(
top: 10, left: 10, right: 10, bottom: 10),
top: 10,
left: 10,
right: 10,
bottom: 10),
child: Column(
children: [
InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context).add(
FetchIcons(
BlocProvider.of<SettingBloc>(
context)
.add(FetchIcons(
expanded: !context
.read<SettingBloc>()
.read<
SettingBloc>()
.isExpanded));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Icons',
@ -177,13 +216,18 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight
.w400,
fontSize: 14),
),
const Icon(
Icons.arrow_forward_ios_outlined,
color: ColorsManager.textGray,
Icons
.arrow_forward_ios_outlined,
color: ColorsManager
.textGray,
size: 15,
)
],
@ -199,7 +243,9 @@ class SettingHelper {
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Show on devices page',
@ -207,17 +253,21 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
height: 30,
width: 1,
color: ColorsManager.graysColor,
color: ColorsManager
.graysColor,
),
Transform.scale(
scale: .8,
@ -241,7 +291,9 @@ class SettingHelper {
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Executed by',
@ -249,8 +301,10 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textPrimaryColor,
fontWeight: FontWeight.w400,
color: ColorsManager
.textPrimaryColor,
fontWeight:
FontWeight.w400,
fontSize: 14),
),
Text('Cloud',
@ -258,8 +312,12 @@ class SettingHelper {
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
fontWeight: FontWeight.w400,
color:
ColorsManager
.textGray,
fontWeight:
FontWeight
.w400,
fontSize: 14)),
],
),
@ -268,12 +326,14 @@ class SettingHelper {
],
),
),
if (context.read<SettingBloc>().isExpanded && !isAutomation)
if (context.read<SettingBloc>().isExpanded &&
!isAutomation)
SizedBox(
width: 400,
height: 150,
child: state is LoadingState
? const Center(child: CircularProgressIndicator())
? const Center(
child: CircularProgressIndicator())
: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
@ -290,7 +350,8 @@ class SettingHelper {
height: 35,
child: InkWell(
onTap: () {
BlocProvider.of<SettingBloc>(context)
BlocProvider.of<SettingBloc>(
context)
.add(SelectIcon(
iconId: iconModel.uuid,
));
@ -299,12 +360,16 @@ class SettingHelper {
child: SizedBox(
child: ClipOval(
child: Container(
padding: const EdgeInsets.all(1),
padding:
const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(
color: selectedIcon == iconModel.uuid
? ColorsManager.primaryColorWithOpacity
: Colors.transparent,
color: selectedIcon ==
iconModel.uuid
? ColorsManager
.primaryColorWithOpacity
: Colors
.transparent,
width: 2,
),
shape: BoxShape.circle,
@ -319,8 +384,12 @@ class SettingHelper {
);
},
)),
if (context.read<SettingBloc>().isExpanded && isAutomation)
const SizedBox(height: 350, width: 400, child: EffectivePeriodView())
if (context.read<SettingBloc>().isExpanded &&
isAutomation)
const SizedBox(
height: 350,
width: 400,
child: EffectivePeriodView())
],
),
Container(
@ -344,14 +413,20 @@ class SettingHelper {
alignment: AlignmentDirectional.center,
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray,
),
),
),
),
),
Container(width: 1, height: 50, color: ColorsManager.greyColor),
Container(
width: 1,
height: 50,
color: ColorsManager.greyColor),
Expanded(
child: InkWell(
onTap: () {
@ -361,8 +436,12 @@ class SettingHelper {
alignment: AlignmentDirectional.center,
child: Text(
'Confirm',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager
.primaryColorWithOpacity,
),
),
),

View File

@ -0,0 +1,230 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
class CreateSceneModel {
String spaceUuid;
String iconId;
bool showInDevice;
String sceneName;
String decisionExpr;
List<CreateSceneAction> actions;
CreateSceneModel({
required this.spaceUuid,
required this.iconId,
required this.showInDevice,
required this.sceneName,
required this.decisionExpr,
required this.actions,
});
CreateSceneModel copyWith({
String? spaceUuid,
String? iconId,
bool? showInDevice,
String? sceneName,
String? decisionExpr,
List<CreateSceneAction>? actions,
bool? showInHomePage,
}) {
return CreateSceneModel(
spaceUuid: spaceUuid ?? this.spaceUuid,
iconId: iconId ?? this.iconId,
showInDevice: showInDevice ?? this.showInDevice,
sceneName: sceneName ?? this.sceneName,
decisionExpr: decisionExpr ?? this.decisionExpr,
actions: actions ?? this.actions,
);
}
Map<String, dynamic> toMap([String? sceneId]) {
return {
if (sceneId == null) 'spaceUuid': spaceUuid,
if (iconId.isNotEmpty) 'iconUuid': iconId,
'showInHomePage': showInDevice,
'sceneName': sceneName,
'decisionExpr': decisionExpr,
'actions': actions.map((x) => x.toMap()).toList(),
};
}
factory CreateSceneModel.fromMap(Map<String, dynamic> map) {
return CreateSceneModel(
spaceUuid: map['spaceUuid'] ?? '',
showInDevice: map['showInHomePage'] ?? false,
iconId: map['iconUuid'] ?? '',
sceneName: map['sceneName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
actions: List<CreateSceneAction>.from(
map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
);
}
String toJson([String? sceneId]) => json.encode(toMap(sceneId));
factory CreateSceneModel.fromJson(String source) =>
CreateSceneModel.fromMap(json.decode(source));
@override
String toString() {
return 'CreateSceneModel(unitUuid: $spaceUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneModel &&
other.spaceUuid == spaceUuid &&
other.iconId == iconId &&
other.showInDevice == showInDevice &&
other.sceneName == sceneName &&
other.decisionExpr == decisionExpr &&
listEquals(other.actions, actions);
}
@override
int get hashCode {
return spaceUuid.hashCode ^
sceneName.hashCode ^
decisionExpr.hashCode ^
actions.hashCode;
}
}
class CreateSceneAction {
String entityId;
String actionExecutor;
CreateSceneExecutorProperty? executorProperty;
CreateSceneAction({
required this.entityId,
required this.actionExecutor,
required this.executorProperty,
});
CreateSceneAction copyWith({
String? entityId,
String? actionExecutor,
CreateSceneExecutorProperty? executorProperty,
}) {
return CreateSceneAction(
entityId: entityId ?? this.entityId,
actionExecutor: actionExecutor ?? this.actionExecutor,
executorProperty: executorProperty ?? this.executorProperty,
);
}
Map<String, dynamic> toMap() {
if (executorProperty != null) {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
'executorProperty': executorProperty?.toMap(actionExecutor),
};
} else {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
};
}
}
factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
return CreateSceneAction(
entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '',
executorProperty:
CreateSceneExecutorProperty.fromMap(map['executorProperty']),
);
}
String toJson() => json.encode(toMap());
factory CreateSceneAction.fromJson(String source) =>
CreateSceneAction.fromMap(json.decode(source));
@override
String toString() =>
'CreateSceneAction(entityId: $entityId, actionExecutor: $actionExecutor, executorProperty: $executorProperty)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneAction &&
other.entityId == entityId &&
other.actionExecutor == actionExecutor &&
other.executorProperty == executorProperty;
}
@override
int get hashCode =>
entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode;
}
class CreateSceneExecutorProperty {
String functionCode;
dynamic functionValue;
int delaySeconds;
CreateSceneExecutorProperty({
required this.functionCode,
required this.functionValue,
required this.delaySeconds,
});
CreateSceneExecutorProperty copyWith({
String? functionCode,
dynamic functionValue,
int? delaySeconds,
}) {
return CreateSceneExecutorProperty(
functionCode: functionCode ?? this.functionCode,
functionValue: functionValue ?? this.functionValue,
delaySeconds: delaySeconds ?? this.delaySeconds,
);
}
Map<String, dynamic> toMap(String actionExecutor) {
final map = <String, dynamic>{};
if (functionCode.isNotEmpty) map['functionCode'] = functionCode;
if (functionValue != null) map['functionValue'] = functionValue;
if (actionExecutor == 'delay' && delaySeconds > 0) {
map['delaySeconds'] = delaySeconds;
}
return map;
}
factory CreateSceneExecutorProperty.fromMap(Map<String, dynamic> map) {
return CreateSceneExecutorProperty(
functionCode: map['functionCode'] ?? '',
functionValue: map['functionValue'] ?? '',
delaySeconds: map['delaySeconds']?.toInt() ?? 0,
);
}
String toJson(String actionExecutor) => json.encode(toMap(actionExecutor));
factory CreateSceneExecutorProperty.fromJson(String source) =>
CreateSceneExecutorProperty.fromMap(json.decode(source));
@override
String toString() =>
'CreateSceneExecutorProperty(functionCode: $functionCode, functionValue: $functionValue, delaySeconds: $delaySeconds)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneExecutorProperty &&
other.functionCode == functionCode &&
other.functionValue == functionValue &&
other.delaySeconds == delaySeconds;
}
@override
int get hashCode =>
functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode;
}

View File

@ -1,6 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/main.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/helper/setting_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart';
@ -28,7 +32,9 @@ class RoutineSearchAndButtons extends StatelessWidget {
children: [
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
maxWidth: constraints.maxWidth > 700
? 450
: constraints.maxWidth - 32),
child: StatefulTextField(
title: 'Routine Name',
height: 40,
@ -38,6 +44,11 @@ class RoutineSearchAndButtons extends StatelessWidget {
elevation: 0,
borderRadius: 15,
width: 450,
onChanged: (value) {
context
.read<RoutineBloc>()
.add(SearchRoutines(value));
},
),
),
(constraints.maxWidth <= 1000)
@ -48,8 +59,17 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: Center(
child: DefaultButton(
onPressed: () async {
final result = await SettingHelper.showSettingDialog(
context: context, isAutomation: true);
final result =
await SettingHelper.showSettingDialog(
context: context,
isAutomation: context
.read<RoutineBloc>()
.isAutomation);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddSelectedIcon(result));
}
},
borderRadius: 15,
elevation: 0,
@ -100,7 +120,9 @@ class RoutineSearchAndButtons extends StatelessWidget {
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
onPressed: () {
SaveRoutineHelper.showSaveRoutineDialog(context);
},
borderRadius: 15,
elevation: 0,
backgroundColor: ColorsManager.primaryColor,

View File

@ -4,7 +4,8 @@ import 'package:syncrow_web/pages/routiens/widgets/routines_title_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class ConditionTitleAndSearchBar extends StatelessWidget with HelperResponsiveLayout {
class ConditionTitleAndSearchBar extends StatelessWidget
with HelperResponsiveLayout {
const ConditionTitleAndSearchBar({
super.key,
});
@ -33,6 +34,7 @@ class ConditionTitleAndSearchBar extends StatelessWidget with HelperResponsiveLa
borderRadius: BorderRadius.circular(15),
),
controller: TextEditingController(),
onChanged: (value) {},
),
],
)
@ -55,6 +57,7 @@ class ConditionTitleAndSearchBar extends StatelessWidget with HelperResponsiveLa
borderRadius: BorderRadius.circular(15),
),
controller: TextEditingController(),
onChanged: (value) {},
),
],
);

View File

@ -1,3 +1,4 @@
import 'package:syncrow_web/pages/routiens/models/create_scene/create_scene_model.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
@ -7,22 +8,22 @@ class SceneApi {
static final HTTPService _httpService = HTTPService();
// //create scene
// static Future<Map<String, dynamic>> createScene(
// CreateSceneModel createSceneModel) async {
// try {
// final response = await _httpService.post(
// path: ApiEndpoints.createScene,
// body: createSceneModel.toMap(),
// showServerMessage: false,
// expectedResponseModel: (json) {
// return json;
// },
// );
// return response;
// } catch (e) {
// rethrow;
// }
// }
static Future<Map<String, dynamic>> createScene(
CreateSceneModel createSceneModel) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.createScene,
body: createSceneModel.toMap(),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
//
// // create automation
// static Future<Map<String, dynamic>> createAutomation(

View File

@ -11,12 +11,14 @@ abstract class ApiEndpoints {
static const String visitorPassword = '/visitor-password';
static const String getDevices = '/visitor-password/devices';
static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time';
static const String sendOnlineOneTime =
'/visitor-password/temporary-password/online/one-time';
static const String sendOnlineMultipleTime =
'/visitor-password/temporary-password/online/multiple-time';
//offline Password
static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time';
static const String sendOffLineOneTime =
'/visitor-password/temporary-password/offline/one-time';
static const String sendOffLineMultipleTime =
'/visitor-password/temporary-password/offline/multiple-time';
@ -38,13 +40,18 @@ abstract class ApiEndpoints {
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
static const String getScheduleByDeviceId =
'/schedule/{deviceUuid}?category={category}';
static const String deleteScheduleByDeviceId =
'/schedule/{deviceUuid}/{scheduleUuid}';
static const String updateScheduleByDeviceId =
'/schedule/enable/{deviceUuid}';
static const String factoryReset = '/device/factory/reset/{deviceUuid}';
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
static const String powerClamp =
'/device/{powerClampUuid}/power-clamp/status';
static const String getSpaceScenes = '/scene/tap-to-run/{unitUuid}';
static const String getSpaceAutomation = '/automation/{unitUuid}';
static const String getIconScene = '/scene/icon';
static const String createScene = '/scene/tap-to-run';
}