Compare commits

...
Author SHA1 Message Date
hannathkadher dd5fe10a21 removed print 2024-12-09 10:03:40 +04:00
hannathkadher 4055265b0a for create 2024-12-09 10:02:57 +04:00
hannathkadher aff7ceeac4 added condition to check parent name 2024-12-09 10:02:20 +04:00
Abdullah Alassaf 246098b83a Read icon from fetch scene api 2024-12-05 12:42:53 +03:00
Abdullah Alassaf 26f50d59dd Bug fixes 2024-12-05 11:19:22 +03:00
hannathkadher 610cdf83a0 Merge pull request #59 from SyncrowIOT/bugfix/SP-852
Bugfix/sp 852
2024-12-04 12:10:02 +04:00
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 768996ca45 Merge pull request #58 from SyncrowIOT/dev
Dev
2024-12-03 16:02:31 +03: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
ashrafzarkanisala 76e44c8910 push changes 2024-12-01 01:07:26 +03:00
ashrafzarkanisala 02c788d03a merge and push 2024-12-01 00:43:30 +03:00
ashrafzarkanisala 938f8c6b51 update scene debug 2024-12-01 00:42:00 +03:00
Abdullah Alassaf 3ce9a313f7 Bug fixes 2024-12-01 00:21:15 +03:00
Abdullah Alassaf b732b0b957 Pulled latest changes 2024-11-30 21:37:55 +03:00
Abdullah Alassaf fb6b922df4 Added delete scene function 2024-11-30 20:45:25 +03:00
ashrafzarkanisala ce6e7700bc push changes 2024-11-30 20:43:21 +03:00
Abdullah 8d388ac482 Merge pull request #55 from SyncrowIOT/bugfix/community-flow
Bugfix/community flow
2024-11-29 18:36:03 +03:00
Abdullah 875787919e Merge pull request #54 from SyncrowIOT/bugfix/add-new-community
Bugfix/add new community
2024-11-29 14:41:52 +03:00
Abdullah 5da42e975a Merge pull request #46 from SyncrowIOT/dev
Dev
2024-10-30 16:38:18 +03:00
Abdullah bc02664a09 Merge pull request #40 from SyncrowIOT/dev
Dev
2024-10-09 21:36:15 +03:00
Abdullah 057ea8515a Merge pull request #37 from SyncrowIOT/dev
Dev
2024-10-09 11:50:32 +03:00
Abdullah 9cc9fd7f24 Merge pull request #24 from SyncrowIOT/dev
Dev
2024-09-12 16:32:36 +03:00
Abdullah f5958c1599 Merge pull request #21 from SyncrowIOT/dev
fix changes
2024-09-09 15:03:21 +03:00
Abdullah 1d0ef20015 Merge pull request #20 from SyncrowIOT/dev
fix changes
2024-09-09 14:30:05 +03:00
Abdullah 2b7a70e0a1 Merge pull request #19 from SyncrowIOT/dev
Dev
2024-09-09 13:13:36 +03:00
Abdullah 321a9b5fa2 Merge pull request #17 from SyncrowIOT/dev
Dev
2024-09-09 12:17:55 +03:00
Abdullah Alassaf 9058f29787 Merged with dev 2024-09-08 23:24:04 +03:00
Abdullah Alassaf fd497d5797 Updated the pipeline 2024-09-04 17:05:13 +03:00
faris Aljohari 5f4aa93e01 remove last workflows 2024-09-04 17:01:28 +03:00
faris Aljohari 96bf262466 setup new staging 2024-09-04 16:55:06 +03:00
faris Aljohari 2969e936d0 ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure [email protected]
2024-09-04 16:51:55 +03:00
Abdullah Alassaf e6d0e95ddc Updated github workflowf file 2024-09-04 01:07:31 +03:00
Abdullah Alassaf c7b1ed5b8e Added staging base url 2024-09-04 00:50:16 +03:00
faris Aljohari d0e7d12279 test deploy 2024-09-03 17:06:28 +03:00
67 changed files with 2095 additions and 1146 deletions
@@ -0,0 +1,60 @@
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- name: Checkout Code
uses: actions/checkout@v3
with:
submodules: true
lfs: false
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2' # Specify the Flutter version you want to use
- name: Install dependencies
run: flutter pub get
- name: Build Flutter Web App
run: flutter build web --release --dart-define=FLAVOR=production
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_MANGO_BUSH_01E607F10 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/build/web" # App source code path
api_location: "" # Api source code path - optional
output_location: "/build/web" # Built app content directory - optional
###### End of Repository/Build Configurations ######
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_MANGO_BUSH_01E607F10 }}
action: "close"
@@ -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,13 +69,12 @@ 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 const CreateNewRoutineView(); return CreateNewRoutineView();
} }
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
@@ -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,
@@ -16,6 +16,7 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
}; };
EffectPeriodBloc() : super(EffectPeriodState.initial()) { EffectPeriodBloc() : super(EffectPeriodState.initial()) {
on<InitialEffectPeriodEvent>(_initialEvent);
on<SetPeriod>(_onSetPeriod); on<SetPeriod>(_onSetPeriod);
on<ToggleDay>(_onToggleDay); on<ToggleDay>(_onToggleDay);
on<SetCustomTime>(_onSetCustomTime); on<SetCustomTime>(_onSetCustomTime);
@@ -24,6 +25,15 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
on<SetDays>(_setAllDays); on<SetDays>(_setAllDays);
} }
void _initialEvent(InitialEffectPeriodEvent event, Emitter<EffectPeriodState> emit) {
add(SetCustomTime(event.effectiveTime.start, event.effectiveTime.end));
emit(state.copyWith(
selectedDaysBinary: event.effectiveTime.loops,
customStartTime: event.effectiveTime.start,
customEndTime: event.effectiveTime.end,
));
}
void _onSetPeriod(SetPeriod event, Emitter<EffectPeriodState> emit) { void _onSetPeriod(SetPeriod event, Emitter<EffectPeriodState> emit) {
String startTime = ''; String startTime = '';
String endTime = ''; String endTime = '';
@@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
abstract class EffectPeriodEvent extends Equatable { abstract class EffectPeriodEvent extends Equatable {
@@ -8,6 +9,15 @@ abstract class EffectPeriodEvent extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class InitialEffectPeriodEvent extends EffectPeriodEvent {
final EffectiveTime effectiveTime;
const InitialEffectPeriodEvent(this.effectiveTime);
@override
List<Object> get props => [effectiveTime];
}
class SetPeriod extends EffectPeriodEvent { class SetPeriod extends EffectPeriodEvent {
final EnumEffectivePeriodOptions period; final EnumEffectivePeriodOptions period;
File diff suppressed because it is too large Load Diff
@@ -84,8 +84,7 @@ class RemoveDragCard extends RoutineEvent {
final int index; final int index;
final bool isFromThen; final bool isFromThen;
final String key; final String key;
const RemoveDragCard( const RemoveDragCard({required this.index, required this.isFromThen, required this.key});
{required this.index, required this.isFromThen, required this.key});
@override @override
List<Object> get props => [index, isFromThen, key]; List<Object> get props => [index, isFromThen, key];
} }
@@ -105,12 +104,10 @@ class EffectiveTimePeriodEvent extends RoutineEvent {
} }
class CreateAutomationEvent extends RoutineEvent { class CreateAutomationEvent extends RoutineEvent {
// final CreateAutomationModel createAutomationModel;
final String? automationId; final String? automationId;
final bool updateAutomation; final bool updateAutomation;
const CreateAutomationEvent({ const CreateAutomationEvent({
//required this.createAutomationModel,
this.automationId, this.automationId,
this.updateAutomation = false, this.updateAutomation = false,
}); });
@@ -159,21 +156,55 @@ class InitializeRoutineState extends RoutineEvent {
} }
class DeleteScene extends RoutineEvent { class DeleteScene extends RoutineEvent {
final String sceneId; const DeleteScene();
final String unitUuid;
const DeleteScene({required this.sceneId, required this.unitUuid});
@override @override
List<Object> get props => [sceneId]; List<Object> get props => [];
} }
class DeleteAutomation extends RoutineEvent { // class DeleteAutomation extends RoutineEvent {
final String automationId; // final String automationId;
final String unitUuid; // const DeleteAutomation({required this.automationId});
const DeleteAutomation({required this.automationId, required this.unitUuid}); // @override
// List<Object> get props => [automationId];
// }
class UpdateScene extends RoutineEvent {
const UpdateScene();
@override @override
List<Object> get props => [automationId]; List<Object> get props => [];
} }
class UpdateAutomation extends RoutineEvent {
const UpdateAutomation();
@override
List<Object> get props => [];
}
class SetAutomationActionExecutor extends RoutineEvent {
final String automationActionExecutor;
const SetAutomationActionExecutor({required this.automationActionExecutor});
@override
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 ResetRoutineState extends RoutineEvent {} class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {} class ClearFunctions extends RoutineEvent {}
class ResetErrorMessage extends RoutineEvent {}
@@ -21,29 +21,36 @@ class RoutineState extends Equatable {
final String? sceneId; final String? sceneId;
final String? automationId; final String? automationId;
final bool? isUpdate; final bool? isUpdate;
final List<AllDevicesModel> devices;
// 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 [],
this.automations = const [], this.automations = const [],
this.selectedFunctions = const {}, this.selectedFunctions = const {},
this.isLoading = false, this.isLoading = false,
this.errorMessage, this.errorMessage,
this.routineName, this.routineName,
this.selectedIcon, this.selectedIcon,
this.loadScenesErrorMessage, this.loadScenesErrorMessage,
this.loadAutomationErrorMessage, this.loadAutomationErrorMessage,
this.searchText, this.searchText,
this.isTabToRun = false, this.isTabToRun = false,
this.isAutomation = false, this.isAutomation = false,
this.selectedAutomationOperator = 'or', this.selectedAutomationOperator = 'or',
this.effectiveTime, this.effectiveTime,
this.sceneId, this.sceneId,
this.automationId, this.automationId,
this.isUpdate, this.isUpdate,
}); this.devices = const [],
// this.automationActionExecutor,
this.routineTab = false,
this.createRoutineView = false});
RoutineState copyWith({ RoutineState copyWith({
List<Map<String, dynamic>>? ifItems, List<Map<String, dynamic>>? ifItems,
@@ -65,31 +72,39 @@ class RoutineState extends Equatable {
String? sceneId, String? sceneId,
String? automationId, String? automationId,
bool? isUpdate, bool? isUpdate,
List<AllDevicesModel>? devices,
// String? automationActionExecutor,
TextEditingController? nameController,
bool? routineTab,
bool? createRoutineView,
}) { }) {
return RoutineState( return RoutineState(
ifItems: ifItems ?? this.ifItems, ifItems: ifItems ?? this.ifItems,
thenItems: thenItems ?? this.thenItems, thenItems: thenItems ?? this.thenItems,
scenes: scenes ?? this.scenes, scenes: scenes ?? this.scenes,
automations: automations ?? this.automations, automations: automations ?? this.automations,
selectedFunctions: selectedFunctions ?? this.selectedFunctions, selectedFunctions: selectedFunctions ?? this.selectedFunctions,
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage ?? this.errorMessage, errorMessage: errorMessage ?? this.errorMessage,
routineName: routineName ?? this.routineName, routineName: routineName ?? this.routineName,
selectedIcon: selectedIcon ?? this.selectedIcon, selectedIcon: selectedIcon ?? this.selectedIcon,
loadScenesErrorMessage: loadScenesErrorMessage:
loadScenesErrorMessage ?? this.loadScenesErrorMessage, loadScenesErrorMessage ?? this.loadScenesErrorMessage,
loadAutomationErrorMessage: loadAutomationErrorMessage:
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage, loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
searchText: searchText ?? this.searchText, searchText: searchText ?? this.searchText,
isTabToRun: isTabToRun ?? this.isTabToRun, isTabToRun: isTabToRun ?? this.isTabToRun,
isAutomation: isAutomation ?? this.isAutomation, isAutomation: isAutomation ?? this.isAutomation,
selectedAutomationOperator: selectedAutomationOperator:
selectedAutomationOperator ?? this.selectedAutomationOperator, selectedAutomationOperator ?? this.selectedAutomationOperator,
effectiveTime: effectiveTime ?? this.effectiveTime, effectiveTime: effectiveTime ?? this.effectiveTime,
sceneId: sceneId ?? this.sceneId, sceneId: sceneId ?? this.sceneId,
automationId: automationId ?? this.automationId, automationId: automationId ?? this.automationId,
isUpdate: isUpdate ?? this.isUpdate, isUpdate: isUpdate ?? this.isUpdate,
); devices: devices ?? this.devices,
// automationActionExecutor: automationActionExecutor ?? this.automationActionExecutor,
routineTab: routineTab ?? this.routineTab,
createRoutineView: createRoutineView ?? this.createRoutineView);
} }
@override @override
@@ -112,6 +127,10 @@ class RoutineState extends Equatable {
effectiveTime, effectiveTime,
sceneId, sceneId,
automationId, automationId,
isUpdate isUpdate,
devices,
// 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>(
@@ -98,18 +101,29 @@ class SaveRoutineHelper {
final functions = final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? []; state.selectedFunctions[item['uniqueCustomId']] ?? [];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: item['type'] == 'tap_to_run' || item['type'] == 'scene'
item['imagePath'], ? Image.memory(
width: 22, base64Decode(item['icon']),
height: 22, width: 22,
height: 22,
)
: SvgPicture.asset(
item['imagePath'],
width: 22,
height: 22,
),
title: Text(
item['title'],
style: context.textTheme.bodySmall?.copyWith(
fontSize: 14,
color: ColorsManager.grayColor,
),
), ),
title:
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: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.grayColor, fontSize: 8), color: ColorsManager.grayColor, fontSize: 8),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 3, maxLines: 3,
@@ -124,24 +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) {
context.read<RoutineBloc>().add(const CreateAutomationEvent()); if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateAutomation());
} else {
context.read<RoutineBloc>().add(const CreateAutomationEvent());
}
} else { } else {
context.read<RoutineBloc>().add(const CreateSceneEvent()); if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateScene());
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
}
} }
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
Navigator.pop(context, true); Navigator.pop(context);
// }
}, },
isConfirmEnabled: true, isConfirmEnabled: true,
), ),
@@ -17,7 +17,7 @@ class CreateAutomationModel {
required this.actions, required this.actions,
}); });
Map<String, dynamic> toMap() { Map<String, dynamic> toMap([String? automationId]) {
return { return {
'spaceUuid': spaceUuid, 'spaceUuid': spaceUuid,
'automationName': automationName, 'automationName': automationName,
@@ -41,7 +41,7 @@ class CreateAutomationModel {
); );
} }
String toJson() => json.encode(toMap()); String toJson(String? automationId) => json.encode(toMap(automationId));
factory CreateAutomationModel.fromJson(String source) => factory CreateAutomationModel.fromJson(String source) =>
CreateAutomationModel.fromMap(json.decode(source)); CreateAutomationModel.fromMap(json.decode(source));
@@ -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,
'executorProperty': executorProperty?.toMap(), if (executorProperty != null)
'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:
@@ -13,6 +13,8 @@ class RoutineDetailsModel {
final EffectiveTime? effectiveTime; final EffectiveTime? effectiveTime;
final List<RoutineCondition>? conditions; final List<RoutineCondition>? conditions;
final String? type; final String? type;
final String? sceneId;
final String? automationId;
RoutineDetailsModel({ RoutineDetailsModel({
required this.spaceUuid, required this.spaceUuid,
@@ -24,6 +26,8 @@ class RoutineDetailsModel {
this.effectiveTime, this.effectiveTime,
this.conditions, this.conditions,
this.type, this.type,
this.sceneId,
this.automationId,
}); });
// Convert to CreateSceneModel // Convert to CreateSceneModel
@@ -44,8 +48,7 @@ class RoutineDetailsModel {
spaceUuid: spaceUuid, spaceUuid: spaceUuid,
automationName: name, automationName: name,
decisionExpr: decisionExpr, decisionExpr: decisionExpr,
effectiveTime: effectiveTime: effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
conditions: conditions?.map((c) => c.toCondition()).toList() ?? [], conditions: conditions?.map((c) => c.toCondition()).toList() ?? [],
actions: actions.map((a) => a.toAutomationAction()).toList(), actions: actions.map((a) => a.toAutomationAction()).toList(),
); );
@@ -57,12 +60,13 @@ class RoutineDetailsModel {
'name': name, 'name': name,
'decisionExpr': decisionExpr, 'decisionExpr': decisionExpr,
'actions': actions.map((x) => x.toMap()).toList(), 'actions': actions.map((x) => x.toMap()).toList(),
if (iconId != null) 'iconId': iconId, if (iconId != null) 'iconUuid': iconId,
if (showInDevice != null) 'showInDevice': showInDevice, if (showInDevice != null) 'showInDevice': showInDevice,
if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(), if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(),
if (conditions != null) if (conditions != null) 'conditions': conditions!.map((x) => x.toMap()).toList(),
'conditions': conditions!.map((x) => x.toMap()).toList(),
if (type != null) 'type': type, if (type != null) 'type': type,
if (sceneId != null) 'sceneId': sceneId,
if (automationId != null) 'automationId': automationId,
}; };
} }
@@ -74,16 +78,16 @@ class RoutineDetailsModel {
actions: List<RoutineAction>.from( actions: List<RoutineAction>.from(
map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [], map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [],
), ),
iconId: map['iconId'], iconId: map['iconUuid'],
showInDevice: map['showInDevice'], showInDevice: map['showInDevice'],
effectiveTime: map['effectiveTime'] != null effectiveTime:
? EffectiveTime.fromMap(map['effectiveTime']) map['effectiveTime'] != null ? EffectiveTime.fromMap(map['effectiveTime']) : null,
: null,
conditions: map['conditions'] != null conditions: map['conditions'] != null
? List<RoutineCondition>.from( ? List<RoutineCondition>.from(map['conditions'].map((x) => RoutineCondition.fromMap(x)))
map['conditions'].map((x) => RoutineCondition.fromMap(x)))
: null, : null,
type: map['type'], type: map['type'],
sceneId: map['sceneId'],
automationId: map['automationId'],
); );
} }
@@ -96,13 +100,20 @@ class RoutineDetailsModel {
class RoutineAction { class RoutineAction {
final String entityId; final String entityId;
final String actionExecutor; final String actionExecutor;
final String? name;
final RoutineExecutorProperty? executorProperty; final RoutineExecutorProperty? executorProperty;
final String productType;
final String? type;
final String? icon;
RoutineAction({ RoutineAction(
required this.entityId, {required this.entityId,
required this.actionExecutor, required this.actionExecutor,
this.executorProperty, required this.productType,
}); this.executorProperty,
this.name,
this.type,
this.icon});
CreateSceneAction toCreateSceneAction() { CreateSceneAction toCreateSceneAction() {
return CreateSceneAction( return CreateSceneAction(
@@ -124,19 +135,23 @@ class RoutineAction {
return { return {
'entityId': entityId, 'entityId': entityId,
'actionExecutor': actionExecutor, 'actionExecutor': actionExecutor,
if (executorProperty != null) if (type != null) 'type': type,
'executorProperty': executorProperty!.toMap(), if (name != null) 'name': name,
if (executorProperty != null) 'executorProperty': executorProperty!.toMap(),
}; };
} }
factory RoutineAction.fromMap(Map<String, dynamic> map) { factory RoutineAction.fromMap(Map<String, dynamic> map) {
return RoutineAction( return RoutineAction(
entityId: map['entityId'] ?? '', entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '', actionExecutor: map['actionExecutor'] ?? '',
executorProperty: map['executorProperty'] != null productType: map['productType'] ?? '',
? RoutineExecutorProperty.fromMap(map['executorProperty']) name: map['name'] ?? '',
: null, type: map['type'] ?? '',
); executorProperty: map['executorProperty'] != null
? RoutineExecutorProperty.fromMap(map['executorProperty'])
: null,
icon: map['icon']);
} }
} }
@@ -189,12 +204,14 @@ class RoutineCondition {
final String entityId; final String entityId;
final String entityType; final String entityType;
final RoutineConditionExpr expr; final RoutineConditionExpr expr;
final String productType;
RoutineCondition({ RoutineCondition({
required this.code, required this.code,
required this.entityId, required this.entityId,
required this.entityType, required this.entityType,
required this.expr, required this.expr,
required this.productType,
}); });
Condition toCondition() { Condition toCondition() {
@@ -221,6 +238,7 @@ class RoutineCondition {
entityId: map['entityId'] ?? '', entityId: map['entityId'] ?? '',
entityType: map['entityType'] ?? '', entityType: map['entityType'] ?? '',
expr: RoutineConditionExpr.fromMap(map['expr']), expr: RoutineConditionExpr.fromMap(map['expr']),
productType: map['productType'] ?? '',
); );
} }
} }
@@ -16,6 +16,7 @@ class CreateNewRoutineView extends StatelessWidget {
this.routineId, this.routineId,
this.isScene = true, this.isScene = true,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
@@ -67,7 +68,7 @@ class CreateNewRoutineView extends StatelessWidget {
width: double.infinity, width: double.infinity,
color: ColorsManager.dialogBlueTitle, color: ColorsManager.dialogBlueTitle,
), ),
/// THEN Container /// THEN Container
Expanded( Expanded(
child: Card( child: Card(
+17 -7
View File
@@ -1,20 +1,30 @@
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';
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';
class RoutinesView extends StatelessWidget { class RoutinesView extends StatefulWidget {
const RoutinesView({super.key}); const RoutinesView({super.key});
@override
State<RoutinesView> createState() => _RoutinesViewState();
}
class _RoutinesViewState extends State<RoutinesView> {
@override
void initState() {
super.initState();
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
@override @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(
@@ -36,12 +46,12 @@ class RoutinesView extends StatelessWidget {
), ),
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: '',
@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DeleteSceneWidget extends StatelessWidget {
const DeleteSceneWidget({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
GestureDetector(
onTap: () async {
await showCustomDialog(
context: context,
message: 'Are you sure you want to delete this scene?',
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.textGray,
),
),
),
),
Container(width: 1, height: 50, color: ColorsManager.greyColor),
InkWell(
onTap: () {
context.read<RoutineBloc>().add(const DeleteScene());
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'Confirm',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
),
),
],
),
]);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.delete,
color: ColorsManager.red,
),
const SizedBox(
width: 2,
),
Text(
'Delete',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.red,
),
),
],
),
),
const SizedBox(
height: 10,
),
],
);
}
}
+24 -15
View File
@@ -32,15 +32,27 @@ class DraggableCard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
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(
angle: -0.1, angle: -0.1,
child: child: _buildCardContent(context, deviceFunctions, padding: padding),
_buildCardContent(context, deviceFunctions, padding: padding),
), ),
childWhenDragging: _buildGreyContainer(), childWhenDragging: _buildGreyContainer(),
child: _buildCardContent(context, deviceFunctions, padding: padding), child: _buildCardContent(context, deviceFunctions, padding: padding),
@@ -49,8 +61,7 @@ class DraggableCard extends StatelessWidget {
); );
} }
Widget _buildCardContent( Widget _buildCardContent(BuildContext context, List<DeviceFunctionData> deviceFunctions,
BuildContext context, List<DeviceFunctionData> deviceFunctions,
{EdgeInsetsGeometry? padding}) { {EdgeInsetsGeometry? padding}) {
return Stack( return Stack(
children: [ children: [
@@ -79,19 +90,19 @@ class DraggableCard extends StatelessWidget {
), ),
), ),
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: imagePath.contains('.svg') child: deviceData['type'] == 'tap_to_run' || deviceData['type'] == 'scene'
? SvgPicture.asset( ? Image.memory(
imagePath, base64Decode(deviceData['icon']),
) )
: Image.memory( : SvgPicture.asset(
base64Decode(imagePath), imagePath,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 3), padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text( child: Text(
title, deviceData['title'] ?? deviceData['name'] ?? title,
textAlign: TextAlign.center, textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
@@ -104,7 +115,6 @@ class DraggableCard extends StatelessWidget {
], ],
), ),
if (deviceFunctions.isNotEmpty) if (deviceFunctions.isNotEmpty)
// const Divider(height: 1),
...deviceFunctions.map((function) => Row( ...deviceFunctions.map((function) => Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -149,8 +159,7 @@ class DraggableCard extends StatelessWidget {
} }
String _formatFunctionValue(DeviceFunctionData function) { String _formatFunctionValue(DeviceFunctionData function) {
if (function.functionCode == 'temp_set' || if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
function.functionCode == 'temp_current') {
return '${(function.value / 10).toStringAsFixed(0)}°C'; return '${(function.value / 10).toStringAsFixed(0)}°C';
} else if (function.functionCode.contains('countdown')) { } else if (function.functionCode.contains('countdown')) {
final seconds = function.value.toInt(); final seconds = function.value.toInt();
@@ -11,8 +11,7 @@ class FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key}); const FetchRoutineScenesAutomation({super.key});
@override @override
State<FetchRoutineScenesAutomation> createState() => State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
_FetchRoutineScenesState();
} }
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation> class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
@@ -67,49 +66,23 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
padding: EdgeInsets.only( padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0, right: isSmallScreenSize(context) ? 4.0 : 8.0,
), ),
child: Stack( child: RoutineViewCard(
children: [ onTap: () {
RoutineViewCard( BlocProvider.of<RoutineBloc>(context).add(
onTap: () {}, const CreateNewRoutineViewEvent(createRoutineView: true),
textString: state.scenes[index].name, );
icon: state.scenes[index].icon ?? context.read<RoutineBloc>().add(
Assets.logoHorizontal, GetSceneDetails(
isFromScenes: true, sceneId: state.scenes[index].id,
iconInBytes: isTabToRun: true,
state.scenes[index].iconInBytes, isUpdate: true,
),
Positioned(
top: 0,
right: 0,
child: InkWell(
onTap: () => context
.read<RoutineBloc>()
.add(
DeleteScene(
sceneId: state.scenes[index].id,
unitUuid: spaceId,
),
),
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.grayColor,
width: 2.0,
),
), ),
child: const Center( );
child: Icon(Icons.delete, },
size: 15, textString: state.scenes[index].name,
color: ColorsManager.grayColor), icon: state.scenes[index].icon ?? Assets.logoHorizontal,
), isFromScenes: true,
), iconInBytes: state.scenes[index].iconInBytes,
),
),
],
), ),
), ),
), ),
@@ -142,46 +115,20 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
padding: EdgeInsets.only( padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0, right: isSmallScreenSize(context) ? 4.0 : 8.0,
), ),
child: Stack( child: RoutineViewCard(
children: [ onTap: () {
RoutineViewCard( BlocProvider.of<RoutineBloc>(context).add(
onTap: () {}, const CreateNewRoutineViewEvent(createRoutineView: true),
textString: state.automations[index].name, );
icon: state.automations[index].icon ?? context.read<RoutineBloc>().add(
Assets.automation, GetAutomationDetails(
), automationId: state.automations[index].id,
Positioned( isAutomation: true,
top: 0, isUpdate: true),
right: 0, );
child: InkWell( },
onTap: () => textString: state.automations[index].name,
context.read<RoutineBloc>().add( icon: state.automations[index].icon ?? Assets.automation,
DeleteAutomation(
automationId: state
.automations[index].id,
unitUuid: spaceId,
),
),
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.grayColor,
width: 2.0,
),
),
child: const Center(
child: Icon(Icons.delete,
size: 15,
color: ColorsManager.grayColor),
),
),
),
),
],
), ),
), ),
), ),
+60 -62
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/device_mgmt_bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
@@ -10,68 +9,67 @@ class RoutineDevices extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocBuilder<RoutineBloc, RoutineState>(
create: (context) => DeviceManagementBloc()..add(FetchDevices()), builder: (context, state) {
child: BlocBuilder<DeviceManagementBloc, DeviceManagementState>( if (state.isLoading) {
builder: (context, state) {
if (state is DeviceManagementLoaded) {
List<AllDevicesModel> deviceList = state.devices
.where((device) =>
device.productType == 'AC' ||
device.productType == '1G' ||
device.productType == '2G' ||
device.productType == '3G')
.toList();
// Provide the RoutineBloc to the child widgets
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, routineState) {
return Wrap(
spacing: 10,
runSpacing: 10,
children: deviceList.asMap().entries.map((entry) {
final device = entry.value;
if (routineState.searchText != null && routineState.searchText!.isNotEmpty) {
return device.name!
.toLowerCase()
.contains(routineState.searchText!.toLowerCase())
? DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
)
: Container();
} else {
return DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
);
}
}).toList(),
);
},
);
}
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
}, }
),
Future.delayed(const Duration(seconds: 1), () {
if (state.devices.isEmpty) {
return const Center(child: Text('No devices found'));
}
});
List<AllDevicesModel> deviceList = state.devices
.where((device) =>
device.productType == 'AC' ||
device.productType == '1G' ||
device.productType == '2G' ||
device.productType == '3G')
.toList();
return Wrap(
spacing: 10,
runSpacing: 10,
children: deviceList.asMap().entries.map((entry) {
final device = entry.value;
if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name!
.toLowerCase()
.contains(state.searchText!.toLowerCase())
? DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
)
: Container();
} else {
return DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
);
}
}).toList(),
);
},
); );
} }
} }
@@ -11,20 +11,36 @@ 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
_AutomationDialogState createState() => _AutomationDialogState(); State<AutomationDialog> createState() => _AutomationDialogState();
} }
class _AutomationDialogState extends State<AutomationDialog> { class _AutomationDialogState extends State<AutomationDialog> {
bool _isEnabled = true; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -41,26 +57,25 @@ class _AutomationDialogState extends State<AutomationDialog> {
ListTile( ListTile(
leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24), leading: 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) {
setState(() { setState(() {
_isEnabled = value!; selectedAutomationActionExecutor = 'rule_enable';
}); });
}, }),
),
), ),
ListTile( ListTile(
leading: leading:
SvgPicture.asset(Assets.acPowerOff, 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) {
setState(() { setState(() {
_isEnabled = value!; selectedAutomationActionExecutor = 'rule_disable';
}); });
}, },
), ),
@@ -68,23 +83,25 @@ class _AutomationDialogState extends State<AutomationDialog> {
const SizedBox(height: 16), const SizedBox(height: 16),
DialogFooter( DialogFooter(
onConfirm: () { onConfirm: () {
context.read<RoutineBloc>().add( if (selectedAutomationActionExecutor != null) {
AddFunctionToRoutine( context.read<RoutineBloc>().add(
[ AddFunctionToRoutine(
DeviceFunctionData( [
entityId: widget.automationId, DeviceFunctionData(
functionCode: 'automation', entityId: widget.automationId,
value: _isEnabled ? 'rule_enable' : 'rule_disable', functionCode: 'automation',
operationName: 'Automation', value: selectedAutomationActionExecutor,
), operationName: 'Automation',
], ),
widget.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,6 +1,7 @@
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/routiens/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart'; import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart';
@@ -9,6 +10,7 @@ import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart'
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart'; import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/delete_scene.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@@ -22,11 +24,18 @@ class SettingHelper {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
final isAutomation = context.read<RoutineBloc>().state.isAutomation; final isAutomation = context.read<RoutineBloc>().state.isAutomation;
final effectiveTime = context.read<RoutineBloc>().state.effectiveTime;
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider( if (effectiveTime != null)
create: (_) => EffectPeriodBloc(), BlocProvider(
), create: (_) => EffectPeriodBloc()..add(InitialEffectPeriodEvent(effectiveTime)),
),
if (effectiveTime == null)
BlocProvider(
create: (_) => EffectPeriodBloc(),
),
BlocProvider( BlocProvider(
create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? ''))), create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? ''))),
], ],
@@ -44,7 +53,7 @@ class SettingHelper {
} }
return Container( return Container(
width: context.read<SettingBloc>().isExpanded ? 800 : 400, width: context.read<SettingBloc>().isExpanded ? 800 : 400,
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 300, height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 350,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
@@ -167,6 +176,9 @@ class SettingHelper {
fontSize: 14)), fontSize: 14)),
], ],
), ),
if (context.read<RoutineBloc>().state.isUpdate ??
false)
const DeleteSceneWidget()
], ],
)), )),
], ],
@@ -284,6 +296,9 @@ class SettingHelper {
fontSize: 14)), fontSize: 14)),
], ],
), ),
if (context.read<RoutineBloc>().state.isUpdate ??
false)
const DeleteSceneWidget()
], ],
)), )),
], ],
@@ -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';
@@ -10,20 +9,47 @@ 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';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class RoutineSearchAndButtons extends StatelessWidget { class RoutineSearchAndButtons extends StatefulWidget {
const RoutineSearchAndButtons({ const RoutineSearchAndButtons({
super.key, super.key,
}); });
@override
State<RoutineSearchAndButtons> createState() => _RoutineSearchAndButtonsState();
}
class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
late TextEditingController _nameController;
@override
void initState() {
super.initState();
_nameController = TextEditingController();
}
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
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: [
@@ -37,24 +63,6 @@ class RoutineSearchAndButtons extends StatelessWidget {
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: maxWidth:
constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32), constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
// child: StatefulTextField(
// title: 'Routine Name',
// initialValue: state.routineName ?? '',
// height: 40,
// // controller: TextEditingController(),
// hintText: 'Please enter the name',
// boxDecoration: containerWhiteDecoration,
// elevation: 0,
// borderRadius: 15,
// isRequired: true,
// width: 450,
// onSubmitted: (value) {
// // context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// onChanged: (value) {
// context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -81,7 +89,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: TextFormField( child: TextFormField(
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.blackColor), .copyWith(color: ColorsManager.blackColor),
initialValue: state.routineName, controller: _nameController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Please enter the name', hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium! hintStyle: context.textTheme.bodyMedium!
@@ -90,8 +98,10 @@ class RoutineSearchAndButtons extends StatelessWidget {
const EdgeInsets.symmetric(horizontal: 12, vertical: 10), const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
border: InputBorder.none, border: InputBorder.none,
), ),
onChanged: (value) { onTapOutside: (_) {
context.read<RoutineBloc>().add(SetRoutineName(value)); context
.read<RoutineBloc>()
.add(SetRoutineName(_nameController.text));
}, },
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@@ -115,6 +125,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
? () async { ? () async {
final result = await SettingHelper.showSettingDialog( final result = await SettingHelper.showSettingDialog(
context: context, context: context,
iconId: state.selectedIcon ?? '',
); );
if (result != null) { if (result != null) {
context context
@@ -208,16 +219,18 @@ class RoutineSearchAndButtons extends StatelessWidget {
); );
return; return;
} }
final result = // final result =
await SaveRoutineHelper.showSaveRoutineDialog(context); // await
if (result != null && result) { BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
BlocProvider.of<SwitchTabsBloc>(context).add( SaveRoutineHelper.showSaveRoutineDialog(context);
const CreateNewRoutineViewEvent(false), // if (result != null && result) {
); // BlocProvider.of<RoutineBloc>(context).add(
BlocProvider.of<SwitchTabsBloc>(context).add( // const CreateNewRoutineViewEvent(createRoutineView: false),
const TriggerSwitchTabsEvent(true), // );
); // BlocProvider.of<RoutineBloc>(context).add(
} // const TriggerSwitchTabsEvent(isRoutineTab: true),
// );
// }
}, },
borderRadius: 15, borderRadius: 15,
elevation: 0, elevation: 0,
@@ -249,8 +262,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
onPressed: state.isAutomation || state.isTabToRun onPressed: state.isAutomation || state.isTabToRun
? () async { ? () async {
final result = await SettingHelper.showSettingDialog( final result = await SettingHelper.showSettingDialog(
context: context, context: context, iconId: state.selectedIcon ?? '');
);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddSelectedIcon(result)); context.read<RoutineBloc>().add(AddSelectedIcon(result));
} }
@@ -301,7 +313,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
width: 200, width: 200,
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () async {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
@@ -335,7 +347,18 @@ class RoutineSearchAndButtons extends StatelessWidget {
); );
return; return;
} }
// final result =
// await
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
SaveRoutineHelper.showSaveRoutineDialog(context); SaveRoutineHelper.showSaveRoutineDialog(context);
// if (result != null && result) {
// BlocProvider.of<RoutineBloc>(context).add(
// const CreateNewRoutineViewEvent(createRoutineView: false),
// );
// BlocProvider.of<RoutineBloc>(context).add(
// const TriggerSwitchTabsEvent(isRoutineTab: true),
// );
// }
}, },
borderRadius: 15, borderRadius: 15,
elevation: 0, elevation: 0,
+93 -65
View File
@@ -26,73 +26,88 @@ class ThenContainer extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('THEN', const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
Wrap( state.isLoading && state.isUpdate == true
spacing: 8, ? const Center(
runSpacing: 8, child: CircularProgressIndicator(),
children: List.generate( )
state.thenItems.length, : Wrap(
(index) => GestureDetector( spacing: 8,
onTap: () async { runSpacing: 8,
if (state.thenItems[index]['deviceId'] == children: List.generate(
'delay') { state.thenItems.length,
final result = await DelayHelper (index) => GestureDetector(
.showDelayPickerDialog( onTap: () async {
if (state.thenItems[index]['deviceId'] == 'delay') {
final result = await DelayHelper.showDelayPickerDialog(
context, state.thenItems[index]); context, state.thenItems[index]);
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(AddToThenContainer({
.read<RoutineBloc>() ...state.thenItems[index],
.add(AddToThenContainer({ 'imagePath': Assets.delay,
...state.thenItems[index], 'title': 'Delay',
'imagePath': Assets.delay, }));
'title': 'Delay', }
})); return;
} }
return;
}
final result = await DeviceDialogHelper if (state.thenItems[index]['type'] == 'automation') {
.showDeviceDialog( final result = await showDialog<bool>(
context: context,
builder: (BuildContext context) => AutomationDialog(
automationName:
state.thenItems[index]['name'] ?? 'Automation',
automationId:
state.thenItems[index]['deviceId'] ?? '',
uniqueCustomId: state.thenItems[index]
['uniqueCustomId'],
),
);
if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({
...state.thenItems[index],
'imagePath': Assets.automation,
'title': state.thenItems[index]['name'] ??
state.thenItems[index]['title'],
}));
}
return;
}
final result = await DeviceDialogHelper.showDeviceDialog(
context, state.thenItems[index], context, state.thenItems[index],
removeComparetors: true); removeComparetors: true);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add( context
AddToThenContainer( .read<RoutineBloc>()
state.thenItems[index])); .add(AddToThenContainer(state.thenItems[index]));
} else if (!['AC', '1G', '2G', '3G'] } else if (!['AC', '1G', '2G', '3G']
.contains(state.thenItems[index] .contains(state.thenItems[index]['productType'])) {
['productType'])) { context
context.read<RoutineBloc>().add( .read<RoutineBloc>()
AddToThenContainer( .add(AddToThenContainer(state.thenItems[index]));
state.thenItems[index])); }
} },
}, child: DraggableCard(
child: DraggableCard( imagePath: state.thenItems[index]['imagePath'] ?? '',
imagePath: state.thenItems[index] title: state.thenItems[index]['title'] ?? '',
['imagePath'] ?? deviceData: state.thenItems[index],
'', padding:
title: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
state.thenItems[index]['title'] ?? '', isFromThen: true,
deviceData: state.thenItems[index], isFromIf: false,
padding: const EdgeInsets.symmetric( onRemove: () {
horizontal: 4, vertical: 8), context.read<RoutineBloc>().add(RemoveDragCard(
isFromThen: true,
isFromIf: false,
onRemove: () {
context.read<RoutineBloc>().add(
RemoveDragCard(
index: index, index: index,
isFromThen: true, isFromThen: true,
key: state.thenItems[index] key: state.thenItems[index]['uniqueCustomId']));
['uniqueCustomId'])); },
}, ),
), ))),
))),
], ],
), ),
), ),
@@ -108,7 +123,18 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
if (state.automationId == mutableData['deviceId'] ||
state.sceneId == mutableData['deviceId']) {
return;
}
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(
@@ -129,9 +155,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'],
})); }));
@@ -143,8 +174,7 @@ class ThenContainer extends StatelessWidget {
} }
if (mutableData['deviceId'] == 'delay') { if (mutableData['deviceId'] == 'delay') {
final result = final result = await DelayHelper.showDelayPickerDialog(context, mutableData);
await DelayHelper.showDelayPickerDialog(context, mutableData);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context.read<RoutineBloc>().add(AddToThenContainer({
@@ -156,13 +186,11 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
final result = await DeviceDialogHelper.showDeviceDialog( final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData,
context, mutableData,
removeComparetors: true); removeComparetors: true);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} else if (!['AC', '1G', '2G', '3G'] } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) {
.contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} }
}, },
@@ -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';
@@ -309,8 +309,6 @@ class SpaceManagementBloc
await _api.deleteSpace(communityUuid, parent.uuid!); await _api.deleteSpace(communityUuid, parent.uuid!);
} }
} catch (e) { } catch (e) {
print(
'Error deleting space ${parent.name} (UUID: ${parent.uuid}, Community UUID: $communityUuid): $e');
rethrow; // Decide whether to stop execution or continue rethrow; // Decide whether to stop execution or continue
} }
} }
@@ -342,7 +340,6 @@ class SpaceManagementBloc
space.uuid = response?.uuid; space.uuid = response?.uuid;
} }
} catch (e) { } catch (e) {
print('Error creating space ${space.name}: $e');
rethrow; // Stop further execution on failure rethrow; // Stop further execution on failure
} }
} }
@@ -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,19 +15,28 @@ 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,
required this.nameController, required this.nameController,
required this.onSave, required this.onSave,
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';
@@ -133,13 +133,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
isNameFieldInvalid = value.isEmpty; isNameFieldInvalid = value.isEmpty;
if (!isNameFieldInvalid) { if (!isNameFieldInvalid) {
if ((widget.parentSpace?.children.any( if (_isNameConflict(value)) {
(child) => child.name == value) ??
false) ||
(widget.parentSpace?.name == value) ||
(widget.editSpace?.children.any(
(child) => child.name == value) ??
false)) {
isNameFieldExist = true; isNameFieldExist = true;
isOkButtonEnabled = false; isOkButtonEnabled = false;
} else { } else {
@@ -387,7 +381,14 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
); );
} }
bool _isNameConflict(String value) {
return (widget.parentSpace?.children.any((child) => child.name == value) ??
false) ||
(widget.parentSpace?.name == value) ||
(widget.editSpace?.parent?.name == value) ||
(widget.editSpace?.children.any((child) => child.name == value) ??
false);
}
String _mapIconToProduct(String uuid, List<ProductModel> products) { String _mapIconToProduct(String uuid, List<ProductModel> products) {
// Find the product with the matching UUID // Find the product with the matching UUID
@@ -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';
+41 -59
View File
@@ -36,7 +36,6 @@ class SceneApi {
static Future<Map<String, dynamic>> createAutomation( static Future<Map<String, dynamic>> createAutomation(
CreateAutomationModel createAutomationModel) async { CreateAutomationModel createAutomationModel) async {
try { try {
debugPrint("automation body ${createAutomationModel.toMap()}");
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.createAutomation, path: ApiEndpoints.createAutomation,
body: createAutomationModel.toMap(), body: createAutomationModel.toMap(),
@@ -138,29 +137,49 @@ class SceneApi {
path: ApiEndpoints.getAutomationDetails path: ApiEndpoints.getAutomationDetails
.replaceAll('{automationId}', automationId), .replaceAll('{automationId}', automationId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), expectedResponseModel: (json) => RoutineDetailsModel.fromMap(json),
);
return response;
} catch (e) {
rethrow;
}
}
//update Scene
static updateScene(CreateSceneModel createSceneModel, String sceneId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
body: createSceneModel
.toJson(sceneId.isNotEmpty == true ? sceneId : null),
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
//update automation
static updateAutomation(
CreateAutomationModel createAutomationModel, String automationId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateAutomation
.replaceAll('{automationId}', automationId),
body: createAutomationModel
.toJson(automationId.isNotEmpty == true ? automationId : null),
expectedResponseModel: (json) {
return json;
},
); );
return response; return response;
} catch (e) { } catch (e) {
rethrow; rethrow;
} }
} }
//
// //updateAutomationStatus
// static Future<bool> updateAutomationStatus(String automationId,
// AutomationStatusUpdate createAutomationEnable) async {
// try {
// final response = await _httpService.put(
// path: ApiEndpoints.updateAutomationStatus
// .replaceAll('{automationId}', automationId),
// body: createAutomationEnable.toMap(),
// expectedResponseModel: (json) => json['success'],
// );
// return response;
// } catch (e) {
// rethrow;
// }
// }
//getScene //getScene
static Future<RoutineDetailsModel> getSceneDetails(String sceneId) async { static Future<RoutineDetailsModel> getSceneDetails(String sceneId) async {
@@ -168,52 +187,15 @@ class SceneApi {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json), expectedResponseModel: (json) =>
RoutineDetailsModel.fromMap(json['data']),
); );
return response; return response;
} catch (e) { } catch (e) {
rethrow; rethrow;
} }
} }
//
// //update Scene
// static updateScene(CreateSceneModel createSceneModel, String sceneId) async {
// try {
// final response = await _httpService.put(
// path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
// body: createSceneModel
// .toJson(sceneId.isNotEmpty == true ? sceneId : null),
// expectedResponseModel: (json) {
// return json;
// },
// );
// return response;
// } catch (e) {
// rethrow;
// }
// }
//
// //update automation
// static updateAutomation(
// CreateAutomationModel createAutomationModel, String automationId) async {
// try {
// final response = await _httpService.put(
// path: ApiEndpoints.updateAutomation
// .replaceAll('{automationId}', automationId),
// body: createAutomationModel
// .toJson(automationId.isNotEmpty == true ? automationId : null),
// expectedResponseModel: (json) {
// return json;
// },
// );
// return response;
// } catch (e) {
// rethrow;
// }
// }
//
//delete Scene //delete Scene
static Future<bool> deleteScene( static Future<bool> deleteScene(
{required String unitUuid, required String sceneId}) async { {required String unitUuid, required String sceneId}) async {
@@ -230,7 +212,7 @@ class SceneApi {
rethrow; rethrow;
} }
} }
// delete automation // delete automation
static Future<bool> deleteAutomation( static Future<bool> deleteAutomation(
{required String unitUuid, required String automationId}) async { {required String unitUuid, required String automationId}) async {
+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()),
]; ];
} }
} }
+3
View File
@@ -74,4 +74,7 @@ abstract class ApiEndpoints {
static const String deleteScene = '/scene/tap-to-run/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{sceneId}';
static const String deleteAutomation = '/automation/{automationId}'; static const String deleteAutomation = '/automation/{automationId}';
static const String updateScene = '/scene/tap-to-run/{sceneId}';
static const String updateAutomation = '/automation/{automationId}';
} }
+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),
) )
]), ]),
); );