Sp 1771 fe device name and subspace changes not reflected immediately after update on device management page (#313)

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1771](https://syncrow.atlassian.net/browse/SP-1771)

## Description

Synced state between settings and devices table.

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1771]:
https://syncrow.atlassian.net/browse/SP-1771?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This commit is contained in:
Faris Armoush
2025-06-29 16:09:37 +03:00
committed by GitHub
13 changed files with 358 additions and 138 deletions

View File

@ -46,11 +46,11 @@ class AirQualityDistributionBloc
} }
} }
Future<void> _onClearAirQualityDistribution( void _onClearAirQualityDistribution(
ClearAirQualityDistribution event, ClearAirQualityDistribution event,
Emitter<AirQualityDistributionState> emit, Emitter<AirQualityDistributionState> emit,
) async { ) {
emit(const AirQualityDistributionState()); emit(AirQualityDistributionState(selectedAqiType: state.selectedAqiType));
} }
void _onUpdateAqiTypeEvent( void _onUpdateAqiTypeEvent(

View File

@ -75,6 +75,6 @@ class RangeOfAqiBloc extends Bloc<RangeOfAqiEvent, RangeOfAqiState> {
ClearRangeOfAqiEvent event, ClearRangeOfAqiEvent event,
Emitter<RangeOfAqiState> emit, Emitter<RangeOfAqiState> emit,
) { ) {
emit(const RangeOfAqiState()); emit(RangeOfAqiState(selectedAqiType: state.selectedAqiType));
} }
} }

View File

@ -34,6 +34,7 @@ class AqiDistributionChartTitle extends StatelessWidget {
alignment: AlignmentDirectional.centerEnd, alignment: AlignmentDirectional.centerEnd,
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: AqiTypeDropdown( child: AqiTypeDropdown(
selectedAqiType: context.watch<AirQualityDistributionBloc>().state.selectedAqiType,
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
final bloc = context.read<AirQualityDistributionBloc>(); final bloc = context.read<AirQualityDistributionBloc>();

View File

@ -18,19 +18,20 @@ enum AqiType {
} }
class AqiTypeDropdown extends StatefulWidget { class AqiTypeDropdown extends StatefulWidget {
const AqiTypeDropdown({super.key, required this.onChanged}); const AqiTypeDropdown({
required this.onChanged,
this.selectedAqiType,
super.key,
});
final ValueChanged<AqiType?> onChanged; final ValueChanged<AqiType?> onChanged;
final AqiType? selectedAqiType;
@override @override
State<AqiTypeDropdown> createState() => _AqiTypeDropdownState(); State<AqiTypeDropdown> createState() => _AqiTypeDropdownState();
} }
class _AqiTypeDropdownState extends State<AqiTypeDropdown> { class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
AqiType? _selectedItem = AqiType.aqi;
void _updateSelectedItem(AqiType? item) => setState(() => _selectedItem = item);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
@ -41,8 +42,8 @@ class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
width: 1, width: 1,
), ),
), ),
child: DropdownButton<AqiType?>( child: DropdownButton<AqiType>(
value: _selectedItem, value: widget.selectedAqiType,
isDense: true, isDense: true,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
dropdownColor: ColorsManager.whiteColors, dropdownColor: ColorsManager.whiteColors,
@ -59,10 +60,7 @@ class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
items: AqiType.values items: AqiType.values
.map((e) => DropdownMenuItem(value: e, child: Text(e.value))) .map((e) => DropdownMenuItem(value: e, child: Text(e.value)))
.toList(), .toList(),
onChanged: (value) { onChanged: widget.onChanged,
_updateSelectedItem(value);
widget.onChanged(value);
},
), ),
); );
} }

View File

@ -63,15 +63,15 @@ class RangeOfAqiChartTitle extends StatelessWidget {
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.centerEnd, alignment: AlignmentDirectional.centerEnd,
child: AqiTypeDropdown( child: AqiTypeDropdown(
selectedAqiType: context.watch<RangeOfAqiBloc>().state.selectedAqiType,
onChanged: (value) { onChanged: (value) {
final spaceTreeState = context.read<SpaceTreeBloc>().state; final spaceTreeState = context.read<SpaceTreeBloc>().state;
final spaceUuid = spaceTreeState.selectedSpaces.firstOrNull; final spaceUuid = spaceTreeState.selectedSpaces.firstOrNull;
if (spaceUuid == null) return;
if (value != null) { if (value != null) {
context.read<RangeOfAqiBloc>().add(UpdateAqiTypeEvent(value)); context.read<RangeOfAqiBloc>().add(UpdateAqiTypeEvent(value));
} }
if (spaceUuid == null) return;
}, },
), ),
), ),

View File

@ -16,11 +16,12 @@ class DeviceManagementBloc
int _onlineCount = 0; int _onlineCount = 0;
int _offlineCount = 0; int _offlineCount = 0;
int _lowBatteryCount = 0; int _lowBatteryCount = 0;
List<AllDevicesModel> _selectedDevices = []; final List<AllDevicesModel> _selectedDevices = [];
List<AllDevicesModel> _filteredDevices = []; List<AllDevicesModel> _filteredDevices = [];
String currentProductName = ''; String currentProductName = '';
String? currentCommunity; String? currentCommunity;
String? currentUnitName; String? currentUnitName;
String subSpaceName = '';
DeviceManagementBloc() : super(DeviceManagementInitial()) { DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices); on<FetchDevices>(_onFetchDevices);
@ -31,25 +32,26 @@ class DeviceManagementBloc
on<ResetFilters>(_onResetFilters); on<ResetFilters>(_onResetFilters);
on<ResetSelectedDevices>(_onResetSelectedDevices); on<ResetSelectedDevices>(_onResetSelectedDevices);
on<UpdateSelection>(_onUpdateSelection); on<UpdateSelection>(_onUpdateSelection);
on<UpdateDeviceName>(_onUpdateDeviceName);
on<UpdateSubSpaceName>(_onUpdateSubSpaceName);
} }
Future<void> _onFetchDevices( Future<void> _onFetchDevices(
FetchDevices event, Emitter<DeviceManagementState> emit) async { FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading()); emit(DeviceManagementLoading());
try { try {
List<AllDevicesModel> devices = []; var devices = <AllDevicesModel>[];
_devices.clear(); _devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>(); final spaceBloc = event.context.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) { if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
await DevicesManagementApi().fetchDevices('', '', projectUuid);
} else { } else {
for (var community in spaceBloc.state.selectedCommunities) { for (final community in spaceBloc.state.selectedCommunities) {
List<String> spacesList = final spacesList =
spaceBloc.state.selectedCommunityAndSpaces[community] ?? []; spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
for (var space in spacesList) { for (final space in spacesList) {
devices.addAll(await DevicesManagementApi() devices.addAll(await DevicesManagementApi()
.fetchDevices(community, space, projectUuid)); .fetchDevices(community, space, projectUuid));
} }
@ -74,7 +76,7 @@ class DeviceManagementBloc
} }
} }
void _onFilterDevices( Future<void> _onFilterDevices(
FilterDevices event, Emitter<DeviceManagementState> emit) async { FilterDevices event, Emitter<DeviceManagementState> emit) async {
if (_devices.isNotEmpty) { if (_devices.isNotEmpty) {
_filteredDevices = List.from(_devices.where((device) { _filteredDevices = List.from(_devices.where((device) {
@ -156,8 +158,7 @@ class DeviceManagementBloc
add(FilterDevices(_getFilterFromIndex(_selectedIndex))); add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
} }
void _onSelectDevice( void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
SelectDevice event, Emitter<DeviceManagementState> emit) {
final selectedUuid = event.selectedDevice.uuid; final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
@ -166,9 +167,9 @@ class DeviceManagementBloc
_selectedDevices.add(event.selectedDevice); _selectedDevices.add(event.selectedDevice);
} }
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices); final clonedSelectedDevices = List<AllDevicesModel>.from(_selectedDevices);
bool isControlButtonEnabled = final isControlButtonEnabled =
_checkIfControlButtonEnabled(clonedSelectedDevices); _checkIfControlButtonEnabled(clonedSelectedDevices);
if (state is DeviceManagementLoaded) { if (state is DeviceManagementLoaded) {
@ -198,8 +199,8 @@ class DeviceManagementBloc
void _onUpdateSelection( void _onUpdateSelection(
UpdateSelection event, Emitter<DeviceManagementState> emit) { UpdateSelection event, Emitter<DeviceManagementState> emit) {
List<AllDevicesModel> selectedDevices = []; final selectedDevices = <AllDevicesModel>[];
List<AllDevicesModel> devicesToSelectFrom = []; var devicesToSelectFrom = <AllDevicesModel>[];
if (state is DeviceManagementLoaded) { if (state is DeviceManagementLoaded) {
devicesToSelectFrom = (state as DeviceManagementLoaded).devices; devicesToSelectFrom = (state as DeviceManagementLoaded).devices;
@ -207,7 +208,7 @@ class DeviceManagementBloc
devicesToSelectFrom = (state as DeviceManagementFiltered).filteredDevices; devicesToSelectFrom = (state as DeviceManagementFiltered).filteredDevices;
} }
for (int i = 0; i < event.selectedRows.length; i++) { for (var i = 0; i < event.selectedRows.length; i++) {
if (event.selectedRows[i]) { if (event.selectedRows[i]) {
selectedDevices.add(devicesToSelectFrom[i]); selectedDevices.add(devicesToSelectFrom[i]);
} }
@ -253,8 +254,7 @@ class DeviceManagementBloc
_onlineCount = _devices.where((device) => device.online == true).length; _onlineCount = _devices.where((device) => device.online == true).length;
_offlineCount = _devices.where((device) => device.online == false).length; _offlineCount = _devices.where((device) => device.online == false).length;
_lowBatteryCount = _devices _lowBatteryCount = _devices
.where((device) => .where((device) => device.batteryLevel != null && device.batteryLevel! < 20)
device.batteryLevel != null && device.batteryLevel! < 20)
.length; .length;
} }
@ -270,8 +270,8 @@ class DeviceManagementBloc
return 'All'; return 'All';
} }
} }
void _onSearchDevices(
SearchDevices event, Emitter<DeviceManagementState> emit) { void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
if ((event.community == null || event.community!.isEmpty) && if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) &&
(event.deviceNameOrProductName == null || (event.deviceNameOrProductName == null ||
@ -300,7 +300,7 @@ class DeviceManagementBloc
currentCommunity = event.community; currentCommunity = event.community;
currentUnitName = event.unitName; currentUnitName = event.unitName;
List<AllDevicesModel> devicesToSearch = _devices; final devicesToSearch = _devices;
if (devicesToSearch.isNotEmpty) { if (devicesToSearch.isNotEmpty) {
final searchText = event.deviceNameOrProductName?.toLowerCase() ?? ''; final searchText = event.deviceNameOrProductName?.toLowerCase() ?? '';
@ -343,5 +343,134 @@ class DeviceManagementBloc
} }
} }
void _onUpdateDeviceName(
UpdateDeviceName event, Emitter<DeviceManagementState> emit) {
final devices = _devices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
_selectedDevices.removeWhere((device) => device.uuid == event.deviceId);
_selectedDevices.add(modifiedDevice);
return modifiedDevice;
}
return device;
}).toList();
final filteredDevices = _filteredDevices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
_selectedDevices.removeWhere((device) => device.uuid == event.deviceId);
_selectedDevices.add(modifiedDevice);
return modifiedDevice;
}
return device;
}).toList();
_devices = devices;
_filteredDevices = filteredDevices;
if (state is DeviceManagementLoaded) {
final loaded = state as DeviceManagementLoaded;
final selectedDevices01 = _selectedDevices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
return modifiedDevice;
}
return device;
}).toList();
emit(DeviceManagementLoaded(
devices: devices,
selectedIndex: loaded.selectedIndex,
onlineCount: loaded.onlineCount,
offlineCount: loaded.offlineCount,
lowBatteryCount: loaded.lowBatteryCount,
selectedDevice: selectedDevices01,
isControlButtonEnabled: loaded.isControlButtonEnabled,
));
} else if (state is DeviceManagementFiltered) {
final filtered = state as DeviceManagementFiltered;
final selectedDevices01 = filtered.selectedDevice?.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
return modifiedDevice;
}
return device;
}).toList();
emit(DeviceManagementFiltered(
filteredDevices: filteredDevices,
selectedIndex: filtered.selectedIndex,
onlineCount: filtered.onlineCount,
offlineCount: filtered.offlineCount,
lowBatteryCount: filtered.lowBatteryCount,
selectedDevice: selectedDevices01,
isControlButtonEnabled: filtered.isControlButtonEnabled,
));
}
}
void _onUpdateSubSpaceName(
UpdateSubSpaceName event, Emitter<DeviceManagementState> emit) {
final devices = _devices.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
final filteredDevices = _filteredDevices.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
_devices = devices;
_filteredDevices = filteredDevices;
if (state is DeviceManagementLoaded) {
final loaded = state as DeviceManagementLoaded;
final selectedDevices = loaded.selectedDevice?.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
emit(DeviceManagementLoaded(
devices: _devices,
selectedIndex: loaded.selectedIndex,
onlineCount: loaded.onlineCount,
offlineCount: loaded.offlineCount,
lowBatteryCount: loaded.lowBatteryCount,
selectedDevice: selectedDevices,
isControlButtonEnabled: loaded.isControlButtonEnabled,
));
} else if (state is DeviceManagementFiltered) {
// final filtered = state as DeviceManagementFiltered;
// emit(DeviceManagementFiltered(
// filteredDevices: _filteredDevices,
// selectedIndex: filtered.selectedIndex,
// onlineCount: filtered.onlineCount,
// offlineCount: filtered.offlineCount,
// lowBatteryCount: filtered.lowBatteryCount,
// selectedDevice: filtered.selectedDevice,
// isControlButtonEnabled: filtered.isControlButtonEnabled,
// ));
}
}
void changeSubspaceName(
String deviceId, String newSubSpaceName, String subspaceId) {
add(UpdateSubSpaceName(
deviceId: deviceId,
newSubSpaceName: newSubSpaceName,
subspaceId: subspaceId,
));
}
List<AllDevicesModel> get selectedDevices => _selectedDevices; List<AllDevicesModel> get selectedDevices => _selectedDevices;
} }

View File

@ -70,3 +70,21 @@ class UpdateSelection extends DeviceManagementEvent {
const UpdateSelection(this.selectedRows); const UpdateSelection(this.selectedRows);
} }
class UpdateDeviceName extends DeviceManagementEvent {
final String deviceId;
final String newName;
const UpdateDeviceName({required this.deviceId, required this.newName});
}
class UpdateSubSpaceName extends DeviceManagementEvent {
final String deviceId;
final String newSubSpaceName;
final String subspaceId;
const UpdateSubSpaceName(
{required this.deviceId,
required this.newSubSpaceName,
required this.subspaceId});
}

View File

@ -44,4 +44,20 @@ class DeviceSubspace {
static List<Map<String, dynamic>> listToJson(List<DeviceSubspace> subspaces) { static List<Map<String, dynamic>> listToJson(List<DeviceSubspace> subspaces) {
return subspaces.map((subspace) => subspace.toJson()).toList(); return subspaces.map((subspace) => subspace.toJson()).toList();
} }
DeviceSubspace copyWith({
String? uuid,
DateTime? createdAt,
DateTime? updatedAt,
String? subspaceName,
bool? disabled,
}) {
return DeviceSubspace(
uuid: uuid ?? this.uuid,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
subspaceName: subspaceName ?? this.subspaceName,
disabled: disabled ?? this.disabled,
);
}
} }

View File

@ -588,4 +588,72 @@ SOS
"NCPS": DeviceType.NCPS, "NCPS": DeviceType.NCPS,
"PC": DeviceType.PC, "PC": DeviceType.PC,
}; };
AllDevicesModel copyWith({
DevicesModelRoom? room,
DeviceSubspace? subspace,
DevicesModelUnit? unit,
DeviceCommunityModel? community,
String? productUuid,
String? productType,
String? permissionType,
int? activeTime,
String? category,
String? categoryName,
int? createTime,
String? gatewayId,
String? icon,
String? ip,
String? lat,
String? localKey,
String? lon,
String? model,
String? name,
String? nodeId,
bool? online,
String? ownerId,
bool? sub,
String? timeZone,
int? updateTime,
String? uuid,
int? batteryLevel,
String? productName,
List<DeviceSpaceModel>? spaces,
List<DeviceTagModel>? deviceTags,
DeviceSubSpace? deviceSubSpace,
}) {
return AllDevicesModel(
room: room ?? this.room,
subspace: subspace ?? this.subspace,
unit: unit ?? this.unit,
community: community ?? this.community,
productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType,
permissionType: permissionType ?? this.permissionType,
activeTime: activeTime ?? this.activeTime,
category: category ?? this.category,
categoryName: categoryName ?? this.categoryName,
createTime: createTime ?? this.createTime,
gatewayId: gatewayId ?? this.gatewayId,
icon: icon ?? this.icon,
ip: ip ?? this.ip,
lat: lat ?? this.lat,
localKey: localKey ?? this.localKey,
lon: lon ?? this.lon,
model: model ?? this.model,
name: name ?? this.name,
nodeId: nodeId ?? this.nodeId,
online: online ?? this.online,
ownerId: ownerId ?? this.ownerId,
sub: sub ?? this.sub,
timeZone: timeZone ?? this.timeZone,
updateTime: updateTime ?? this.updateTime,
uuid: uuid ?? this.uuid,
batteryLevel: batteryLevel ?? this.batteryLevel,
productName: productName ?? this.productName,
spaces: spaces ?? this.spaces,
deviceTags: deviceTags ?? this.deviceTags,
deviceSubSpace: deviceSubSpace ?? this.deviceSubSpace,
);
}
} }

View File

@ -23,6 +23,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) { builder: (context, state) {
List<AllDevicesModel> devicesToShow = []; List<AllDevicesModel> devicesToShow = [];
int selectedIndex = 0; int selectedIndex = 0;
@ -31,7 +32,6 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
int lowBatteryCount = 0; int lowBatteryCount = 0;
bool isControlButtonEnabled = false; bool isControlButtonEnabled = false;
List<AllDevicesModel> selectedDevices = []; List<AllDevicesModel> selectedDevices = [];
if (state is DeviceManagementLoaded) { if (state is DeviceManagementLoaded) {
devicesToShow = state.devices; devicesToShow = state.devices;
selectedIndex = state.selectedIndex; selectedIndex = state.selectedIndex;
@ -192,7 +192,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Product Name', 'Product Name',
'Device ID', 'Device ID',
'Space Name', 'Space Name',
'location', 'Location',
'Battery Level', 'Battery Level',
'Installation Date and Time', 'Installation Date and Time',
'Status', 'Status',
@ -244,7 +244,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
.map((device) => device.uuid!) .map((device) => device.uuid!)
.toList(), .toList(),
isEmpty: devicesToShow.isEmpty, isEmpty: devicesToShow.isEmpty,
onSettingsPressed: (rowIndex) { onSettingsPressed: (rowIndex) async {
final device = devicesToShow[rowIndex]; final device = devicesToShow[rowIndex];
showDeviceSettingsSidebar(context, device); showDeviceSettingsSidebar(context, device);
}, },
@ -266,7 +266,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
barrierDismissible: true, barrierDismissible: true,
barrierLabel: "Device Settings", barrierLabel: "Device Settings",
transitionDuration: const Duration(milliseconds: 300), transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, anim1, anim2) { pageBuilder: (_, anim1, anim2) {
return Align( return Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Material( child: Material(
@ -276,6 +276,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: DeviceSettingsPanel( child: DeviceSettingsPanel(
device: device, device: device,
onClose: () => Navigator.of(context).pop(), onClose: () => Navigator.of(context).pop(),
deviceManagementBloc: context.read<DeviceManagementBloc>(),
), ),
), ),
), ),

View File

@ -19,11 +19,14 @@ class DeviceManagementContent extends StatelessWidget {
required this.device, required this.device,
required this.subSpaces, required this.subSpaces,
required this.deviceInfo, required this.deviceInfo,
required this.deviceManagementBloc,
}); });
final AllDevicesModel device; final AllDevicesModel device;
final List<SubSpaceModel> subSpaces; final List<SubSpaceModel> subSpaces;
final DeviceInfoModel deviceInfo; final DeviceInfoModel deviceInfo;
final DeviceManagementBloc deviceManagementBloc;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -87,6 +90,11 @@ class DeviceManagementContent extends StatelessWidget {
), ),
); );
}); });
deviceManagementBloc.add(UpdateSubSpaceName(
subspaceId: selectedSubSpace.id!,
deviceId: device.uuid!,
newSubSpaceName: selectedSubSpace.name ?? ''));
} }
}, },
child: infoRow( child: infoRow(

View File

@ -1,13 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.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/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/device_icon_type_helper.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/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/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/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/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -17,7 +19,13 @@ import 'package:syncrow_web/web_layout/default_container.dart';
class DeviceSettingsPanel extends StatelessWidget { class DeviceSettingsPanel extends StatelessWidget {
final VoidCallback? onClose; final VoidCallback? onClose;
final AllDevicesModel device; final AllDevicesModel device;
const DeviceSettingsPanel({super.key, this.onClose, required this.device}); final DeviceManagementBloc deviceManagementBloc;
const DeviceSettingsPanel({
super.key,
this.onClose,
required this.device,
required this.deviceManagementBloc,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -71,10 +79,10 @@ class DeviceSettingsPanel extends StatelessWidget {
'Device Settings', 'Device Settings',
style: context.theme.textTheme.titleLarge! style: context.theme.textTheme.titleLarge!
.copyWith( .copyWith(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: ColorsManager.vividBlue color: ColorsManager.vividBlue
.withOpacity(0.7), .withOpacity(0.7),
fontSize: 24), fontSize: 24),
), ),
], ],
), ),
@ -134,8 +142,14 @@ class DeviceSettingsPanel extends StatelessWidget {
onFieldSubmitted: (value) { onFieldSubmitted: (value) {
_bloc.add(const ChangeNameEvent( _bloc.add(const ChangeNameEvent(
value: false)); value: false));
deviceManagementBloc
..add(UpdateDeviceName(
deviceId: device.uuid!,
newName: _bloc
.nameController
.text))..add(ResetSelectedDevices());
}, },
decoration: InputDecoration( decoration:const InputDecoration(
isDense: true, isDense: true,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
border: InputBorder.none, border: InputBorder.none,
@ -157,7 +171,7 @@ class DeviceSettingsPanel extends StatelessWidget {
onTap: () { onTap: () {
_bloc.add( _bloc.add(
const ChangeNameEvent( const ChangeNameEvent(
value: true)); value: true));
}, },
child: SvgPicture.asset( child: SvgPicture.asset(
Assets Assets
@ -190,6 +204,7 @@ class DeviceSettingsPanel extends StatelessWidget {
device: device, device: device,
subSpaces: subSpaces.cast<SubSpaceModel>(), subSpaces: subSpaces.cast<SubSpaceModel>(),
deviceInfo: deviceInfo, deviceInfo: deviceInfo,
deviceManagementBloc: deviceManagementBloc,
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
RemoveDeviceWidget(bloc: _bloc), RemoveDeviceWidget(bloc: _bloc),

View File

@ -2,10 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
@ -23,8 +21,8 @@ class VisitorPasswordDialog extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
var text = Theme.of(context) final text = Theme.of(context)
.textTheme .textTheme
.bodySmall! .bodySmall!
.copyWith(color: Colors.black, fontSize: 13); .copyWith(color: Colors.black, fontSize: 13);
@ -41,8 +39,7 @@ class VisitorPasswordDialog extends StatelessWidget {
title: 'Sent Successfully', title: 'Sent Successfully',
widgeta: Column( widgeta: Column(
children: [ children: [
if (visitorBloc if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
.passwordStatus!.failedOperations.isNotEmpty)
Column( Column(
children: [ children: [
const Text('Failed Devices'), const Text('Failed Devices'),
@ -56,22 +53,19 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.failedOperations.length, .passwordStatus!.failedOperations.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return Container( return Container(
margin: EdgeInsets.all(5), margin: const EdgeInsets.all(5),
decoration: containerDecoration, decoration: containerDecoration,
height: 45, height: 45,
child: Center( child: Center(
child: Text(visitorBloc child: Text(visitorBloc.passwordStatus!
.passwordStatus! .failedOperations[index].deviceName)),
.failedOperations[index]
.deviceName)),
); );
}, },
), ),
), ),
], ],
), ),
if (visitorBloc if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
.passwordStatus!.successOperations.isNotEmpty)
Column( Column(
children: [ children: [
const Text('Success Devices'), const Text('Success Devices'),
@ -85,14 +79,12 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.successOperations.length, .passwordStatus!.successOperations.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return Container( return Container(
margin: EdgeInsets.all(5), margin: const EdgeInsets.all(5),
decoration: containerDecoration, decoration: containerDecoration,
height: 45, height: 45,
child: Center( child: Center(
child: Text(visitorBloc child: Text(visitorBloc.passwordStatus!
.passwordStatus! .successOperations[index].deviceName)),
.successOperations[index]
.deviceName)),
); );
}, },
), ),
@ -115,16 +107,14 @@ class VisitorPasswordDialog extends StatelessWidget {
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>( child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (BuildContext context, VisitorPasswordState state) { builder: (BuildContext context, VisitorPasswordState state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context); final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
bool isRepeat = final isRepeat =
state is IsRepeatState ? state.repeat : visitorBloc.repeat; state is IsRepeatState ? state.repeat : visitorBloc.repeat;
return AlertDialog( return AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: Text( title: Text(
'Create visitor password', 'Create visitor password',
style: Theme.of(context).textTheme.headlineLarge!.copyWith( style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black),
fontSize: 24,
color: Colors.black),
), ),
content: state is LoadingInitialState content: state is LoadingInitialState
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
@ -310,14 +300,12 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected ==
'Offline Password') { 'Offline Password') {
visitorBloc.add(SelectTimeEvent( visitorBloc.add(SelectTimeEvent(
context: context, context: context, isEffective: false));
isEffective: false));
} else { } else {
visitorBloc.add( visitorBloc.add(SelectTimeVisitorPassword(
SelectTimeVisitorPassword( context: context,
context: context, isStart: false,
isStart: false, isRepeat: false));
isRepeat: false));
} }
}, },
startTime: () { startTime: () {
@ -326,31 +314,28 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected ==
'Offline Password') { 'Offline Password') {
visitorBloc.add(SelectTimeEvent( visitorBloc.add(SelectTimeEvent(
context: context, context: context, isEffective: true));
isEffective: true));
} else { } else {
visitorBloc.add( visitorBloc.add(SelectTimeVisitorPassword(
SelectTimeVisitorPassword( context: context,
context: context, isStart: true,
isStart: true, isRepeat: false));
isRepeat: false));
} }
}, },
firstString: (visitorBloc firstString:
.usageFrequencySelected == (visitorBloc.usageFrequencySelected ==
'Periodic' && 'Periodic' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected ==
'Offline Password') 'Offline Password')
? visitorBloc.effectiveTime ? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess : visitorBloc.startTimeAccess,
.toString(),
secondString: (visitorBloc secondString: (visitorBloc
.usageFrequencySelected == .usageFrequencySelected ==
'Periodic' && 'Periodic' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected ==
'Offline Password') 'Offline Password')
? visitorBloc.expirationTime ? visitorBloc.expirationTime
: visitorBloc.endTimeAccess.toString(), : visitorBloc.endTimeAccess,
icon: Assets.calendarIcon), icon: Assets.calendarIcon),
const SizedBox( const SizedBox(
height: 10, height: 10,
@ -410,8 +395,7 @@ class VisitorPasswordDialog extends StatelessWidget {
child: CupertinoSwitch( child: CupertinoSwitch(
value: visitorBloc.repeat, value: visitorBloc.repeat,
onChanged: (value) { onChanged: (value) {
visitorBloc visitorBloc.add(ToggleRepeatEvent());
.add(ToggleRepeatEvent());
}, },
applyTheme: true, applyTheme: true,
), ),
@ -442,8 +426,7 @@ class VisitorPasswordDialog extends StatelessWidget {
}, },
).then((listDevice) { ).then((listDevice) {
if (listDevice != null) { if (listDevice != null) {
visitorBloc.selectedDevices = visitorBloc.selectedDevices = listDevice;
listDevice;
} }
}); });
}, },
@ -455,8 +438,7 @@ class VisitorPasswordDialog extends StatelessWidget {
.bodySmall! .bodySmall!
.copyWith( .copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: color: ColorsManager.whiteColors,
ColorsManager.whiteColors,
fontSize: 12), fontSize: 12),
), ),
), ),
@ -495,37 +477,30 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () { onPressed: () {
if (visitorBloc.forgetFormKey.currentState!.validate()) { if (visitorBloc.forgetFormKey.currentState!.validate()) {
if (visitorBloc.selectedDevices.isNotEmpty) { if (visitorBloc.selectedDevices.isNotEmpty) {
if (visitorBloc.usageFrequencySelected == if (visitorBloc.usageFrequencySelected == 'One-Time' &&
'One-Time' && visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.accessTypeSelected ==
'Offline Password') {
setPasswordFunction(context, size, visitorBloc); setPasswordFunction(context, size, visitorBloc);
} else if (visitorBloc.usageFrequencySelected == } else if (visitorBloc.usageFrequencySelected ==
'Periodic' && 'Periodic' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected == 'Offline Password') {
'Offline Password') {
if (visitorBloc.expirationTime != 'End Time' && if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time') { visitorBloc.effectiveTime != 'Start Time') {
setPasswordFunction(context, size, visitorBloc); setPasswordFunction(context, size, visitorBloc);
} else { } else {
visitorBloc.stateDialog( visitorBloc.stateDialog(
context: context, context: context,
message: message: 'Please select Access Period to continue',
'Please select Access Period to continue',
title: 'Access Period'); title: 'Access Period');
} }
} else if (visitorBloc.endTimeAccess.toString() != } else if (visitorBloc.endTimeAccess != 'End Time' &&
'End Time' && visitorBloc.startTimeAccess != 'Start Time') {
visitorBloc.startTimeAccess.toString() !=
'Start Time') {
if (visitorBloc.effectiveTimeTimeStamp != null && if (visitorBloc.effectiveTimeTimeStamp != null &&
visitorBloc.expirationTimeTimeStamp != null) { visitorBloc.expirationTimeTimeStamp != null) {
if (isRepeat == true) { if (isRepeat == true) {
if (visitorBloc.expirationTime != 'End Time' && if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time' && visitorBloc.effectiveTime != 'Start Time' &&
visitorBloc.selectedDays.isNotEmpty) { visitorBloc.selectedDays.isNotEmpty) {
setPasswordFunction( setPasswordFunction(context, size, visitorBloc);
context, size, visitorBloc);
} else { } else {
visitorBloc.stateDialog( visitorBloc.stateDialog(
context: context, context: context,
@ -539,15 +514,13 @@ class VisitorPasswordDialog extends StatelessWidget {
} else { } else {
visitorBloc.stateDialog( visitorBloc.stateDialog(
context: context, context: context,
message: message: 'Please select Access Period to continue',
'Please select Access Period to continue',
title: 'Access Period'); title: 'Access Period');
} }
} else { } else {
visitorBloc.stateDialog( visitorBloc.stateDialog(
context: context, context: context,
message: message: 'Please select Access Period to continue',
'Please select Access Period to continue',
title: 'Access Period'); title: 'Access Period');
} }
} else { } else {
@ -593,9 +566,8 @@ class VisitorPasswordDialog extends StatelessWidget {
alignment: Alignment.center, alignment: Alignment.center,
content: SizedBox( content: SizedBox(
height: size.height * 0.25, height: size.height * 0.25,
child: Center( child: const Center(
child: child: CircularProgressIndicator(), // Display a loading spinner
CircularProgressIndicator(), // Display a loading spinner
), ),
), ),
); );
@ -619,14 +591,12 @@ class VisitorPasswordDialog extends StatelessWidget {
), ),
Text( Text(
'Set Password', 'Set Password',
style: Theme.of(context) style:
.textTheme Theme.of(context).textTheme.headlineLarge!.copyWith(
.headlineLarge! fontSize: 30,
.copyWith( fontWeight: FontWeight.w400,
fontSize: 30, color: Colors.black,
fontWeight: FontWeight.w400, ),
color: Colors.black,
),
), ),
], ],
), ),
@ -672,8 +642,7 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
if (visitorBloc.usageFrequencySelected == 'One-Time' && if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected == 'Online Password') {
'Online Password') {
visitorBloc.add(OnlineOneTimePasswordEvent( visitorBloc.add(OnlineOneTimePasswordEvent(
context: context, context: context,
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
@ -681,8 +650,7 @@ class VisitorPasswordDialog extends StatelessWidget {
)); ));
} else if (visitorBloc.usageFrequencySelected == } else if (visitorBloc.usageFrequencySelected ==
'Periodic' && 'Periodic' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected == 'Online Password') {
'Online Password') {
visitorBloc.add(OnlineMultipleTimePasswordEvent( visitorBloc.add(OnlineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text, email: visitorBloc.emailController.text,
@ -693,8 +661,7 @@ class VisitorPasswordDialog extends StatelessWidget {
)); ));
} else if (visitorBloc.usageFrequencySelected == } else if (visitorBloc.usageFrequencySelected ==
'One-Time' && 'One-Time' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected == 'Offline Password') {
'Offline Password') {
visitorBloc.add(OfflineOneTimePasswordEvent( visitorBloc.add(OfflineOneTimePasswordEvent(
context: context, context: context,
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
@ -702,8 +669,7 @@ class VisitorPasswordDialog extends StatelessWidget {
)); ));
} else if (visitorBloc.usageFrequencySelected == } else if (visitorBloc.usageFrequencySelected ==
'Periodic' && 'Periodic' &&
visitorBloc.accessTypeSelected == visitorBloc.accessTypeSelected == 'Offline Password') {
'Offline Password') {
visitorBloc.add(OfflineMultipleTimePasswordEvent( visitorBloc.add(OfflineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text, email: visitorBloc.emailController.text,