Compare commits

...
Author SHA1 Message Date
hannathkadher d423a3eb59 added focused border 2024-12-04 11:14:02 +04:00
hannathkadher 9bddd151bb added bloc for create community 2024-12-04 11:05:46 +04:00
hannathkadher 0b628c85a5 updated create community dialog to mange edit/create 2024-12-04 10:36:41 +04:00
Abdullah Alassaf 7ee335ab1a Bug fixes 2024-12-03 12:01:17 +03:00
Abdullah d5ad06335b Merge pull request #57 from SyncrowIOT/routines_issues
Routines issues
2024-12-03 01:04:12 +03:00
ashraf_personal d9ef5b4574 push fixes and merge 2024-12-03 01:02:13 +03:00
ashraf_personal c58f2eb24e fixed autoamtion update 2024-12-03 00:29:59 +03:00
Abdullah Alassaf 4e94d2df89 Bug fixes 2024-12-03 00:02:52 +03:00
mohammad 2f5c5d7da1 rule_disable selected & CreateAutomation actionType 2024-12-02 16:49:44 +03:00
Abdullah Alassaf ff4ce8ed01 Bug fixes 2024-12-02 03:58:31 +03:00
mohammad 1aa6b78fb4 Bug fixes 2024-12-01 12:05:19 +03:00
Abdullah e003d9b8b3 Merge pull request #56 from SyncrowIOT/routines_fixes
Routines fixes
2024-12-01 10:50:54 +03:00
58 changed files with 1184 additions and 1050 deletions
@@ -1,28 +0,0 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
part 'switch_tabs_event.dart';
part 'switch_tabs_state.dart';
class SwitchTabsBloc extends Bloc<SwitchTabsEvent, SwitchTabsState> {
SwitchTabsBloc() : super(SwitchTabsInitial()) {
on<TriggerSwitchTabsEvent>(_switchTab);
on<CreateNewRoutineViewEvent>(_newRoutineView);
}
FutureOr<void> _switchTab(
TriggerSwitchTabsEvent event,
Emitter<SwitchTabsState> emit,
) {
emit(SelectedTabState(event.isRoutineView));
}
FutureOr<void> _newRoutineView(
CreateNewRoutineViewEvent event,
Emitter<SwitchTabsState> emit,
) {
emit(ShowCreateRoutineState(event.showCreateNewRoutineView));
}
}
@@ -1,21 +0,0 @@
part of 'switch_tabs_bloc.dart';
sealed class SwitchTabsEvent extends Equatable {
const SwitchTabsEvent();
}
class TriggerSwitchTabsEvent extends SwitchTabsEvent {
final bool isRoutineView;
const TriggerSwitchTabsEvent(this.isRoutineView);
@override
List<Object?> get props => [isRoutineView];
}
class CreateNewRoutineViewEvent extends SwitchTabsEvent {
final bool showCreateNewRoutineView;
const CreateNewRoutineViewEvent(this.showCreateNewRoutineView);
@override
List<Object?> get props => [showCreateNewRoutineView];
}
@@ -1,26 +0,0 @@
part of 'switch_tabs_bloc.dart';
sealed class SwitchTabsState extends Equatable {
const SwitchTabsState();
}
final class SwitchTabsInitial extends SwitchTabsState {
@override
List<Object> get props => [];
}
class SelectedTabState extends SwitchTabsState {
final bool selectedTab;
const SelectedTabState(this.selectedTab);
@override
List<Object?> get props => [selectedTab];
}
class ShowCreateRoutineState extends SwitchTabsState {
final bool showCreateRoutine;
const ShowCreateRoutineState(this.showCreateRoutine);
@override
List<Object?> get props => [showCreateRoutine];
}
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/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/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/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/view/routines_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/color_manager.dart';
@@ -18,10 +18,6 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider(
create: (context) =>
SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
),
BlocProvider( BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()), create: (context) => DeviceManagementBloc()..add(FetchDevices()),
), ),
@@ -33,8 +29,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
style: Theme.of(context).textTheme.headlineLarge, style: Theme.of(context).textTheme.headlineLarge,
), ),
), ),
centerBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>( centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
builder: (context, state) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -44,20 +39,14 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
), ),
onPressed: () { onPressed: () {
context context
.read<SwitchTabsBloc>() .read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(false)); .add(const TriggerSwitchTabsEvent(isRoutineTab: false));
}, },
child: Text( child: Text(
'Devices', 'Devices',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
state is SelectedTabState && state.selectedTab == false fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400,
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight: (state is SelectedTabState) &&
state.selectedTab == false
? FontWeight.w700
: FontWeight.w400,
), ),
), ),
), ),
@@ -66,21 +55,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null, backgroundColor: null,
), ),
onPressed: () { onPressed: () {
context context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true));
.read<SwitchTabsBloc>()
.add(const TriggerSwitchTabsEvent(true));
}, },
child: Text( child: Text(
'Routines', 'Routines',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
(state is SelectedTabState) && state.selectedTab == true fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400,
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
(state is SelectedTabState) && state.selectedTab == true
? FontWeight.w700
: FontWeight.w400,
), ),
), ),
), ),
@@ -88,12 +69,11 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
); );
}), }),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>( scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
builder: (context, state) { if (state.routineTab) {
if (state is SelectedTabState && state.selectedTab) {
return const RoutinesView(); return const RoutinesView();
} }
if (state is ShowCreateRoutineState && state.showCreateRoutine) { if (state.createRoutineView) {
return CreateNewRoutineView(); return CreateNewRoutineView();
} }
@@ -104,8 +84,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
} else if (deviceState is DeviceManagementLoaded) { } else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices); return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) { } else if (deviceState is DeviceManagementFiltered) {
return DeviceManagementBody( return DeviceManagementBody(devices: deviceState.filteredDevices);
devices: deviceState.filteredDevices);
} else { } else {
return const Center(child: Text('Error fetching Devices')); return const Center(child: Text('Error fetching Devices'));
} }
+4 -2
View File
@@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/home_model/home_item_model.dart'; import 'package:syncrow_web/pages/home/home_model/home_item_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/services/home_api.dart'; import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@@ -41,8 +42,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async { Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try { try {
var uuid = var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid); user = await HomeApi().fetchUserInfo(uuid);
emit(HomeInitial()); emit(HomeInitial());
} catch (e) { } catch (e) {
@@ -84,6 +84,8 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.devicesIcon, icon: Assets.devicesIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
BlocProvider.of<RoutineBloc>(context)
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.go(RoutesConst.deviceManagementPage); context.go(RoutesConst.deviceManagementPage);
}, },
color: ColorsManager.primaryColor, color: ColorsManager.primaryColor,
File diff suppressed because it is too large Load Diff
@@ -187,8 +187,24 @@ class SetAutomationActionExecutor extends RoutineEvent {
List<Object> get props => [automationActionExecutor]; List<Object> get props => [automationActionExecutor];
} }
class TriggerSwitchTabsEvent extends RoutineEvent {
final bool isRoutineTab;
const TriggerSwitchTabsEvent({required this.isRoutineTab});
@override
List<Object> get props => [isRoutineTab];
}
class CreateNewRoutineViewEvent extends RoutineEvent {
final bool createRoutineView;
const CreateNewRoutineViewEvent({required this.createRoutineView});
@override
List<Object> get props => [createRoutineView];
}
class FetchDevicesInRoutine extends RoutineEvent {} class FetchDevicesInRoutine extends RoutineEvent {}
class ResetRoutineState extends RoutineEvent {} class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {} class ClearFunctions extends RoutineEvent {}
class ResetErrorMessage extends RoutineEvent {}
@@ -22,10 +22,12 @@ class RoutineState extends Equatable {
final String? automationId; final String? automationId;
final bool? isUpdate; final bool? isUpdate;
final List<AllDevicesModel> devices; final List<AllDevicesModel> devices;
final String? automationActionExecutor; // final String? automationActionExecutor;
final bool routineTab;
final bool createRoutineView;
const RoutineState({ const RoutineState(
this.ifItems = const [], {this.ifItems = const [],
this.thenItems = const [], this.thenItems = const [],
this.availableCards = const [], this.availableCards = const [],
this.scenes = const [], this.scenes = const [],
@@ -46,8 +48,9 @@ class RoutineState extends Equatable {
this.automationId, this.automationId,
this.isUpdate, this.isUpdate,
this.devices = const [], this.devices = const [],
this.automationActionExecutor, // this.automationActionExecutor,
}); this.routineTab = false,
this.createRoutineView = false});
RoutineState copyWith({ RoutineState copyWith({
List<Map<String, dynamic>>? ifItems, List<Map<String, dynamic>>? ifItems,
@@ -70,7 +73,10 @@ class RoutineState extends Equatable {
String? automationId, String? automationId,
bool? isUpdate, bool? isUpdate,
List<AllDevicesModel>? devices, List<AllDevicesModel>? devices,
String? automationActionExecutor, // String? automationActionExecutor,
TextEditingController? nameController,
bool? routineTab,
bool? createRoutineView,
}) { }) {
return RoutineState( return RoutineState(
ifItems: ifItems ?? this.ifItems, ifItems: ifItems ?? this.ifItems,
@@ -96,9 +102,9 @@ class RoutineState extends Equatable {
automationId: automationId ?? this.automationId, automationId: automationId ?? this.automationId,
isUpdate: isUpdate ?? this.isUpdate, isUpdate: isUpdate ?? this.isUpdate,
devices: devices ?? this.devices, devices: devices ?? this.devices,
automationActionExecutor: // automationActionExecutor: automationActionExecutor ?? this.automationActionExecutor,
automationActionExecutor ?? this.automationActionExecutor, routineTab: routineTab ?? this.routineTab,
); createRoutineView: createRoutineView ?? this.createRoutineView);
} }
@override @override
@@ -123,6 +129,8 @@ class RoutineState extends Equatable {
automationId, automationId,
isUpdate, isUpdate,
devices, devices,
automationActionExecutor, // automationActionExecutor,
routineTab,
createRoutineView
]; ];
} }
@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@@ -6,10 +8,11 @@ import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SaveRoutineHelper { class SaveRoutineHelper {
static Future<bool?> showSaveRoutineDialog(BuildContext context) async { static Future<void> showSaveRoutineDialog(BuildContext context) async {
return showDialog<bool?>( return showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
@@ -54,27 +57,23 @@ class SaveRoutineHelper {
), ),
if (state.isAutomation) if (state.isAutomation)
...state.ifItems.map((item) { ...state.ifItems.map((item) {
final functions = state.selectedFunctions[ final functions =
item['uniqueCustomId']] ?? state.selectedFunctions[item['uniqueCustomId']] ?? [];
[];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(
item['imagePath'], item['imagePath'],
width: 22, width: 22,
height: 22, height: 22,
), ),
title: Text(item['title'], title:
style: const TextStyle(fontSize: 14)), Text(item['title'], style: const TextStyle(fontSize: 14)),
subtitle: Wrap( subtitle: Wrap(
children: functions children: functions
.map((f) => Text( .map((f) => Text(
'${f.operationName}: ${f.value}, ', '${f.operationName}: ${f.value}, ',
style: const TextStyle( style: const TextStyle(
color: ColorsManager color: ColorsManager.grayColor, fontSize: 8),
.grayColor, overflow: TextOverflow.ellipsis,
fontSize: 8),
overflow:
TextOverflow.ellipsis,
maxLines: 3, maxLines: 3,
)) ))
.toList(), .toList(),
@@ -99,25 +98,33 @@ class SaveRoutineHelper {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
...state.thenItems.map((item) { ...state.thenItems.map((item) {
final functions = state.selectedFunctions[ final functions =
item['uniqueCustomId']] ?? state.selectedFunctions[item['uniqueCustomId']] ?? [];
[];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: item['type'] == 'tap_to_run'
? Image.memory(
base64Decode(item['icon']),
width: 22,
height: 22,
)
: SvgPicture.asset(
item['imagePath'], item['imagePath'],
width: 22, width: 22,
height: 22, height: 22,
), ),
title: Text(item['title'], title: Text(
style: const TextStyle(fontSize: 14)), item['title'],
style: context.textTheme.bodySmall?.copyWith(
fontSize: 14,
color: ColorsManager.grayColor,
),
),
subtitle: Wrap( subtitle: Wrap(
children: functions children: functions
.map((f) => Text( .map((f) => Text(
'${f.operationName}: ${f.value}, ', '${f.operationName}: ${f.value}, ',
style: const TextStyle( style: context.textTheme.bodySmall?.copyWith(
color: color: ColorsManager.grayColor, fontSize: 8),
ColorsManager.grayColor,
fontSize: 8),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 3, maxLines: 3,
)) ))
@@ -131,42 +138,33 @@ class SaveRoutineHelper {
], ],
), ),
), ),
if (state.errorMessage != null) // if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
Padding( // Padding(
padding: const EdgeInsets.all(8.0), // padding: const EdgeInsets.all(8.0),
child: Text( // child: Text(
state.errorMessage!, // state.errorMessage!,
style: const TextStyle(color: Colors.red), // style: const TextStyle(color: Colors.red),
), // ),
), // ),
DialogFooter( DialogFooter(
onCancel: () => Navigator.pop(context, false), onCancel: () => Navigator.pop(context),
onConfirm: () { onConfirm: () async {
if (state.isAutomation) { if (state.isAutomation) {
if (state.automationId != null) { if (state.isUpdate ?? false) {
context context.read<RoutineBloc>().add(const UpdateAutomation());
.read<RoutineBloc>()
.add(const UpdateAutomation());
} else { } else {
context context.read<RoutineBloc>().add(const CreateAutomationEvent());
.read<RoutineBloc>()
.add(const CreateAutomationEvent());
} }
} else { } else {
if (state.sceneId != null) { if (state.isUpdate ?? false) {
context context.read<RoutineBloc>().add(const UpdateScene());
.read<RoutineBloc>()
.add(const UpdateScene());
} else { } else {
context context.read<RoutineBloc>().add(const CreateSceneEvent());
.read<RoutineBloc>()
.add(const CreateSceneEvent());
}
if (context.read<RoutineBloc>().state.errorMessage ==
null) {
Navigator.pop(context, true);
} }
} }
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
Navigator.pop(context);
// }
}, },
isConfirmEnabled: true, isConfirmEnabled: true,
), ),
@@ -92,7 +92,7 @@ class Condition {
return { return {
'code': code, 'code': code,
'entityId': entityId, 'entityId': entityId,
'entityType': entityType, 'entityType': 'device_report',
'expr': expr.toMap(), 'expr': expr.toMap(),
}; };
} }
@@ -137,11 +137,13 @@ class ConditionExpr {
class AutomationAction { class AutomationAction {
String entityId; String entityId;
String? actionType;
String actionExecutor; String actionExecutor;
ExecutorProperty? executorProperty; ExecutorProperty? executorProperty;
AutomationAction({ AutomationAction({
required this.entityId, required this.entityId,
this.actionType,
required this.actionExecutor, required this.actionExecutor,
this.executorProperty, this.executorProperty,
}); });
@@ -150,12 +152,15 @@ class AutomationAction {
return { return {
'entityId': entityId, 'entityId': entityId,
'actionExecutor': actionExecutor, 'actionExecutor': actionExecutor,
if (executorProperty != null)
'executorProperty': executorProperty?.toMap(), 'executorProperty': executorProperty?.toMap(),
'actionType': actionType
}; };
} }
factory AutomationAction.fromMap(Map<String, dynamic> map) { factory AutomationAction.fromMap(Map<String, dynamic> map) {
return AutomationAction( return AutomationAction(
actionType: map['actionType'],
entityId: map['entityId'] ?? '', entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '', actionExecutor: map['actionExecutor'] ?? '',
executorProperty: map['executorProperty'] != null executorProperty: map['executorProperty'] != null
@@ -95,10 +95,12 @@ class CreateSceneModel {
class CreateSceneAction { class CreateSceneAction {
String entityId; String entityId;
String? actionType;
String actionExecutor; String actionExecutor;
CreateSceneExecutorProperty? executorProperty; CreateSceneExecutorProperty? executorProperty;
CreateSceneAction({ CreateSceneAction({
this.actionType,
required this.entityId, required this.entityId,
required this.actionExecutor, required this.actionExecutor,
required this.executorProperty, required this.executorProperty,
@@ -110,6 +112,7 @@ class CreateSceneAction {
CreateSceneExecutorProperty? executorProperty, CreateSceneExecutorProperty? executorProperty,
}) { }) {
return CreateSceneAction( return CreateSceneAction(
actionType: actionType ?? this.actionType,
entityId: entityId ?? this.entityId, entityId: entityId ?? this.entityId,
actionExecutor: actionExecutor ?? this.actionExecutor, actionExecutor: actionExecutor ?? this.actionExecutor,
executorProperty: executorProperty ?? this.executorProperty, executorProperty: executorProperty ?? this.executorProperty,
@@ -125,6 +128,7 @@ class CreateSceneAction {
}; };
} else { } else {
return { return {
"actionType": actionType,
'entityId': entityId, 'entityId': entityId,
'actionExecutor': actionExecutor, 'actionExecutor': actionExecutor,
}; };
@@ -133,6 +137,7 @@ class CreateSceneAction {
factory CreateSceneAction.fromMap(Map<String, dynamic> map) { factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
return CreateSceneAction( return CreateSceneAction(
actionType: map['actionType'],
entityId: map['entityId'] ?? '', entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '', actionExecutor: map['actionExecutor'] ?? '',
executorProperty: executorProperty:
+6 -9
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart'; import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
@@ -23,12 +22,10 @@ class _RoutinesViewState extends State<RoutinesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<SwitchTabsBloc, SwitchTabsState>( return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
if (state is ShowCreateRoutineState && state.showCreateRoutine) { if (state.createRoutineView) {
return const CreateNewRoutineView( return const CreateNewRoutineView();
);
} }
return Padding( return Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@@ -49,12 +46,12 @@ class _RoutinesViewState extends State<RoutinesView> {
), ),
RoutineViewCard( RoutineViewCard(
onTap: () { onTap: () {
BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(true),
);
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(
(ResetRoutineState()), (ResetRoutineState()),
); );
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
}, },
icon: Icons.add, icon: Icons.add,
textString: '', textString: '',
+4 -11
View File
@@ -33,31 +33,24 @@ class DeleteSceneWidget extends StatelessWidget {
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
child: Text( child: Text(
'Cancel', 'Cancel',
style: Theme.of(context) style: Theme.of(context).textTheme.bodyMedium!.copyWith(
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
), ),
), ),
), ),
Container( Container(width: 1, height: 50, color: ColorsManager.greyColor),
width: 1, height: 50, color: ColorsManager.greyColor),
InkWell( InkWell(
onTap: () { onTap: () {
context.read<RoutineBloc>().add(const DeleteScene()); context.read<RoutineBloc>().add(const DeleteScene());
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pop(true); Navigator.of(context).pop();
}, },
child: Container( child: Container(
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
child: Text( child: Text(
'Confirm', 'Confirm',
style: Theme.of(context) style: Theme.of(context).textTheme.bodyMedium!.copyWith(
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.primaryColorWithOpacity, color: ColorsManager.primaryColorWithOpacity,
), ),
), ),
+18 -8
View File
@@ -35,6 +35,20 @@ class DraggableCard extends StatelessWidget {
final deviceFunctions = final deviceFunctions =
state.selectedFunctions[deviceData['uniqueCustomId']] ?? []; state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
int index = state.thenItems.indexWhere(
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
if (index != -1) {
return _buildCardContent(context, deviceFunctions, padding: padding);
}
int ifIndex = state.ifItems.indexWhere(
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
if (ifIndex != -1) {
return _buildCardContent(context, deviceFunctions, padding: padding);
}
return Draggable<Map<String, dynamic>>( return Draggable<Map<String, dynamic>>(
data: deviceData, data: deviceData,
feedback: Transform.rotate( feedback: Transform.rotate(
@@ -79,16 +93,12 @@ class DraggableCard extends StatelessWidget {
), ),
), ),
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: imagePath.contains('.svg') child: deviceData['type'] == 'tap_to_run'
? SvgPicture.asset( ? Image.memory(
imagePath, base64Decode(deviceData['icon']),
) )
: imagePath.contains('.png') : SvgPicture.asset(
? Image.asset(
imagePath, imagePath,
)
: Image.memory(
base64Decode(imagePath),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart'; import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@@ -69,8 +68,8 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
), ),
child: RoutineViewCard( child: RoutineViewCard(
onTap: () { onTap: () {
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(true), const CreateNewRoutineViewEvent(createRoutineView: true),
); );
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(
GetSceneDetails( GetSceneDetails(
@@ -118,8 +117,8 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
), ),
child: RoutineViewCard( child: RoutineViewCard(
onTap: () { onTap: () {
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(true), const CreateNewRoutineViewEvent(createRoutineView: true),
); );
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(
GetAutomationDetails( GetAutomationDetails(
@@ -7,98 +7,106 @@ import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class AutomationDialog extends StatelessWidget { class AutomationDialog extends StatefulWidget {
final String automationName; final String automationName;
final String automationId; final String automationId;
final String uniqueCustomId; final String uniqueCustomId;
final String? passedAutomationActionExecutor;
const AutomationDialog({ const AutomationDialog({
super.key, super.key,
required this.automationName, required this.automationName,
required this.automationId, required this.automationId,
required this.uniqueCustomId, required this.uniqueCustomId,
this.passedAutomationActionExecutor,
}); });
@override @override
Widget build(BuildContext context) { State<AutomationDialog> createState() => _AutomationDialogState();
return BlocBuilder<RoutineBloc, RoutineState>( }
builder: (context, state) {
final isEnabled = state.automationActionExecutor == 'rule_enable';
class _AutomationDialogState extends State<AutomationDialog> {
String? selectedAutomationActionExecutor;
@override
void initState() {
super.initState();
List<DeviceFunctionData>? functions = context
.read<RoutineBloc>()
.state
.selectedFunctions[widget.uniqueCustomId];
for (DeviceFunctionData data in functions ?? []) {
if (data.entityId == widget.automationId) {
selectedAutomationActionExecutor = data.value;
}
}
}
@override
Widget build(BuildContext context) {
return Dialog( return Dialog(
shape: shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container( child: Container(
width: 400, width: 400,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
DialogHeader(automationName), DialogHeader(widget.automationName),
const SizedBox(height: 16), const SizedBox(height: 16),
ListTile( ListTile(
leading: leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24),
SvgPicture.asset(Assets.acPower, width: 24, height: 24),
title: const Text('Enable'), title: const Text('Enable'),
trailing: Radio<bool>( trailing: Radio<String?>(
value: true, value: 'rule_enable',
groupValue: isEnabled, groupValue: selectedAutomationActionExecutor,
onChanged: (bool? value) { onChanged: (String? value) {
if (value == true) { setState(() {
context.read<RoutineBloc>().add( selectedAutomationActionExecutor = 'rule_enable';
const SetAutomationActionExecutor( });
automationActionExecutor: 'rule_enable', }),
),
);
}
},
),
), ),
ListTile( ListTile(
leading: SvgPicture.asset(Assets.acPowerOff, leading:
width: 24, height: 24), SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
title: const Text('Disable'), title: const Text('Disable'),
trailing: Radio<bool>( trailing: Radio<String?>(
value: false, value: 'rule_disable',
groupValue: isEnabled, groupValue: selectedAutomationActionExecutor,
onChanged: (bool? value) { onChanged: (String? value) {
if (value == false) { setState(() {
context.read<RoutineBloc>().add( selectedAutomationActionExecutor = 'rule_disable';
const SetAutomationActionExecutor( });
automationActionExecutor: 'rule_disable',
),
);
}
}, },
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
DialogFooter( DialogFooter(
onConfirm: () { onConfirm: () {
if (selectedAutomationActionExecutor != null) {
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(
AddFunctionToRoutine( AddFunctionToRoutine(
[ [
DeviceFunctionData( DeviceFunctionData(
entityId: automationId, entityId: widget.automationId,
functionCode: 'automation', functionCode: 'automation',
value: state.automationActionExecutor, value: selectedAutomationActionExecutor,
operationName: 'Automation', operationName: 'Automation',
), ),
], ],
uniqueCustomId, widget.uniqueCustomId,
), ),
); );
}
Navigator.of(context).pop(true); Navigator.of(context).pop(true);
}, },
onCancel: () => Navigator.of(context).pop(false), onCancel: () => Navigator.of(context).pop(),
isConfirmEnabled: true, isConfirmEnabled: selectedAutomationActionExecutor != null,
dialogWidth: 400, dialogWidth: 400,
), ),
], ],
), ),
), ),
); );
},
);
} }
} }
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@@ -49,11 +48,11 @@ class DiscardDialog {
onConfirm: () { onConfirm: () {
context.read<RoutineBloc>().add(ResetRoutineState()); context.read<RoutineBloc>().add(ResetRoutineState());
Navigator.pop(context); Navigator.pop(context);
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(false), const CreateNewRoutineViewEvent(createRoutineView: false),
); );
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<RoutineBloc>(context).add(
const TriggerSwitchTabsEvent(true), const TriggerSwitchTabsEvent(isRoutineTab: true),
); );
}); });
} }
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.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/discard_dialog.dart';
@@ -16,8 +15,7 @@ class RoutineSearchAndButtons extends StatefulWidget {
}); });
@override @override
State<RoutineSearchAndButtons> createState() => State<RoutineSearchAndButtons> createState() => _RoutineSearchAndButtonsState();
_RoutineSearchAndButtonsState();
} }
class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> { class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
@@ -37,20 +35,21 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocConsumer<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
listenWhen: (previous, current) =>
previous.routineName != current.routineName,
listener: (context, state) {
if (state.routineName != _nameController.text) {
_nameController.text = state.routineName ?? '';
}
},
builder: (context, state) { builder: (context, state) {
_nameController.text = state.routineName ?? '';
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
return Wrap( return Wrap(
runSpacing: 16, runSpacing: 16,
children: [ children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
state.errorMessage ?? '',
style: const TextStyle(color: Colors.red),
),
),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
@@ -62,9 +61,8 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: constraints.maxWidth > 700 maxWidth:
? 450 constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
: constraints.maxWidth - 32),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -73,13 +71,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
children: [ children: [
Text('* ', Text('* ',
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!
.copyWith( .copyWith(color: ColorsManager.red, fontSize: 13)),
color: ColorsManager.red,
fontSize: 13)),
Text( Text(
'Routine Name', 'Routine Name',
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!.copyWith(
.copyWith(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: ColorsManager.blackColor, color: ColorsManager.blackColor,
@@ -93,24 +88,20 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
decoration: containerWhiteDecoration, decoration: containerWhiteDecoration,
child: TextFormField( child: TextFormField(
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!
.copyWith( .copyWith(color: ColorsManager.blackColor),
color: ColorsManager.blackColor),
controller: _nameController, controller: _nameController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Please enter the name', hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium! hintStyle: context.textTheme.bodyMedium!
.copyWith( .copyWith(fontSize: 12, color: ColorsManager.grayColor),
fontSize: 12,
color: ColorsManager.grayColor),
contentPadding: contentPadding:
const EdgeInsets.symmetric( const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
horizontal: 12, vertical: 10),
border: InputBorder.none, border: InputBorder.none,
), ),
onChanged: (value) { onTapOutside: (_) {
context context
.read<RoutineBloc>() .read<RoutineBloc>()
.add(SetRoutineName(value)); .add(SetRoutineName(_nameController.text));
}, },
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@@ -130,18 +121,16 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
width: 200, width: 200,
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: state.isAutomation || onPressed: state.isAutomation || state.isTabToRun
state.isTabToRun
? () async { ? () async {
final result = await SettingHelper final result = await SettingHelper.showSettingDialog(
.showSettingDialog(
context: context, context: context,
iconId: iconId: state.selectedIcon ?? '',
state.selectedIcon ?? '',
); );
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add( context
AddSelectedIcon(result)); .read<RoutineBloc>()
.add(AddSelectedIcon(result));
} }
} }
: null, : null,
@@ -197,12 +186,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () async { onPressed: () async {
if (state.routineName == null || if (state.routineName == null || state.routineName!.isEmpty) {
state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text( content: const Text('Please enter the routine name'),
'Please enter the routine name'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@@ -216,12 +203,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
return; return;
} }
if (state.ifItems.isEmpty || if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text( content: const Text('Please add if and then condition'),
'Please add if and then condition'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@@ -234,18 +219,18 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
); );
return; return;
} }
final result = await SaveRoutineHelper // final result =
.showSaveRoutineDialog(context); // await
if (result != null && result) { BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
BlocProvider.of<SwitchTabsBloc>(context) SaveRoutineHelper.showSaveRoutineDialog(context);
.add( // if (result != null && result) {
const CreateNewRoutineViewEvent(false), // BlocProvider.of<RoutineBloc>(context).add(
); // const CreateNewRoutineViewEvent(createRoutineView: false),
BlocProvider.of<SwitchTabsBloc>(context) // );
.add( // BlocProvider.of<RoutineBloc>(context).add(
const TriggerSwitchTabsEvent(true), // const TriggerSwitchTabsEvent(isRoutineTab: true),
); // );
} // }
}, },
borderRadius: 15, borderRadius: 15,
elevation: 0, elevation: 0,
@@ -276,14 +261,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
child: DefaultButton( child: DefaultButton(
onPressed: state.isAutomation || state.isTabToRun onPressed: state.isAutomation || state.isTabToRun
? () async { ? () async {
final result = final result = await SettingHelper.showSettingDialog(
await SettingHelper.showSettingDialog( context: context, iconId: state.selectedIcon ?? '');
context: context,
iconId: state.selectedIcon ?? '');
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(AddSelectedIcon(result));
.read<RoutineBloc>()
.add(AddSelectedIcon(result));
} }
} }
: null, : null,
@@ -333,12 +314,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () async { onPressed: () async {
if (state.routineName == null || if (state.routineName == null || state.routineName!.isEmpty) {
state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text( content: const Text('Please enter the routine name'),
'Please enter the routine name'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@@ -352,12 +331,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
return; return;
} }
if (state.ifItems.isEmpty || if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text( content: const Text('Please add if and then condition'),
'Please add if and then condition'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@@ -370,17 +347,18 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
); );
return; return;
} }
final result = // final result =
await SaveRoutineHelper.showSaveRoutineDialog( // await
context); BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
if (result != null && result) { SaveRoutineHelper.showSaveRoutineDialog(context);
BlocProvider.of<SwitchTabsBloc>(context).add( // if (result != null && result) {
const CreateNewRoutineViewEvent(false), // BlocProvider.of<RoutineBloc>(context).add(
); // const CreateNewRoutineViewEvent(createRoutineView: false),
BlocProvider.of<SwitchTabsBloc>(context).add( // );
const TriggerSwitchTabsEvent(true), // BlocProvider.of<RoutineBloc>(context).add(
); // const TriggerSwitchTabsEvent(isRoutineTab: true),
} // );
// }
}, },
borderRadius: 15, borderRadius: 15,
elevation: 0, elevation: 0,
+17 -3
View File
@@ -87,8 +87,11 @@ class ThenContainer extends StatelessWidget {
...state.thenItems[index], ...state.thenItems[index],
'imagePath': 'imagePath':
Assets.automation, Assets.automation,
'title': state 'title':
.thenItems[index]['name'], state.thenItems[index]
['name'] ??
state.thenItems[index]
['title'],
})); }));
} }
return; return;
@@ -149,6 +152,12 @@ class ThenContainer extends StatelessWidget {
} }
if (mutableData['type'] == 'automation') { if (mutableData['type'] == 'automation') {
int index = state.thenItems.indexWhere(
(item) => item['deviceId'] == mutableData['deviceId']);
if (index != -1) {
return;
}
final result = await showDialog<bool>( final result = await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) => AutomationDialog( builder: (BuildContext context) => AutomationDialog(
@@ -169,9 +178,14 @@ class ThenContainer extends StatelessWidget {
} }
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) { if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
int index = state.thenItems.indexWhere(
(item) => item['deviceId'] == mutableData['deviceId']);
if (index != -1) {
return;
}
context.read<RoutineBloc>().add(AddToThenContainer({ context.read<RoutineBloc>().add(AddToThenContainer({
...mutableData, ...mutableData,
'imagePath': Assets.logo, 'imagePath': mutableData['imagePath'] ?? Assets.logo,
'title': mutableData['name'], 'title': mutableData['name'],
})); }));
@@ -1,9 +1,9 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/product_api.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
@@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; // Import for Offset import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; // Import for Offset
abstract class SpaceManagementEvent extends Equatable { abstract class SpaceManagementEvent extends Equatable {
const SpaceManagementEvent(); const SpaceManagementEvent();
@@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
abstract class SpaceManagementState extends Equatable { abstract class SpaceManagementState extends Equatable {
const SpaceManagementState(); const SpaceManagementState();
@@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/auth/model/region_model.dart'; import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
class CommunityModel { class CommunityModel {
final String uuid; final String uuid;
@@ -1,4 +1,4 @@
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
class Connection { class Connection {
final SpaceModel startSpace; final SpaceModel startSpace;
@@ -1,7 +1,7 @@
import 'dart:ui'; import 'dart:ui';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart';
import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/product_api.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/counter_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_community_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class BlankCommunityWidget extends StatefulWidget { class BlankCommunityWidget extends StatefulWidget {
@@ -72,7 +72,8 @@ class _BlankCommunityWidgetState extends State<BlankCommunityWidget> {
showDialog( showDialog(
context: parentContext, context: parentContext,
builder: (context) => CreateCommunityDialog( builder: (context) => CreateCommunityDialog(
communities: widget.communities, isEditMode: false,
existingCommunityNames: widget.communities.map((community) => community.name).toList(),
onCreateCommunity: (String communityName, String description) { onCreateCommunity: (String communityName, String description) {
parentContext.read<SpaceManagementBloc>().add( parentContext.read<SpaceManagementBloc>().add(
CreateCommunityEvent( CreateCommunityEvent(
@@ -84,4 +85,5 @@ class _BlankCommunityWidgetState extends State<BlankCommunityWidget> {
), ),
); );
} }
} }
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class CommunityStructureHeader extends StatelessWidget { class CommunityStructureHeader extends StatefulWidget {
final String? communityName; final String? communityName;
final bool isEditingName; final bool isEditingName;
final bool isSave; final bool isSave;
@@ -13,9 +15,11 @@ class CommunityStructureHeader extends StatelessWidget {
final VoidCallback onDelete; final VoidCallback onDelete;
final VoidCallback onEditName; final VoidCallback onEditName;
final ValueChanged<String> onNameSubmitted; final ValueChanged<String> onNameSubmitted;
final List<CommunityModel> communities;
final CommunityModel? community;
const CommunityStructureHeader({ const CommunityStructureHeader(
Key? key, {super.key,
required this.communityName, required this.communityName,
required this.isSave, required this.isSave,
required this.isEditingName, required this.isEditingName,
@@ -24,8 +28,15 @@ class CommunityStructureHeader extends StatelessWidget {
required this.onDelete, required this.onDelete,
required this.onEditName, required this.onEditName,
required this.onNameSubmitted, required this.onNameSubmitted,
}) : super(key: key); this.community,
required this.communities});
@override
State<CommunityStructureHeader> createState() =>
_CommunityStructureHeaderState();
}
class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
@@ -60,47 +71,63 @@ class CommunityStructureHeader extends StatelessWidget {
); );
} }
void _showCreateCommunityDialog(BuildContext parentContext) {
showDialog(
context: parentContext,
builder: (context) => CreateCommunityDialog(
isEditMode: true,
existingCommunityNames:
widget.communities.map((community) => community.name).toList(),
initialName: widget.community?.name ?? '',
onCreateCommunity: (String communityName, String description) {
widget.onNameSubmitted(communityName);
},
),
);
}
Widget _buildCommunityInfo(ThemeData theme, double screenWidth) { Widget _buildCommunityInfo(ThemeData theme, double screenWidth) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'Community Structure', 'Community Structure',
style: theme.textTheme.headlineLarge?.copyWith(color: ColorsManager.blackColor), style: theme.textTheme.headlineLarge
?.copyWith(color: ColorsManager.blackColor),
), ),
if (communityName != null) if (widget.communityName != null)
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
if (!isEditingName) if (!widget.isEditingName)
Flexible( Flexible(
child: Text( child: Text(
communityName!, widget.communityName!,
style: style: theme.textTheme.bodyLarge
theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), ?.copyWith(color: ColorsManager.blackColor),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
), ),
), ),
if (isEditingName) if (widget.isEditingName)
SizedBox( SizedBox(
width: screenWidth * 0.1, width: screenWidth * 0.1,
child: TextField( child: TextField(
controller: nameController, controller: widget.nameController,
decoration: const InputDecoration( decoration: const InputDecoration(
border: InputBorder.none, border: InputBorder.none,
isDense: true, isDense: true,
), ),
style: style: theme.textTheme.bodyLarge
theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), ?.copyWith(color: ColorsManager.blackColor),
onSubmitted: onNameSubmitted, onSubmitted: widget.onNameSubmitted,
), ),
), ),
const SizedBox(width: 2), const SizedBox(width: 2),
GestureDetector( GestureDetector(
onTap: onEditName, onTap: () => _showCreateCommunityDialog(context),
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.iconEdit, Assets.iconEdit,
width: 16, width: 16,
@@ -110,7 +137,7 @@ class CommunityStructureHeader extends StatelessWidget {
], ],
), ),
), ),
if (isSave) ...[ if (widget.isSave) ...[
const SizedBox(width: 8), const SizedBox(width: 8),
_buildActionButtons(theme), _buildActionButtons(theme),
], ],
@@ -127,8 +154,9 @@ class CommunityStructureHeader extends StatelessWidget {
children: [ children: [
_buildButton( _buildButton(
label: "Save", label: "Save",
icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), icon: const Icon(Icons.save,
onPressed: onSave, size: 18, color: ColorsManager.spaceColor),
onPressed: widget.onSave,
theme: theme), theme: theme),
], ],
); );
@@ -159,7 +187,8 @@ class CommunityStructureHeader extends StatelessWidget {
Flexible( Flexible(
child: Text( child: Text(
label, label,
style: theme.textTheme.bodySmall?.copyWith(color: ColorsManager.blackColor), style: theme.textTheme.bodySmall
?.copyWith(color: ColorsManager.blackColor),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
), ),
@@ -4,19 +4,19 @@ import 'package:flutter_bloc/flutter_bloc.dart';
// Syncrow project imports // Syncrow project imports
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/blank_community_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_header_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class CommunityStructureArea extends StatefulWidget { class CommunityStructureArea extends StatefulWidget {
@@ -119,7 +119,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
CommunityStructureHeader( CommunityStructureHeader(
communities: widget.communities,
communityName: widget.selectedCommunity?.name, communityName: widget.selectedCommunity?.name,
community: widget.selectedCommunity,
isSave: isSave(spaces), isSave: isSave(spaces),
isEditingName: isEditingName, isEditingName: isEditingName,
nameController: _nameController, nameController: _nameController,
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class CurvedLinePainter extends CustomPainter { class CurvedLinePainter extends CustomPainter {
@@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/hoverable_button.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/space_icon_const.dart'; import 'package:syncrow_web/utils/constants/space_icon_const.dart';
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart';
class LoadedSpaceView extends StatefulWidget { class LoadedSpaceView extends StatefulWidget {
final List<CommunityModel> communities; final List<CommunityModel> communities;
@@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/common/search_bar.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/space_tile_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
@@ -0,0 +1,21 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'community_dialog_event.dart';
import 'community_dialog_state.dart';
class CommunityDialogBloc extends Bloc<CommunityDialogEvent, CommunityDialogState> {
final List<String> existingCommunityNames;
CommunityDialogBloc(this.existingCommunityNames)
: super(CommunityDialogInitial()) {
on<ValidateCommunityNameEvent>((event, emit) {
final trimmedName = event.name.trim();
final isNameValid = !existingCommunityNames.contains(trimmedName);
final isNameEmpty = trimmedName.isEmpty;
emit(CommunityNameValidationState(
isNameValid: isNameValid,
isNameEmpty: isNameEmpty,
));
});
}
}
@@ -0,0 +1,15 @@
import 'package:equatable/equatable.dart';
abstract class CommunityDialogEvent extends Equatable {
@override
List<Object?> get props => [];
}
class ValidateCommunityNameEvent extends CommunityDialogEvent {
final String name;
ValidateCommunityNameEvent(this.name);
@override
List<Object?> get props => [name];
}
@@ -0,0 +1,21 @@
import 'package:equatable/equatable.dart';
abstract class CommunityDialogState extends Equatable {
@override
List<Object?> get props => [];
}
class CommunityDialogInitial extends CommunityDialogState {}
class CommunityNameValidationState extends CommunityDialogState {
final bool isNameValid;
final bool isNameEmpty;
CommunityNameValidationState({
required this.isNameValid,
required this.isNameEmpty,
});
@override
List<Object?> get props => [isNameValid, isNameEmpty];
}
@@ -0,0 +1,170 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/bloc/community_dialog_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/bloc/community_dialog_event.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/bloc/community_dialog_state.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
class CreateCommunityDialog extends StatelessWidget {
final Function(String name, String description) onCreateCommunity;
final List<String> existingCommunityNames;
final bool isEditMode;
final String? initialName;
const CreateCommunityDialog({
super.key,
required this.onCreateCommunity,
required this.existingCommunityNames,
required this.isEditMode,
this.initialName,
});
@override
Widget build(BuildContext context) {
final nameController =
TextEditingController(text: isEditMode ? initialName : '');
return BlocProvider(
create: (_) => CommunityDialogBloc(existingCommunityNames),
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
backgroundColor: ColorsManager.transparentColor,
child: Stack(
children: [
Container(
width: MediaQuery.of(context).size.width * 0.3,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.25),
blurRadius: 20,
spreadRadius: 5,
offset: const Offset(0, 5),
),
],
),
child: SingleChildScrollView(
child: BlocBuilder<CommunityDialogBloc, CommunityDialogState>(
builder: (context, state) {
bool isNameValid = true;
bool isNameEmpty = false;
if (state is CommunityNameValidationState) {
isNameValid = state.isNameValid;
isNameEmpty = state.isNameEmpty;
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isEditMode ? 'Edit Community Name' : 'Community Name',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 18),
TextField(
controller: nameController,
onChanged: (value) {
context
.read<CommunityDialogBloc>()
.add(ValidateCommunityNameEvent(value));
},
style: const TextStyle(
color: ColorsManager.blackColor,
),
decoration: InputDecoration(
hintText: 'Please enter the community name',
filled: true,
fillColor: ColorsManager.boxColor,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isNameValid && !isNameEmpty
? ColorsManager.boxColor
: ColorsManager.red,
width: 1,
),
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(
color: ColorsManager.boxColor,
width: 1.5,
),
),
),
),
if (!isNameValid)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'*Name already exists.',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: ColorsManager.red),
),
),
if (isNameEmpty)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'*Name should not be empty.',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: ColorsManager.red),
),
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CancelButton(
label: 'Cancel',
onPressed: () => Navigator.of(context).pop(),
),
),
const SizedBox(width: 16),
Expanded(
child: DefaultButton(
onPressed: () {
if (isNameValid && !isNameEmpty) {
onCreateCommunity(
nameController.text.trim(),
"",
);
Navigator.of(context).pop();
}
},
backgroundColor: isNameValid && !isNameEmpty
? ColorsManager.secondaryColor
: ColorsManager.lightGrayColor,
borderRadius: 10,
foregroundColor: ColorsManager.whiteColors,
child: const Text('OK'),
),
),
],
),
],
);
},
),
),
),
],
),
),
);
}
}
@@ -1,177 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CreateCommunityDialog extends StatefulWidget {
final Function(String name, String description) onCreateCommunity;
final List<CommunityModel> communities;
const CreateCommunityDialog(
{super.key, required this.onCreateCommunity, required this.communities});
@override
CreateCommunityDialogState createState() => CreateCommunityDialogState();
}
class CreateCommunityDialogState extends State<CreateCommunityDialog> {
String enteredName = '';
bool isNameFieldExist = false;
bool isNameEmpty = false;
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
backgroundColor:
ColorsManager.transparentColor, // Transparent for shadow effect
child: Stack(
children: [
// Background container with shadow and rounded corners
Container(
width: screenWidth * 0.3,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.25),
blurRadius: 20,
spreadRadius: 5,
offset: const Offset(0, 5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Community Name',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
// Input field for the community name
TextField(
onChanged: (value) {
setState(() {
enteredName = value.trim();
isNameFieldExist = widget.communities.any(
(community) => community.name == enteredName,
);
if (value.isEmpty) {
isNameEmpty = true;
} else {
isNameEmpty = false;
}
});
},
style: const TextStyle(
color: ColorsManager.blackColor,
),
decoration: InputDecoration(
hintText: 'Please enter the community name',
filled: true,
fillColor: ColorsManager.boxColor,
hintStyle: const TextStyle(
fontSize: 14,
color: ColorsManager.grayBorder,
fontWeight: FontWeight.w400,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: isNameFieldExist || isNameEmpty
? ColorsManager.red
: ColorsManager.boxColor,
width: 1),
borderRadius: BorderRadius.circular(10),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isNameFieldExist || isNameEmpty
? ColorsManager.red
: ColorsManager.boxColor, width: 1),
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(
color: isNameFieldExist || isNameEmpty
? ColorsManager.red
: ColorsManager.boxColor,
width: 1),
),
),
),
if (isNameFieldExist)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'*Name already exists.',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: ColorsManager.red),
),
),
if (isNameEmpty)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'*Name should not be empty.',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: ColorsManager.red),
),
),
const SizedBox(height: 24),
// Action buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CancelButton(
label: 'Cancel',
onPressed: () => Navigator.of(context).pop(),
),
),
const SizedBox(width: 16),
Expanded(
child: DefaultButton(
onPressed: () {
if (enteredName.isNotEmpty && !isNameFieldExist) {
widget.onCreateCommunity(
enteredName,
"",
);
Navigator.of(context).pop();
}
},
backgroundColor: isNameFieldExist || isNameEmpty
? ColorsManager.lightGrayColor
: ColorsManager.secondaryColor,
borderRadius: 10,
foregroundColor: ColorsManager.whiteColors,
child: const Text('OK'),
),
),
],
),
],
),
),
],
),
);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
+4 -4
View File
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
+2 -2
View File
@@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/access_management/view/access_management.dart'
import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/pages/spaces_management/view/spaces_management_page.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/view/spaces_management_page.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
@@ -32,7 +32,7 @@ class AppRoutes {
), ),
GoRoute( GoRoute(
path: RoutesConst.spacesManagementPage, path: RoutesConst.spacesManagementPage,
builder: (context, state) => SpaceManagementPage()), builder: (context, state) => const SpaceManagementPage()),
]; ];
} }
} }
+34 -2
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/navigation_service.dart'; import 'package:syncrow_web/utils/navigation_service.dart';
class CustomSnackBar { class CustomSnackBar {
@@ -11,6 +12,35 @@ class CustomSnackBar {
} }
} }
static redSnackBar(String message) {
final key = NavigationService.snackbarKey;
BuildContext? currentContext = key?.currentContext;
if (key != null && currentContext != null) {
final snackBar = SnackBar(
padding: const EdgeInsets.all(16),
backgroundColor: ColorsManager.red,
content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
const Icon(
Icons.check_circle,
color: ColorsManager.whiteColors,
size: 32,
),
const SizedBox(
width: 8,
),
Text(
message,
style: Theme.of(currentContext)
.textTheme
.bodySmall!
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
)
]),
);
key.currentState?.showSnackBar(snackBar);
}
}
static greenSnackBar(String message) { static greenSnackBar(String message) {
final key = NavigationService.snackbarKey; final key = NavigationService.snackbarKey;
BuildContext? currentContext = key?.currentContext; BuildContext? currentContext = key?.currentContext;
@@ -29,8 +59,10 @@ class CustomSnackBar {
), ),
Text( Text(
message, message,
style: Theme.of(currentContext).textTheme.bodySmall!.copyWith( style: Theme.of(currentContext)
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green), .textTheme
.bodySmall!
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
) )
]), ]),
); );