mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Merge pull request #224 from SyncrowIOT/SP-1597-FE-Add-Device-Settings-Column-and-Build-Device-Settings-Dialog-UI
Sp 1597 fe add device settings column and build device settings dialog UI
This commit is contained in:
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 |
@ -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 =
|
||||||
|
selectedDevices
|
||||||
|
.map((device) =>
|
||||||
|
device.productType)
|
||||||
.toSet();
|
.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}%'
|
||||||
|
: '-',
|
||||||
|
formatDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
(device.createTime ?? 0) * 1000)),
|
(device.createTime ?? 0) * 1000)),
|
||||||
device.online == true ? 'Online' : 'Offline',
|
device.online == true ? 'Online' : 'Offline',
|
||||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
formatDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
(device.updateTime ?? 0) * 1000)),
|
(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 ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -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,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