mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-16 18:16:34 +00:00
Compare commits
64 Commits
feature/sp
...
feat/updat
Author | SHA1 | Date | |
---|---|---|---|
4896b6c593 | |||
246098b83a | |||
26f50d59dd | |||
610cdf83a0 | |||
d423a3eb59 | |||
9bddd151bb | |||
0b628c85a5 | |||
768996ca45 | |||
7ee335ab1a | |||
d5ad06335b | |||
d9ef5b4574 | |||
c58f2eb24e | |||
4e94d2df89 | |||
2f5c5d7da1 | |||
ff4ce8ed01 | |||
1aa6b78fb4 | |||
e003d9b8b3 | |||
76e44c8910 | |||
02c788d03a | |||
938f8c6b51 | |||
3ce9a313f7 | |||
b732b0b957 | |||
fb6b922df4 | |||
ce6e7700bc | |||
8d388ac482 | |||
42c7577b35 | |||
ae2b4b9b06 | |||
98faef2bde | |||
edf297c569 | |||
2cd850b66f | |||
3c543b2e0f | |||
a3c6421b0d | |||
875787919e | |||
51402720dd | |||
123291fd89 | |||
69e3f50269 | |||
bd78c8c1cc | |||
4047664c59 | |||
084f4fbda8 | |||
817671dbeb | |||
660b5aaf6c | |||
0f500a4efa | |||
0b2d44fc2b | |||
b75f8a3440 | |||
d02d680520 | |||
68ae992963 | |||
ea20afd34e | |||
b0f0a91cda | |||
5da42e975a | |||
bc02664a09 | |||
057ea8515a | |||
9cc9fd7f24 | |||
f5958c1599 | |||
1d0ef20015 | |||
2b7a70e0a1 | |||
321a9b5fa2 | |||
9058f29787 | |||
fd497d5797 | |||
5f4aa93e01 | |||
96bf262466 | |||
2969e936d0 | |||
e6d0e95ddc | |||
c7b1ed5b8e | |||
d0e7d12279 |
60
.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml
vendored
Normal file
60
.github/workflows/azure-static-web-apps-mango-bush-01e607f10.yml
vendored
Normal file
@ -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_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||
import 'package:syncrow_web/pages/routiens/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/routines_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
@ -18,10 +18,6 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
|
||||
),
|
||||
@ -33,8 +29,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
),
|
||||
centerBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
||||
builder: (context, state) {
|
||||
centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -44,20 +39,14 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<SwitchTabsBloc>()
|
||||
.add(const TriggerSwitchTabsEvent(false));
|
||||
.read<RoutineBloc>()
|
||||
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||
},
|
||||
child: Text(
|
||||
'Devices',
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
color:
|
||||
state is SelectedTabState && state.selectedTab == false
|
||||
? ColorsManager.whiteColors
|
||||
: ColorsManager.grayColor,
|
||||
fontWeight: (state is SelectedTabState) &&
|
||||
state.selectedTab == false
|
||||
? FontWeight.w700
|
||||
: FontWeight.w400,
|
||||
color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
|
||||
fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -66,21 +55,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
backgroundColor: null,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<SwitchTabsBloc>()
|
||||
.add(const TriggerSwitchTabsEvent(true));
|
||||
context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true));
|
||||
},
|
||||
child: Text(
|
||||
'Routines',
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
color:
|
||||
(state is SelectedTabState) && state.selectedTab == true
|
||||
? ColorsManager.whiteColors
|
||||
: ColorsManager.grayColor,
|
||||
fontWeight:
|
||||
(state is SelectedTabState) && state.selectedTab == true
|
||||
? FontWeight.w700
|
||||
: FontWeight.w400,
|
||||
color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
|
||||
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -88,13 +69,12 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
);
|
||||
}),
|
||||
rightBody: const NavigateHomeGridView(),
|
||||
scaffoldBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
||||
builder: (context, state) {
|
||||
if (state is SelectedTabState && state.selectedTab) {
|
||||
scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
|
||||
if (state.routineTab) {
|
||||
return const RoutinesView();
|
||||
}
|
||||
if (state is ShowCreateRoutineState && state.showCreateRoutine) {
|
||||
return const CreateNewRoutineView();
|
||||
if (state.createRoutineView) {
|
||||
return CreateNewRoutineView();
|
||||
}
|
||||
|
||||
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||
@ -104,8 +84,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
} else if (deviceState is DeviceManagementLoaded) {
|
||||
return DeviceManagementBody(devices: deviceState.devices);
|
||||
} else if (deviceState is DeviceManagementFiltered) {
|
||||
return DeviceManagementBody(
|
||||
devices: deviceState.filteredDevices);
|
||||
return DeviceManagementBody(devices: deviceState.filteredDevices);
|
||||
} else {
|
||||
return const Center(child: Text('Error fetching Devices'));
|
||||
}
|
||||
|
@ -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_state.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/utils/color_manager.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 {
|
||||
try {
|
||||
var uuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
user = await HomeApi().fetchUserInfo(uuid);
|
||||
emit(HomeInitial());
|
||||
} catch (e) {
|
||||
@ -84,6 +84,8 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
icon: Assets.devicesIcon,
|
||||
active: true,
|
||||
onPress: (context) {
|
||||
BlocProvider.of<RoutineBloc>(context)
|
||||
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||
context.go(RoutesConst.deviceManagementPage);
|
||||
},
|
||||
color: ColorsManager.primaryColor,
|
||||
|
@ -16,6 +16,7 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
};
|
||||
|
||||
EffectPeriodBloc() : super(EffectPeriodState.initial()) {
|
||||
on<InitialEffectPeriodEvent>(_initialEvent);
|
||||
on<SetPeriod>(_onSetPeriod);
|
||||
on<ToggleDay>(_onToggleDay);
|
||||
on<SetCustomTime>(_onSetCustomTime);
|
||||
@ -24,6 +25,15 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
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) {
|
||||
String startTime = '';
|
||||
String endTime = '';
|
||||
|
@ -1,4 +1,5 @@
|
||||
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';
|
||||
|
||||
abstract class EffectPeriodEvent extends Equatable {
|
||||
@ -8,6 +9,15 @@ abstract class EffectPeriodEvent extends Equatable {
|
||||
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 {
|
||||
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 bool isFromThen;
|
||||
final String key;
|
||||
const RemoveDragCard(
|
||||
{required this.index, required this.isFromThen, required this.key});
|
||||
const RemoveDragCard({required this.index, required this.isFromThen, required this.key});
|
||||
@override
|
||||
List<Object> get props => [index, isFromThen, key];
|
||||
}
|
||||
@ -105,12 +104,10 @@ class EffectiveTimePeriodEvent extends RoutineEvent {
|
||||
}
|
||||
|
||||
class CreateAutomationEvent extends RoutineEvent {
|
||||
// final CreateAutomationModel createAutomationModel;
|
||||
final String? automationId;
|
||||
final bool updateAutomation;
|
||||
|
||||
const CreateAutomationEvent({
|
||||
//required this.createAutomationModel,
|
||||
this.automationId,
|
||||
this.updateAutomation = false,
|
||||
});
|
||||
@ -159,21 +156,55 @@ class InitializeRoutineState extends RoutineEvent {
|
||||
}
|
||||
|
||||
class DeleteScene extends RoutineEvent {
|
||||
final String sceneId;
|
||||
final String unitUuid;
|
||||
const DeleteScene({required this.sceneId, required this.unitUuid});
|
||||
const DeleteScene();
|
||||
@override
|
||||
List<Object> get props => [sceneId];
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class DeleteAutomation extends RoutineEvent {
|
||||
final String automationId;
|
||||
final String unitUuid;
|
||||
const DeleteAutomation({required this.automationId, required this.unitUuid});
|
||||
// class DeleteAutomation extends RoutineEvent {
|
||||
// final String automationId;
|
||||
// const DeleteAutomation({required this.automationId});
|
||||
// @override
|
||||
// List<Object> get props => [automationId];
|
||||
// }
|
||||
|
||||
class UpdateScene extends RoutineEvent {
|
||||
const UpdateScene();
|
||||
@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 ClearFunctions extends RoutineEvent {}
|
||||
|
||||
class ResetErrorMessage extends RoutineEvent {}
|
||||
|
@ -21,29 +21,36 @@ class RoutineState extends Equatable {
|
||||
final String? sceneId;
|
||||
final String? automationId;
|
||||
final bool? isUpdate;
|
||||
final List<AllDevicesModel> devices;
|
||||
// final String? automationActionExecutor;
|
||||
final bool routineTab;
|
||||
final bool createRoutineView;
|
||||
|
||||
const RoutineState({
|
||||
this.ifItems = const [],
|
||||
this.thenItems = const [],
|
||||
this.availableCards = const [],
|
||||
this.scenes = const [],
|
||||
this.automations = const [],
|
||||
this.selectedFunctions = const {},
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
this.routineName,
|
||||
this.selectedIcon,
|
||||
this.loadScenesErrorMessage,
|
||||
this.loadAutomationErrorMessage,
|
||||
this.searchText,
|
||||
this.isTabToRun = false,
|
||||
this.isAutomation = false,
|
||||
this.selectedAutomationOperator = 'or',
|
||||
this.effectiveTime,
|
||||
this.sceneId,
|
||||
this.automationId,
|
||||
this.isUpdate,
|
||||
});
|
||||
const RoutineState(
|
||||
{this.ifItems = const [],
|
||||
this.thenItems = const [],
|
||||
this.availableCards = const [],
|
||||
this.scenes = const [],
|
||||
this.automations = const [],
|
||||
this.selectedFunctions = const {},
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
this.routineName,
|
||||
this.selectedIcon,
|
||||
this.loadScenesErrorMessage,
|
||||
this.loadAutomationErrorMessage,
|
||||
this.searchText,
|
||||
this.isTabToRun = false,
|
||||
this.isAutomation = false,
|
||||
this.selectedAutomationOperator = 'or',
|
||||
this.effectiveTime,
|
||||
this.sceneId,
|
||||
this.automationId,
|
||||
this.isUpdate,
|
||||
this.devices = const [],
|
||||
// this.automationActionExecutor,
|
||||
this.routineTab = false,
|
||||
this.createRoutineView = false});
|
||||
|
||||
RoutineState copyWith({
|
||||
List<Map<String, dynamic>>? ifItems,
|
||||
@ -65,31 +72,39 @@ class RoutineState extends Equatable {
|
||||
String? sceneId,
|
||||
String? automationId,
|
||||
bool? isUpdate,
|
||||
List<AllDevicesModel>? devices,
|
||||
// String? automationActionExecutor,
|
||||
TextEditingController? nameController,
|
||||
bool? routineTab,
|
||||
bool? createRoutineView,
|
||||
}) {
|
||||
return RoutineState(
|
||||
ifItems: ifItems ?? this.ifItems,
|
||||
thenItems: thenItems ?? this.thenItems,
|
||||
scenes: scenes ?? this.scenes,
|
||||
automations: automations ?? this.automations,
|
||||
selectedFunctions: selectedFunctions ?? this.selectedFunctions,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
routineName: routineName ?? this.routineName,
|
||||
selectedIcon: selectedIcon ?? this.selectedIcon,
|
||||
loadScenesErrorMessage:
|
||||
loadScenesErrorMessage ?? this.loadScenesErrorMessage,
|
||||
loadAutomationErrorMessage:
|
||||
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
|
||||
searchText: searchText ?? this.searchText,
|
||||
isTabToRun: isTabToRun ?? this.isTabToRun,
|
||||
isAutomation: isAutomation ?? this.isAutomation,
|
||||
selectedAutomationOperator:
|
||||
selectedAutomationOperator ?? this.selectedAutomationOperator,
|
||||
effectiveTime: effectiveTime ?? this.effectiveTime,
|
||||
sceneId: sceneId ?? this.sceneId,
|
||||
automationId: automationId ?? this.automationId,
|
||||
isUpdate: isUpdate ?? this.isUpdate,
|
||||
);
|
||||
ifItems: ifItems ?? this.ifItems,
|
||||
thenItems: thenItems ?? this.thenItems,
|
||||
scenes: scenes ?? this.scenes,
|
||||
automations: automations ?? this.automations,
|
||||
selectedFunctions: selectedFunctions ?? this.selectedFunctions,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
routineName: routineName ?? this.routineName,
|
||||
selectedIcon: selectedIcon ?? this.selectedIcon,
|
||||
loadScenesErrorMessage:
|
||||
loadScenesErrorMessage ?? this.loadScenesErrorMessage,
|
||||
loadAutomationErrorMessage:
|
||||
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
|
||||
searchText: searchText ?? this.searchText,
|
||||
isTabToRun: isTabToRun ?? this.isTabToRun,
|
||||
isAutomation: isAutomation ?? this.isAutomation,
|
||||
selectedAutomationOperator:
|
||||
selectedAutomationOperator ?? this.selectedAutomationOperator,
|
||||
effectiveTime: effectiveTime ?? this.effectiveTime,
|
||||
sceneId: sceneId ?? this.sceneId,
|
||||
automationId: automationId ?? this.automationId,
|
||||
isUpdate: isUpdate ?? this.isUpdate,
|
||||
devices: devices ?? this.devices,
|
||||
// automationActionExecutor: automationActionExecutor ?? this.automationActionExecutor,
|
||||
routineTab: routineTab ?? this.routineTab,
|
||||
createRoutineView: createRoutineView ?? this.createRoutineView);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -112,6 +127,10 @@ class RoutineState extends Equatable {
|
||||
effectiveTime,
|
||||
sceneId,
|
||||
automationId,
|
||||
isUpdate
|
||||
isUpdate,
|
||||
devices,
|
||||
// automationActionExecutor,
|
||||
routineTab,
|
||||
createRoutineView
|
||||
];
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SaveRoutineHelper {
|
||||
static Future<bool?> showSaveRoutineDialog(BuildContext context) async {
|
||||
return showDialog<bool?>(
|
||||
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
@ -98,18 +101,29 @@ class SaveRoutineHelper {
|
||||
final functions =
|
||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||
return ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
item['imagePath'],
|
||||
width: 22,
|
||||
height: 22,
|
||||
leading: item['type'] == 'tap_to_run' || item['type'] == 'scene'
|
||||
? Image.memory(
|
||||
base64Decode(item['icon']),
|
||||
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(
|
||||
children: functions
|
||||
.map((f) => Text(
|
||||
'${f.operationName}: ${f.value}, ',
|
||||
style: const TextStyle(
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor, fontSize: 8),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
@ -124,24 +138,33 @@ class SaveRoutineHelper {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.errorMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
state.errorMessage!,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: Text(
|
||||
// state.errorMessage!,
|
||||
// style: const TextStyle(color: Colors.red),
|
||||
// ),
|
||||
// ),
|
||||
DialogFooter(
|
||||
onCancel: () => Navigator.pop(context, false),
|
||||
onConfirm: () {
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: () async {
|
||||
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 {
|
||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||
if (state.isUpdate ?? false) {
|
||||
context.read<RoutineBloc>().add(const UpdateScene());
|
||||
} else {
|
||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||
}
|
||||
}
|
||||
|
||||
Navigator.pop(context, true);
|
||||
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
|
||||
Navigator.pop(context);
|
||||
// }
|
||||
},
|
||||
isConfirmEnabled: true,
|
||||
),
|
||||
|
@ -17,7 +17,7 @@ class CreateAutomationModel {
|
||||
required this.actions,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> toMap([String? automationId]) {
|
||||
return {
|
||||
'spaceUuid': spaceUuid,
|
||||
'automationName': automationName,
|
||||
@ -41,7 +41,7 @@ class CreateAutomationModel {
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
String toJson(String? automationId) => json.encode(toMap(automationId));
|
||||
|
||||
factory CreateAutomationModel.fromJson(String source) =>
|
||||
CreateAutomationModel.fromMap(json.decode(source));
|
||||
@ -92,7 +92,7 @@ class Condition {
|
||||
return {
|
||||
'code': code,
|
||||
'entityId': entityId,
|
||||
'entityType': entityType,
|
||||
'entityType': 'device_report',
|
||||
'expr': expr.toMap(),
|
||||
};
|
||||
}
|
||||
@ -137,11 +137,13 @@ class ConditionExpr {
|
||||
|
||||
class AutomationAction {
|
||||
String entityId;
|
||||
String? actionType;
|
||||
String actionExecutor;
|
||||
ExecutorProperty? executorProperty;
|
||||
|
||||
AutomationAction({
|
||||
required this.entityId,
|
||||
this.actionType,
|
||||
required this.actionExecutor,
|
||||
this.executorProperty,
|
||||
});
|
||||
@ -150,12 +152,15 @@ class AutomationAction {
|
||||
return {
|
||||
'entityId': entityId,
|
||||
'actionExecutor': actionExecutor,
|
||||
'executorProperty': executorProperty?.toMap(),
|
||||
if (executorProperty != null)
|
||||
'executorProperty': executorProperty?.toMap(),
|
||||
'actionType': actionType
|
||||
};
|
||||
}
|
||||
|
||||
factory AutomationAction.fromMap(Map<String, dynamic> map) {
|
||||
return AutomationAction(
|
||||
actionType: map['actionType'],
|
||||
entityId: map['entityId'] ?? '',
|
||||
actionExecutor: map['actionExecutor'] ?? '',
|
||||
executorProperty: map['executorProperty'] != null
|
||||
|
@ -95,10 +95,12 @@ class CreateSceneModel {
|
||||
|
||||
class CreateSceneAction {
|
||||
String entityId;
|
||||
String? actionType;
|
||||
String actionExecutor;
|
||||
CreateSceneExecutorProperty? executorProperty;
|
||||
|
||||
CreateSceneAction({
|
||||
this.actionType,
|
||||
required this.entityId,
|
||||
required this.actionExecutor,
|
||||
required this.executorProperty,
|
||||
@ -110,6 +112,7 @@ class CreateSceneAction {
|
||||
CreateSceneExecutorProperty? executorProperty,
|
||||
}) {
|
||||
return CreateSceneAction(
|
||||
actionType: actionType ?? this.actionType,
|
||||
entityId: entityId ?? this.entityId,
|
||||
actionExecutor: actionExecutor ?? this.actionExecutor,
|
||||
executorProperty: executorProperty ?? this.executorProperty,
|
||||
@ -125,6 +128,7 @@ class CreateSceneAction {
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
"actionType": actionType,
|
||||
'entityId': entityId,
|
||||
'actionExecutor': actionExecutor,
|
||||
};
|
||||
@ -133,6 +137,7 @@ class CreateSceneAction {
|
||||
|
||||
factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
|
||||
return CreateSceneAction(
|
||||
actionType: map['actionType'],
|
||||
entityId: map['entityId'] ?? '',
|
||||
actionExecutor: map['actionExecutor'] ?? '',
|
||||
executorProperty:
|
||||
|
@ -13,6 +13,8 @@ class RoutineDetailsModel {
|
||||
final EffectiveTime? effectiveTime;
|
||||
final List<RoutineCondition>? conditions;
|
||||
final String? type;
|
||||
final String? sceneId;
|
||||
final String? automationId;
|
||||
|
||||
RoutineDetailsModel({
|
||||
required this.spaceUuid,
|
||||
@ -24,6 +26,8 @@ class RoutineDetailsModel {
|
||||
this.effectiveTime,
|
||||
this.conditions,
|
||||
this.type,
|
||||
this.sceneId,
|
||||
this.automationId,
|
||||
});
|
||||
|
||||
// Convert to CreateSceneModel
|
||||
@ -44,8 +48,7 @@ class RoutineDetailsModel {
|
||||
spaceUuid: spaceUuid,
|
||||
automationName: name,
|
||||
decisionExpr: decisionExpr,
|
||||
effectiveTime:
|
||||
effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
|
||||
effectiveTime: effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
|
||||
conditions: conditions?.map((c) => c.toCondition()).toList() ?? [],
|
||||
actions: actions.map((a) => a.toAutomationAction()).toList(),
|
||||
);
|
||||
@ -57,12 +60,13 @@ class RoutineDetailsModel {
|
||||
'name': name,
|
||||
'decisionExpr': decisionExpr,
|
||||
'actions': actions.map((x) => x.toMap()).toList(),
|
||||
if (iconId != null) 'iconId': iconId,
|
||||
if (iconId != null) 'iconUuid': iconId,
|
||||
if (showInDevice != null) 'showInDevice': showInDevice,
|
||||
if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(),
|
||||
if (conditions != null)
|
||||
'conditions': conditions!.map((x) => x.toMap()).toList(),
|
||||
if (conditions != null) 'conditions': conditions!.map((x) => x.toMap()).toList(),
|
||||
if (type != null) 'type': type,
|
||||
if (sceneId != null) 'sceneId': sceneId,
|
||||
if (automationId != null) 'automationId': automationId,
|
||||
};
|
||||
}
|
||||
|
||||
@ -74,16 +78,16 @@ class RoutineDetailsModel {
|
||||
actions: List<RoutineAction>.from(
|
||||
map['actions']?.map((x) => RoutineAction.fromMap(x)) ?? [],
|
||||
),
|
||||
iconId: map['iconId'],
|
||||
iconId: map['iconUuid'],
|
||||
showInDevice: map['showInDevice'],
|
||||
effectiveTime: map['effectiveTime'] != null
|
||||
? EffectiveTime.fromMap(map['effectiveTime'])
|
||||
: null,
|
||||
effectiveTime:
|
||||
map['effectiveTime'] != null ? EffectiveTime.fromMap(map['effectiveTime']) : null,
|
||||
conditions: map['conditions'] != null
|
||||
? List<RoutineCondition>.from(
|
||||
map['conditions'].map((x) => RoutineCondition.fromMap(x)))
|
||||
? List<RoutineCondition>.from(map['conditions'].map((x) => RoutineCondition.fromMap(x)))
|
||||
: null,
|
||||
type: map['type'],
|
||||
sceneId: map['sceneId'],
|
||||
automationId: map['automationId'],
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,13 +100,20 @@ class RoutineDetailsModel {
|
||||
class RoutineAction {
|
||||
final String entityId;
|
||||
final String actionExecutor;
|
||||
final String? name;
|
||||
final RoutineExecutorProperty? executorProperty;
|
||||
final String productType;
|
||||
final String? type;
|
||||
final String? icon;
|
||||
|
||||
RoutineAction({
|
||||
required this.entityId,
|
||||
required this.actionExecutor,
|
||||
this.executorProperty,
|
||||
});
|
||||
RoutineAction(
|
||||
{required this.entityId,
|
||||
required this.actionExecutor,
|
||||
required this.productType,
|
||||
this.executorProperty,
|
||||
this.name,
|
||||
this.type,
|
||||
this.icon});
|
||||
|
||||
CreateSceneAction toCreateSceneAction() {
|
||||
return CreateSceneAction(
|
||||
@ -124,19 +135,23 @@ class RoutineAction {
|
||||
return {
|
||||
'entityId': entityId,
|
||||
'actionExecutor': actionExecutor,
|
||||
if (executorProperty != null)
|
||||
'executorProperty': executorProperty!.toMap(),
|
||||
if (type != null) 'type': type,
|
||||
if (name != null) 'name': name,
|
||||
if (executorProperty != null) 'executorProperty': executorProperty!.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
factory RoutineAction.fromMap(Map<String, dynamic> map) {
|
||||
return RoutineAction(
|
||||
entityId: map['entityId'] ?? '',
|
||||
actionExecutor: map['actionExecutor'] ?? '',
|
||||
executorProperty: map['executorProperty'] != null
|
||||
? RoutineExecutorProperty.fromMap(map['executorProperty'])
|
||||
: null,
|
||||
);
|
||||
entityId: map['entityId'] ?? '',
|
||||
actionExecutor: map['actionExecutor'] ?? '',
|
||||
productType: map['productType'] ?? '',
|
||||
name: map['name'] ?? '',
|
||||
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 entityType;
|
||||
final RoutineConditionExpr expr;
|
||||
final String productType;
|
||||
|
||||
RoutineCondition({
|
||||
required this.code,
|
||||
required this.entityId,
|
||||
required this.entityType,
|
||||
required this.expr,
|
||||
required this.productType,
|
||||
});
|
||||
|
||||
Condition toCondition() {
|
||||
@ -221,6 +238,7 @@ class RoutineCondition {
|
||||
entityId: map['entityId'] ?? '',
|
||||
entityType: map['entityType'] ?? '',
|
||||
expr: RoutineConditionExpr.fromMap(map['expr']),
|
||||
productType: map['productType'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class CreateNewRoutineView extends StatelessWidget {
|
||||
this.routineId,
|
||||
this.isScene = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
@ -1,20 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/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/routine_view_card.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class RoutinesView extends StatelessWidget {
|
||||
class RoutinesView extends StatefulWidget {
|
||||
const RoutinesView({super.key});
|
||||
|
||||
@override
|
||||
State<RoutinesView> createState() => _RoutinesViewState();
|
||||
}
|
||||
|
||||
class _RoutinesViewState extends State<RoutinesView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
if (state is ShowCreateRoutineState && state.showCreateRoutine) {
|
||||
if (state.createRoutineView) {
|
||||
return const CreateNewRoutineView();
|
||||
}
|
||||
return Padding(
|
||||
@ -36,12 +46,12 @@ class RoutinesView extends StatelessWidget {
|
||||
),
|
||||
RoutineViewCard(
|
||||
onTap: () {
|
||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(true),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
(ResetRoutineState()),
|
||||
);
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||
);
|
||||
},
|
||||
icon: Icons.add,
|
||||
textString: '',
|
||||
|
88
lib/pages/routiens/widgets/delete_scene.dart
Normal file
88
lib/pages/routiens/widgets/delete_scene.dart
Normal file
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -32,15 +32,27 @@ class DraggableCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
final deviceFunctions =
|
||||
state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
|
||||
final deviceFunctions = 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>>(
|
||||
data: deviceData,
|
||||
feedback: Transform.rotate(
|
||||
angle: -0.1,
|
||||
child:
|
||||
_buildCardContent(context, deviceFunctions, padding: padding),
|
||||
child: _buildCardContent(context, deviceFunctions, padding: padding),
|
||||
),
|
||||
childWhenDragging: _buildGreyContainer(),
|
||||
child: _buildCardContent(context, deviceFunctions, padding: padding),
|
||||
@ -49,8 +61,7 @@ class DraggableCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCardContent(
|
||||
BuildContext context, List<DeviceFunctionData> deviceFunctions,
|
||||
Widget _buildCardContent(BuildContext context, List<DeviceFunctionData> deviceFunctions,
|
||||
{EdgeInsetsGeometry? padding}) {
|
||||
return Stack(
|
||||
children: [
|
||||
@ -79,19 +90,19 @@ class DraggableCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: imagePath.contains('.svg')
|
||||
? SvgPicture.asset(
|
||||
imagePath,
|
||||
child: deviceData['type'] == 'tap_to_run' || deviceData['type'] == 'scene'
|
||||
? Image.memory(
|
||||
base64Decode(deviceData['icon']),
|
||||
)
|
||||
: Image.memory(
|
||||
base64Decode(imagePath),
|
||||
: SvgPicture.asset(
|
||||
imagePath,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Text(
|
||||
title,
|
||||
deviceData['title'] ?? deviceData['name'] ?? title,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
@ -104,7 +115,6 @@ class DraggableCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
if (deviceFunctions.isNotEmpty)
|
||||
// const Divider(height: 1),
|
||||
...deviceFunctions.map((function) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -149,8 +159,7 @@ class DraggableCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
String _formatFunctionValue(DeviceFunctionData function) {
|
||||
if (function.functionCode == 'temp_set' ||
|
||||
function.functionCode == 'temp_current') {
|
||||
if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
|
||||
return '${(function.value / 10).toStringAsFixed(0)}°C';
|
||||
} else if (function.functionCode.contains('countdown')) {
|
||||
final seconds = function.value.toInt();
|
||||
|
@ -11,8 +11,7 @@ class FetchRoutineScenesAutomation extends StatefulWidget {
|
||||
const FetchRoutineScenesAutomation({super.key});
|
||||
|
||||
@override
|
||||
State<FetchRoutineScenesAutomation> createState() =>
|
||||
_FetchRoutineScenesState();
|
||||
State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
|
||||
}
|
||||
|
||||
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
||||
@ -67,49 +66,23 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
||||
padding: EdgeInsets.only(
|
||||
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
RoutineViewCard(
|
||||
onTap: () {},
|
||||
textString: state.scenes[index].name,
|
||||
icon: state.scenes[index].icon ??
|
||||
Assets.logoHorizontal,
|
||||
isFromScenes: true,
|
||||
iconInBytes:
|
||||
state.scenes[index].iconInBytes,
|
||||
),
|
||||
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: RoutineViewCard(
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetSceneDetails(
|
||||
sceneId: state.scenes[index].id,
|
||||
isTabToRun: true,
|
||||
isUpdate: true,
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(Icons.delete,
|
||||
size: 15,
|
||||
color: ColorsManager.grayColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
textString: state.scenes[index].name,
|
||||
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(
|
||||
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
RoutineViewCard(
|
||||
onTap: () {},
|
||||
textString: state.automations[index].name,
|
||||
icon: state.automations[index].icon ??
|
||||
Assets.automation,
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: InkWell(
|
||||
onTap: () =>
|
||||
context.read<RoutineBloc>().add(
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: RoutineViewCard(
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetAutomationDetails(
|
||||
automationId: state.automations[index].id,
|
||||
isAutomation: true,
|
||||
isUpdate: true),
|
||||
);
|
||||
},
|
||||
textString: state.automations[index].name,
|
||||
icon: state.automations[index].icon ?? Assets.automation,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
|
||||
@ -10,68 +9,67 @@ class RoutineDevices extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
|
||||
child: BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||
builder: (context, state) {
|
||||
if (state is DeviceManagementLoaded) {
|
||||
List<AllDevicesModel> deviceList = state.devices
|
||||
.where((device) =>
|
||||
device.productType == 'AC' ||
|
||||
device.productType == '1G' ||
|
||||
device.productType == '2G' ||
|
||||
device.productType == '3G')
|
||||
.toList();
|
||||
|
||||
// Provide the RoutineBloc to the child widgets
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, routineState) {
|
||||
return Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: deviceList.asMap().entries.map((entry) {
|
||||
final device = entry.value;
|
||||
if (routineState.searchText != null && routineState.searchText!.isNotEmpty) {
|
||||
return device.name!
|
||||
.toLowerCase()
|
||||
.contains(routineState.searchText!.toLowerCase())
|
||||
? DraggableCard(
|
||||
imagePath: device.getDefaultIcon(device.productType),
|
||||
title: device.name ?? '',
|
||||
deviceData: {
|
||||
'device': device,
|
||||
'imagePath': device.getDefaultIcon(device.productType),
|
||||
'title': device.name ?? '',
|
||||
'deviceId': device.uuid,
|
||||
'productType': device.productType,
|
||||
'functions': device.functions,
|
||||
'uniqueCustomId': '',
|
||||
},
|
||||
)
|
||||
: Container();
|
||||
} else {
|
||||
return DraggableCard(
|
||||
imagePath: device.getDefaultIcon(device.productType),
|
||||
title: device.name ?? '',
|
||||
deviceData: {
|
||||
'device': device,
|
||||
'imagePath': device.getDefaultIcon(device.productType),
|
||||
'title': device.name ?? '',
|
||||
'deviceId': device.uuid,
|
||||
'productType': device.productType,
|
||||
'functions': device.functions,
|
||||
'uniqueCustomId': '',
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (state.devices.isEmpty) {
|
||||
return const Center(child: Text('No devices found'));
|
||||
}
|
||||
});
|
||||
|
||||
List<AllDevicesModel> deviceList = state.devices
|
||||
.where((device) =>
|
||||
device.productType == 'AC' ||
|
||||
device.productType == '1G' ||
|
||||
device.productType == '2G' ||
|
||||
device.productType == '3G')
|
||||
.toList();
|
||||
|
||||
return Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: deviceList.asMap().entries.map((entry) {
|
||||
final device = entry.value;
|
||||
if (state.searchText != null && state.searchText!.isNotEmpty) {
|
||||
return device.name!
|
||||
.toLowerCase()
|
||||
.contains(state.searchText!.toLowerCase())
|
||||
? DraggableCard(
|
||||
imagePath: device.getDefaultIcon(device.productType),
|
||||
title: device.name ?? '',
|
||||
deviceData: {
|
||||
'device': device,
|
||||
'imagePath': device.getDefaultIcon(device.productType),
|
||||
'title': device.name ?? '',
|
||||
'deviceId': device.uuid,
|
||||
'productType': device.productType,
|
||||
'functions': device.functions,
|
||||
'uniqueCustomId': '',
|
||||
},
|
||||
)
|
||||
: Container();
|
||||
} else {
|
||||
return DraggableCard(
|
||||
imagePath: device.getDefaultIcon(device.productType),
|
||||
title: device.name ?? '',
|
||||
deviceData: {
|
||||
'device': device,
|
||||
'imagePath': device.getDefaultIcon(device.productType),
|
||||
'title': device.name ?? '',
|
||||
'deviceId': device.uuid,
|
||||
'productType': device.productType,
|
||||
'functions': device.functions,
|
||||
'uniqueCustomId': '',
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,20 +11,36 @@ class AutomationDialog extends StatefulWidget {
|
||||
final String automationName;
|
||||
final String automationId;
|
||||
final String uniqueCustomId;
|
||||
final String? passedAutomationActionExecutor;
|
||||
|
||||
const AutomationDialog({
|
||||
super.key,
|
||||
required this.automationName,
|
||||
required this.automationId,
|
||||
required this.uniqueCustomId,
|
||||
this.passedAutomationActionExecutor,
|
||||
});
|
||||
|
||||
@override
|
||||
_AutomationDialogState createState() => _AutomationDialogState();
|
||||
State<AutomationDialog> createState() => _AutomationDialogState();
|
||||
}
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
@ -41,26 +57,25 @@ class _AutomationDialogState extends State<AutomationDialog> {
|
||||
ListTile(
|
||||
leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24),
|
||||
title: const Text('Enable'),
|
||||
trailing: Radio<bool>(
|
||||
value: true,
|
||||
groupValue: _isEnabled,
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
_isEnabled = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
trailing: Radio<String?>(
|
||||
value: 'rule_enable',
|
||||
groupValue: selectedAutomationActionExecutor,
|
||||
onChanged: (String? value) {
|
||||
setState(() {
|
||||
selectedAutomationActionExecutor = 'rule_enable';
|
||||
});
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
leading:
|
||||
SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
|
||||
title: const Text('Disable'),
|
||||
trailing: Radio<bool>(
|
||||
value: false,
|
||||
groupValue: _isEnabled,
|
||||
onChanged: (bool? value) {
|
||||
trailing: Radio<String?>(
|
||||
value: 'rule_disable',
|
||||
groupValue: selectedAutomationActionExecutor,
|
||||
onChanged: (String? value) {
|
||||
setState(() {
|
||||
_isEnabled = value!;
|
||||
selectedAutomationActionExecutor = 'rule_disable';
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -68,23 +83,25 @@ class _AutomationDialogState extends State<AutomationDialog> {
|
||||
const SizedBox(height: 16),
|
||||
DialogFooter(
|
||||
onConfirm: () {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
[
|
||||
DeviceFunctionData(
|
||||
entityId: widget.automationId,
|
||||
functionCode: 'automation',
|
||||
value: _isEnabled ? 'rule_enable' : 'rule_disable',
|
||||
operationName: 'Automation',
|
||||
),
|
||||
],
|
||||
widget.uniqueCustomId,
|
||||
),
|
||||
);
|
||||
if (selectedAutomationActionExecutor != null) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
[
|
||||
DeviceFunctionData(
|
||||
entityId: widget.automationId,
|
||||
functionCode: 'automation',
|
||||
value: selectedAutomationActionExecutor,
|
||||
operationName: 'Automation',
|
||||
),
|
||||
],
|
||||
widget.uniqueCustomId,
|
||||
),
|
||||
);
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
onCancel: () => Navigator.of(context).pop(false),
|
||||
isConfirmEnabled: true,
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
isConfirmEnabled: selectedAutomationActionExecutor != null,
|
||||
dialogWidth: 400,
|
||||
),
|
||||
],
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
@ -49,11 +48,11 @@ class DiscardDialog {
|
||||
onConfirm: () {
|
||||
context.read<RoutineBloc>().add(ResetRoutineState());
|
||||
Navigator.pop(context);
|
||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(false),
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: false),
|
||||
);
|
||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
||||
const TriggerSwitchTabsEvent(true),
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const TriggerSwitchTabsEvent(isRoutineTab: true),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/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/icon_model.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/utils/color_manager.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@ -22,11 +24,18 @@ class SettingHelper {
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final isAutomation = context.read<RoutineBloc>().state.isAutomation;
|
||||
final effectiveTime = context.read<RoutineBloc>().state.effectiveTime;
|
||||
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) => EffectPeriodBloc(),
|
||||
),
|
||||
if (effectiveTime != null)
|
||||
BlocProvider(
|
||||
create: (_) => EffectPeriodBloc()..add(InitialEffectPeriodEvent(effectiveTime)),
|
||||
),
|
||||
if (effectiveTime == null)
|
||||
BlocProvider(
|
||||
create: (_) => EffectPeriodBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? ''))),
|
||||
],
|
||||
@ -44,7 +53,7 @@ class SettingHelper {
|
||||
}
|
||||
return Container(
|
||||
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
|
||||
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 300,
|
||||
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 350,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@ -167,6 +176,9 @@ class SettingHelper {
|
||||
fontSize: 14)),
|
||||
],
|
||||
),
|
||||
if (context.read<RoutineBloc>().state.isUpdate ??
|
||||
false)
|
||||
const DeleteSceneWidget()
|
||||
],
|
||||
)),
|
||||
],
|
||||
@ -284,6 +296,9 @@ class SettingHelper {
|
||||
fontSize: 14)),
|
||||
],
|
||||
),
|
||||
if (context.read<RoutineBloc>().state.isUpdate ??
|
||||
false)
|
||||
const DeleteSceneWidget()
|
||||
],
|
||||
)),
|
||||
],
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/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/helper/save_routine_helper.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/style.dart';
|
||||
|
||||
class RoutineSearchAndButtons extends StatelessWidget {
|
||||
class RoutineSearchAndButtons extends StatefulWidget {
|
||||
const RoutineSearchAndButtons({
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
_nameController.text = state.routineName ?? '';
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
state.errorMessage ?? '',
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
@ -37,24 +63,6 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
constraints: BoxConstraints(
|
||||
maxWidth:
|
||||
constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
|
||||
// child: StatefulTextField(
|
||||
// title: 'Routine Name',
|
||||
// initialValue: state.routineName ?? '',
|
||||
// height: 40,
|
||||
// // controller: TextEditingController(),
|
||||
// hintText: 'Please enter the name',
|
||||
// boxDecoration: containerWhiteDecoration,
|
||||
// elevation: 0,
|
||||
// borderRadius: 15,
|
||||
// isRequired: true,
|
||||
// width: 450,
|
||||
// onSubmitted: (value) {
|
||||
// // context.read<RoutineBloc>().add(SetRoutineName(value));
|
||||
// },
|
||||
// onChanged: (value) {
|
||||
// context.read<RoutineBloc>().add(SetRoutineName(value));
|
||||
// },
|
||||
// ),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -81,7 +89,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
child: TextFormField(
|
||||
style: context.textTheme.bodyMedium!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
initialValue: state.routineName,
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: context.textTheme.bodyMedium!
|
||||
@ -90,8 +98,10 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onChanged: (value) {
|
||||
context.read<RoutineBloc>().add(SetRoutineName(value));
|
||||
onTapOutside: (_) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(SetRoutineName(_nameController.text));
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
@ -115,6 +125,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
? () async {
|
||||
final result = await SettingHelper.showSettingDialog(
|
||||
context: context,
|
||||
iconId: state.selectedIcon ?? '',
|
||||
);
|
||||
if (result != null) {
|
||||
context
|
||||
@ -208,16 +219,18 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
);
|
||||
return;
|
||||
}
|
||||
final result =
|
||||
await SaveRoutineHelper.showSaveRoutineDialog(context);
|
||||
if (result != null && result) {
|
||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(false),
|
||||
);
|
||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
||||
const TriggerSwitchTabsEvent(true),
|
||||
);
|
||||
}
|
||||
// final result =
|
||||
// await
|
||||
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||
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,
|
||||
elevation: 0,
|
||||
@ -249,8 +262,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
onPressed: state.isAutomation || state.isTabToRun
|
||||
? () async {
|
||||
final result = await SettingHelper.showSettingDialog(
|
||||
context: context,
|
||||
);
|
||||
context: context, iconId: state.selectedIcon ?? '');
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddSelectedIcon(result));
|
||||
}
|
||||
@ -301,7 +313,7 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
width: 200,
|
||||
child: Center(
|
||||
child: DefaultButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
@ -335,7 +347,18 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// final result =
|
||||
// await
|
||||
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||
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,
|
||||
elevation: 0,
|
||||
|
@ -26,73 +26,88 @@ class ThenContainer extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('THEN',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: List.generate(
|
||||
state.thenItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (state.thenItems[index]['deviceId'] ==
|
||||
'delay') {
|
||||
final result = await DelayHelper
|
||||
.showDelayPickerDialog(
|
||||
state.isLoading && state.isUpdate == true
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: List.generate(
|
||||
state.thenItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (state.thenItems[index]['deviceId'] == 'delay') {
|
||||
final result = await DelayHelper.showDelayPickerDialog(
|
||||
context, state.thenItems[index]);
|
||||
|
||||
if (result != null) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToThenContainer({
|
||||
...state.thenItems[index],
|
||||
'imagePath': Assets.delay,
|
||||
'title': 'Delay',
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer({
|
||||
...state.thenItems[index],
|
||||
'imagePath': Assets.delay,
|
||||
'title': 'Delay',
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await DeviceDialogHelper
|
||||
.showDeviceDialog(
|
||||
if (state.thenItems[index]['type'] == 'automation') {
|
||||
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],
|
||||
removeComparetors: true);
|
||||
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToThenContainer(
|
||||
state.thenItems[index]));
|
||||
} else if (!['AC', '1G', '2G', '3G']
|
||||
.contains(state.thenItems[index]
|
||||
['productType'])) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToThenContainer(
|
||||
state.thenItems[index]));
|
||||
}
|
||||
},
|
||||
child: DraggableCard(
|
||||
imagePath: state.thenItems[index]
|
||||
['imagePath'] ??
|
||||
'',
|
||||
title:
|
||||
state.thenItems[index]['title'] ?? '',
|
||||
deviceData: state.thenItems[index],
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 8),
|
||||
isFromThen: true,
|
||||
isFromIf: false,
|
||||
onRemove: () {
|
||||
context.read<RoutineBloc>().add(
|
||||
RemoveDragCard(
|
||||
if (result != null) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToThenContainer(state.thenItems[index]));
|
||||
} else if (!['AC', '1G', '2G', '3G']
|
||||
.contains(state.thenItems[index]['productType'])) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToThenContainer(state.thenItems[index]));
|
||||
}
|
||||
},
|
||||
child: DraggableCard(
|
||||
imagePath: state.thenItems[index]['imagePath'] ?? '',
|
||||
title: state.thenItems[index]['title'] ?? '',
|
||||
deviceData: state.thenItems[index],
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
|
||||
isFromThen: true,
|
||||
isFromIf: false,
|
||||
onRemove: () {
|
||||
context.read<RoutineBloc>().add(RemoveDragCard(
|
||||
index: index,
|
||||
isFromThen: true,
|
||||
key: state.thenItems[index]
|
||||
['uniqueCustomId']));
|
||||
},
|
||||
),
|
||||
))),
|
||||
key: state.thenItems[index]['uniqueCustomId']));
|
||||
},
|
||||
),
|
||||
))),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -108,7 +123,18 @@ class ThenContainer extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.automationId == mutableData['deviceId'] ||
|
||||
state.sceneId == mutableData['deviceId']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mutableData['type'] == 'automation') {
|
||||
int index =
|
||||
state.thenItems.indexWhere((item) => item['deviceId'] == mutableData['deviceId']);
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AutomationDialog(
|
||||
@ -129,9 +155,14 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
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({
|
||||
...mutableData,
|
||||
'imagePath': Assets.logo,
|
||||
'imagePath': mutableData['imagePath'] ?? Assets.logo,
|
||||
'title': mutableData['name'],
|
||||
}));
|
||||
|
||||
@ -143,8 +174,7 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (mutableData['deviceId'] == 'delay') {
|
||||
final result =
|
||||
await DelayHelper.showDelayPickerDialog(context, mutableData);
|
||||
final result = await DelayHelper.showDelayPickerDialog(context, mutableData);
|
||||
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer({
|
||||
@ -156,13 +186,11 @@ class ThenContainer extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await DeviceDialogHelper.showDeviceDialog(
|
||||
context, mutableData,
|
||||
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData,
|
||||
removeComparetors: true);
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
} else if (!['AC', '1G', '2G', '3G']
|
||||
.contains(mutableData['productType'])) {
|
||||
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
}
|
||||
},
|
||||
|
@ -1,26 +1,31 @@
|
||||
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/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.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/space_mana_api.dart';
|
||||
|
||||
class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||
class SpaceManagementBloc
|
||||
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||
final CommunitySpaceManagementApi _api;
|
||||
final ProductApi _productApi;
|
||||
|
||||
List<ProductModel>? _cachedProducts;
|
||||
|
||||
SpaceManagementBloc(this._api, this._productApi) : super(SpaceManagementInitial()) {
|
||||
SpaceManagementBloc(this._api, this._productApi)
|
||||
: super(SpaceManagementInitial()) {
|
||||
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
||||
on<UpdateSpacePositionEvent>(_onUpdateSpacePosition);
|
||||
on<CreateCommunityEvent>(_onCreateCommunity);
|
||||
on<SaveSpacesEvent>(_onSaveSpaces);
|
||||
on<FetchProductsEvent>(_onFetchProducts);
|
||||
on<SelectCommunityEvent>(_onSelectCommunity);
|
||||
on<DeleteCommunityEvent>(_onCommunityDelete);
|
||||
on<UpdateCommunityEvent>(_onUpdateCommunity);
|
||||
on<SaveSpacesEvent>(_onSaveSpaces);
|
||||
on<FetchProductsEvent>(_onFetchProducts);
|
||||
on<SelectSpaceEvent>(_onSelectSpace);
|
||||
on<NewCommunityEvent>(_onNewCommunity);
|
||||
}
|
||||
|
||||
void _onUpdateCommunity(
|
||||
@ -30,22 +35,23 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
final previousState = state;
|
||||
try {
|
||||
emit(SpaceManagementLoading());
|
||||
final success = await _api.updateCommunity(event.communityUuid, event.name);
|
||||
final success =
|
||||
await _api.updateCommunity(event.communityUuid, event.name);
|
||||
if (success) {
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities);
|
||||
for(var community in updatedCommunities){
|
||||
if(community.uuid == event.communityUuid){
|
||||
final updatedCommunities =
|
||||
List<CommunityModel>.from(previousState.communities);
|
||||
for (var community in updatedCommunities) {
|
||||
if (community.uuid == event.communityUuid) {
|
||||
community.name = event.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: updatedCommunities,
|
||||
products: previousState.products,
|
||||
selectedCommunity: previousState.selectedCommunity,
|
||||
));
|
||||
|
||||
communities: updatedCommunities,
|
||||
products: previousState.products,
|
||||
selectedCommunity: previousState.selectedCommunity,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
emit(const SpaceManagementError('Failed to update the community.'));
|
||||
@ -55,40 +61,61 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
}
|
||||
}
|
||||
|
||||
void _onloadProducts() async {
|
||||
if (_cachedProducts == null) {
|
||||
final products = await _productApi.fetchProducts();
|
||||
_cachedProducts = products;
|
||||
}
|
||||
}
|
||||
|
||||
void _onFetchProducts(
|
||||
FetchProductsEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) async {
|
||||
if (_cachedProducts != null) {
|
||||
// Products are already cached, no need to fetch again
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final products = await _productApi.fetchProducts();
|
||||
_cachedProducts = products; // Cache the products locally
|
||||
_onloadProducts();
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error fetching products: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(
|
||||
String communityUuid) async {
|
||||
return await _api.getSpaceHierarchy(communityUuid);
|
||||
}
|
||||
|
||||
void _onNewCommunity(
|
||||
NewCommunityEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) {
|
||||
try {
|
||||
if (event.communities.isEmpty) {
|
||||
emit(const SpaceManagementError('No communities provided.'));
|
||||
return;
|
||||
}
|
||||
|
||||
emit(BlankState(
|
||||
communities: event.communities,
|
||||
products: _cachedProducts ?? [],
|
||||
));
|
||||
} catch (error) {
|
||||
emit(SpaceManagementError('Error loading communities: $error'));
|
||||
}
|
||||
}
|
||||
|
||||
void _onLoadCommunityAndSpaces(
|
||||
LoadCommunityAndSpacesEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) async {
|
||||
emit(SpaceManagementLoading());
|
||||
try {
|
||||
if (_cachedProducts == null) {
|
||||
final products = await _productApi.fetchProducts();
|
||||
_cachedProducts = products;
|
||||
}
|
||||
|
||||
// Fetch all communities
|
||||
_onloadProducts();
|
||||
List<CommunityModel> communities = await _api.fetchCommunities();
|
||||
|
||||
List<CommunityModel> updatedCommunities = await Future.wait(
|
||||
communities.map((community) async {
|
||||
List<SpaceModel> spaces = await _api.getSpaceHierarchy(community.uuid);
|
||||
List<SpaceModel> spaces =
|
||||
await _fetchSpacesForCommunity(community.uuid);
|
||||
return CommunityModel(
|
||||
uuid: community.uuid,
|
||||
createdAt: community.createdAt,
|
||||
@ -101,7 +128,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
emit(SpaceManagementLoaded(communities: updatedCommunities, products: _cachedProducts ?? []));
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: updatedCommunities, products: _cachedProducts ?? []));
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
||||
}
|
||||
@ -139,16 +167,21 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
CommunityModel? newCommunity = await _api.createCommunity(event.name, event.description);
|
||||
CommunityModel? newCommunity =
|
||||
await _api.createCommunity(event.name, event.description);
|
||||
|
||||
if (newCommunity != null) {
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities)
|
||||
..add(newCommunity);
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState) {
|
||||
final prevCommunities = List<CommunityModel>.from(
|
||||
(previousState as dynamic).communities,
|
||||
);
|
||||
final updatedCommunities = prevCommunities..add(newCommunity);
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: updatedCommunities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: newCommunity));
|
||||
selectedCommunity: newCommunity,
|
||||
selectedSpace: null));
|
||||
}
|
||||
} else {
|
||||
emit(const SpaceManagementError('Error creating community'));
|
||||
@ -158,6 +191,54 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectCommunity(
|
||||
SelectCommunityEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) async {
|
||||
_handleCommunitySpaceStateUpdate(
|
||||
emit: emit,
|
||||
selectedCommunity: event.selectedCommunity,
|
||||
selectedSpace: null,
|
||||
);
|
||||
}
|
||||
|
||||
void _onSelectSpace(
|
||||
SelectSpaceEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) {
|
||||
_handleCommunitySpaceStateUpdate(
|
||||
emit: emit,
|
||||
selectedCommunity: event.selectedCommunity,
|
||||
selectedSpace: event.selectedSpace,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleCommunitySpaceStateUpdate({
|
||||
required Emitter<SpaceManagementState> emit,
|
||||
CommunityModel? selectedCommunity,
|
||||
SpaceModel? selectedSpace,
|
||||
}) {
|
||||
final previousState = state;
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState) {
|
||||
final communities = List<CommunityModel>.from(
|
||||
(previousState as dynamic).communities,
|
||||
);
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error updating state: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
void _onSaveSpaces(
|
||||
SaveSpacesEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
@ -166,17 +247,54 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid);
|
||||
final updatedSpaces =
|
||||
await saveSpacesHierarchically(event.spaces, event.communityUuid);
|
||||
|
||||
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
||||
|
||||
emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
||||
add(LoadCommunityAndSpacesEvent());
|
||||
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
_updateLoadedState(
|
||||
previousState,
|
||||
allSpaces,
|
||||
event.communityUuid,
|
||||
emit,
|
||||
);
|
||||
} else {
|
||||
add(LoadCommunityAndSpacesEvent());
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error saving spaces: $e'));
|
||||
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
emit(previousState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _updateLoadedState(
|
||||
SpaceManagementLoaded previousState,
|
||||
List<SpaceModel> allSpaces,
|
||||
String communityUuid,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) {
|
||||
final communities = List<CommunityModel>.from(previousState.communities);
|
||||
|
||||
for (var community in communities) {
|
||||
if (community.uuid == communityUuid) {
|
||||
community.spaces = allSpaces;
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: community,
|
||||
selectedSpace: null,
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
||||
List<SpaceModel> spaces, String communityUuid) async {
|
||||
final orderedSpaces = flattenHierarchy(spaces);
|
||||
@ -187,7 +305,6 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
||||
|
||||
for (var parent in parentsToDelete) {
|
||||
try {
|
||||
// Ensure parent.uuid is not null before calling the API
|
||||
if (parent.uuid != null) {
|
||||
await _api.deleteSpace(communityUuid, parent.uuid!);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.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/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; // Import for Offset
|
||||
|
||||
abstract class SpaceManagementEvent extends Equatable {
|
||||
const SpaceManagementEvent();
|
||||
@ -83,17 +84,6 @@ class CreateCommunityEvent extends SpaceManagementEvent {
|
||||
List<Object> get props => [name, description];
|
||||
}
|
||||
|
||||
class FetchProductsEvent extends SpaceManagementEvent {}
|
||||
|
||||
class LoadSpaceHierarchyEvent extends SpaceManagementEvent {
|
||||
final String communityId;
|
||||
|
||||
const LoadSpaceHierarchyEvent({required this.communityId});
|
||||
|
||||
@override
|
||||
List<Object> get props => [communityId];
|
||||
}
|
||||
|
||||
class UpdateCommunityEvent extends SpaceManagementEvent {
|
||||
final String communityUuid;
|
||||
final String name;
|
||||
@ -106,3 +96,47 @@ class UpdateCommunityEvent extends SpaceManagementEvent {
|
||||
@override
|
||||
List<Object> get props => [communityUuid, name];
|
||||
}
|
||||
|
||||
class SelectCommunityEvent extends SpaceManagementEvent {
|
||||
final CommunityModel? selectedCommunity;
|
||||
|
||||
const SelectCommunityEvent({
|
||||
required this.selectedCommunity,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class NewCommunityEvent extends SpaceManagementEvent {
|
||||
final List<CommunityModel> communities;
|
||||
|
||||
const NewCommunityEvent({required this.communities});
|
||||
|
||||
@override
|
||||
List<Object> get props => [communities];
|
||||
}
|
||||
|
||||
class SelectSpaceEvent extends SpaceManagementEvent {
|
||||
final CommunityModel? selectedCommunity;
|
||||
final SpaceModel? selectedSpace;
|
||||
|
||||
const SelectSpaceEvent({
|
||||
required this.selectedCommunity,
|
||||
required this.selectedSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class FetchProductsEvent extends SpaceManagementEvent {}
|
||||
|
||||
class LoadSpaceHierarchyEvent extends SpaceManagementEvent {
|
||||
final String communityId;
|
||||
|
||||
const LoadSpaceHierarchyEvent({required this.communityId});
|
||||
|
||||
@override
|
||||
List<Object> get props => [communityId];
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
|
||||
abstract class SpaceManagementState extends Equatable {
|
||||
const SpaceManagementState();
|
||||
@ -17,10 +17,24 @@ class SpaceManagementLoading extends SpaceManagementState {}
|
||||
class SpaceManagementLoaded extends SpaceManagementState {
|
||||
final List<CommunityModel> communities;
|
||||
final List<ProductModel> products;
|
||||
CommunityModel? selectedCommunity; // Include products in the state
|
||||
CommunityModel? selectedCommunity;
|
||||
SpaceModel? selectedSpace;
|
||||
|
||||
SpaceManagementLoaded(
|
||||
{required this.communities, required this.products, this.selectedCommunity});
|
||||
{required this.communities,
|
||||
required this.products,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace});
|
||||
}
|
||||
|
||||
class BlankState extends SpaceManagementState {
|
||||
final List<CommunityModel> communities;
|
||||
final List<ProductModel> products;
|
||||
|
||||
BlankState({
|
||||
required this.communities,
|
||||
required this.products,
|
||||
});
|
||||
}
|
||||
|
||||
class SpaceCreationSuccess extends SpaceManagementState {
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
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 {
|
||||
final SpaceModel startSpace;
|
@ -1,7 +1,7 @@
|
||||
import 'dart:ui';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_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:uuid/uuid.dart';
|
||||
|
||||
@ -44,7 +44,8 @@ class SpaceModel {
|
||||
this.selectedProducts = const [],
|
||||
}) : internalId = internalId ?? const Uuid().v4();
|
||||
|
||||
factory SpaceModel.fromJson(Map<String, dynamic> json, {String? parentInternalId}) {
|
||||
factory SpaceModel.fromJson(Map<String, dynamic> json,
|
||||
{String? parentInternalId}) {
|
||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||
|
||||
final List<SpaceModel> children = json['children'] != null
|
||||
@ -56,7 +57,7 @@ class SpaceModel {
|
||||
}).toList()
|
||||
: [];
|
||||
|
||||
return SpaceModel(
|
||||
final instance = SpaceModel(
|
||||
internalId: internalId,
|
||||
uuid: json['uuid'] ?? '',
|
||||
spaceTuyaUuid: json['spaceTuyaUuid'],
|
||||
@ -72,11 +73,14 @@ class SpaceModel {
|
||||
isPrivate: json['parent']?['isPrivate'] ?? false,
|
||||
invitationCode: json['parent']?['invitationCode'],
|
||||
children: [],
|
||||
position: Offset(json['parent']?['x'] ?? 0, json['parent']?['y'] ?? 0),
|
||||
position:
|
||||
Offset(json['parent']?['x'] ?? 0, json['parent']?['y'] ?? 0),
|
||||
icon: json['parent']?['icon'] ?? Assets.location,
|
||||
)
|
||||
: null,
|
||||
community: json['community'] != null ? CommunityModel.fromJson(json['community']) : null,
|
||||
community: json['community'] != null
|
||||
? CommunityModel.fromJson(json['community'])
|
||||
: null,
|
||||
children: children,
|
||||
icon: json['icon'] ?? Assets.location,
|
||||
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
||||
@ -90,6 +94,20 @@ class SpaceModel {
|
||||
}).toList()
|
||||
: [],
|
||||
);
|
||||
|
||||
if (json['incomingConnections'] != null &&
|
||||
json['incomingConnections'] is List &&
|
||||
(json['incomingConnections'] as List).isNotEmpty &&
|
||||
instance.parent != null) {
|
||||
final conn = json['incomingConnections'][0];
|
||||
instance.incomingConnection = Connection(
|
||||
startSpace: instance.parent ?? instance, // Parent space
|
||||
endSpace: instance, // This space instance
|
||||
direction: conn['direction'],
|
||||
);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.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/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.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/space_mana_api.dart';
|
||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||
|
||||
class SpaceManagementPage extends StatefulWidget {
|
||||
const SpaceManagementPage({super.key});
|
||||
|
||||
@override
|
||||
SpaceManagementPageState createState() => SpaceManagementPageState();
|
||||
}
|
||||
|
||||
class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi();
|
||||
final ProductApi _productApi = ProductApi();
|
||||
Map<String, List<SpaceModel>> communitySpaces = {};
|
||||
List<ProductModel> products = [];
|
||||
bool isProductDataLoaded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SpaceManagementBloc(_api, _productApi)
|
||||
..add(LoadCommunityAndSpacesEvent()),
|
||||
child: WebScaffold(
|
||||
appBarTitle: Text('Space Management',
|
||||
style: Theme.of(context).textTheme.headlineLarge),
|
||||
enableMenuSidebar: false,
|
||||
rightBody: const NavigateHomeGridView(),
|
||||
scaffoldBody: BlocBuilder<SpaceManagementBloc, SpaceManagementState>(
|
||||
builder: (context, state) {
|
||||
if (state is SpaceManagementLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is BlankState) {
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
selectedCommunity: null,
|
||||
selectedSpace: null,
|
||||
products: state.products,
|
||||
);
|
||||
} else if (state is SpaceManagementLoaded) {
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
selectedCommunity: state.selectedCommunity,
|
||||
selectedSpace: state.selectedSpace,
|
||||
products: state.products,
|
||||
);
|
||||
} else if (state is SpaceManagementError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/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/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.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/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
@ -1,32 +1,42 @@
|
||||
import 'package:flutter/material.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/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_community_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.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';
|
||||
|
||||
class BlankCommunityWidget extends StatelessWidget {
|
||||
const BlankCommunityWidget({Key? key}) : super(key: key);
|
||||
class BlankCommunityWidget extends StatefulWidget {
|
||||
final List<CommunityModel> communities;
|
||||
|
||||
BlankCommunityWidget({required this.communities});
|
||||
|
||||
@override
|
||||
_BlankCommunityWidgetState createState() => _BlankCommunityWidgetState();
|
||||
}
|
||||
|
||||
class _BlankCommunityWidgetState extends State<BlankCommunityWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
color: ColorsManager.whiteColors, // Parent container with white background
|
||||
color:
|
||||
ColorsManager.whiteColors, // Parent container with white background
|
||||
child: GridView.builder(
|
||||
padding: const EdgeInsets.only(left: 40.0, top: 20.0),
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 400, // Each item's width will be 400 or less
|
||||
mainAxisSpacing: 10, // Spacing between items
|
||||
crossAxisSpacing: 10, // Spacing between items
|
||||
childAspectRatio: 2.0, // Aspect ratio for width:height (e.g., 300:150 = 2.0)
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 400,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 2.0,
|
||||
),
|
||||
itemCount: 1, // Only one item
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onTap: () => _showCreateCommunityDialog(context),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center, // Center align the content
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center, // Center align the content
|
||||
children: [
|
||||
Expanded(
|
||||
child: AspectRatio(
|
||||
@ -34,7 +44,7 @@ class BlankCommunityWidget extends StatelessWidget {
|
||||
child: Container(
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
side: const BorderSide(
|
||||
width: 4,
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
color: ColorsManager.borderColor,
|
||||
@ -45,9 +55,8 @@ class BlankCommunityWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 9), // Space between item and text
|
||||
Text('Blank', // Text below the item
|
||||
const SizedBox(height: 9),
|
||||
Text('Blank',
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
)),
|
||||
@ -63,6 +72,8 @@ class BlankCommunityWidget extends StatelessWidget {
|
||||
showDialog(
|
||||
context: parentContext,
|
||||
builder: (context) => CreateCommunityDialog(
|
||||
isEditMode: false,
|
||||
existingCommunityNames: widget.communities.map((community) => community.name).toList(),
|
||||
onCreateCommunity: (String communityName, String description) {
|
||||
parentContext.read<SpaceManagementBloc>().add(
|
||||
CreateCommunityEvent(
|
||||
@ -74,4 +85,5 @@ class BlankCommunityWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/constants/assets.dart';
|
||||
|
||||
class CommunityStructureHeader extends StatelessWidget {
|
||||
class CommunityStructureHeader extends StatefulWidget {
|
||||
final String? communityName;
|
||||
final bool isEditingName;
|
||||
final bool isSave;
|
||||
@ -13,19 +15,28 @@ class CommunityStructureHeader extends StatelessWidget {
|
||||
final VoidCallback onDelete;
|
||||
final VoidCallback onEditName;
|
||||
final ValueChanged<String> onNameSubmitted;
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? community;
|
||||
|
||||
const CommunityStructureHeader({
|
||||
Key? key,
|
||||
required this.communityName,
|
||||
required this.isSave,
|
||||
required this.isEditingName,
|
||||
required this.nameController,
|
||||
required this.onSave,
|
||||
required this.onDelete,
|
||||
required this.onEditName,
|
||||
required this.onNameSubmitted,
|
||||
}) : super(key: key);
|
||||
const CommunityStructureHeader(
|
||||
{super.key,
|
||||
required this.communityName,
|
||||
required this.isSave,
|
||||
required this.isEditingName,
|
||||
required this.nameController,
|
||||
required this.onSave,
|
||||
required this.onDelete,
|
||||
required this.onEditName,
|
||||
required this.onNameSubmitted,
|
||||
this.community,
|
||||
required this.communities});
|
||||
|
||||
@override
|
||||
State<CommunityStructureHeader> createState() =>
|
||||
_CommunityStructureHeaderState();
|
||||
}
|
||||
|
||||
class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
||||
@override
|
||||
Widget build(BuildContext 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) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'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(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
if (!isEditingName)
|
||||
if (!widget.isEditingName)
|
||||
Flexible(
|
||||
child: Text(
|
||||
communityName!,
|
||||
style:
|
||||
theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor),
|
||||
widget.communityName!,
|
||||
style: theme.textTheme.bodyLarge
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
if (isEditingName)
|
||||
if (widget.isEditingName)
|
||||
SizedBox(
|
||||
width: screenWidth * 0.3,
|
||||
width: screenWidth * 0.1,
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
controller: widget.nameController,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
),
|
||||
style:
|
||||
theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor),
|
||||
onSubmitted: onNameSubmitted,
|
||||
style: theme.textTheme.bodyLarge
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
onSubmitted: widget.onNameSubmitted,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
GestureDetector(
|
||||
onTap: onEditName,
|
||||
onTap: () => _showCreateCommunityDialog(context),
|
||||
child: SvgPicture.asset(
|
||||
Assets.iconEdit,
|
||||
width: 16,
|
||||
@ -110,7 +137,7 @@ class CommunityStructureHeader extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isSave) ...[
|
||||
if (widget.isSave) ...[
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButtons(theme),
|
||||
],
|
||||
@ -127,8 +154,9 @@ class CommunityStructureHeader extends StatelessWidget {
|
||||
children: [
|
||||
_buildButton(
|
||||
label: "Save",
|
||||
icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor),
|
||||
onPressed: onSave,
|
||||
icon: const Icon(Icons.save,
|
||||
size: 18, color: ColorsManager.spaceColor),
|
||||
onPressed: widget.onSave,
|
||||
theme: theme),
|
||||
],
|
||||
);
|
||||
@ -159,7 +187,8 @@ class CommunityStructureHeader extends StatelessWidget {
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: ColorsManager.blackColor),
|
||||
style: theme.textTheme.bodySmall
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
@ -4,19 +4,19 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
// Syncrow project imports
|
||||
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/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/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/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/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CommunityStructureArea extends StatefulWidget {
|
||||
@ -24,12 +24,13 @@ class CommunityStructureArea extends StatefulWidget {
|
||||
SpaceModel? selectedSpace;
|
||||
final List<ProductModel>? products;
|
||||
final ValueChanged<SpaceModel?>? onSpaceSelected;
|
||||
|
||||
final List<CommunityModel> communities;
|
||||
final List<SpaceModel> spaces;
|
||||
|
||||
CommunityStructureArea({
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
required this.communities,
|
||||
this.products,
|
||||
required this.spaces,
|
||||
this.onSpaceSelected,
|
||||
@ -52,7 +53,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
_nameController = TextEditingController(
|
||||
text: widget.selectedCommunity?.name ?? '',
|
||||
@ -79,12 +81,14 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
if (oldWidget.spaces != widget.spaces) {
|
||||
setState(() {
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
});
|
||||
}
|
||||
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||
widget.selectedSpace != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_moveToSpace(widget.selectedSpace!);
|
||||
});
|
||||
@ -94,14 +98,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.selectedCommunity == null) {
|
||||
return BlankCommunityWidget();
|
||||
return BlankCommunityWidget(
|
||||
communities: widget.communities,
|
||||
);
|
||||
}
|
||||
|
||||
Size screenSize = MediaQuery.of(context).size;
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_deselectSpace();
|
||||
_deselectSpace(context);
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
@ -113,7 +119,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CommunityStructureHeader(
|
||||
communities: widget.communities,
|
||||
communityName: widget.selectedCommunity?.name,
|
||||
community: widget.selectedCommunity,
|
||||
isSave: isSave(spaces),
|
||||
isEditingName: isEditingName,
|
||||
nameController: _nameController,
|
||||
@ -156,9 +164,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
children: [
|
||||
for (var connection in connections)
|
||||
Opacity(
|
||||
opacity:
|
||||
_isHighlightedConnection(connection) ? 1.0 : 0.3, // Adjust opacity
|
||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
||||
opacity: _isHighlightedConnection(connection)
|
||||
? 1.0
|
||||
: 0.3, // Adjust opacity
|
||||
child: CustomPaint(
|
||||
painter: CurvedLinePainter([connection])),
|
||||
),
|
||||
for (var entry in spaces.asMap().entries)
|
||||
if (entry.value.status != SpaceStatus.deleted)
|
||||
@ -167,10 +177,12 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
top: entry.value.position.dy,
|
||||
child: SpaceCardWidget(
|
||||
index: entry.key,
|
||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
||||
onButtonTap: (int index, Offset newPosition,
|
||||
String direction) {
|
||||
_showCreateSpaceDialog(
|
||||
screenSize,
|
||||
position: spaces[index].position + newPosition,
|
||||
position:
|
||||
spaces[index].position + newPosition,
|
||||
parentIndex: index,
|
||||
direction: direction,
|
||||
);
|
||||
@ -183,7 +195,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
_updateNodePosition(entry.value, newPosition);
|
||||
},
|
||||
buildSpaceContainer: (int index) {
|
||||
final bool isHighlighted = _isHighlightedSpace(spaces[index]);
|
||||
final bool isHighlighted =
|
||||
_isHighlightedSpace(spaces[index]);
|
||||
|
||||
return Opacity(
|
||||
opacity: isHighlighted ? 1.0 : 0.3,
|
||||
@ -193,7 +206,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
_showEditSpaceDialog(spaces[index]);
|
||||
},
|
||||
onTap: () {
|
||||
_selectSpace(spaces[index]);
|
||||
_selectSpace(context, spaces[index]);
|
||||
},
|
||||
icon: spaces[index].icon ?? '',
|
||||
name: spaces[index].name,
|
||||
@ -210,7 +223,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
child: AddSpaceButton(
|
||||
onTap: () {
|
||||
_showCreateSpaceDialog(screenSize,
|
||||
canvasHeight: canvasHeight, canvasWidth: canvasWidth);
|
||||
canvasHeight: canvasHeight,
|
||||
canvasWidth: canvasWidth);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -270,12 +284,14 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
builder: (BuildContext context) {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
parentSpace: parentIndex != null? spaces[parentIndex] : null,
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
|
||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||
onCreateSpace: (String name, String icon,
|
||||
List<SelectedProduct> selectedProducts) {
|
||||
setState(() {
|
||||
// Set the first space in the center or use passed position
|
||||
|
||||
Offset centerPosition = position ?? _getCenterPosition(screenSize);
|
||||
Offset centerPosition =
|
||||
position ?? _getCenterPosition(screenSize);
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
name: name,
|
||||
icon: icon,
|
||||
@ -317,9 +333,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
products: widget.products,
|
||||
name: space.name,
|
||||
icon: space.icon,
|
||||
editSpace: space,
|
||||
isEdit: true,
|
||||
selectedProducts: space.selectedProducts,
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
|
||||
onCreateSpace: (String name, String icon,
|
||||
List<SelectedProduct> selectedProducts) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
space.name = name;
|
||||
@ -331,8 +349,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
}
|
||||
});
|
||||
},
|
||||
// Pre-fill the dialog with current space data
|
||||
key: Key(space.name), // Add a unique key to ensure dialog refresh
|
||||
key: Key(space.name),
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -465,40 +482,30 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
..scale(1.2);
|
||||
}
|
||||
|
||||
void _selectSpace(SpaceModel space) {
|
||||
setState(() {
|
||||
widget.selectedSpace = space;
|
||||
});
|
||||
|
||||
if (widget.onSpaceSelected != null) {
|
||||
widget.onSpaceSelected!(space);
|
||||
}
|
||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: space),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isHighlightedSpace(SpaceModel space) {
|
||||
if (widget.selectedSpace == null) return true;
|
||||
if (space == widget.selectedSpace) return true;
|
||||
if (widget.selectedSpace?.parent?.internalId == space.internalId) return true;
|
||||
if (widget.selectedSpace?.children != null) {
|
||||
for (var child in widget.selectedSpace!.children) {
|
||||
if (child.internalId == space.internalId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
final selectedSpace = widget.selectedSpace;
|
||||
if (selectedSpace == null) return true;
|
||||
|
||||
return space == selectedSpace ||
|
||||
selectedSpace.parent?.internalId == space.internalId ||
|
||||
selectedSpace.children
|
||||
?.any((child) => child.internalId == space.internalId) ==
|
||||
true;
|
||||
}
|
||||
|
||||
void _deselectSpace() {
|
||||
if (widget.selectedSpace != null) {
|
||||
setState(() {
|
||||
widget.selectedSpace = null;
|
||||
});
|
||||
|
||||
if (widget.onSpaceSelected != null) {
|
||||
widget.onSpaceSelected!(null); // Notify parent that no space is selected
|
||||
}
|
||||
}
|
||||
void _deselectSpace(BuildContext context) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isHighlightedConnection(Connection connection) {
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
class CurvedLinePainter extends CustomPainter {
|
@ -2,24 +2,26 @@ import 'package:flutter/material.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/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/widgets/hoverable_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.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/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
||||
|
||||
class CreateSpaceDialog extends StatefulWidget {
|
||||
final Function(String, String, List<SelectedProduct> selectedProducts) onCreateSpace;
|
||||
final Function(String, String, List<SelectedProduct> selectedProducts)
|
||||
onCreateSpace;
|
||||
final List<ProductModel>? products;
|
||||
final String? name;
|
||||
final String? icon;
|
||||
final bool isEdit;
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final SpaceModel? parentSpace;
|
||||
final SpaceModel? editSpace;
|
||||
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
@ -29,6 +31,7 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
this.name,
|
||||
this.icon,
|
||||
this.isEdit = false,
|
||||
this.editSpace,
|
||||
this.selectedProducts = const []});
|
||||
|
||||
@override
|
||||
@ -49,8 +52,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
super.initState();
|
||||
selectedIcon = widget.icon ?? Assets.location;
|
||||
nameController = TextEditingController(text: widget.name ?? '');
|
||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
selectedProducts =
|
||||
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled =
|
||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -59,7 +64,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return AlertDialog(
|
||||
title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'),
|
||||
title: widget.isEdit
|
||||
? const Text('Edit Space')
|
||||
: const Text('Create New Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.5, // Limit dialog width
|
||||
@ -126,11 +133,17 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (widget.parentSpace?.children
|
||||
.any((child) => child.name == value) ??
|
||||
false) {
|
||||
if ((widget.parentSpace?.children.any(
|
||||
(child) => child.name == value) ??
|
||||
false) ||
|
||||
(widget.parentSpace?.name == value) ||
|
||||
(widget.editSpace?.children.any(
|
||||
(child) => child.name == value) ??
|
||||
false)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
@ -218,7 +231,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.addIcon,
|
||||
width: screenWidth * 0.015, // Adjust icon size
|
||||
width: screenWidth *
|
||||
0.015, // Adjust icon size
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
@ -226,8 +240,11 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Add devices / Assign a space model',
|
||||
overflow: TextOverflow.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -262,16 +279,20 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
||||
String newName = enteredName.isNotEmpty
|
||||
? enteredName
|
||||
: (widget.name ?? '');
|
||||
if (newName.isNotEmpty) {
|
||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts);
|
||||
widget.onCreateSpace(
|
||||
newName, selectedIcon, selectedProducts);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: 10,
|
||||
backgroundColor:
|
||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
||||
backgroundColor: isOkButtonEnabled
|
||||
? ColorsManager.secondaryColor
|
||||
: ColorsManager.grayColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
@ -318,7 +339,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
children: [
|
||||
for (var i = 0; i < selectedProducts.length; i++) ...[
|
||||
HoverableButton(
|
||||
iconPath: _mapIconToProduct(selectedProducts[i].productId, products),
|
||||
iconPath:
|
||||
_mapIconToProduct(selectedProducts[i].productId, products),
|
||||
text: 'x${selectedProducts[i].count}',
|
||||
onTap: () {
|
||||
setState(() {
|
||||
@ -328,7 +350,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
},
|
||||
),
|
||||
if (i < selectedProducts.length - 1)
|
||||
const SizedBox(width: 2), // Add space except after the last button
|
||||
const SizedBox(
|
||||
width: 2), // Add space except after the last button
|
||||
],
|
||||
const SizedBox(width: 2),
|
||||
GestureDetector(
|
||||
@ -364,6 +387,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
String _mapIconToProduct(String uuid, List<ProductModel> products) {
|
||||
// Find the product with the matching UUID
|
||||
final product = products.firstWhere(
|
@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart';
|
||||
|
||||
class LoadedSpaceView extends StatefulWidget {
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? selectedCommunity;
|
||||
final SpaceModel? selectedSpace;
|
||||
final List<ProductModel>? products;
|
||||
|
||||
const LoadedSpaceView({
|
||||
super.key,
|
||||
required this.communities,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
});
|
||||
|
||||
@override
|
||||
_LoadedStateViewState createState() => _LoadedStateViewState();
|
||||
}
|
||||
|
||||
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SidebarWidget(
|
||||
communities: widget.communities,
|
||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
||||
widget.selectedCommunity?.uuid ??
|
||||
'',
|
||||
),
|
||||
CommunityStructureArea(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||
products: widget.products,
|
||||
communities: widget.communities,
|
||||
),
|
||||
],
|
||||
),
|
||||
const GradientCanvasBorderWidget(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
SpaceModel? findSpaceByUuid(String? uuid, List<CommunityModel> communities) {
|
||||
for (var community in communities) {
|
||||
for (var space in community.spaces) {
|
||||
if (space.uuid == uuid) return space;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -2,30 +2,22 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.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/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_community_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/space_tile_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.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/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class SidebarWidget extends StatefulWidget {
|
||||
final Function(CommunityModel)? onCommunitySelected;
|
||||
final Function(SpaceModel?)? onSpaceSelected;
|
||||
final List<CommunityModel> communities;
|
||||
final Function(String?)? onSelectedSpaceChanged; // New callback
|
||||
|
||||
final String? selectedSpaceUuid;
|
||||
|
||||
const SidebarWidget({
|
||||
super.key,
|
||||
this.onCommunitySelected,
|
||||
this.onSpaceSelected,
|
||||
this.onSelectedSpaceChanged,
|
||||
required this.communities,
|
||||
this.selectedSpaceUuid,
|
||||
});
|
||||
@ -42,7 +34,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedId = widget.selectedSpaceUuid; // Initialize with the passed selected space UUID
|
||||
_selectedId = widget
|
||||
.selectedSpaceUuid; // Initialize with the passed selected space UUID
|
||||
}
|
||||
|
||||
@override
|
||||
@ -55,22 +48,6 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
void _showCreateCommunityDialog(BuildContext parentContext) {
|
||||
showDialog(
|
||||
context: parentContext,
|
||||
builder: (context) => CreateCommunityDialog(
|
||||
onCreateCommunity: (String communityName, String description) {
|
||||
parentContext.read<SpaceManagementBloc>().add(
|
||||
CreateCommunityEvent(
|
||||
name: communityName,
|
||||
description: description,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Function to filter communities based on the search query
|
||||
List<CommunityModel> _filterCommunities() {
|
||||
if (_searchQuery.isEmpty) {
|
||||
@ -83,8 +60,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
return widget.communities.where((community) {
|
||||
final containsQueryInCommunity =
|
||||
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
||||
final containsQueryInSpaces =
|
||||
community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase()));
|
||||
final containsQueryInSpaces = community.spaces
|
||||
.any((space) => _containsQuery(space, _searchQuery.toLowerCase()));
|
||||
|
||||
return containsQueryInCommunity || containsQueryInSpaces;
|
||||
}).toList();
|
||||
@ -93,8 +70,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
// Helper function to determine if any space or its children match the search query
|
||||
bool _containsQuery(SpaceModel space, String query) {
|
||||
final matchesSpace = space.name.toLowerCase().contains(query);
|
||||
final matchesChildren =
|
||||
space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
|
||||
final matchesChildren = space.children.any((child) =>
|
||||
_containsQuery(child, query)); // Recursive check for children
|
||||
|
||||
// If the space or any of its children match the query, expand this space
|
||||
if (matchesSpace || matchesChildren) {
|
||||
@ -128,7 +105,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
width: 300,
|
||||
decoration: subSectionContainerDecoration,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // Ensures the Column only takes necessary height
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures the Column only takes necessary height
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Communities title with the add button
|
||||
@ -143,7 +121,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
color: Colors.black,
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () => _showCreateCommunityDialog(context),
|
||||
onTap: () => _navigateToBlank(context),
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
@ -176,7 +154,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: filteredCommunities.map((community) {
|
||||
return _buildCommunityTile(community);
|
||||
return _buildCommunityTile(context, community);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
@ -185,7 +163,16 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommunityTile(CommunityModel community) {
|
||||
void _navigateToBlank(BuildContext context) {
|
||||
setState(() {
|
||||
_selectedId = '';
|
||||
});
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
NewCommunityEvent(communities: widget.communities),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommunityTile(BuildContext context, CommunityModel community) {
|
||||
bool hasChildren = community.spaces.isNotEmpty;
|
||||
|
||||
return CommunityTile(
|
||||
@ -199,23 +186,23 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
_selectedSpaceUuid = null; // Update the selected community
|
||||
});
|
||||
|
||||
if (widget.onCommunitySelected != null) {
|
||||
widget.onCommunitySelected!(community);
|
||||
widget.onSpaceSelected!(null); // Pass the entire community
|
||||
}
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectCommunityEvent(selectedCommunity: community),
|
||||
);
|
||||
},
|
||||
onExpansionChanged: (String title, bool expanded) {
|
||||
_handleExpansionChange(community.uuid, expanded);
|
||||
},
|
||||
children: hasChildren
|
||||
? community.spaces.map((space) => _buildSpaceTile(space, community)).toList()
|
||||
? community.spaces
|
||||
.map((space) => _buildSpaceTile(space, community))
|
||||
.toList()
|
||||
: null, // Render spaces within the community
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSpaceTile(SpaceModel space, CommunityModel community) {
|
||||
bool isExpandedSpace = _isSpaceOrChildSelected(space);
|
||||
// Check if space should be expanded
|
||||
return SpaceTile(
|
||||
title: space.name,
|
||||
key: ValueKey(space.uuid),
|
||||
@ -230,17 +217,15 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
_selectedSpaceUuid = space.uuid;
|
||||
});
|
||||
|
||||
if (widget.onSpaceSelected != null) {
|
||||
widget.onCommunitySelected!(community);
|
||||
widget.onSpaceSelected!(space);
|
||||
}
|
||||
|
||||
if (widget.onSelectedSpaceChanged != null) {
|
||||
widget.onSelectedSpaceChanged!(space.uuid);
|
||||
}
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: community, selectedSpace: space),
|
||||
);
|
||||
},
|
||||
children: space.children.isNotEmpty
|
||||
? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
|
||||
? space.children
|
||||
.map((childSpace) => _buildSpaceTile(childSpace, community))
|
||||
.toList()
|
||||
: [], // Recursively render child spaces if available
|
||||
);
|
||||
}
|
@ -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,85 +0,0 @@
|
||||
import 'package:flutter/material.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/spaces_management/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart';
|
||||
import 'package:syncrow_web/services/product_api.dart';
|
||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||
|
||||
class SpaceManagementPage extends StatefulWidget {
|
||||
const SpaceManagementPage({super.key});
|
||||
|
||||
@override
|
||||
SpaceManagementPageState createState() => SpaceManagementPageState();
|
||||
}
|
||||
|
||||
class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
CommunityModel? selectedCommunity;
|
||||
SpaceModel? selectedSpace;
|
||||
final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi();
|
||||
final ProductApi _productApi = ProductApi();
|
||||
Map<String, List<SpaceModel>> communitySpaces = {};
|
||||
List<ProductModel> products = [];
|
||||
bool isProductDataLoaded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
SpaceManagementBloc(_api, _productApi)..add(LoadCommunityAndSpacesEvent()),
|
||||
child: WebScaffold(
|
||||
appBarTitle: Text('Space Management', style: Theme.of(context).textTheme.headlineLarge),
|
||||
enableMenuSidebar: false,
|
||||
rightBody: const NavigateHomeGridView(),
|
||||
scaffoldBody:
|
||||
BlocBuilder<SpaceManagementBloc, SpaceManagementState>(builder: (context, state) {
|
||||
if (state is SpaceManagementLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is SpaceManagementLoaded) {
|
||||
int selectedIndex = state.communities.indexWhere(
|
||||
(community) => community.uuid == selectedCommunity?.uuid,
|
||||
);
|
||||
if (selectedIndex != -1) {
|
||||
selectedCommunity = state.communities[selectedIndex];
|
||||
} else if (state.selectedCommunity != null) {
|
||||
selectedCommunity = state.selectedCommunity;
|
||||
} else {
|
||||
selectedCommunity = null;
|
||||
selectedSpace = null;
|
||||
}
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
products: state.products,
|
||||
onCommunitySelected: (community) {
|
||||
setState(() {
|
||||
selectedCommunity = community;
|
||||
});
|
||||
},
|
||||
onSpaceSelected: (space) {
|
||||
setState(() {
|
||||
selectedSpace = space;
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (state is SpaceManagementError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CreateCommunityDialog extends StatefulWidget {
|
||||
final Function(String name, String description) onCreateCommunity;
|
||||
|
||||
const CreateCommunityDialog({super.key, required this.onCreateCommunity});
|
||||
|
||||
@override
|
||||
CreateCommunityDialogState createState() => CreateCommunityDialogState();
|
||||
}
|
||||
|
||||
class CreateCommunityDialogState extends State<CreateCommunityDialog> {
|
||||
String enteredName = ''; // Store the entered community name
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
backgroundColor: Colors.transparent, // Transparent for shadow effect
|
||||
child: Stack(
|
||||
children: [
|
||||
// Background container with shadow and rounded corners
|
||||
Container(
|
||||
width: 490,
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.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) {
|
||||
enteredName = value; // Capture entered name
|
||||
},
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
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: const BorderSide(color: ColorsManager.boxColor, width: 1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: ColorsManager.boxColor, width: 1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: ColorsManager.boxColor, width: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// Action buttons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cancel',
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (enteredName.isNotEmpty) {
|
||||
widget.onCreateCommunity(
|
||||
enteredName,
|
||||
"",
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: const Text('OK'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/widgets/sidebar_widget.dart';
|
||||
|
||||
class LoadedSpaceView extends StatefulWidget {
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? selectedCommunity;
|
||||
final SpaceModel? selectedSpace;
|
||||
final ValueChanged<CommunityModel>? onCommunitySelected;
|
||||
final ValueChanged<SpaceModel?>? onSpaceSelected;
|
||||
final List<ProductModel>? products;
|
||||
|
||||
const LoadedSpaceView({
|
||||
super.key,
|
||||
required this.communities,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
required this.onCommunitySelected,
|
||||
required this.onSpaceSelected,
|
||||
this.products,
|
||||
});
|
||||
|
||||
@override
|
||||
_LoadedStateViewState createState() => _LoadedStateViewState();
|
||||
}
|
||||
|
||||
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SidebarWidget(
|
||||
communities: widget.communities,
|
||||
onCommunitySelected: widget.onCommunitySelected,
|
||||
onSpaceSelected: widget.onSpaceSelected,
|
||||
selectedSpaceUuid: widget.selectedSpace?.uuid,
|
||||
onSelectedSpaceChanged: (String? spaceUuid) {
|
||||
setState(() {
|
||||
final selectedSpace = findSpaceByUuid(spaceUuid, widget.communities);
|
||||
if (selectedSpace != null) {
|
||||
widget.onSpaceSelected!(selectedSpace);
|
||||
}
|
||||
});
|
||||
}),
|
||||
CommunityStructureArea(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||
products: widget.products,
|
||||
onSpaceSelected: (SpaceModel? space) {
|
||||
setState(() {
|
||||
widget.onSpaceSelected!(space);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const GradientCanvasBorderWidget(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
SpaceModel? findSpaceByUuid(String? uuid, List<CommunityModel> communities) {
|
||||
for (var community in communities) {
|
||||
for (var space in community.spaces) {
|
||||
if (space.uuid == uuid) return space;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
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/utils/constants/api_const.dart';
|
||||
|
||||
|
@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/routiens/models/routine_details_model.dart';
|
||||
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
import 'package:syncrow_web/utils/constants/temp_const.dart';
|
||||
|
||||
class SceneApi {
|
||||
static final HTTPService _httpService = HTTPService();
|
||||
@ -36,7 +37,6 @@ class SceneApi {
|
||||
static Future<Map<String, dynamic>> createAutomation(
|
||||
CreateAutomationModel createAutomationModel) async {
|
||||
try {
|
||||
debugPrint("automation body ${createAutomationModel.toMap()}");
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.createAutomation,
|
||||
body: createAutomationModel.toMap(),
|
||||
@ -77,7 +77,8 @@ class SceneApi {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getUnitScenes
|
||||
.replaceAll('{spaceUuid}', unitId)
|
||||
.replaceAll('{communityUuid}', communityId),
|
||||
.replaceAll('{communityUuid}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
queryParameters: {'showInHomePage': showInDevice},
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
@ -138,29 +139,49 @@ class SceneApi {
|
||||
path: ApiEndpoints.getAutomationDetails
|
||||
.replaceAll('{automationId}', automationId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json),
|
||||
expectedResponseModel: (json) => RoutineDetailsModel.fromMap(json),
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
//update Scene
|
||||
static updateScene(CreateSceneModel createSceneModel, String sceneId) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
|
||||
body: createSceneModel
|
||||
.toJson(sceneId.isNotEmpty == true ? sceneId : null),
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
//update automation
|
||||
static updateAutomation(
|
||||
CreateAutomationModel createAutomationModel, String automationId) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.updateAutomation
|
||||
.replaceAll('{automationId}', automationId),
|
||||
body: createAutomationModel
|
||||
.toJson(automationId.isNotEmpty == true ? automationId : null),
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
//
|
||||
// //updateAutomationStatus
|
||||
// static Future<bool> updateAutomationStatus(String automationId,
|
||||
// AutomationStatusUpdate createAutomationEnable) async {
|
||||
// try {
|
||||
// final response = await _httpService.put(
|
||||
// path: ApiEndpoints.updateAutomationStatus
|
||||
// .replaceAll('{automationId}', automationId),
|
||||
// body: createAutomationEnable.toMap(),
|
||||
// expectedResponseModel: (json) => json['success'],
|
||||
// );
|
||||
// return response;
|
||||
// } catch (e) {
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
|
||||
//getScene
|
||||
static Future<RoutineDetailsModel> getSceneDetails(String sceneId) async {
|
||||
@ -168,51 +189,14 @@ class SceneApi {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) => RoutineDetailsModel.fromJson(json),
|
||||
expectedResponseModel: (json) =>
|
||||
RoutineDetailsModel.fromMap(json['data']),
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
//
|
||||
// //update Scene
|
||||
// static updateScene(CreateSceneModel createSceneModel, String sceneId) async {
|
||||
// try {
|
||||
// final response = await _httpService.put(
|
||||
// path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
|
||||
// body: createSceneModel
|
||||
// .toJson(sceneId.isNotEmpty == true ? sceneId : null),
|
||||
// expectedResponseModel: (json) {
|
||||
// return json;
|
||||
// },
|
||||
// );
|
||||
// return response;
|
||||
// } catch (e) {
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //update automation
|
||||
// static updateAutomation(
|
||||
// CreateAutomationModel createAutomationModel, String automationId) async {
|
||||
// try {
|
||||
// final response = await _httpService.put(
|
||||
// path: ApiEndpoints.updateAutomation
|
||||
// .replaceAll('{automationId}', automationId),
|
||||
// body: createAutomationModel
|
||||
// .toJson(automationId.isNotEmpty == true ? automationId : null),
|
||||
// expectedResponseModel: (json) {
|
||||
// return json;
|
||||
// },
|
||||
// );
|
||||
// return response;
|
||||
// } catch (e) {
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
|
||||
//delete Scene
|
||||
static Future<bool> deleteScene(
|
||||
|
@ -1,29 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_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/utils/constants/api_const.dart';
|
||||
import 'package:syncrow_web/utils/constants/temp_const.dart';
|
||||
|
||||
class CommunitySpaceManagementApi {
|
||||
// Community Management APIs
|
||||
Future<List<CommunityModel>> fetchCommunities() async {
|
||||
Future<List<CommunityModel>> fetchCommunities({int page = 1}) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList,
|
||||
expectedResponseModel: (json) {
|
||||
// Access the 'data' key from the response
|
||||
List<dynamic> jsonData = json['data'];
|
||||
List<CommunityModel> allCommunities = [];
|
||||
bool hasNext = true;
|
||||
|
||||
// Check if jsonData is actually a List
|
||||
List<CommunityModel> communityList = jsonData.map((jsonItem) {
|
||||
return CommunityModel.fromJson(jsonItem);
|
||||
}).toList();
|
||||
return communityList;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
while (hasNext) {
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
queryParameters: {'page': page},
|
||||
expectedResponseModel: (json) {
|
||||
List<dynamic> jsonData = json['data'];
|
||||
hasNext = json['hasNext'] ?? false;
|
||||
int currentPage = json['page'] ?? 1;
|
||||
List<CommunityModel> communityList = jsonData.map((jsonItem) {
|
||||
return CommunityModel.fromJson(jsonItem);
|
||||
}).toList();
|
||||
|
||||
allCommunities.addAll(communityList);
|
||||
page = currentPage + 1;
|
||||
return communityList;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return allCommunities;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching communities: $e');
|
||||
return [];
|
||||
@ -33,7 +44,8 @@ class CommunitySpaceManagementApi {
|
||||
Future<CommunityModel?> getCommunityById(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.getCommunityById
|
||||
.replaceAll('{communityId}', communityId),
|
||||
expectedResponseModel: (json) {
|
||||
return CommunityModel.fromJson(json['data']);
|
||||
},
|
||||
@ -45,10 +57,12 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<CommunityModel?> createCommunity(String name, String description) async {
|
||||
Future<CommunityModel?> createCommunity(
|
||||
String name, String description) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.createCommunity,
|
||||
path: ApiEndpoints.createCommunity
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
body: {
|
||||
'name': name,
|
||||
'description': description,
|
||||
@ -67,7 +81,9 @@ class CommunitySpaceManagementApi {
|
||||
Future<bool> updateCommunity(String communityId, String name) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateCommunity.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.updateCommunity
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
body: {
|
||||
'name': name,
|
||||
},
|
||||
@ -85,7 +101,9 @@ class CommunitySpaceManagementApi {
|
||||
Future<bool> deleteCommunity(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.deleteCommunity.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.deleteCommunity
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
@ -100,7 +118,9 @@ class CommunitySpaceManagementApi {
|
||||
Future<SpacesResponse> fetchSpaces(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.listSpaces.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.listSpaces
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
expectedResponseModel: (json) {
|
||||
return SpacesResponse.fromJson(json);
|
||||
},
|
||||
@ -126,7 +146,8 @@ class CommunitySpaceManagementApi {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpace
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{spaceId}', spaceId),
|
||||
.replaceAll('{spaceId}', spaceId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
expectedResponseModel: (json) {
|
||||
return SpaceModel.fromJson(json);
|
||||
},
|
||||
@ -162,7 +183,9 @@ class CommunitySpaceManagementApi {
|
||||
body['parentUuid'] = parentId;
|
||||
}
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.createSpace.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.createSpace
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
body: body,
|
||||
expectedResponseModel: (json) {
|
||||
return SpaceModel.fromJson(json['data']);
|
||||
@ -203,7 +226,8 @@ class CommunitySpaceManagementApi {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateSpace
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{spaceId}', spaceId),
|
||||
.replaceAll('{spaceId}', spaceId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
body: body,
|
||||
expectedResponseModel: (json) {
|
||||
return SpaceModel.fromJson(json['data']);
|
||||
@ -221,7 +245,8 @@ class CommunitySpaceManagementApi {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.deleteSpace
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{spaceId}', spaceId),
|
||||
.replaceAll('{spaceId}', spaceId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
@ -236,10 +261,13 @@ class CommunitySpaceManagementApi {
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpaceHierarchy.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.getSpaceHierarchy
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', TempConst.projectId),
|
||||
expectedResponseModel: (json) {
|
||||
final spaceModels =
|
||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
|
||||
return spaceModels;
|
||||
},
|
||||
|
@ -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/device_managment/all_devices/view/device_managment_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/utils/constants/routes_const.dart';
|
||||
|
||||
@ -32,7 +32,7 @@ class AppRoutes {
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutesConst.spacesManagementPage,
|
||||
builder: (context, state) => SpaceManagementPage()),
|
||||
builder: (context, state) => const SpaceManagementPage()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -36,21 +36,21 @@ abstract class ApiEndpoints {
|
||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||
|
||||
// Space Module
|
||||
static const String createSpace = '/communities/{communityId}/spaces';
|
||||
static const String listSpaces = '/communities/{communityId}/spaces';
|
||||
static const String deleteSpace = '/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String updateSpace = '/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpace = '/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpaceHierarchy = '/communities/{communityId}/spaces';
|
||||
static const String createSpace = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String listSpaces = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String deleteSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String updateSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpaceHierarchy = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
|
||||
// Community Module
|
||||
static const String createCommunity = '/communities';
|
||||
static const String getCommunityList = '/communities';
|
||||
static const String getCommunityById = '/communities/{communityId}';
|
||||
static const String updateCommunity = '/communities/{communityId}';
|
||||
static const String deleteCommunity = '/communities/{communityId}';
|
||||
static const String getUserCommunities = '/communities/user/{userUuid}';
|
||||
static const String createUserCommunity = '/communities/user';
|
||||
static const String createCommunity = '/projects/{projectId}/communities';
|
||||
static const String getCommunityList = '/projects/{projectId}/communities';
|
||||
static const String getCommunityById = '/projects/{projectId}/communities/{communityId}';
|
||||
static const String updateCommunity = '/projects/{projectId}/communities/{communityId}';
|
||||
static const String deleteCommunity = '/projects/{projectId}communities/{communityId}';
|
||||
static const String getUserCommunities = '/projects/{projectId}/communities/user/{userUuid}';
|
||||
static const String createUserCommunity = '/projects/{projectId}/communities/user';
|
||||
static const String getDeviceLogsByDate =
|
||||
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
@ -68,10 +68,13 @@ abstract class ApiEndpoints {
|
||||
static const String getIconScene = '/scene/icon';
|
||||
static const String createScene = '/scene/tap-to-run';
|
||||
static const String createAutomation = '/automation';
|
||||
static const String getUnitScenes = '/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
|
||||
static const String getUnitScenes = '/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
|
||||
static const String getAutomationDetails = '/automation/details/{automationId}';
|
||||
static const String getScene = '/scene/tap-to-run/{sceneId}';
|
||||
static const String deleteScene = '/scene/tap-to-run/{sceneId}';
|
||||
|
||||
static const String deleteAutomation = '/automation/{automationId}';
|
||||
static const String updateScene = '/scene/tap-to-run/{sceneId}';
|
||||
|
||||
static const String updateAutomation = '/automation/{automationId}';
|
||||
}
|
||||
|
3
lib/utils/constants/temp_const.dart
Normal file
3
lib/utils/constants/temp_const.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class TempConst {
|
||||
static const projectId = '0e62577c-06fa-41b9-8a92-99a21fbaf51c';
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
|
||||
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) {
|
||||
final key = NavigationService.snackbarKey;
|
||||
BuildContext? currentContext = key?.currentContext;
|
||||
@ -29,8 +59,10 @@ class CustomSnackBar {
|
||||
),
|
||||
Text(
|
||||
message,
|
||||
style: Theme.of(currentContext).textTheme.bodySmall!.copyWith(
|
||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||
style: Theme.of(currentContext)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||
)
|
||||
]),
|
||||
);
|
||||
|
Reference in New Issue
Block a user