diff --git a/assets/icons/close_settings_icon.svg b/assets/icons/close_settings_icon.svg
new file mode 100644
index 00000000..93e615d8
--- /dev/null
+++ b/assets/icons/close_settings_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/edit_name_icon_settings.svg b/assets/icons/edit_name_icon_settings.svg
new file mode 100644
index 00000000..54bee0af
--- /dev/null
+++ b/assets/icons/edit_name_icon_settings.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart
index 62760a16..0abe075b 100644
--- a/lib/pages/common/custom_table.dart
+++ b/lib/pages/common/custom_table.dart
@@ -211,6 +211,7 @@ class _DynamicTableState extends State {
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
+
),
);
}
@@ -281,6 +282,7 @@ class _DynamicTableState extends State {
padding: EdgeInsets.symmetric(
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
vertical: 4),
+
child: Text(
title,
style: context.textTheme.titleSmall!.copyWith(
@@ -301,6 +303,7 @@ class _DynamicTableState extends State {
required int rowIndex,
required int columnIndex,
}) {
+
bool isBatteryLevel = content.endsWith('%');
double? batteryLevel;
@@ -312,6 +315,7 @@ class _DynamicTableState extends State {
if (isSettingsColumn) {
return _buildSettingsIcon(rowIndex, size);
}
+
Color? statusColor;
switch (content) {
diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart
index fd3a2574..755bc8b7 100644
--- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart
+++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart
@@ -95,7 +95,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return const RoutinesView();
}
if (state.createRoutineView) {
- return CreateNewRoutineView();
+ return const CreateNewRoutineView();
}
return BlocBuilder(
diff --git a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart
index a3c975c1..f4baad0c 100644
--- a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart
+++ b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart
@@ -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/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/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_control_dialog.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/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart';
@@ -58,7 +60,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Low Battery ($lowBatteryCount)',
];
- final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
+ final buttonLabel =
+ (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
return Row(
children: [
@@ -105,18 +108,23 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
if (selectedDevices.length == 1) {
showDialog(
context: context,
- builder: (context) => DeviceControlDialog(
+ builder: (context) =>
+ DeviceControlDialog(
device: selectedDevices.first,
),
);
- } else if (selectedDevices.length > 1) {
- final productTypes = selectedDevices
- .map((device) => device.productType)
- .toSet();
+ } else if (selectedDevices.length >
+ 1) {
+ final productTypes =
+ selectedDevices
+ .map((device) =>
+ device.productType)
+ .toSet();
if (productTypes.length == 1) {
showDialog(
context: context,
- builder: (context) => DeviceBatchControlDialog(
+ builder: (context) =>
+ DeviceBatchControlDialog(
devices: selectedDevices,
),
);
@@ -130,7 +138,9 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
textAlign: TextAlign.center,
style: TextStyle(
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',
'Status',
'Last Offline Date and Time',
+ 'Settings'
],
data: devicesToShow.map((device) {
final combinedSpaceNames = device.spaces != null
- ? device.spaces!.map((space) => space.spaceName).join(' > ') +
+ ? device.spaces!
+ .map((space) => space.spaceName)
+ .join(' > ') +
(device.community != null
? ' > ${device.community!.name}'
: '')
- : (device.community != null ? device.community!.name : '');
+ : (device.community != null
+ ? device.community!.name
+ : '');
return [
device.name ?? '',
device.productName ?? '',
device.uuid ?? '',
- (device.spaces != null && device.spaces!.isNotEmpty)
+ (device.spaces != null &&
+ device.spaces!.isNotEmpty)
? device.spaces![0].spaceName
: '',
combinedSpaceNames,
- device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
- formatDateTime(DateTime.fromMillisecondsSinceEpoch(
- (device.createTime ?? 0) * 1000)),
+ device.batteryLevel != null
+ ? '${device.batteryLevel}%'
+ : '-',
+ formatDateTime(
+ DateTime.fromMillisecondsSinceEpoch(
+ (device.createTime ?? 0) * 1000)),
device.online == true ? 'Online' : 'Offline',
- formatDateTime(DateTime.fromMillisecondsSinceEpoch(
- (device.updateTime ?? 0) * 1000)),
+ formatDateTime(
+ DateTime.fromMillisecondsSinceEpoch(
+ (device.updateTime ?? 0) * 1000)),
+ 'Settings',
];
}).toList(),
onSelectionChanged: (selectedRows) {
@@ -202,6 +223,10 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
.map((device) => device.uuid!)
.toList(),
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(
+ begin: const Offset(1, 0),
+ end: Offset.zero,
+ ).animate(anim1),
+ child: child,
+ );
+ },
+ );
+ }
}
diff --git a/lib/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart b/lib/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart
new file mode 100644
index 00000000..c996cf72
--- /dev/null
+++ b/lib/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart
@@ -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 {
+ final String deviceId;
+ SettingDeviceBloc({
+ required this.deviceId,
+ }) : super(const DeviceSettingsInitial()) {
+ on(_fetchDeviceInfo);
+ on(_saveName);
+ on(_changeName);
+ on(_deleteDevice);
+ on(_fetchRooms);
+ on(_onAssignDevice);
+ }
+ final TextEditingController nameController = TextEditingController();
+ List 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 _saveName(
+ SettingBlocSaveName event, Emitter 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 _fetchDeviceInfo(
+ DeviceSettingInitialInfo event, Emitter 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 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 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 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 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;
+ }
+ }
+}
diff --git a/lib/pages/device_managment/device_setting/bloc/setting_bloc_event.dart b/lib/pages/device_managment/device_setting/bloc/setting_bloc_event.dart
new file mode 100644
index 00000000..7fb62ed9
--- /dev/null
+++ b/lib/pages/device_managment/device_setting/bloc/setting_bloc_event.dart
@@ -0,0 +1,69 @@
+part of 'setting_bloc_bloc.dart';
+
+abstract class DeviceSettingEvent extends Equatable {
+ const DeviceSettingEvent();
+ @override
+ List