Refactor device management and space management APIs, update event and state classes, and add RemoveDeviceWidget for device removal functionality.

This commit is contained in:
mohammad
2025-06-03 16:34:00 +03:00
parent cabd37a08a
commit 906c2d0430
10 changed files with 549 additions and 479 deletions

View File

@ -10,26 +10,24 @@ import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
part 'setting_bloc_event.dart';
class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
class SettingDeviceBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
final String deviceId;
SettingBlocBloc({
SettingDeviceBloc({
required this.deviceId,
}) : super(const SettingBlocInitial()) {
on<DeviceSettingInitialInfo>(fetchDeviceInfo);
on<SaveNameEvent>(saveName);
}) : super(const DeviceSettingsInitial()) {
on<DeviceSettingInitialInfo>(_fetchDeviceInfo);
on<SettingBlocSaveName>(_saveName);
on<ChangeNameEvent>(_changeName);
on<DeleteDeviceEvent>(deleteDevice);
on<FetchRoomsEvent>(_fetchRooms);
on<AssignRoomEvent>(_assignDevice);
on<SettingBlocDeleteDevice>(_deleteDevice);
on<SettingBlocFetchRooms>(_fetchRooms);
on<SettingBlocAssignRoom>(_onAssignDevice);
}
static String deviceName = '';
final TextEditingController nameController =
TextEditingController(text: deviceName);
final TextEditingController nameController = TextEditingController();
List<SubSpaceModel> roomsList = [];
bool isEditingName = false;
bool _validateInputs() {
final nameError = fullNameValidator(nameController.text);
final nameError = _fullNameValidator(nameController.text);
if (nameError != null) {
CustomSnackBar.displaySnackBar(nameError);
return true;
@ -37,7 +35,7 @@ class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
return false;
}
String? fullNameValidator(String? value) {
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) {
@ -49,69 +47,35 @@ class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
return null;
}
Future<void> saveName(
SaveNameEvent event, Emitter<DeviceSettingsState> emit) async {
Future<void> _saveName(
SettingBlocSaveName event, Emitter<DeviceSettingsState> emit) async {
if (_validateInputs()) return;
try {
emit(SettingLoadingState());
emit(DeviceSettingsLoading());
await DevicesManagementApi.putDeviceName(
deviceId: deviceId, deviceName: nameController.text);
add(DeviceSettingInitialInfo());
CustomSnackBar.displaySnackBar('Save Successfully');
emit(UpdateSettingState(deviceName: nameController.text));
emit(DeviceSettingsUpdate(deviceName: nameController.text));
} catch (e) {
emit(ErrorState(message: e.toString()));
emit(DeviceSettingsError(message: e.toString()));
}
}
DeviceInfoModel deviceInfo = 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: "",
),
);
Future fetchDeviceInfo(
Future<void> _fetchDeviceInfo(
DeviceSettingInitialInfo event, Emitter<DeviceSettingsState> emit) async {
try {
emit(SettingLoadingState());
emit(DeviceSettingsLoading());
var response = await DevicesManagementApi.getDeviceInfo(deviceId);
deviceInfo = DeviceInfoModel.fromJson(response);
DeviceInfoModel deviceInfo = DeviceInfoModel.fromJson(response);
nameController.text = deviceInfo.name;
emit(UpdateSettingState(
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(ErrorState(message: e.toString()));
emit(DeviceSettingsError(message: e.toString()));
}
}
@ -119,46 +83,50 @@ class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
final FocusNode focusNode = FocusNode();
void _changeName(ChangeNameEvent event, Emitter<DeviceSettingsState> emit) {
emit(SettingLoadingState());
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 SaveNameEvent());
add(const SettingBlocSaveName());
focusNode.unfocus();
}
emit(UpdateSettingState(
deviceName: deviceName,
deviceInfo: deviceInfo,
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
}
void deleteDevice(
DeleteDeviceEvent event, Emitter<DeviceSettingsState> emit) async {
void _deleteDevice(
SettingBlocDeleteDevice event, Emitter<DeviceSettingsState> emit) async {
try {
emit(SettingLoadingState());
await DevicesManagementApi.resetDevise(devicesUuid: deviceId);
emit(DeviceSettingsLoading());
await DevicesManagementApi.resetDevice(devicesUuid: deviceId);
CustomSnackBar.displaySnackBar('Reset Successfully');
emit(UpdateSettingState(
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: deviceInfo,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(ErrorState(message: e.toString()));
emit(DeviceSettingsError(message: e.toString()));
return;
}
}
//=========================== assign device to room ==========================
void _assignDevice(
AssignRoomEvent event, Emitter<DeviceSettingsState> emit) async {
void _onAssignDevice(
SettingBlocAssignRoom event, Emitter<DeviceSettingsState> emit) async {
try {
emit(SettingLoadingState());
emit(DeviceSettingsLoading());
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
await CommunitySpaceManagementApi.assignDeviceToRoom(
communityId: event.communityUuid,
@ -168,29 +136,29 @@ class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
projectId: projectUuid);
add(DeviceSettingInitialInfo());
CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveSelectionSuccessState());
emit(DeviceSettingsSaveSelectionSuccess());
} catch (e) {
emit(ErrorState(message: e.toString()));
emit(DeviceSettingsError(message: e.toString()));
return;
}
}
void _fetchRooms(
FetchRoomsEvent event, Emitter<DeviceSettingsState> emit) async {
SettingBlocFetchRooms event, Emitter<DeviceSettingsState> emit) async {
try {
emit(SettingLoadingState());
emit(DeviceSettingsLoading());
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
roomsList = await CommunitySpaceManagementApi.getSubSpaceBySpaceId(
communityId: event.communityUuid,
spaceId: event.spaceUuid,
projectId: projectUuid);
emit(UpdateSettingState(
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: deviceInfo,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(ErrorState(message: e.toString()));
emit(DeviceSettingsError(message: e.toString()));
return;
}
}

View File

@ -6,40 +6,42 @@ abstract class SettingBlocEvent extends Equatable {
List<Object?> get props => [];
}
class SaveDeviceName extends SettingBlocEvent {
class SettingBlocSaveDeviceName extends SettingBlocEvent {
final String deviceName;
final String deviceId;
const SaveDeviceName({required this.deviceName, required this.deviceId});
const SettingBlocSaveDeviceName(
{required this.deviceName, required this.deviceId});
@override
List<Object?> get props => [deviceName, deviceId];
}
class StartEditingName extends SettingBlocEvent {}
class SettingBlocStartEditingName extends SettingBlocEvent {}
class CancelEditingName extends SettingBlocEvent {}
class SettingBlocCancelEditingName extends SettingBlocEvent {}
class ChangeEditingNameValue extends SettingBlocEvent {
class SettingBlocChangeEditingNameValue extends SettingBlocEvent {
final String value;
const ChangeEditingNameValue(this.value);
const SettingBlocChangeEditingNameValue(this.value);
@override
List<Object?> get props => [value];
}
class FetchRoomsEvent extends SettingBlocEvent {
class SettingBlocFetchRooms extends SettingBlocEvent {
final String communityUuid;
final String spaceUuid;
const FetchRoomsEvent({required this.communityUuid, required this.spaceUuid});
const SettingBlocFetchRooms(
{required this.communityUuid, required this.spaceUuid});
@override
List<Object?> get props => [communityUuid, spaceUuid];
}
class SaveNameEvent extends SettingBlocEvent {
const SaveNameEvent();
class SettingBlocSaveName extends SettingBlocEvent {
const SettingBlocSaveName();
}
class DeviceSettingInitialInfo extends SettingBlocEvent {}
@ -49,14 +51,14 @@ class ChangeNameEvent extends SettingBlocEvent {
const ChangeNameEvent({this.value});
}
class DeleteDeviceEvent extends SettingBlocEvent {}
class SettingBlocDeleteDevice extends SettingBlocEvent {}
class AssignRoomEvent extends SettingBlocEvent {
class SettingBlocAssignRoom extends SettingBlocEvent {
final String communityUuid;
final String spaceUuid;
final String subSpaceUuid;
const AssignRoomEvent({
const SettingBlocAssignRoom({
required this.communityUuid,
required this.spaceUuid,
required this.subSpaceUuid,
@ -65,3 +67,4 @@ class AssignRoomEvent extends SettingBlocEvent {
@override
List<Object?> get props => [spaceUuid, communityUuid, subSpaceUuid];
}

View File

@ -3,32 +3,35 @@ import 'package:syncrow_web/pages/device_managment/device_setting/settings_model
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
abstract class DeviceSettingsState extends Equatable {
const DeviceSettingsState();
const DeviceSettingsState({this.deviceInfo});
final DeviceInfoModel? deviceInfo;
@override
List<Object?> get props => [];
List<Object?> get props => [deviceInfo];
}
class SettingBlocInitial extends DeviceSettingsState {
class DeviceSettingsInitial extends DeviceSettingsState {
final String deviceName;
final String deviceId;
final bool isEditingName;
final String editingNameValue;
const SettingBlocInitial({
const DeviceSettingsInitial({
this.deviceName = '',
this.deviceId = '',
this.isEditingName = false,
this.editingNameValue = '',
super.deviceInfo,
});
SettingBlocInitial copyWith({
DeviceSettingsInitial copyWith({
String? deviceName,
String? deviceId,
bool? isEditingName,
String? editingNameValue,
}) =>
SettingBlocInitial(
DeviceSettingsInitial(
deviceName: deviceName ?? this.deviceName,
deviceId: deviceId ?? this.deviceId,
isEditingName: isEditingName ?? this.isEditingName,
@ -40,36 +43,39 @@ class SettingBlocInitial extends DeviceSettingsState {
[deviceName, deviceId, isEditingName, editingNameValue];
}
class SettingLoadingState extends DeviceSettingsState {}
class DeviceSettingsLoading extends DeviceSettingsState {}
class UpdateSettingState extends DeviceSettingsState {
class DeviceSettingsUpdate extends DeviceSettingsState {
final String? deviceName;
final DeviceInfoModel? deviceInfo;
final List<SubSpaceModel> roomsList;
const UpdateSettingState({
const DeviceSettingsUpdate({
this.deviceName,
this.deviceInfo,
super.deviceInfo,
this.roomsList = const [],
});
@override
List<Object?> get props => [deviceName, deviceInfo, roomsList];
}
class ErrorState extends DeviceSettingsState {
class DeviceSettingsError extends DeviceSettingsState {
final String message;
const ErrorState({required this.message});
const DeviceSettingsError({required this.message});
@override
List<Object?> get props => [message];
}
class FetchRoomsState extends DeviceSettingsState {
class DeviceSettingsFetchRooms extends DeviceSettingsState {
final List<SubSpaceModel> roomsList;
const FetchRoomsState({required this.roomsList});
const DeviceSettingsFetchRooms({required this.roomsList});
@override
List<Object?> get props => [roomsList];
}
class SaveSelectionSuccessState extends DeviceSettingsState {}
class DeviceSettingsSaveSelectionSuccess extends DeviceSettingsState {}
class ChangeNameState extends DeviceSettingsState {}

View File

@ -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),
],
),
);
}
}

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.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/sub_space_dialog.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';
@ -18,75 +18,43 @@ 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,
);
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 BlocProvider(
create: (context) => SettingBlocBloc(
create: (context) => SettingDeviceBloc(
deviceId: device.uuid ?? '',
)
..add(DeviceSettingInitialInfo())
..add(FetchRoomsEvent(
..add(SettingBlocFetchRooms(
communityUuid: device.community!.uuid!,
spaceUuid: device.spaces!.first.uuid!,
)),
child: BlocBuilder<SettingBlocBloc, DeviceSettingsState>(
child: Builder(
builder: (context) {
return BlocBuilder<SettingDeviceBloc, DeviceSettingsState>(
builder: (context, state) {
final iconPath =
DeviceIconTypeHelper.getDeviceIconByTypeCode(device.productType);
final _bloc = BlocProvider.of<SettingBlocBloc>(context);
DeviceInfoModel deviceInfo = DeviceInfoModel.empty();
List<SubSpaceModel> subSpaces = [];
if (state is UpdateSettingState) {
deviceInfo = state.deviceInfo!;
subSpaces = state.roomsList;
}
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),
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 24),
child: ListView(
children: [
// Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -102,7 +70,8 @@ class DeviceSettingsPanel extends StatelessWidget {
children: [
Text(
'Device Settings',
style: context.theme.textTheme.titleLarge!.copyWith(
style:
context.theme.textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.bold,
color: ColorsManager.primaryColor,
),
@ -110,7 +79,6 @@ class DeviceSettingsPanel extends StatelessWidget {
],
),
const SizedBox(height: 24),
// Device Name + Icon
DefaultContainer(
child: Row(
children: [
@ -135,7 +103,8 @@ class DeviceSettingsPanel extends StatelessWidget {
children: [
Text(
'Device Name:',
style: context.textTheme.bodyMedium!.copyWith(
style: context.textTheme.bodyMedium!
.copyWith(
color: ColorsManager.grayColor,
),
),
@ -149,8 +118,8 @@ class DeviceSettingsPanel extends StatelessWidget {
controller: _bloc.nameController,
enabled: _bloc.editName,
onFieldSubmitted: (value) {
_bloc.add(
const ChangeNameEvent(value: false));
_bloc.add(const ChangeNameEvent(
value: false));
},
decoration: const InputDecoration(
border: InputBorder.none,
@ -167,7 +136,8 @@ class DeviceSettingsPanel extends StatelessWidget {
replacement: const SizedBox(),
child: GestureDetector(
onTap: () {
_bloc.add(const ChangeNameEvent(value: true));
_bloc.add(
const ChangeNameEvent(value: true));
},
child: SvgPicture.asset(
Assets.editNameIconSettings,
@ -181,151 +151,18 @@ class DeviceSettingsPanel extends StatelessWidget {
),
),
const SizedBox(height: 32),
// Device Management
Text('Device Management', style: sectionTitle),
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),
],
),
DeviceManagementContent(
device: device,
subSpaces: subSpaces.cast<SubSpaceModel>(),
deviceInfo: deviceInfo,
),
const SizedBox(height: 32),
// Remove Device Button
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(DeleteDeviceEvent());
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),
),
),
),
),
),
RemoveDeviceWidget(bloc: _bloc),
],
),
),
if (state is SettingLoadingState)
if (state is DeviceSettingsLoading)
Positioned.fill(
child: Container(
color: Colors.black.withOpacity(0.1),
@ -339,6 +176,8 @@ class DeviceSettingsPanel extends StatelessWidget {
],
);
},
);
},
),
);
}

View File

@ -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),
),
),
),
),
);
}
}

View File

@ -2,6 +2,7 @@ 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';
@ -77,71 +78,7 @@ class _SubSpaceDialogState extends State<SubSpaceDialog> {
}).toList(),
const SizedBox(height: 12),
const Divider(height: 1, thickness: 1),
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,
),
),
),
),
),
],
),
),
SubSpaceDialogButtons(selectedId: _selectedId, widget: widget),
],
),
),
@ -164,8 +101,8 @@ void showSubSpaceDialog(
selected: selected,
onConfirmed: (selectedModel) {
if (selectedModel != null) {
context.read<SettingBlocBloc>().add(
AssignRoomEvent(
context.read<SettingDeviceBloc>().add(
SettingBlocAssignRoom(
communityUuid: communityUuid,
spaceUuid: spaceUuid,
subSpaceUuid: selectedModel.id ?? '',

View File

@ -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 ?? '',
),
);
}
},
),
);
}

View File

@ -370,7 +370,8 @@ class DevicesManagementApi {
});
return response;
}
static Future resetDevise({
static Future resetDevice({
String? devicesUuid,
}) async {
final response = await HTTPService().post(
@ -385,7 +386,4 @@ class DevicesManagementApi {
);
return response;
}
}

View File

@ -373,7 +373,6 @@ class CommunitySpaceManagementApi {
required String spaceId,
required String projectId}) async {
try {
// Construct the API path
final path = ApiEndpoints.listSubspace
.replaceFirst('{communityUuid}', communityId)
.replaceFirst('{spaceUuid}', spaceId)
@ -389,9 +388,6 @@ class CommunitySpaceManagementApi {
for (var subspace in json['data']) {
rooms.add(SubSpaceModel.fromJson(subspace));
}
} else {
debugPrint(
"Warning: 'data' key is missing or null in response JSON.");
}
return rooms;
},
@ -399,7 +395,7 @@ class CommunitySpaceManagementApi {
return response;
} catch (error, stackTrace) {
return []; // Return an empty list if there's an error
return [];
}
}