mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-27 19:34:55 +00:00
Compare commits
7 Commits
SP-1175-FE
...
SP-1597-FE
| Author | SHA1 | Date | |
|---|---|---|---|
| 2797dce637 | |||
| 906c2d0430 | |||
| cabd37a08a | |||
| 98ad4246e1 | |||
| ba08fcf71f | |||
| cf5e05a888 | |||
| a44d4231f1 |
3
assets/icons/close_settings_icon.svg
Normal file
3
assets/icons/close_settings_icon.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 24C8.79469 24 5.78123 22.7518 3.51469 20.4853C1.24823 18.2188 0 15.2053 0 12C0 8.79469 1.24823 5.78123 3.51469 3.51469C5.78123 1.24823 8.79469 0 12 0C15.2053 0 18.2188 1.24823 20.4853 3.51469C22.7518 5.78123 24 8.79469 24 12C24 15.2053 22.7518 18.2188 20.4853 20.4853C18.2188 22.7518 15.2053 24 12 24ZM12 1.875C9.2955 1.875 6.75291 2.92819 4.84055 4.84055C2.92819 6.75291 1.875 9.2955 1.875 12C1.875 14.7045 2.92819 17.2471 4.84055 19.1595C6.75291 21.0718 9.2955 22.125 12 22.125C14.7045 22.125 17.2471 21.0718 19.1595 19.1595C21.0718 17.2471 22.125 14.7045 22.125 12C22.125 9.2955 21.0718 6.75291 19.1595 4.84055C17.2471 2.92819 14.7045 1.875 12 1.875ZM15.9775 17.3033L12 13.3258L8.02252 17.3033L6.6967 15.9775L10.6742 12L6.6967 8.02252L8.02252 6.6967L12 10.6742L15.9775 6.6967L17.3033 8.02252L13.3258 12L17.3033 15.9775L15.9775 17.3033Z" fill="#999999" fill-opacity="0.7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 992 B |
3
assets/icons/edit_name_icon_settings.svg
Normal file
3
assets/icons/edit_name_icon_settings.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.4854 3.1332L9.8668 0.515228C9.64704 0.295536 9.34903 0.172119 9.03829 0.172119C8.72755 0.172119 8.42953 0.295536 8.20977 0.515228L0.983989 7.74042C0.874797 7.84895 0.788225 7.97806 0.729285 8.12027C0.670346 8.26249 0.640212 8.41499 0.640629 8.56894V11.1875C0.640629 11.4983 0.764094 11.7964 0.983864 12.0161C1.20363 12.2359 1.5017 12.3594 1.8125 12.3594H11.6563C11.8427 12.3594 12.0216 12.2853 12.1534 12.1534C12.2853 12.0216 12.3594 11.8427 12.3594 11.6562C12.3594 11.4698 12.2853 11.2909 12.1534 11.1591C12.0216 11.0272 11.8427 10.9531 11.6563 10.9531H6.32422L12.4854 4.79081C12.5942 4.68199 12.6806 4.55278 12.7395 4.41057C12.7984 4.26836 12.8288 4.11594 12.8288 3.96201C12.8288 3.80807 12.7984 3.65565 12.7395 3.51344C12.6806 3.37123 12.5942 3.24202 12.4854 3.1332ZM4.33204 10.9531H2.04688V8.66796L6.96875 3.74609L9.25391 6.03124L4.33204 10.9531ZM10.25 5.03515L7.96485 2.74999L9.03946 1.67538L11.3246 3.96054L10.25 5.03515Z" fill="#D5D5D5"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -13,7 +13,6 @@ import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
|||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
|
||||||
import 'package:syncrow_web/services/auth_api.dart';
|
import 'package:syncrow_web/services/auth_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/strings_manager.dart';
|
import 'package:syncrow_web/utils/constants/strings_manager.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
|
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
|
||||||
@ -100,8 +99,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changePassword(
|
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||||
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
|
||||||
emit(LoadingForgetState());
|
emit(LoadingForgetState());
|
||||||
try {
|
try {
|
||||||
var response = await AuthenticationAPI.verifyOtp(
|
var response = await AuthenticationAPI.verifyOtp(
|
||||||
@ -115,14 +113,14 @@ Future<void> changePassword(
|
|||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
emit(SuccessForgetState());
|
emit(SuccessForgetState());
|
||||||
}
|
}
|
||||||
} on APIException catch (e) {
|
} on DioException catch (e) {
|
||||||
final errorMessage = e.message;
|
final errorData = e.response!.data;
|
||||||
|
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
|
||||||
validate = errorMessage;
|
validate = errorMessage;
|
||||||
emit(AuthInitialState());
|
emit(AuthInitialState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String? validateCode(String? value) {
|
String? validateCode(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Code is required';
|
return 'Code is required';
|
||||||
@ -151,7 +149,6 @@ Future<void> changePassword(
|
|||||||
static UserModel? user;
|
static UserModel? user;
|
||||||
bool showValidationMessage = false;
|
bool showValidationMessage = false;
|
||||||
|
|
||||||
|
|
||||||
void _login(LoginButtonPressed event, Emitter<AuthState> emit) async {
|
void _login(LoginButtonPressed event, Emitter<AuthState> emit) async {
|
||||||
emit(AuthLoading());
|
emit(AuthLoading());
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
@ -168,20 +165,21 @@ Future<void> changePassword(
|
|||||||
password: event.password,
|
password: event.password,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} on APIException catch (e) {
|
} on DioException catch (e) {
|
||||||
validate = e.message;
|
final errorData = e.response!.data;
|
||||||
emit(LoginInitial());
|
String errorMessage = errorData['error']['message'];
|
||||||
return;
|
if (errorMessage == "Access denied for web platform") {
|
||||||
} catch (e) {
|
validate = errorMessage;
|
||||||
validate = 'Something went wrong';
|
} else {
|
||||||
|
validate = 'Invalid Credentials!';
|
||||||
|
}
|
||||||
emit(LoginInitial());
|
emit(LoginInitial());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.accessTokenIsNotEmpty) {
|
if (token.accessTokenIsNotEmpty) {
|
||||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
await storage.write(
|
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||||
key: Token.loginAccessTokenKey, value: token.accessToken);
|
|
||||||
const FlutterSecureStorage().write(
|
const FlutterSecureStorage().write(
|
||||||
key: UserModel.userUuidKey,
|
key: UserModel.userUuidKey,
|
||||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||||
@ -197,7 +195,6 @@ Future<void> changePassword(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
checkBoxToggle(
|
checkBoxToggle(
|
||||||
CheckBoxEvent event,
|
CheckBoxEvent event,
|
||||||
Emitter<AuthState> emit,
|
Emitter<AuthState> emit,
|
||||||
|
|||||||
@ -211,6 +211,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||||
? _toggleSelectAll
|
? _toggleSelectAll
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -281,6 +282,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
||||||
vertical: 4),
|
vertical: 4),
|
||||||
|
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: context.textTheme.titleSmall!.copyWith(
|
style: context.textTheme.titleSmall!.copyWith(
|
||||||
@ -301,6 +303,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
required int rowIndex,
|
required int rowIndex,
|
||||||
required int columnIndex,
|
required int columnIndex,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
bool isBatteryLevel = content.endsWith('%');
|
bool isBatteryLevel = content.endsWith('%');
|
||||||
double? batteryLevel;
|
double? batteryLevel;
|
||||||
|
|
||||||
@ -313,6 +316,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
return _buildSettingsIcon(rowIndex, size);
|
return _buildSettingsIcon(rowIndex, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Color? statusColor;
|
Color? statusColor;
|
||||||
switch (content) {
|
switch (content) {
|
||||||
case 'Effective':
|
case 'Effective':
|
||||||
|
|||||||
@ -95,7 +95,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
return const RoutinesView();
|
return const RoutinesView();
|
||||||
}
|
}
|
||||||
if (state.createRoutineView) {
|
if (state.createRoutineView) {
|
||||||
return CreateNewRoutineView();
|
return const CreateNewRoutineView();
|
||||||
}
|
}
|
||||||
|
|
||||||
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||||
|
|||||||
@ -6,9 +6,11 @@ import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/device_settings_panel.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
@ -58,7 +60,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
'Low Battery ($lowBatteryCount)',
|
'Low Battery ($lowBatteryCount)',
|
||||||
];
|
];
|
||||||
|
|
||||||
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
final buttonLabel =
|
||||||
|
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@ -105,18 +108,23 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
if (selectedDevices.length == 1) {
|
if (selectedDevices.length == 1) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DeviceControlDialog(
|
builder: (context) =>
|
||||||
|
DeviceControlDialog(
|
||||||
device: selectedDevices.first,
|
device: selectedDevices.first,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (selectedDevices.length > 1) {
|
} else if (selectedDevices.length >
|
||||||
final productTypes = selectedDevices
|
1) {
|
||||||
.map((device) => device.productType)
|
final productTypes =
|
||||||
.toSet();
|
selectedDevices
|
||||||
|
.map((device) =>
|
||||||
|
device.productType)
|
||||||
|
.toSet();
|
||||||
if (productTypes.length == 1) {
|
if (productTypes.length == 1) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DeviceBatchControlDialog(
|
builder: (context) =>
|
||||||
|
DeviceBatchControlDialog(
|
||||||
devices: selectedDevices,
|
devices: selectedDevices,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -130,7 +138,9 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: isControlButtonEnabled ? Colors.white : Colors.grey,
|
color: isControlButtonEnabled
|
||||||
|
? Colors.white
|
||||||
|
: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -166,29 +176,40 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
'Installation Date and Time',
|
'Installation Date and Time',
|
||||||
'Status',
|
'Status',
|
||||||
'Last Offline Date and Time',
|
'Last Offline Date and Time',
|
||||||
|
'Settings'
|
||||||
],
|
],
|
||||||
data: devicesToShow.map((device) {
|
data: devicesToShow.map((device) {
|
||||||
final combinedSpaceNames = device.spaces != null
|
final combinedSpaceNames = device.spaces != null
|
||||||
? device.spaces!.map((space) => space.spaceName).join(' > ') +
|
? device.spaces!
|
||||||
|
.map((space) => space.spaceName)
|
||||||
|
.join(' > ') +
|
||||||
(device.community != null
|
(device.community != null
|
||||||
? ' > ${device.community!.name}'
|
? ' > ${device.community!.name}'
|
||||||
: '')
|
: '')
|
||||||
: (device.community != null ? device.community!.name : '');
|
: (device.community != null
|
||||||
|
? device.community!.name
|
||||||
|
: '');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
device.name ?? '',
|
device.name ?? '',
|
||||||
device.productName ?? '',
|
device.productName ?? '',
|
||||||
device.uuid ?? '',
|
device.uuid ?? '',
|
||||||
(device.spaces != null && device.spaces!.isNotEmpty)
|
(device.spaces != null &&
|
||||||
|
device.spaces!.isNotEmpty)
|
||||||
? device.spaces![0].spaceName
|
? device.spaces![0].spaceName
|
||||||
: '',
|
: '',
|
||||||
combinedSpaceNames,
|
combinedSpaceNames,
|
||||||
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
|
device.batteryLevel != null
|
||||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
? '${device.batteryLevel}%'
|
||||||
(device.createTime ?? 0) * 1000)),
|
: '-',
|
||||||
|
formatDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
(device.createTime ?? 0) * 1000)),
|
||||||
device.online == true ? 'Online' : 'Offline',
|
device.online == true ? 'Online' : 'Offline',
|
||||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
formatDateTime(
|
||||||
(device.updateTime ?? 0) * 1000)),
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
(device.updateTime ?? 0) * 1000)),
|
||||||
|
'Settings',
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onSelectionChanged: (selectedRows) {
|
onSelectionChanged: (selectedRows) {
|
||||||
@ -202,6 +223,10 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
.map((device) => device.uuid!)
|
.map((device) => device.uuid!)
|
||||||
.toList(),
|
.toList(),
|
||||||
isEmpty: devicesToShow.isEmpty,
|
isEmpty: devicesToShow.isEmpty,
|
||||||
|
onSettingsPressed: (rowIndex) {
|
||||||
|
final device = devicesToShow[rowIndex];
|
||||||
|
showDeviceSettingsSidebar(context, device);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -213,4 +238,37 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showDeviceSettingsSidebar(BuildContext context, AllDevicesModel device) {
|
||||||
|
showGeneralDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: "Device Settings",
|
||||||
|
transitionDuration: const Duration(milliseconds: 300),
|
||||||
|
pageBuilder: (context, anim1, anim2) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Material(
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
child: DeviceSettingsPanel(
|
||||||
|
device: device,
|
||||||
|
onClose: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionBuilder: (context, anim1, anim2, child) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween<Offset>(
|
||||||
|
begin: const Offset(1, 0),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(anim1),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,165 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||||
|
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||||
|
part 'setting_bloc_event.dart';
|
||||||
|
|
||||||
|
class SettingDeviceBloc extends Bloc<DeviceSettingEvent, DeviceSettingsState> {
|
||||||
|
final String deviceId;
|
||||||
|
SettingDeviceBloc({
|
||||||
|
required this.deviceId,
|
||||||
|
}) : super(const DeviceSettingsInitial()) {
|
||||||
|
on<DeviceSettingInitialInfo>(_fetchDeviceInfo);
|
||||||
|
on<SettingBlocSaveName>(_saveName);
|
||||||
|
on<ChangeNameEvent>(_changeName);
|
||||||
|
on<SettingBlocDeleteDevice>(_deleteDevice);
|
||||||
|
on<SettingBlocFetchRooms>(_fetchRooms);
|
||||||
|
on<SettingBlocAssignRoom>(_onAssignDevice);
|
||||||
|
}
|
||||||
|
final TextEditingController nameController = TextEditingController();
|
||||||
|
List<SubSpaceModel> roomsList = [];
|
||||||
|
bool isEditingName = false;
|
||||||
|
|
||||||
|
bool _validateInputs() {
|
||||||
|
final nameError = _fullNameValidator(nameController.text);
|
||||||
|
if (nameError != null) {
|
||||||
|
CustomSnackBar.displaySnackBar(nameError);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _fullNameValidator(String? value) {
|
||||||
|
if (value == null) return 'name is required';
|
||||||
|
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
|
||||||
|
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
|
||||||
|
return 'name must be between 2 and 30 characters long';
|
||||||
|
}
|
||||||
|
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
|
||||||
|
return 'Only alphanumeric characters, space, dash and single quote are allowed';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveName(
|
||||||
|
SettingBlocSaveName event, Emitter<DeviceSettingsState> emit) async {
|
||||||
|
if (_validateInputs()) return;
|
||||||
|
try {
|
||||||
|
emit(DeviceSettingsLoading());
|
||||||
|
await DevicesManagementApi.putDeviceName(
|
||||||
|
deviceId: deviceId, deviceName: nameController.text);
|
||||||
|
add(DeviceSettingInitialInfo());
|
||||||
|
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||||
|
emit(DeviceSettingsUpdate(deviceName: nameController.text));
|
||||||
|
} catch (e) {
|
||||||
|
emit(DeviceSettingsError(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchDeviceInfo(
|
||||||
|
DeviceSettingInitialInfo event, Emitter<DeviceSettingsState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(DeviceSettingsLoading());
|
||||||
|
var response = await DevicesManagementApi.getDeviceInfo(deviceId);
|
||||||
|
DeviceInfoModel deviceInfo = DeviceInfoModel.fromJson(response);
|
||||||
|
nameController.text = deviceInfo.name;
|
||||||
|
emit(DeviceSettingsUpdate(
|
||||||
|
deviceName: nameController.text,
|
||||||
|
deviceInfo: deviceInfo,
|
||||||
|
roomsList: roomsList,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
emit(DeviceSettingsError(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool editName = false;
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
void _changeName(ChangeNameEvent event, Emitter<DeviceSettingsState> emit) {
|
||||||
|
emit(DeviceSettingsInitial(
|
||||||
|
deviceName: nameController.text,
|
||||||
|
deviceId: deviceId,
|
||||||
|
isEditingName: event.value ?? false,
|
||||||
|
editingNameValue: event.value?.toString() ?? '',
|
||||||
|
deviceInfo: state.deviceInfo,
|
||||||
|
));
|
||||||
|
editName = event.value!;
|
||||||
|
if (editName) {
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
focusNode.requestFocus();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
add(const SettingBlocSaveName());
|
||||||
|
focusNode.unfocus();
|
||||||
|
}
|
||||||
|
emit(DeviceSettingsUpdate(
|
||||||
|
deviceName: nameController.text,
|
||||||
|
deviceInfo: state.deviceInfo,
|
||||||
|
roomsList: roomsList,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deleteDevice(
|
||||||
|
SettingBlocDeleteDevice event, Emitter<DeviceSettingsState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(DeviceSettingsLoading());
|
||||||
|
await DevicesManagementApi.resetDevice(devicesUuid: deviceId);
|
||||||
|
CustomSnackBar.displaySnackBar('Reset Successfully');
|
||||||
|
emit(DeviceSettingsUpdate(
|
||||||
|
deviceName: nameController.text,
|
||||||
|
deviceInfo: state.deviceInfo,
|
||||||
|
roomsList: roomsList,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
emit(DeviceSettingsError(message: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAssignDevice(
|
||||||
|
SettingBlocAssignRoom event, Emitter<DeviceSettingsState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(DeviceSettingsLoading());
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
await CommunitySpaceManagementApi.assignDeviceToRoom(
|
||||||
|
communityId: event.communityUuid,
|
||||||
|
spaceId: event.spaceUuid,
|
||||||
|
subSpaceId: event.subSpaceUuid,
|
||||||
|
deviceId: deviceId,
|
||||||
|
projectId: projectUuid);
|
||||||
|
add(DeviceSettingInitialInfo());
|
||||||
|
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||||
|
emit(DeviceSettingsSaveSelectionSuccess());
|
||||||
|
} catch (e) {
|
||||||
|
emit(DeviceSettingsError(message: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fetchRooms(
|
||||||
|
SettingBlocFetchRooms event, Emitter<DeviceSettingsState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(DeviceSettingsLoading());
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
roomsList = await CommunitySpaceManagementApi.getSubSpaceBySpaceId(
|
||||||
|
communityId: event.communityUuid,
|
||||||
|
spaceId: event.spaceUuid,
|
||||||
|
projectId: projectUuid);
|
||||||
|
emit(DeviceSettingsUpdate(
|
||||||
|
deviceName: nameController.text,
|
||||||
|
deviceInfo: state.deviceInfo,
|
||||||
|
roomsList: roomsList,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
emit(DeviceSettingsError(message: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
part of 'setting_bloc_bloc.dart';
|
||||||
|
|
||||||
|
abstract class DeviceSettingEvent extends Equatable {
|
||||||
|
const DeviceSettingEvent();
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingBlocSaveDeviceName extends DeviceSettingEvent {
|
||||||
|
final String deviceName;
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const SettingBlocSaveDeviceName(
|
||||||
|
{required this.deviceName, required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [deviceName, deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingBlocStartEditingName extends DeviceSettingEvent {}
|
||||||
|
|
||||||
|
class SettingBlocCancelEditingName extends DeviceSettingEvent {}
|
||||||
|
|
||||||
|
class SettingBlocChangeEditingNameValue extends DeviceSettingEvent {
|
||||||
|
final String value;
|
||||||
|
const SettingBlocChangeEditingNameValue(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingBlocFetchRooms extends DeviceSettingEvent {
|
||||||
|
final String communityUuid;
|
||||||
|
final String spaceUuid;
|
||||||
|
|
||||||
|
const SettingBlocFetchRooms(
|
||||||
|
{required this.communityUuid, required this.spaceUuid});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [communityUuid, spaceUuid];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingBlocSaveName extends DeviceSettingEvent {
|
||||||
|
const SettingBlocSaveName();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingInitialInfo extends DeviceSettingEvent {}
|
||||||
|
|
||||||
|
class ChangeNameEvent extends DeviceSettingEvent {
|
||||||
|
final bool? value;
|
||||||
|
const ChangeNameEvent({this.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingBlocDeleteDevice extends DeviceSettingEvent {}
|
||||||
|
|
||||||
|
class SettingBlocAssignRoom extends DeviceSettingEvent {
|
||||||
|
final String communityUuid;
|
||||||
|
final String spaceUuid;
|
||||||
|
final String subSpaceUuid;
|
||||||
|
|
||||||
|
const SettingBlocAssignRoom({
|
||||||
|
required this.communityUuid,
|
||||||
|
required this.spaceUuid,
|
||||||
|
required this.subSpaceUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [spaceUuid, communityUuid, subSpaceUuid];
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
|
|
||||||
|
abstract class DeviceSettingsState extends Equatable {
|
||||||
|
const DeviceSettingsState({this.deviceInfo});
|
||||||
|
|
||||||
|
final DeviceInfoModel? deviceInfo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [deviceInfo];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingsInitial extends DeviceSettingsState {
|
||||||
|
final String deviceName;
|
||||||
|
final String deviceId;
|
||||||
|
final bool isEditingName;
|
||||||
|
final String editingNameValue;
|
||||||
|
|
||||||
|
const DeviceSettingsInitial({
|
||||||
|
this.deviceName = '',
|
||||||
|
this.deviceId = '',
|
||||||
|
this.isEditingName = false,
|
||||||
|
this.editingNameValue = '',
|
||||||
|
super.deviceInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
DeviceSettingsInitial copyWith({
|
||||||
|
String? deviceName,
|
||||||
|
String? deviceId,
|
||||||
|
bool? isEditingName,
|
||||||
|
String? editingNameValue,
|
||||||
|
}) =>
|
||||||
|
DeviceSettingsInitial(
|
||||||
|
deviceName: deviceName ?? this.deviceName,
|
||||||
|
deviceId: deviceId ?? this.deviceId,
|
||||||
|
isEditingName: isEditingName ?? this.isEditingName,
|
||||||
|
editingNameValue: editingNameValue ?? this.editingNameValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props =>
|
||||||
|
[deviceName, deviceId, isEditingName, editingNameValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingsLoading extends DeviceSettingsState {}
|
||||||
|
|
||||||
|
class DeviceSettingsUpdate extends DeviceSettingsState {
|
||||||
|
final String? deviceName;
|
||||||
|
final List<SubSpaceModel> roomsList;
|
||||||
|
|
||||||
|
const DeviceSettingsUpdate({
|
||||||
|
this.deviceName,
|
||||||
|
super.deviceInfo,
|
||||||
|
this.roomsList = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [deviceName, deviceInfo, roomsList];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingsError extends DeviceSettingsState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const DeviceSettingsError({required this.message});
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingsFetchRooms extends DeviceSettingsState {
|
||||||
|
final List<SubSpaceModel> roomsList;
|
||||||
|
|
||||||
|
const DeviceSettingsFetchRooms({required this.roomsList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [roomsList];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceSettingsSaveSelectionSuccess extends DeviceSettingsState {}
|
||||||
|
|
||||||
|
class ChangeNameState extends DeviceSettingsState {}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class DeviceIconTypeHelper {
|
||||||
|
static const Map<String, String> _iconMap = {
|
||||||
|
'AC': Assets.ac,
|
||||||
|
'GW': Assets.gateway,
|
||||||
|
'CPS': Assets.sensors,
|
||||||
|
'DL': Assets.doorLock,
|
||||||
|
'WPS': Assets.sensors,
|
||||||
|
'3G': Assets.gangSwitch,
|
||||||
|
'2G': Assets.twoGang,
|
||||||
|
'1G': Assets.oneGang,
|
||||||
|
'CUR': Assets.curtain,
|
||||||
|
'WH': Assets.waterHeater,
|
||||||
|
'DS': Assets.doorSensor,
|
||||||
|
'1GT': Assets.oneTouchSwitch,
|
||||||
|
'2GT': Assets.twoTouchSwitch,
|
||||||
|
'3GT': Assets.threeTouchSwitch,
|
||||||
|
'GD': Assets.garageDoor,
|
||||||
|
'WL': Assets.waterLeakNormal,
|
||||||
|
'NCPS': Assets.sensors,
|
||||||
|
};
|
||||||
|
|
||||||
|
static String getDeviceIconByTypeCode(String? typeCode) {
|
||||||
|
if (typeCode == null) return Assets.logoHorizontal;
|
||||||
|
return _iconMap[typeCode] ?? Assets.logoHorizontal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class DeviceManagementContent extends StatelessWidget {
|
||||||
|
const DeviceManagementContent({
|
||||||
|
super.key,
|
||||||
|
required this.device,
|
||||||
|
required this.subSpaces,
|
||||||
|
required this.deviceInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
final AllDevicesModel device;
|
||||||
|
final List<SubSpaceModel> subSpaces;
|
||||||
|
final DeviceInfoModel deviceInfo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget infoRow(
|
||||||
|
{required String label,
|
||||||
|
required String value,
|
||||||
|
Widget? trailing,
|
||||||
|
required Color? valueColor}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: context.theme.textTheme.bodyMedium!.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: context.theme.textTheme.bodyMedium!
|
||||||
|
.copyWith(fontSize: 14, color: valueColor),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
trailing ?? const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultContainer(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showSubSpaceDialog(
|
||||||
|
context,
|
||||||
|
communityUuid: device.community!.uuid!,
|
||||||
|
spaceUuid: device.spaces!.first.uuid!,
|
||||||
|
subSpaces: subSpaces,
|
||||||
|
selected: device.subspace!.uuid,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: infoRow(
|
||||||
|
label: 'Sub-Space:',
|
||||||
|
value: deviceInfo.subspace.subspaceName,
|
||||||
|
valueColor: ColorsManager.textGray,
|
||||||
|
trailing: const Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 16,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(color: ColorsManager.dividerColor),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: infoRow(
|
||||||
|
label: 'Virtual Address:',
|
||||||
|
value: deviceInfo.productUuid,
|
||||||
|
valueColor: ColorsManager.blackColor,
|
||||||
|
trailing: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: device.productUuid ?? ''),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Virtual Address copied to clipboard'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.copy,
|
||||||
|
size: 16,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(color: ColorsManager.dividerColor),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: infoRow(
|
||||||
|
label: 'MAC Address:',
|
||||||
|
valueColor: ColorsManager.blackColor,
|
||||||
|
value: deviceInfo.macAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/device_icon_type_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/device_management_content.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/remove_device_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.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';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class DeviceSettingsPanel extends StatelessWidget {
|
||||||
|
final VoidCallback? onClose;
|
||||||
|
final AllDevicesModel device;
|
||||||
|
const DeviceSettingsPanel({super.key, this.onClose, required this.device});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sectionTitle = context.theme.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
);
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => SettingDeviceBloc(
|
||||||
|
deviceId: device.uuid ?? '',
|
||||||
|
)
|
||||||
|
..add(DeviceSettingInitialInfo())
|
||||||
|
..add(SettingBlocFetchRooms(
|
||||||
|
communityUuid: device.community!.uuid!,
|
||||||
|
spaceUuid: device.spaces!.first.uuid!,
|
||||||
|
)),
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return BlocBuilder<SettingDeviceBloc, DeviceSettingsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final _bloc = context.read<SettingDeviceBloc>();
|
||||||
|
final iconPath = DeviceIconTypeHelper.getDeviceIconByTypeCode(
|
||||||
|
device.productType);
|
||||||
|
final deviceInfo = state is DeviceSettingsUpdate
|
||||||
|
? state.deviceInfo ?? DeviceInfoModel.empty()
|
||||||
|
: DeviceInfoModel.empty();
|
||||||
|
final subSpaces =
|
||||||
|
state is DeviceSettingsUpdate ? state.roomsList ?? [] : [];
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
|
color: ColorsManager.grey25,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20, vertical: 24),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: SvgPicture.asset(Assets.closeSettingsIcon),
|
||||||
|
onPressed:
|
||||||
|
onClose ?? () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Device Settings',
|
||||||
|
style:
|
||||||
|
context.theme.textTheme.titleLarge!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
DefaultContainer(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 40,
|
||||||
|
backgroundColor:
|
||||||
|
const Color.fromARGB(177, 213, 213, 213),
|
||||||
|
child: CircleAvatar(
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
radius: 36,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
iconPath,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Device Name:',
|
||||||
|
style: context.textTheme.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
maxLength: 30,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
focusNode: _bloc.focusNode,
|
||||||
|
controller: _bloc.nameController,
|
||||||
|
enabled: _bloc.editName,
|
||||||
|
onFieldSubmitted: (value) {
|
||||||
|
_bloc.add(const ChangeNameEvent(
|
||||||
|
value: false));
|
||||||
|
},
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
fillColor: Colors.white10,
|
||||||
|
counterText: '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Visibility(
|
||||||
|
visible: _bloc.editName != true,
|
||||||
|
replacement: const SizedBox(),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_bloc.add(
|
||||||
|
const ChangeNameEvent(value: true));
|
||||||
|
},
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.editNameIconSettings,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Text('Device Management', style: sectionTitle),
|
||||||
|
DeviceManagementContent(
|
||||||
|
device: device,
|
||||||
|
subSpaces: subSpaces.cast<SubSpaceModel>(),
|
||||||
|
deviceInfo: deviceInfo,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
RemoveDeviceWidget(bloc: _bloc),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (state is DeviceSettingsLoading)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: ColorsManager.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class RemoveDeviceWidget extends StatelessWidget {
|
||||||
|
const RemoveDeviceWidget({
|
||||||
|
super.key,
|
||||||
|
required SettingDeviceBloc bloc,
|
||||||
|
}) : _bloc = bloc;
|
||||||
|
|
||||||
|
final SettingDeviceBloc _bloc;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
'Remove Device',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: ColorsManager.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
content: Text(
|
||||||
|
'Are you sure you want to remove this device?',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Cancel',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_bloc.add(SettingBlocDeleteDevice());
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Remove',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(25),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Remove Device',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
color: ColorsManager.red,
|
||||||
|
fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,183 @@
|
|||||||
|
class DeviceInfoModel {
|
||||||
|
final int activeTime;
|
||||||
|
final String category;
|
||||||
|
final String categoryName;
|
||||||
|
final int createTime;
|
||||||
|
final String gatewayId;
|
||||||
|
final String icon;
|
||||||
|
final String ip;
|
||||||
|
final String lat;
|
||||||
|
final String localKey;
|
||||||
|
final String lon;
|
||||||
|
final String model;
|
||||||
|
final String name;
|
||||||
|
final String nodeId;
|
||||||
|
final bool online;
|
||||||
|
final String ownerId;
|
||||||
|
final String productName;
|
||||||
|
final bool sub;
|
||||||
|
final String timeZone;
|
||||||
|
final int updateTime;
|
||||||
|
final String uuid;
|
||||||
|
final String productUuid;
|
||||||
|
final String productType;
|
||||||
|
final String permissionType;
|
||||||
|
final String macAddress;
|
||||||
|
final Subspace subspace;
|
||||||
|
|
||||||
|
DeviceInfoModel({
|
||||||
|
required this.activeTime,
|
||||||
|
required this.category,
|
||||||
|
required this.categoryName,
|
||||||
|
required this.createTime,
|
||||||
|
required this.gatewayId,
|
||||||
|
required this.icon,
|
||||||
|
required this.ip,
|
||||||
|
required this.lat,
|
||||||
|
required this.localKey,
|
||||||
|
required this.lon,
|
||||||
|
required this.model,
|
||||||
|
required this.name,
|
||||||
|
required this.nodeId,
|
||||||
|
required this.online,
|
||||||
|
required this.ownerId,
|
||||||
|
required this.productName,
|
||||||
|
required this.sub,
|
||||||
|
required this.timeZone,
|
||||||
|
required this.updateTime,
|
||||||
|
required this.uuid,
|
||||||
|
required this.productUuid,
|
||||||
|
required this.productType,
|
||||||
|
required this.permissionType,
|
||||||
|
required this.macAddress,
|
||||||
|
required this.subspace,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return DeviceInfoModel(
|
||||||
|
activeTime: json['activeTime'] as int? ?? 0,
|
||||||
|
category: json['category'] ?? '',
|
||||||
|
categoryName: json['categoryName'] as String? ?? '',
|
||||||
|
createTime: json['createTime'] as int? ?? 0,
|
||||||
|
gatewayId: json['gatewayId'] as String? ?? '',
|
||||||
|
icon: json['icon'] as String? ?? '',
|
||||||
|
ip: json['ip'] as String? ?? '',
|
||||||
|
lat: json['lat'] as String? ?? '',
|
||||||
|
localKey: json['localKey'] as String? ?? '',
|
||||||
|
lon: json['lon'] as String? ?? '',
|
||||||
|
model: json['model'] as String? ?? '',
|
||||||
|
name: json['name'] as String? ?? '',
|
||||||
|
nodeId: json['nodeId'] as String? ?? '',
|
||||||
|
online: json['online'] as bool? ?? false,
|
||||||
|
ownerId: json['ownerId'] as String? ?? '',
|
||||||
|
productName: json['productName'] as String? ?? '',
|
||||||
|
sub: json['sub'] as bool? ?? false,
|
||||||
|
timeZone: json['timeZone'] as String? ?? '',
|
||||||
|
updateTime: json['updateTime'] as int? ?? 0,
|
||||||
|
uuid: json['uuid'] as String? ?? '',
|
||||||
|
productUuid: json['productUuid'] as String? ?? '',
|
||||||
|
productType: json['productType'] as String? ?? '',
|
||||||
|
permissionType: json['permissionType'] as String? ?? '',
|
||||||
|
macAddress: json['macAddress'] as String? ?? '',
|
||||||
|
subspace:
|
||||||
|
Subspace.fromJson(json['subspace'] as Map<String, dynamic>? ?? {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'activeTime': activeTime,
|
||||||
|
'category': category,
|
||||||
|
'categoryName': categoryName,
|
||||||
|
'createTime': createTime,
|
||||||
|
'gatewayId': gatewayId,
|
||||||
|
'icon': icon,
|
||||||
|
'ip': ip,
|
||||||
|
'lat': lat,
|
||||||
|
'localKey': localKey,
|
||||||
|
'lon': lon,
|
||||||
|
'model': model,
|
||||||
|
'name': name,
|
||||||
|
'nodeId': nodeId,
|
||||||
|
'online': online,
|
||||||
|
'ownerId': ownerId,
|
||||||
|
'productName': productName,
|
||||||
|
'sub': sub,
|
||||||
|
'timeZone': timeZone,
|
||||||
|
'updateTime': updateTime,
|
||||||
|
'uuid': uuid,
|
||||||
|
'productUuid': productUuid,
|
||||||
|
'productType': productType,
|
||||||
|
'permissionType': permissionType,
|
||||||
|
'macAddress': macAddress,
|
||||||
|
'subspace': subspace.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceInfoModel empty() {
|
||||||
|
return DeviceInfoModel(
|
||||||
|
activeTime: 0,
|
||||||
|
category: '',
|
||||||
|
categoryName: '',
|
||||||
|
createTime: 0,
|
||||||
|
gatewayId: '',
|
||||||
|
icon: '',
|
||||||
|
ip: '',
|
||||||
|
lat: '',
|
||||||
|
localKey: '',
|
||||||
|
lon: '',
|
||||||
|
model: '',
|
||||||
|
name: '',
|
||||||
|
nodeId: '',
|
||||||
|
online: false,
|
||||||
|
ownerId: '',
|
||||||
|
productName: '',
|
||||||
|
sub: false,
|
||||||
|
timeZone: '',
|
||||||
|
updateTime: 0,
|
||||||
|
uuid: '',
|
||||||
|
productUuid: '',
|
||||||
|
productType: '',
|
||||||
|
permissionType: '',
|
||||||
|
macAddress: '',
|
||||||
|
subspace: Subspace(
|
||||||
|
uuid: '',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
subspaceName: '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Subspace {
|
||||||
|
final String uuid;
|
||||||
|
final String createdAt;
|
||||||
|
final String updatedAt;
|
||||||
|
final String subspaceName;
|
||||||
|
|
||||||
|
Subspace({
|
||||||
|
required this.uuid,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.subspaceName,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Subspace(
|
||||||
|
uuid: json['uuid'] as String? ?? '',
|
||||||
|
createdAt: json['createdAt'] as String? ?? '',
|
||||||
|
updatedAt: json['updatedAt'] as String? ?? '',
|
||||||
|
subspaceName: json['subspaceName'] as String? ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'uuid': uuid,
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'updatedAt': updatedAt,
|
||||||
|
'subspaceName': subspaceName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||||
|
|
||||||
|
class SubSpaceModel {
|
||||||
|
final String? id;
|
||||||
|
final String? name;
|
||||||
|
List<DeviceModel>? devices;
|
||||||
|
|
||||||
|
SubSpaceModel({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.devices,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'devices': devices?.map((device) => device.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory SubSpaceModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
List<DeviceModel> devices = [];
|
||||||
|
if (json['devices'] != null) {
|
||||||
|
for (var device in json['devices']) {
|
||||||
|
devices.add(DeviceModel.fromJson(device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SubSpaceModel(
|
||||||
|
id: json['uuid'] as String? ?? '',
|
||||||
|
name: json['subspaceName'] as String? ?? '',
|
||||||
|
devices: devices.isNotEmpty ? devices : null as List<DeviceModel>?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
115
lib/pages/device_managment/device_setting/sub_space_dialog.dart
Normal file
115
lib/pages/device_managment/device_setting/sub_space_dialog.dart
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/subspace_dialog_buttons.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubSpaceDialog extends StatefulWidget {
|
||||||
|
final List<SubSpaceModel> subSpaces;
|
||||||
|
final String? selected;
|
||||||
|
final void Function(SubSpaceModel?) onConfirmed;
|
||||||
|
|
||||||
|
const SubSpaceDialog({
|
||||||
|
Key? key,
|
||||||
|
required this.subSpaces,
|
||||||
|
this.selected,
|
||||||
|
required this.onConfirmed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubSpaceDialog> createState() => _SubSpaceDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubSpaceDialogState extends State<SubSpaceDialog> {
|
||||||
|
String? _selectedId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedId = widget.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 60),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.35,
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 24, 0, 0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Sub-Space',
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
fontSize: 20),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
...widget.subSpaces.map((space) {
|
||||||
|
return RadioListTile<String>(
|
||||||
|
value: space.id!,
|
||||||
|
groupValue: _selectedId,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_selectedId = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
activeColor: Color(0xFF2962FF),
|
||||||
|
title: Text(
|
||||||
|
space.name ?? 'Unnamed Sub-Space',
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
fontSize: 15,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
controlAffinity: ListTileControlAffinity.trailing,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
const Divider(height: 1, thickness: 1),
|
||||||
|
SubSpaceDialogButtons(selectedId: _selectedId, widget: widget),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSubSpaceDialog(
|
||||||
|
BuildContext context, {
|
||||||
|
required List<SubSpaceModel> subSpaces,
|
||||||
|
String? selected,
|
||||||
|
required String communityUuid,
|
||||||
|
required String spaceUuid,
|
||||||
|
}) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (ctx) => SubSpaceDialog(
|
||||||
|
subSpaces: subSpaces,
|
||||||
|
selected: selected,
|
||||||
|
onConfirmed: (selectedModel) {
|
||||||
|
if (selectedModel != null) {
|
||||||
|
context.read<SettingDeviceBloc>().add(
|
||||||
|
SettingBlocAssignRoom(
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
spaceUuid: spaceUuid,
|
||||||
|
subSpaceUuid: selectedModel.id ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubSpaceDialogButtons extends StatelessWidget {
|
||||||
|
const SubSpaceDialogButtons({
|
||||||
|
super.key,
|
||||||
|
required String? selectedId,
|
||||||
|
required this.widget,
|
||||||
|
}) : _selectedId = selectedId;
|
||||||
|
|
||||||
|
final String? _selectedId;
|
||||||
|
final SubSpaceDialog widget;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 50,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: ColorsManager.dividerColor,
|
||||||
|
width: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Cancel',
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
left: BorderSide(
|
||||||
|
color: ColorsManager.dividerColor,
|
||||||
|
width: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: _selectedId == null
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
final selectedModel = widget.subSpaces.firstWhere(
|
||||||
|
(space) => space.id == _selectedId,
|
||||||
|
orElse: () =>
|
||||||
|
SubSpaceModel(id: null, name: '', devices: []));
|
||||||
|
widget.onConfirmed(selectedModel);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Confirm',
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: ColorsManager.secondaryColor,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSubSpaceDialog(
|
||||||
|
BuildContext context, {
|
||||||
|
required List<SubSpaceModel> subSpaces,
|
||||||
|
String? selected,
|
||||||
|
required String communityUuid,
|
||||||
|
required String spaceUuid,
|
||||||
|
}) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (ctx) => SubSpaceDialog(
|
||||||
|
subSpaces: subSpaces,
|
||||||
|
selected: selected,
|
||||||
|
onConfirmed: (selectedModel) {
|
||||||
|
if (selectedModel != null) {
|
||||||
|
context.read<SettingDeviceBloc>().add(
|
||||||
|
SettingBlocAssignRoom(
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
spaceUuid: spaceUuid,
|
||||||
|
subSpaceUuid: selectedModel.id ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
@ -16,7 +15,6 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
|
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
|
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
|
||||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
import 'package:syncrow_web/services/routines_api.dart';
|
import 'package:syncrow_web/services/routines_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
@ -66,8 +64,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
TriggerSwitchTabsEvent event,
|
TriggerSwitchTabsEvent event,
|
||||||
Emitter<RoutineState> emit,
|
Emitter<RoutineState> emit,
|
||||||
) {
|
) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
|
||||||
routineTab: event.isRoutineTab, createRoutineView: false));
|
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
if (event.isRoutineTab) {
|
if (event.isRoutineTab) {
|
||||||
add(const LoadScenes());
|
add(const LoadScenes());
|
||||||
@ -93,8 +90,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index = updatedIfItems.indexWhere(
|
int index =
|
||||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
updatedIfItems[index] = event.item;
|
updatedIfItems[index] = event.item;
|
||||||
@ -103,21 +100,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.isTabToRun) {
|
if (event.isTabToRun) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
||||||
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
||||||
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddToThenContainer(
|
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
|
||||||
AddToThenContainer event, Emitter<RoutineState> emit) {
|
|
||||||
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index = currentItems.indexWhere(
|
int index =
|
||||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
currentItems[index] = event.item;
|
currentItems[index] = event.item;
|
||||||
@ -128,8 +122,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(thenItems: currentItems));
|
emit(state.copyWith(thenItems: currentItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddFunctionsToRoutine(
|
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
||||||
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
|
||||||
try {
|
try {
|
||||||
if (event.functions.isEmpty) return;
|
if (event.functions.isEmpty) return;
|
||||||
|
|
||||||
@ -164,8 +157,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
currentSelectedFunctions[event.uniqueCustomId] =
|
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||||
List.from(event.functions);
|
|
||||||
|
|
||||||
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -173,30 +165,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadScenes(
|
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
|
||||||
LoadScenes event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> scenes = [];
|
List<ScenesModel> scenes = [];
|
||||||
try {
|
try {
|
||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
scenes.addAll(
|
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||||
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scenes.addAll(await SceneApi.getScenes(
|
scenes.addAll(await SceneApi.getScenes(
|
||||||
createRoutineBloc.selectedSpaceId,
|
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
|
||||||
createRoutineBloc.selectedCommunityId,
|
|
||||||
projectUuid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -213,8 +199,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadAutomation(
|
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||||
LoadAutomation event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> automations = [];
|
List<ScenesModel> automations = [];
|
||||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -222,22 +207,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
try {
|
try {
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
automations.addAll(
|
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
|
||||||
await SceneApi.getAutomation(spaceId, communityId, projectId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
automations.addAll(await SceneApi.getAutomation(
|
automations.addAll(await SceneApi.getAutomation(
|
||||||
createRoutineBloc.selectedSpaceId,
|
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
|
||||||
createRoutineBloc.selectedCommunityId,
|
|
||||||
projectId));
|
|
||||||
}
|
}
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: automations,
|
automations: automations,
|
||||||
@ -253,16 +233,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSearchRoutines(
|
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
|
||||||
SearchRoutines event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
emit(state.copyWith(isLoading: false, errorMessage: null));
|
emit(state.copyWith(isLoading: false, errorMessage: null));
|
||||||
emit(state.copyWith(searchText: event.query));
|
emit(state.copyWith(searchText: event.query));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onAddSelectedIcon(
|
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
|
||||||
AddSelectedIcon event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(selectedIcon: event.icon));
|
emit(state.copyWith(selectedIcon: event.icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +254,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
return actions.last['deviceId'] == 'delay';
|
return actions.last['deviceId'] == 'delay';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateScene(
|
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||||
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -290,8 +267,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -359,18 +335,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
errorMessage: 'Something went wrong',
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} on APIException catch (e) {
|
} catch (e) {
|
||||||
final errorData = e.message;
|
|
||||||
String errorMessage = errorData;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: errorMessage,
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateAutomation(
|
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
||||||
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
@ -392,8 +365,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
||||||
@ -484,8 +456,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
||||||
await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadAutomation());
|
add(const LoadAutomation());
|
||||||
@ -497,32 +468,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
));
|
));
|
||||||
CustomSnackBar.redSnackBar('Something went wrong');
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
} on APIException catch (e) {
|
} catch (e) {
|
||||||
final errorData = e.message;
|
|
||||||
String errorMessage = errorData;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: errorMessage,
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
CustomSnackBar.redSnackBar(errorMessage);
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onRemoveDragCard(
|
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
|
||||||
RemoveDragCard event, Emitter<RoutineState> emit) {
|
|
||||||
if (event.isFromThen) {
|
if (event.isFromThen) {
|
||||||
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
final selectedFunctions =
|
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
||||||
|
|
||||||
thenItems.removeAt(event.index);
|
thenItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
|
||||||
thenItems: thenItems, selectedFunctions: selectedFunctions));
|
|
||||||
} else {
|
} else {
|
||||||
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
final selectedFunctions =
|
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
||||||
|
|
||||||
ifItems.removeAt(event.index);
|
ifItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
@ -533,8 +498,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
isAutomation: false,
|
isAutomation: false,
|
||||||
isTabToRun: false));
|
isTabToRun: false));
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
|
||||||
ifItems: ifItems, selectedFunctions: selectedFunctions));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,13 +510,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onEffectiveTimeEvent(
|
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
||||||
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSetRoutineName(
|
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
|
||||||
SetRoutineName event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
routineName: event.name,
|
routineName: event.name,
|
||||||
));
|
));
|
||||||
@ -679,8 +641,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// return (thenItems, ifItems, currentFunctions);
|
// return (thenItems, ifItems, currentFunctions);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<void> _onGetSceneDetails(
|
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
|
||||||
GetSceneDetails event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
@ -728,12 +689,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// if (!deviceCards.containsKey(deviceId)) {
|
// if (!deviceCards.containsKey(deviceId)) {
|
||||||
deviceCards[deviceId] = {
|
deviceCards[deviceId] = {
|
||||||
'entityId': action.entityId,
|
'entityId': action.entityId,
|
||||||
'deviceId':
|
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
|
||||||
'uniqueCustomId':
|
? action.entityId
|
||||||
action.type == 'automation' || action.actionExecutor == 'delay'
|
: const Uuid().v4(),
|
||||||
? action.entityId
|
|
||||||
: const Uuid().v4(),
|
|
||||||
'title': action.actionExecutor == 'delay'
|
'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
? 'Delay'
|
||||||
: action.type == 'automation'
|
: action.type == 'automation'
|
||||||
@ -773,8 +732,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
||||||
} else if (action.executorProperty != null &&
|
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||||
action.actionExecutor != 'delay') {
|
|
||||||
final functions = matchingDevice?.functions ?? [];
|
final functions = matchingDevice?.functions ?? [];
|
||||||
final functionCode = action.executorProperty?.functionCode;
|
final functionCode = action.executorProperty?.functionCode;
|
||||||
for (DeviceFunction function in functions) {
|
for (DeviceFunction function in functions) {
|
||||||
@ -840,8 +798,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onResetRoutineState(
|
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||||
ResetRoutineState event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
ifItems: [],
|
ifItems: [],
|
||||||
thenItems: [],
|
thenItems: [],
|
||||||
@ -865,8 +822,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
createRoutineView: false));
|
createRoutineView: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _deleteScene(
|
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
|
||||||
DeleteScene event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
@ -875,8 +831,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
if (state.isTabToRun) {
|
if (state.isTabToRun) {
|
||||||
await SceneApi.deleteScene(
|
await SceneApi.deleteScene(
|
||||||
unitUuid: spaceBloc.state.selectedSpaces[0],
|
unitUuid: spaceBloc.state.selectedSpaces[0], sceneId: state.sceneId ?? '');
|
||||||
sceneId: state.sceneId ?? '');
|
|
||||||
} else {
|
} else {
|
||||||
await SceneApi.deleteAutomation(
|
await SceneApi.deleteAutomation(
|
||||||
unitUuid: spaceBloc.state.selectedSpaces[0],
|
unitUuid: spaceBloc.state.selectedSpaces[0],
|
||||||
@ -899,14 +854,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
add(const LoadAutomation());
|
add(const LoadAutomation());
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
emit(state.copyWith(isLoading: false, createRoutineView: false));
|
emit(state.copyWith(isLoading: false, createRoutineView: false));
|
||||||
} on APIException catch (e) {
|
} catch (e) {
|
||||||
final errorData = e.message;
|
|
||||||
String errorMessage = errorData;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: errorMessage,
|
errorMessage: 'Failed to delete scene',
|
||||||
));
|
));
|
||||||
CustomSnackBar.redSnackBar(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,8 +876,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
FutureOr<void> _fetchDevices(
|
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
||||||
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -934,21 +885,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
|
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
devices.addAll(await DevicesManagementApi()
|
devices.addAll(
|
||||||
.fetchDevices(communityId, spaceId, projectUuid));
|
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
devices.addAll(await DevicesManagementApi().fetchDevices(
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||||
createRoutineBloc.selectedCommunityId,
|
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
|
||||||
createRoutineBloc.selectedSpaceId,
|
|
||||||
projectUuid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(isLoading: false, devices: devices));
|
emit(state.copyWith(isLoading: false, devices: devices));
|
||||||
@ -957,8 +904,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateScene(
|
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
|
||||||
UpdateScene event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -972,8 +918,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -1026,8 +971,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||||
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadScenes());
|
add(const LoadScenes());
|
||||||
@ -1046,8 +990,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateAutomation(
|
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||||
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1171,11 +1114,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
errorMessage: result['message'],
|
errorMessage: result['message'],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} on APIException catch (e) {
|
} catch (e) {
|
||||||
final errorData = e.message;
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: errorData,
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1272,8 +1214,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// if (!deviceThenCards.containsKey(deviceId)) {
|
// if (!deviceThenCards.containsKey(deviceId)) {
|
||||||
deviceThenCards[deviceId] = {
|
deviceThenCards[deviceId] = {
|
||||||
'entityId': action.entityId,
|
'entityId': action.entityId,
|
||||||
'deviceId':
|
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
'uniqueCustomId': const Uuid().v4(),
|
||||||
'title': action.actionExecutor == 'delay'
|
'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
? 'Delay'
|
||||||
@ -1308,8 +1249,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
updatedFunctions[uniqueCustomId] = [];
|
updatedFunctions[uniqueCustomId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.executorProperty != null &&
|
if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||||
action.actionExecutor != 'delay') {
|
|
||||||
final functions = matchingDevice.functions;
|
final functions = matchingDevice.functions;
|
||||||
final functionCode = action.executorProperty!.functionCode;
|
final functionCode = action.executorProperty!.functionCode;
|
||||||
for (var function in functions) {
|
for (var function in functions) {
|
||||||
@ -1351,14 +1291,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ifItems = deviceIfCards.values
|
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
|
||||||
.where((card) => card['type'] == 'condition')
|
|
||||||
.toList();
|
|
||||||
final thenItems = deviceThenCards.values
|
final thenItems = deviceThenCards.values
|
||||||
.where((card) =>
|
.where((card) =>
|
||||||
card['type'] == 'action' ||
|
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
|
||||||
card['type'] == 'automation' ||
|
|
||||||
card['type'] == 'scene')
|
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1380,8 +1316,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onSceneTrigger(
|
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
|
||||||
SceneTrigger event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(loadingSceneId: event.sceneId));
|
emit(state.copyWith(loadingSceneId: event.sceneId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1423,29 +1358,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
||||||
event.automationStatusUpdate.spaceUuid,
|
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
|
||||||
event.communityId,
|
|
||||||
projectId);
|
|
||||||
|
|
||||||
// Remove from loading set safely
|
// Remove from loading set safely
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: updatedAutomations,
|
automations: updatedAutomations,
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update failed',
|
errorMessage: 'Update failed',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update error: ${e.toString()}',
|
errorMessage: 'Update error: ${e.toString()}',
|
||||||
|
|||||||
@ -48,8 +48,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(
|
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||||
builder: (context, state) {
|
|
||||||
final communities = state.searchQuery.isNotEmpty
|
final communities = state.searchQuery.isNotEmpty
|
||||||
? state.filteredCommunity
|
? state.filteredCommunity
|
||||||
: state.communityList;
|
: state.communityList;
|
||||||
@ -133,118 +132,104 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
)
|
)
|
||||||
else
|
else
|
||||||
CustomSearchBar(
|
CustomSearchBar(
|
||||||
onSearchChanged: (query) =>
|
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
||||||
context.read<SpaceTreeBloc>().add(
|
SearchQueryEvent(query),
|
||||||
SearchQueryEvent(query),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: state.isSearching
|
child: state.isSearching
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: communities.isEmpty
|
: SidebarCommunitiesList(
|
||||||
? Center(
|
onScrollToEnd: () {
|
||||||
child: Text(
|
if (!state.paginationIsLoading) {
|
||||||
'No communities found',
|
context.read<SpaceTreeBloc>().add(
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
PaginationEvent(
|
||||||
color: ColorsManager.textGray,
|
state.paginationModel,
|
||||||
),
|
state.communityList,
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
: SidebarCommunitiesList(
|
}
|
||||||
onScrollToEnd: () {
|
},
|
||||||
if (!state.paginationIsLoading) {
|
scrollController: _scrollController,
|
||||||
|
communities: communities,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return CustomExpansionTileSpaceTree(
|
||||||
|
title: communities[index].name,
|
||||||
|
isSelected: state.selectedCommunities
|
||||||
|
.contains(communities[index].uuid),
|
||||||
|
isSoldCheck: state.selectedCommunities
|
||||||
|
.contains(communities[index].uuid),
|
||||||
|
onExpansionChanged: () =>
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
PaginationEvent(
|
OnCommunityExpanded(
|
||||||
state.paginationModel,
|
communities[index].uuid,
|
||||||
state.communityList,
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
isExpanded: state.expandedCommunities.contains(
|
||||||
|
communities[index].uuid,
|
||||||
|
),
|
||||||
|
onItemSelected: () {
|
||||||
|
widget.onSelect();
|
||||||
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnCommunitySelected(
|
||||||
|
communities[index].uuid,
|
||||||
|
communities[index].spaces,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
scrollController: _scrollController,
|
children: communities[index].spaces.map(
|
||||||
communities: communities,
|
(space) {
|
||||||
itemBuilder: (context, index) {
|
return CustomExpansionTileSpaceTree(
|
||||||
return CustomExpansionTileSpaceTree(
|
title: space.name,
|
||||||
title: communities[index].name,
|
isExpanded:
|
||||||
isSelected: state.selectedCommunities
|
state.expandedSpaces.contains(space.uuid),
|
||||||
.contains(communities[index].uuid),
|
onItemSelected: () {
|
||||||
isSoldCheck: state.selectedCommunities
|
final isParentSelected = _isParentSelected(
|
||||||
.contains(communities[index].uuid),
|
state,
|
||||||
onExpansionChanged: () =>
|
communities[index],
|
||||||
|
space,
|
||||||
|
);
|
||||||
|
if (widget
|
||||||
|
.shouldDisableDeselectingChildrenOfSelectedParent &&
|
||||||
|
isParentSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
widget.onSelect();
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnCommunityExpanded(
|
OnSpaceSelected(
|
||||||
communities[index].uuid,
|
communities[index],
|
||||||
|
space.uuid ?? '',
|
||||||
|
space.children,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
isExpanded:
|
|
||||||
state.expandedCommunities.contains(
|
|
||||||
communities[index].uuid,
|
|
||||||
),
|
|
||||||
onItemSelected: () {
|
|
||||||
widget.onSelect();
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnCommunitySelected(
|
|
||||||
communities[index].uuid,
|
|
||||||
communities[index].spaces,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
children: communities[index].spaces.map(
|
|
||||||
(space) {
|
|
||||||
return CustomExpansionTileSpaceTree(
|
|
||||||
title: space.name,
|
|
||||||
isExpanded: state.expandedSpaces
|
|
||||||
.contains(space.uuid),
|
|
||||||
onItemSelected: () {
|
|
||||||
final isParentSelected =
|
|
||||||
_isParentSelected(
|
|
||||||
state,
|
|
||||||
communities[index],
|
|
||||||
space,
|
|
||||||
);
|
);
|
||||||
if (widget
|
|
||||||
.shouldDisableDeselectingChildrenOfSelectedParent &&
|
|
||||||
isParentSelected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
widget.onSelect();
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceSelected(
|
|
||||||
communities[index],
|
|
||||||
space.uuid ?? '',
|
|
||||||
space.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onExpansionChanged: () =>
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceExpanded(
|
|
||||||
communities[index].uuid,
|
|
||||||
space.uuid ?? '',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isSelected: state.selectedSpaces
|
|
||||||
.contains(space.uuid) ||
|
|
||||||
state.soldCheck
|
|
||||||
.contains(space.uuid),
|
|
||||||
isSoldCheck: state.soldCheck
|
|
||||||
.contains(space.uuid),
|
|
||||||
children: _buildNestedSpaces(
|
|
||||||
context,
|
|
||||||
state,
|
|
||||||
space,
|
|
||||||
communities[index],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
).toList(),
|
onExpansionChanged: () =>
|
||||||
);
|
context.read<SpaceTreeBloc>().add(
|
||||||
},
|
OnSpaceExpanded(
|
||||||
),
|
communities[index].uuid,
|
||||||
|
space.uuid ?? '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
isSelected: state.selectedSpaces
|
||||||
|
.contains(space.uuid) ||
|
||||||
|
state.soldCheck.contains(space.uuid),
|
||||||
|
isSoldCheck:
|
||||||
|
state.soldCheck.contains(space.uuid),
|
||||||
|
children: _buildNestedSpaces(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
space,
|
||||||
|
communities[index],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (state.paginationIsLoading)
|
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
||||||
const CircularProgressIndicator(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
class APIException implements Exception {
|
|
||||||
final String message;
|
|
||||||
|
|
||||||
APIException(this.message);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +1,18 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:syncrow_web/pages/auth/model/region_model.dart';
|
import 'package:syncrow_web/pages/auth/model/region_model.dart';
|
||||||
import 'package:syncrow_web/pages/auth/model/token.dart';
|
import 'package:syncrow_web/pages/auth/model/token.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
class AuthenticationAPI {
|
class AuthenticationAPI {
|
||||||
static Future<Token> loginWithEmail({required var model}) async {
|
static Future<Token> loginWithEmail({required var model}) async {
|
||||||
try {
|
final response = await HTTPService().post(
|
||||||
final response = await HTTPService().post(
|
path: ApiEndpoints.login,
|
||||||
path: ApiEndpoints.login,
|
body: model.toJson(),
|
||||||
body: model.toJson(),
|
showServerMessage: true,
|
||||||
showServerMessage: true,
|
expectedResponseModel: (json) {
|
||||||
expectedResponseModel: (json) {
|
return Token.fromJson(json['data']);
|
||||||
return Token.fromJson(json['data']);
|
});
|
||||||
});
|
return response;
|
||||||
return response;
|
|
||||||
} on DioException catch (e) {
|
|
||||||
final message = e.response?.data['error']['message'] ??
|
|
||||||
'An error occurred while logging in';
|
|
||||||
throw APIException(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future forgetPassword({
|
static Future forgetPassword({
|
||||||
@ -28,18 +20,12 @@ class AuthenticationAPI {
|
|||||||
required var password,
|
required var password,
|
||||||
required var otpCode,
|
required var otpCode,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
final response = await HTTPService().post(
|
||||||
final response = await HTTPService().post(
|
path: ApiEndpoints.forgetPassword,
|
||||||
path: ApiEndpoints.forgetPassword,
|
body: {"email": email, "password": password, "otpCode": otpCode},
|
||||||
body: {"email": email, "password": password, "otpCode": otpCode},
|
showServerMessage: true,
|
||||||
showServerMessage: true,
|
expectedResponseModel: (json) {});
|
||||||
expectedResponseModel: (json) {});
|
return response;
|
||||||
return response;
|
|
||||||
} on DioException catch (e) {
|
|
||||||
final message = e.response?.data['error']['message'] ??
|
|
||||||
'An error occurred while resetting the password';
|
|
||||||
throw APIException(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<int?> sendOtp({required String email}) async {
|
static Future<int?> sendOtp({required String email}) async {
|
||||||
@ -53,26 +39,19 @@ class AuthenticationAPI {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future verifyOtp(
|
static Future verifyOtp({required String email, required String otpCode}) async {
|
||||||
{required String email, required String otpCode}) async {
|
final response = await HTTPService().post(
|
||||||
try {
|
path: ApiEndpoints.verifyOtp,
|
||||||
final response = await HTTPService().post(
|
body: {"email": email, "type": "PASSWORD", "otpCode": otpCode},
|
||||||
path: ApiEndpoints.verifyOtp,
|
showServerMessage: true,
|
||||||
body: {"email": email, "type": "PASSWORD", "otpCode": otpCode},
|
expectedResponseModel: (json) {
|
||||||
showServerMessage: true,
|
if (json['message'] == 'Otp Verified Successfully') {
|
||||||
expectedResponseModel: (json) {
|
return true;
|
||||||
if (json['message'] == 'Otp Verified Successfully') {
|
} else {
|
||||||
return true;
|
return false;
|
||||||
} else {
|
}
|
||||||
return false;
|
});
|
||||||
}
|
return response;
|
||||||
});
|
|
||||||
return response;
|
|
||||||
} on APIException catch (e) {
|
|
||||||
throw APIException(e.message);
|
|
||||||
} catch (e) {
|
|
||||||
throw APIException('An error occurred while verifying the OTP');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<RegionModel>> fetchRegion() async {
|
static Future<List<RegionModel>> fetchRegion() async {
|
||||||
@ -80,9 +59,7 @@ class AuthenticationAPI {
|
|||||||
path: ApiEndpoints.getRegion,
|
path: ApiEndpoints.getRegion,
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return (json as List)
|
return (json as List).map((zone) => RegionModel.fromJson(zone)).toList();
|
||||||
.map((zone) => RegionModel.fromJson(zone))
|
|
||||||
.toList();
|
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,8 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deviceBatchControl(List<String> uuids, String code, dynamic value) async {
|
Future<bool> deviceBatchControl(
|
||||||
|
List<String> uuids, String code, dynamic value) async {
|
||||||
try {
|
try {
|
||||||
final body = {
|
final body = {
|
||||||
'devicesUuid': uuids,
|
'devicesUuid': uuids,
|
||||||
@ -116,7 +117,8 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async {
|
static Future<List<DeviceModel>> getDevicesByGatewayId(
|
||||||
|
String gatewayId) async {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
|
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
|
||||||
showServerMessage: false,
|
showServerMessage: false,
|
||||||
@ -150,7 +152,9 @@ class DevicesManagementApi {
|
|||||||
String code,
|
String code,
|
||||||
) async {
|
) async {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
|
path: ApiEndpoints.getDeviceLogs
|
||||||
|
.replaceAll('{uuid}', uuid)
|
||||||
|
.replaceAll('{code}', code),
|
||||||
showServerMessage: false,
|
showServerMessage: false,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return DeviceReport.fromJson(json['data']);
|
return DeviceReport.fromJson(json['data']);
|
||||||
@ -223,7 +227,8 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> addScheduleRecord(ScheduleEntry sendSchedule, String uuid) async {
|
Future<bool> addScheduleRecord(
|
||||||
|
ScheduleEntry sendSchedule, String uuid) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().post(
|
final response = await HTTPService().post(
|
||||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||||
@ -240,7 +245,8 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ScheduleModel>> getDeviceSchedules(String uuid, String category) async {
|
Future<List<ScheduleModel>> getDeviceSchedules(
|
||||||
|
String uuid, String category) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getScheduleByDeviceId
|
path: ApiEndpoints.getScheduleByDeviceId
|
||||||
@ -263,7 +269,9 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateScheduleRecord(
|
Future<bool> updateScheduleRecord(
|
||||||
{required bool enable, required String uuid, required String scheduleId}) async {
|
{required bool enable,
|
||||||
|
required String uuid,
|
||||||
|
required String scheduleId}) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.updateScheduleByDeviceId
|
path: ApiEndpoints.updateScheduleByDeviceId
|
||||||
@ -284,7 +292,8 @@ class DevicesManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> editScheduleRecord(String uuid, ScheduleEntry newSchedule) async {
|
Future<bool> editScheduleRecord(
|
||||||
|
String uuid, ScheduleEntry newSchedule) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||||
@ -335,4 +344,46 @@ class DevicesManagementApi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> putDeviceName(
|
||||||
|
{required String deviceId, required String deviceName}) async {
|
||||||
|
try {
|
||||||
|
final response = await HTTPService().put(
|
||||||
|
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||||
|
body: {"deviceName": deviceName},
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future getDeviceInfo(String deviceId) async {
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||||
|
showServerMessage: false,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'] as Map<String, dynamic>;
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future resetDevice({
|
||||||
|
String? devicesUuid,
|
||||||
|
}) async {
|
||||||
|
final response = await HTTPService().post(
|
||||||
|
path: ApiEndpoints.resetDevice.replaceAll('{deviceUuid}', devicesUuid!),
|
||||||
|
showServerMessage: false,
|
||||||
|
body: {
|
||||||
|
"devicesUuid": [devicesUuid]
|
||||||
|
},
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
|
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
|
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
|
||||||
@ -6,7 +5,6 @@ import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/cr
|
|||||||
import 'package:syncrow_web/pages/routines/models/icon_model.dart';
|
import 'package:syncrow_web/pages/routines/models/icon_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
|
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
|
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
@ -28,10 +26,9 @@ class SceneApi {
|
|||||||
);
|
);
|
||||||
debugPrint('create scene response: $response');
|
debugPrint('create scene response: $response');
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
debugPrint(e.toString());
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
rethrow;
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +48,9 @@ class SceneApi {
|
|||||||
);
|
);
|
||||||
debugPrint('create automation response: $response');
|
debugPrint('create automation response: $response');
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
debugPrint(e.toString());
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
rethrow;
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,10 +165,8 @@ class SceneApi {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
rethrow;
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +185,8 @@ class SceneApi {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
rethrow;
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,10 +217,8 @@ class SceneApi {
|
|||||||
expectedResponseModel: (json) => json['statusCode'] == 200,
|
expectedResponseModel: (json) => json['statusCode'] == 200,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
rethrow;
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,10 +236,8 @@ class SceneApi {
|
|||||||
expectedResponseModel: (json) => json['statusCode'] == 200,
|
expectedResponseModel: (json) => json['statusCode'] == 200,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} catch (e) {
|
||||||
String errorMessage =
|
rethrow;
|
||||||
e.response?.data['error']['message'][0] ?? 'something went wrong';
|
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/model/pagination_model.dart';
|
import 'package:syncrow_web/pages/space_tree/model/pagination_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/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||||
@ -12,14 +13,16 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
|||||||
|
|
||||||
class CommunitySpaceManagementApi {
|
class CommunitySpaceManagementApi {
|
||||||
// Community Management APIs
|
// Community Management APIs
|
||||||
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
|
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
||||||
|
{int page = 1}) async {
|
||||||
try {
|
try {
|
||||||
List<CommunityModel> allCommunities = [];
|
List<CommunityModel> allCommunities = [];
|
||||||
bool hasNext = true;
|
bool hasNext = true;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
await HTTPService().get(
|
await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
path: ApiEndpoints.getCommunityList
|
||||||
|
.replaceAll('{projectId}', projectId),
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'page': page,
|
'page': page,
|
||||||
},
|
},
|
||||||
@ -55,8 +58,14 @@ class CommunitySpaceManagementApi {
|
|||||||
try {
|
try {
|
||||||
bool hasNext = false;
|
bool hasNext = false;
|
||||||
await HTTPService().get(
|
await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
path:
|
||||||
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
|
ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||||
|
queryParameters: {
|
||||||
|
'page': page,
|
||||||
|
'includeSpaces': true,
|
||||||
|
'size': 25,
|
||||||
|
'search': search
|
||||||
|
},
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
try {
|
try {
|
||||||
List<dynamic> jsonData = json['data'] ?? [];
|
List<dynamic> jsonData = json['data'] ?? [];
|
||||||
@ -68,7 +77,10 @@ class CommunitySpaceManagementApi {
|
|||||||
|
|
||||||
page = currentPage + 1;
|
page = currentPage + 1;
|
||||||
paginationModel = PaginationModel(
|
paginationModel = PaginationModel(
|
||||||
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
|
pageNum: page,
|
||||||
|
hasNext: hasNext,
|
||||||
|
size: 25,
|
||||||
|
communities: communityList);
|
||||||
return paginationModel;
|
return paginationModel;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
hasNext = false;
|
hasNext = false;
|
||||||
@ -83,7 +95,8 @@ class CommunitySpaceManagementApi {
|
|||||||
Future<CommunityModel?> getCommunityById(String communityId) async {
|
Future<CommunityModel?> getCommunityById(String communityId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
|
path: ApiEndpoints.getCommunityById
|
||||||
|
.replaceAll('{communityId}', communityId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return CommunityModel.fromJson(json['data']);
|
return CommunityModel.fromJson(json['data']);
|
||||||
},
|
},
|
||||||
@ -95,7 +108,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
|
Future<CommunityModel?> createCommunity(
|
||||||
|
String name, String description, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().post(
|
final response = await HTTPService().post(
|
||||||
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
||||||
@ -114,7 +128,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
|
Future<bool> updateCommunity(
|
||||||
|
String communityId, String name, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.updateCommunity
|
path: ApiEndpoints.updateCommunity
|
||||||
@ -151,7 +166,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
|
Future<SpacesResponse> fetchSpaces(
|
||||||
|
String communityId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.listSpaces
|
path: ApiEndpoints.listSpaces
|
||||||
@ -177,7 +193,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
|
Future<SpaceModel?> getSpace(
|
||||||
|
String communityId, String spaceId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpace
|
path: ApiEndpoints.getSpace
|
||||||
@ -289,7 +306,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
|
Future<bool> deleteSpace(
|
||||||
|
String communityId, String spaceId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().delete(
|
final response = await HTTPService().delete(
|
||||||
path: ApiEndpoints.deleteSpace
|
path: ApiEndpoints.deleteSpace
|
||||||
@ -307,15 +325,17 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
|
Future<List<SpaceModel>> getSpaceHierarchy(
|
||||||
|
String communityId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpaceHierarchy
|
path: ApiEndpoints.getSpaceHierarchy
|
||||||
.replaceAll('{communityId}', communityId)
|
.replaceAll('{communityId}', communityId)
|
||||||
.replaceAll('{projectId}', projectId),
|
.replaceAll('{projectId}', projectId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final spaceModels =
|
final spaceModels = (json['data'] as List)
|
||||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
|
.toList();
|
||||||
|
|
||||||
return spaceModels;
|
return spaceModels;
|
||||||
},
|
},
|
||||||
@ -327,15 +347,17 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
|
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
|
||||||
|
{String? communityId, String? projectId}) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.spaceOnlyWithDevices
|
path: ApiEndpoints.spaceOnlyWithDevices
|
||||||
.replaceAll('{communityId}', communityId!)
|
.replaceAll('{communityId}', communityId!)
|
||||||
.replaceAll('{projectId}', projectId!),
|
.replaceAll('{projectId}', projectId!),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final spaceModels =
|
final spaceModels = (json['data'] as List)
|
||||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
|
.toList();
|
||||||
return spaceModels;
|
return spaceModels;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -345,4 +367,59 @@ class CommunitySpaceManagementApi {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<SubSpaceModel>> getSubSpaceBySpaceId(
|
||||||
|
{required String communityId,
|
||||||
|
required String spaceId,
|
||||||
|
required String projectId}) async {
|
||||||
|
try {
|
||||||
|
final path = ApiEndpoints.listSubspace
|
||||||
|
.replaceFirst('{communityUuid}', communityId)
|
||||||
|
.replaceFirst('{spaceUuid}', spaceId)
|
||||||
|
.replaceAll('{projectUuid}', projectId);
|
||||||
|
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: path,
|
||||||
|
queryParameters: {"page": 1, "pageSize": 10},
|
||||||
|
showServerMessage: false,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
List<SubSpaceModel> rooms = [];
|
||||||
|
if (json['data'] != null) {
|
||||||
|
for (var subspace in json['data']) {
|
||||||
|
rooms.add(SubSpaceModel.fromJson(subspace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rooms;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> assignDeviceToRoom(
|
||||||
|
{required String communityId,
|
||||||
|
required String spaceId,
|
||||||
|
required String subSpaceId,
|
||||||
|
required String deviceId,
|
||||||
|
required String projectId}) async {
|
||||||
|
try {
|
||||||
|
final response = await HTTPService().post(
|
||||||
|
path: ApiEndpoints.assignDeviceToRoom
|
||||||
|
.replaceAll('{projectUuid}', projectId)
|
||||||
|
.replaceAll('{communityUuid}', communityId)
|
||||||
|
.replaceAll('{spaceUuid}', spaceId)
|
||||||
|
.replaceAll('{subSpaceUuid}', subSpaceId)
|
||||||
|
.replaceAll('{deviceUuid}', deviceId),
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,4 +83,7 @@ abstract class ColorsManager {
|
|||||||
static const Color maxPurpleDot = Color(0xFF5F00BD);
|
static const Color maxPurpleDot = Color(0xFF5F00BD);
|
||||||
static const Color minBlue = Color(0xFF93AAFD);
|
static const Color minBlue = Color(0xFF93AAFD);
|
||||||
static const Color minBlueDot = Color(0xFF023DFE);
|
static const Color minBlueDot = Color(0xFF023DFE);
|
||||||
|
static const Color grey25 = Color(0xFFF9F9F9);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,9 +60,12 @@ abstract class ApiEndpoints {
|
|||||||
'/devices/{uuid}/report-logs?code={code}&startTime={startTime}&endTime={endTime}';
|
'/devices/{uuid}/report-logs?code={code}&startTime={startTime}&endTime={endTime}';
|
||||||
|
|
||||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||||
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
|
static const String getScheduleByDeviceId =
|
||||||
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
|
'/schedule/{deviceUuid}?category={category}';
|
||||||
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
|
static const String deleteScheduleByDeviceId =
|
||||||
|
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||||
|
static const String updateScheduleByDeviceId =
|
||||||
|
'/schedule/enable/{deviceUuid}';
|
||||||
static const String factoryReset = '/devices/batch';
|
static const String factoryReset = '/devices/batch';
|
||||||
|
|
||||||
//product
|
//product
|
||||||
@ -124,4 +127,13 @@ abstract class ApiEndpoints {
|
|||||||
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
|
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
|
||||||
static const String spaceOnlyWithDevices =
|
static const String spaceOnlyWithDevices =
|
||||||
'/projects/{projectId}/communities/{communityId}/spaces?onlyWithDevices=true';
|
'/projects/{projectId}/communities/{communityId}/spaces?onlyWithDevices=true';
|
||||||
|
|
||||||
|
static const String listSubspace =
|
||||||
|
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
|
||||||
|
static const String deviceByUuid = '/devices/{deviceUuid}';
|
||||||
|
|
||||||
|
static const String resetDevice = '/factory/reset/{deviceUuid}';
|
||||||
|
|
||||||
|
static const String assignDeviceToRoom =
|
||||||
|
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -452,6 +452,13 @@ class Assets {
|
|||||||
'assets/icons/refresh_status_icon.svg';
|
'assets/icons/refresh_status_icon.svg';
|
||||||
static const String energyConsumedIcon =
|
static const String energyConsumedIcon =
|
||||||
'assets/icons/energy_consumed_icon.svg';
|
'assets/icons/energy_consumed_icon.svg';
|
||||||
|
|
||||||
|
static const String closeSettingsIcon =
|
||||||
|
'assets/icons/close_settings_icon.svg';
|
||||||
|
|
||||||
|
static const String editNameIconSettings =
|
||||||
|
'assets/icons/edit_name_icon_settings.svg';
|
||||||
|
|
||||||
static const String locationPin = 'assets/icons/location_pin.svg';
|
static const String locationPin = 'assets/icons/location_pin.svg';
|
||||||
static const String aqiTemperature = 'assets/icons/aqi_temperature.svg';
|
static const String aqiTemperature = 'assets/icons/aqi_temperature.svg';
|
||||||
static const String aqiHumidity = 'assets/icons/aqi_humidity.svg';
|
static const String aqiHumidity = 'assets/icons/aqi_humidity.svg';
|
||||||
|
|||||||
45
lib/web_layout/default_container.dart
Normal file
45
lib/web_layout/default_container.dart
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DefaultContainer extends StatelessWidget {
|
||||||
|
const DefaultContainer({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.height,
|
||||||
|
this.width,
|
||||||
|
this.color,
|
||||||
|
this.boxConstraints,
|
||||||
|
this.margin,
|
||||||
|
this.padding,
|
||||||
|
this.onTap,
|
||||||
|
this.borderRadius,
|
||||||
|
});
|
||||||
|
|
||||||
|
final double? height;
|
||||||
|
final double? width;
|
||||||
|
final Widget child;
|
||||||
|
final BoxConstraints? boxConstraints;
|
||||||
|
final EdgeInsets? margin;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
final Color? color;
|
||||||
|
final Function()? onTap;
|
||||||
|
final BorderRadius? borderRadius;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
child: Container(
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
margin: margin ?? const EdgeInsets.only(right: 3, bottom: 3),
|
||||||
|
constraints: boxConstraints,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color ?? Colors.white,
|
||||||
|
borderRadius: borderRadius ?? BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
padding: padding ?? const EdgeInsets.all(10),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user