Merge branch 'dev' of https://github.com/SyncrowIOT/web into fix-schedule

This commit is contained in:
mohammad
2025-06-30 15:17:37 +03:00
40 changed files with 953 additions and 594 deletions

View File

@ -0,0 +1,4 @@
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.8033 16.3567C37.2313 15.7848 36.3039 15.7847 35.7318 16.3568L21.5532 30.5353L14.2681 23.2504C13.6962 22.6784 12.7686 22.6783 12.1966 23.2505C11.6246 23.8226 11.6246 24.75 12.1966 25.3221L20.5174 33.6427C20.8034 33.9287 21.1783 34.0717 21.5531 34.0717C21.928 34.0717 22.3029 33.9287 22.5888 33.6426L37.8033 18.4283C38.3754 17.8563 38.3754 16.9288 37.8033 16.3567Z" fill="#023DFE" fill-opacity="0.7"/>
<path d="M42.6776 7.32236C37.9558 2.60049 31.6776 0 25 0C18.3223 0 12.0442 2.60049 7.32236 7.32236C2.60039 12.0443 0 18.3224 0 25C0 31.6778 2.60039 37.9559 7.32236 42.6777C12.0441 47.3996 18.3223 50 25 50C31.6777 50 37.9558 47.3996 42.6776 42.6777C47.3995 37.9559 50 31.6778 50 25C50 18.3224 47.3995 12.0443 42.6776 7.32236ZM25 47.0703C12.8304 47.0703 2.92969 37.1696 2.92969 25C2.92969 12.8304 12.8304 2.92969 25 2.92969C37.1696 2.92969 47.0703 12.8304 47.0703 25C47.0703 37.1696 37.1696 47.0703 25 47.0703Z" fill="#023DFE" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -39,8 +39,12 @@ class AnalyticsDevice {
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
: null,
spaceUuid: json['spaceUuid'] as String?,
latitude: json['lat'] != null ? double.parse(json['lat'] as String? ?? '0.0') : null,
longitude: json['lon'] != null ? double.parse(json['lon'] as String? ?? '0.0') : null,
latitude: json['lat'] != null && json['lat'] != ''
? double.tryParse(json['lat']?.toString() ?? '0.0')
: null,
longitude: json['lon'] != null && json['lon'] != ''
? double.tryParse(json['lon']?.toString() ?? '0.0')
: null,
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,8 +17,8 @@ class DeviceLocationDetailsServiceDecorator implements DeviceLocationService {
'reverse',
queryParameters: {
'format': 'json',
'lat': param.latitude,
'lon': param.longitude,
'lat': 25.1880567,
'lon': 55.266608,
},
);

View File

@ -16,11 +16,12 @@ class DeviceManagementBloc
int _onlineCount = 0;
int _offlineCount = 0;
int _lowBatteryCount = 0;
List<AllDevicesModel> _selectedDevices = [];
final List<AllDevicesModel> _selectedDevices = [];
List<AllDevicesModel> _filteredDevices = [];
String currentProductName = '';
String? currentCommunity;
String? currentUnitName;
String subSpaceName = '';
DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices);
@ -31,24 +32,29 @@ class DeviceManagementBloc
on<ResetFilters>(_onResetFilters);
on<ResetSelectedDevices>(_onResetSelectedDevices);
on<UpdateSelection>(_onUpdateSelection);
on<UpdateDeviceName>(_onUpdateDeviceName);
on<UpdateSubSpaceName>(_onUpdateSubSpaceName);
}
Future<void> _onFetchDevices(
FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading());
try {
List<AllDevicesModel> devices = [];
var devices = <AllDevicesModel>[];
_devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>();
final spaceBloc = event.context.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = await DevicesManagementApi().fetchDevices(projectUuid);
devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
} else {
for (var community in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
for (final community in spaceBloc.state.selectedCommunities) {
final spacesList =
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
for (final space in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(community, space, projectUuid));
}
}
}
@ -70,7 +76,7 @@ class DeviceManagementBloc
}
}
void _onFilterDevices(
Future<void> _onFilterDevices(
FilterDevices event, Emitter<DeviceManagementState> emit) async {
if (_devices.isNotEmpty) {
_filteredDevices = List.from(_devices.where((device) {
@ -152,8 +158,7 @@ class DeviceManagementBloc
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
}
void _onSelectDevice(
SelectDevice event, Emitter<DeviceManagementState> emit) {
void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
@ -162,9 +167,9 @@ class DeviceManagementBloc
_selectedDevices.add(event.selectedDevice);
}
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices);
final clonedSelectedDevices = List<AllDevicesModel>.from(_selectedDevices);
bool isControlButtonEnabled =
final isControlButtonEnabled =
_checkIfControlButtonEnabled(clonedSelectedDevices);
if (state is DeviceManagementLoaded) {
@ -194,8 +199,8 @@ class DeviceManagementBloc
void _onUpdateSelection(
UpdateSelection event, Emitter<DeviceManagementState> emit) {
List<AllDevicesModel> selectedDevices = [];
List<AllDevicesModel> devicesToSelectFrom = [];
final selectedDevices = <AllDevicesModel>[];
var devicesToSelectFrom = <AllDevicesModel>[];
if (state is DeviceManagementLoaded) {
devicesToSelectFrom = (state as DeviceManagementLoaded).devices;
@ -203,7 +208,7 @@ class DeviceManagementBloc
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]) {
selectedDevices.add(devicesToSelectFrom[i]);
}
@ -249,8 +254,7 @@ class DeviceManagementBloc
_onlineCount = _devices.where((device) => device.online == true).length;
_offlineCount = _devices.where((device) => device.online == false).length;
_lowBatteryCount = _devices
.where((device) =>
device.batteryLevel != null && device.batteryLevel! < 20)
.where((device) => device.batteryLevel != null && device.batteryLevel! < 20)
.length;
}
@ -267,8 +271,7 @@ class DeviceManagementBloc
}
}
void _onSearchDevices(
SearchDevices event, Emitter<DeviceManagementState> emit) {
void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) &&
(event.deviceNameOrProductName == null ||
@ -297,7 +300,7 @@ class DeviceManagementBloc
currentCommunity = event.community;
currentUnitName = event.unitName;
List<AllDevicesModel> devicesToSearch = _devices;
final devicesToSearch = _devices;
if (devicesToSearch.isNotEmpty) {
final searchText = event.deviceNameOrProductName?.toLowerCase() ?? '';
@ -340,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;
}

View File

@ -70,3 +70,21 @@ class UpdateSelection extends DeviceManagementEvent {
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

@ -57,6 +57,16 @@ class Status {
};
}
Status copyWith({
String? code,
dynamic value,
}) {
return Status(
code: code ?? this.code,
value: value ?? this.value,
);
}
factory Status.fromJson(String source) => Status.fromMap(json.decode(source));
String toJson() => json.encode(toMap());

View File

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

View File

@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_m
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class AccurteCalibratingDialog extends StatelessWidget {
final String deviceId;
@ -17,14 +18,15 @@ class AccurteCalibratingDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Calibrating',
body: const NormalTextBodyForDialog(
title: '',
step1:
'1. Click Close Button to make the Curtain run to Full Close and Position.',
step2: '2. click Next to complete the Calibration.',
'Click Close Button to make the Curtain run to Full Close and Position.',
step2: 'click Next to complete the Calibration.',
),
leftOnTap: () => Navigator.of(parentContext).pop(),
rightOnTap: () {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class AccurateCalibrationDialog extends StatelessWidget {
final String deviceId;
@ -15,13 +16,14 @@ class AccurateCalibrationDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Accurate Calibration',
body: const NormalTextBodyForDialog(
title: 'Prepare Calibration:',
step1: '1. Run The Curtain to the Fully Open Position,and pause.',
step2: '2. click Next to Start accurate calibration.',
step1: 'Run The Curtain to the Fully Open Position,and pause.',
step2: 'click Next to Start accurate calibration.',
),
leftOnTap: () => Navigator.of(parentContext).pop(),
rightOnTap: () {

View File

@ -17,78 +17,102 @@ class AccurateDialogWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
width: 400,
height: 250,
width: 500,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Text(
title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: ColorsManager.blueColor,
),
Expanded(
flex: 3,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Text(
title,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: ColorsManager.dialogBlueTitle,
),
),
),
const Divider(
indent: 60,
endIndent: 60,
),
],
),
),
const SizedBox(height: 5),
const Divider(
indent: 10,
endIndent: 10,
),
Padding(
padding: const EdgeInsets.all(10),
Expanded(
flex: 5,
child: body,
),
const SizedBox(height: 20),
const Spacer(),
const Divider(),
Row(
children: [
Expanded(
child: InkWell(
onTap: leftOnTap,
child: Container(
height: 60,
alignment: Alignment.center,
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.grayBorder,
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Expanded(child: Divider()),
Row(
children: [
Expanded(
child: InkWell(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(26),
),
onTap: leftOnTap,
child: Container(
height: 40,
alignment: Alignment.center,
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.grayBorder,
),
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(26),
),
),
child: const Text(
'Cancel',
style: TextStyle(color: ColorsManager.grayBorder),
),
),
),
),
child: const Text(
'Cancel',
style: TextStyle(color: ColorsManager.grayBorder),
),
),
),
),
Expanded(
child: InkWell(
onTap: rightOnTap,
child: Container(
height: 60,
alignment: Alignment.center,
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.grayBorder,
Expanded(
child: InkWell(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(26),
),
onTap: rightOnTap,
child: Container(
height: 40,
alignment: Alignment.center,
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.grayBorder,
),
),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(26),
),
),
child: const Text(
'Next',
style: TextStyle(
color: ColorsManager.blueColor,
),
),
),
),
),
child: const Text(
'Next',
style: TextStyle(
color: ColorsManager.blueColor,
),
),
),
),
)
],
)
],
)
],
),
)
],
),

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class CalibrateCompletedDialog extends StatelessWidget {
final BuildContext parentContext;
@ -21,52 +23,62 @@ class CalibrateCompletedDialog extends StatelessWidget {
width: 400,
child: Column(
children: [
const Padding(
padding: EdgeInsets.all(10),
child: Text(
'Calibration Completed',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: ColorsManager.blueColor,
),
Expanded(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Text(
'Calibration Completed',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: ColorsManager.dialogBlueTitle,
),
),
),
const SizedBox(height: 5),
const Divider(
indent: 10,
endIndent: 10,
),
],
),
),
const SizedBox(height: 5),
const Divider(
indent: 10,
endIndent: 10,
Expanded(
child: SvgPicture.asset(Assets.completedDoneIcon),
),
const Icon(
Icons.check_circle,
size: 100,
color: ColorsManager.blueColor,
),
const Spacer(),
const Divider(
indent: 10,
endIndent: 10,
),
InkWell(
onTap: () {
parentContext.read<CurtainModuleBloc>().add(
FetchCurtainModuleStatusEvent(
deviceId: deviceId,
),
);
Navigator.of(parentContext).pop();
Navigator.of(parentContext).pop();
},
child: Container(
height: 40,
width: double.infinity,
alignment: Alignment.center,
child: const Text(
'Close',
style: TextStyle(
color: ColorsManager.grayBorder,
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Divider(
indent: 10,
endIndent: 10,
),
),
InkWell(
onTap: () {
parentContext.read<CurtainModuleBloc>().add(
FetchCurtainModuleStatusEvent(
deviceId: deviceId,
),
);
Navigator.of(parentContext).pop();
Navigator.of(parentContext).pop();
},
child: Container(
height: 40,
width: double.infinity,
alignment: Alignment.center,
child: const Text(
'Close',
style: TextStyle(
color: ColorsManager.grayBorder,
),
),
),
)
],
),
)
],

View File

@ -15,28 +15,72 @@ class NormalTextBodyForDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
color: ColorsManager.grayColor,
return Padding(
padding: EdgeInsetsGeometry.only(left: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title.isEmpty)
const SizedBox()
else
Expanded(
child: Text(
title,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 15,
),
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
width: 10,
),
const Text('1. ',
style: TextStyle(
color: ColorsManager.grayColor,
fontSize: 15,
)),
SizedBox(
width: 450,
child: Text(
step1,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 15,
),
),
),
],
),
),
),
Text(
step1,
style: const TextStyle(
color: ColorsManager.grayColor,
),
),
Text(
step2,
style: const TextStyle(
color: ColorsManager.grayColor,
),
)
],
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
width: 10,
),
const Text('2. ',
style: TextStyle(
color: ColorsManager.grayColor,
fontSize: 15,
)),
Text(
step2,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 15,
),
),
],
),
)
],
),
);
}
}

View File

@ -19,7 +19,7 @@ class NumberInputField extends StatelessWidget {
contentPadding: EdgeInsets.zero,
),
style: const TextStyle(
fontSize: 20,
fontSize: 15,
color: ColorsManager.blackColor,
),
);

View File

@ -18,7 +18,7 @@ class PrefReversCardWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultContainer(
padding: const EdgeInsets.all(12),
padding: const EdgeInsets.all(18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -23,12 +23,12 @@ class CurtainModulePrefrencesDialog extends StatelessWidget {
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.CircleImageBackground,
contentPadding: const EdgeInsets.all(30),
title: const Center(
contentPadding: const EdgeInsets.all(20),
title: Center(
child: Text(
'Preferences',
style: TextStyle(
color: ColorsManager.blueColor,
color: ColorsManager.dialogBlueTitle,
fontSize: 24,
fontWeight: FontWeight.bold,
),

View File

@ -63,55 +63,82 @@ class _QuickCalibratingDialogState extends State<QuickCalibratingDialog> {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Calibrating',
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'1. please Enter the Travel Time:',
style: TextStyle(color: ColorsManager.grayBorder),
),
const SizedBox(height: 10),
Container(
width: 150,
height: 40,
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(12),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: NumberInputField(controller: _controller),
),
const Expanded(
child: Text(
'seconds',
style: TextStyle(
fontSize: 15,
color: ColorsManager.blueColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
if (_errorText != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
_errorText!,
style: const TextStyle(
color: ColorsManager.red,
fontSize: 14,
const Expanded(
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(right: 75),
child: Text(
'1.please Enter the Travel Time:',
style: TextStyle(color: ColorsManager.lightGrayColor),
),
),
),
),
Expanded(
child: Align(
alignment: Alignment.center,
child: Container(
width: 130,
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: ColorsManager.neutralGray.withValues(
alpha: 0.5,
),
borderRadius: BorderRadius.circular(12),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsGeometry.only(left: 5),
child: NumberInputField(controller: _controller)),
),
Expanded(
child: Text(
'seconds',
style: TextStyle(
fontSize: 12,
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
),
),
if (_errorText != null)
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
_errorText!,
style: const TextStyle(
color: ColorsManager.red,
fontSize: 14,
),
),
),
),
const Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Text(
'2.click Next to Complete the calibration',
style: TextStyle(color: ColorsManager.lightGrayColor),
),
),
)
],
),
leftOnTap: () => Navigator.of(widget.parentContext).pop(),

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class QuickCalibrationDialog extends StatelessWidget {
final int timControl;
@ -17,14 +18,15 @@ class QuickCalibrationDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Quick Calibration',
body: const NormalTextBodyForDialog(
title: 'Prepare Calibration:',
step1:
'1. Confirm that the curtain is in the fully closed and suspended state.',
step2: '2. click Next to Start calibration.',
'Confirm that the curtain is in the fully closed and suspended state.',
step2: 'click Next to Start calibration.',
),
leftOnTap: () => Navigator.of(parentContext).pop(),
rightOnTap: () {

View File

@ -19,11 +19,14 @@ class DeviceManagementContent extends StatelessWidget {
required this.device,
required this.subSpaces,
required this.deviceInfo,
required this.deviceManagementBloc,
});
final AllDevicesModel device;
final List<SubSpaceModel> subSpaces;
final DeviceInfoModel deviceInfo;
final DeviceManagementBloc deviceManagementBloc;
@override
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(

View File

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

View File

@ -40,7 +40,7 @@ class OneGangGlassSwitchBloc
emit(OneGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId, emit);
_listenToChanges(event.deviceId);
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
@ -48,42 +48,28 @@ class OneGangGlassSwitchBloc
}
}
void _listenToChanges(
String deviceId,
Emitter<OneGangGlassSwitchState> emit,
) {
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription;
void _listenToChanges(String deviceId) {
try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
final stream = ref.onValue;
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return;
stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>;
final statusList = <Status>[];
if (data['status'] != null) {
for (var element in data['status']) {
statusList.add(
Status(
code: element['code'].toString(),
value: element['value'].toString(),
),
);
}
}
if (statusList.isNotEmpty) {
final newStatus = OneGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
}
}
usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
OneGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList);
add(StatusUpdated(deviceStatus));
});
} catch (e) {
emit(OneGangGlassSwitchError('Failed to listen to changes: $e'));
}
} catch (_) {}
}
void _onStatusUpdated(
@ -174,4 +160,10 @@ class OneGangGlassSwitchBloc
deviceStatus = deviceStatus.copyWith(switch1: value);
}
}
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
}

View File

@ -298,6 +298,15 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
}
final dateTime = DateTime.parse(event.time);
Status status = Status(code: '', value: '');
if (event.category == 'CUR_2') {
status = status.copyWith(
code: 'control',
value: event.functionOn == true ? 'open' : 'close');
} else {
status =
status.copyWith(code: event.category, value: event.functionOn);
}
final updatedSchedule = ScheduleEntry(
scheduleId: event.scheduleId,
category: event.category,

View File

@ -220,7 +220,6 @@ class _ScheduleTableView extends StatelessWidget {
} else {
temp = updatedSchedule.function.value;
}
print('deviceType $deviceType');
context.read<ScheduleBloc>().add(
ScheduleEditEvent(
deviceType: deviceType,

View File

@ -41,7 +41,7 @@ class ThreeGangGlassSwitchBloc
emit(ThreeGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId, emit);
_listenToChanges(event.deviceId);
deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
@ -50,42 +50,28 @@ class ThreeGangGlassSwitchBloc
}
}
void _listenToChanges(
String deviceId,
Emitter<ThreeGangGlassSwitchState> emit,
) {
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription;
void _listenToChanges(String deviceId) {
try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
final stream = ref.onValue;
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return;
stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>;
final statusList = <Status>[];
if (data['status'] != null) {
for (var element in data['status']) {
statusList.add(
Status(
code: element['code'].toString(),
value: element['value'].toString(),
),
);
}
}
if (statusList.isNotEmpty) {
final newStatus = ThreeGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
}
}
usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
ThreeGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList);
add(StatusUpdated(deviceStatus));
});
} catch (e) {
emit(ThreeGangGlassSwitchError('Failed to listen to changes: $e'));
}
} catch (_) {}
}
void _onStatusUpdated(
@ -184,4 +170,10 @@ class ThreeGangGlassSwitchBloc
break;
}
}
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
}

View File

@ -1,173 +1,177 @@
import 'dart:async';
import 'dart:developer';
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart';
part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart';
class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
late TwoGangGlassStatusModel deviceStatus;
late TwoGangGlassStatusModel deviceStatus;
TwoGangGlassSwitchBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(TwoGangGlassSwitchInitial()) {
on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<TwoGangGlassSwitchControl>(_onControl);
on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<TwoGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
Future<void> _onFetchDeviceStatus(
TwoGangGlassSwitchFetchDeviceEvent event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
TwoGangGlassSwitchBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(TwoGangGlassSwitchInitial()) {
on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<TwoGangGlassSwitchControl>(_onControl);
on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<TwoGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
}
void _listenToChanges(String deviceId) {
try {
final ref = FirebaseDatabase.instance.ref(
'device-status/$deviceId',
);
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = TwoGangGlassStatusModel.fromJson(deviceId, statusList);
add(StatusUpdated(deviceStatus));
});
} catch (_) {
log(
'Error listening to changes',
name: 'TwoGangGlassSwitchBloc._listenToChanges',
);
}
}
Future<void> _onControl(
TwoGangGlassSwitchControl event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
try {
await controlDeviceService.controlDevice(
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onBatchControl(
TwoGangGlassSwitchBatchControl event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code,
value: event.value,
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onFetchBatchStatus(
TwoGangGlassSwitchFetchBatchStatusEvent event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = TwoGangGlassStatusModel.fromJson(
event.deviceIds.first,
status.status,
);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onFactoryReset(
TwoGangGlassFactoryReset event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final response = await DevicesManagementApi().factoryReset(
event.factoryReset,
event.deviceId,
);
if (!response) {
emit(TwoGangGlassSwitchError('Failed to reset device'));
} else {
add(TwoGangGlassSwitchFetchDeviceEvent(event.deviceId));
Future<void> _onFetchDeviceStatus(
TwoGangGlassSwitchFetchDeviceEvent event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
}
void _onStatusUpdated(
StatusUpdated event,
Emitter<TwoGangGlassSwitchState> emit,
) {
deviceStatus = event.deviceStatus;
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription;
void _updateLocalValue(String code, bool value) {
switch (code) {
case 'switch_1':
deviceStatus = deviceStatus.copyWith(switch1: value);
break;
case 'switch_2':
deviceStatus = deviceStatus.copyWith(switch2: value);
break;
void _listenToChanges(String deviceId) {
try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return;
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>;
final statusList = <Status>[];
usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
TwoGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList);
add(StatusUpdated(deviceStatus));
});
} catch (_) {}
}
Future<void> _onControl(
TwoGangGlassSwitchControl event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
try {
await controlDeviceService.controlDevice(
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onBatchControl(
TwoGangGlassSwitchBatchControl event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code,
value: event.value,
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onFetchBatchStatus(
TwoGangGlassSwitchFetchBatchStatusEvent event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = TwoGangGlassStatusModel.fromJson(
event.deviceIds.first,
status.status,
);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
}
Future<void> _onFactoryReset(
TwoGangGlassFactoryReset event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
try {
final response = await DevicesManagementApi().factoryReset(
event.factoryReset,
event.deviceId,
);
if (!response) {
emit(TwoGangGlassSwitchError('Failed to reset device'));
} else {
add(TwoGangGlassSwitchFetchDeviceEvent(event.deviceId));
}
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
}
void _onStatusUpdated(
StatusUpdated event,
Emitter<TwoGangGlassSwitchState> emit,
) {
deviceStatus = event.deviceStatus;
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
void _updateLocalValue(String code, bool value) {
switch (code) {
case 'switch_1':
deviceStatus = deviceStatus.copyWith(switch1: value);
break;
case 'switch_2':
deviceStatus = deviceStatus.copyWith(switch2: value);
break;
}
}
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
}

View File

@ -135,7 +135,6 @@ class ScheduleDialogHelper {
function: Status(
code: code ?? 'switch_1',
value: temp,
),
days: _convertSelectedDaysToStrings(selectedDays),
scheduleId: schedule.scheduleId,

View File

@ -170,45 +170,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
@ -936,12 +936,16 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
}
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(projectUuid,
spacesId: [createRoutineBloc.selectedSpaceId]));
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
}
emit(state.copyWith(isLoading: false, devices: devices));

View File

@ -58,7 +58,9 @@ class CurtainHelper {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('AC Functions'),
DialogHeader(dialogType == 'THEN'
? 'Curtain Functions'
: 'Curtain Conditions'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@ -58,11 +58,14 @@ class ProductModel {
'3G': Assets.Gang3SwitchIcon,
'3GT': Assets.threeTouchSwitch,
'CUR': Assets.curtain,
'CUR_2': Assets.curtain,
'GD': Assets.garageDoor,
'GW': Assets.SmartGatewayIcon,
'DL': Assets.DoorLockIcon,
'WL': Assets.waterLeakSensor,
'WH': Assets.waterHeater,
'WM': Assets.waterLeakSensor,
'SOS': Assets.sos,
'AC': Assets.ac,
'CPS': Assets.presenceSensor,
'PC': Assets.powerClamp,

View File

@ -2,10 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/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_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
@ -23,8 +21,8 @@ class VisitorPasswordDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
var text = Theme.of(context)
final size = MediaQuery.of(context).size;
final text = Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 13);
@ -41,8 +39,7 @@ class VisitorPasswordDialog extends StatelessWidget {
title: 'Sent Successfully',
widgeta: Column(
children: [
if (visitorBloc
.passwordStatus!.failedOperations.isNotEmpty)
if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
Column(
children: [
const Text('Failed Devices'),
@ -56,22 +53,19 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.failedOperations.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc
.passwordStatus!
.failedOperations[index]
.deviceName)),
child: Text(visitorBloc.passwordStatus!
.failedOperations[index].deviceName)),
);
},
),
),
],
),
if (visitorBloc
.passwordStatus!.successOperations.isNotEmpty)
if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
Column(
children: [
const Text('Success Devices'),
@ -85,14 +79,12 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.successOperations.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc
.passwordStatus!
.successOperations[index]
.deviceName)),
child: Text(visitorBloc.passwordStatus!
.successOperations[index].deviceName)),
);
},
),
@ -115,16 +107,14 @@ class VisitorPasswordDialog extends StatelessWidget {
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (BuildContext context, VisitorPasswordState state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
bool isRepeat =
final isRepeat =
state is IsRepeatState ? state.repeat : visitorBloc.repeat;
return AlertDialog(
backgroundColor: Colors.white,
title: Text(
'Create visitor password',
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 24,
color: Colors.black),
fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black),
),
content: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
@ -310,14 +300,12 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context,
isEffective: false));
context: context, isEffective: false));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
}
},
startTime: () {
@ -326,31 +314,28 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context,
isEffective: true));
context: context, isEffective: true));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
}
},
firstString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess
.toString(),
firstString:
(visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess,
secondString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.expirationTime
: visitorBloc.endTimeAccess.toString(),
: visitorBloc.endTimeAccess,
icon: Assets.calendarIcon),
const SizedBox(
height: 10,
@ -410,8 +395,7 @@ class VisitorPasswordDialog extends StatelessWidget {
child: CupertinoSwitch(
value: visitorBloc.repeat,
onChanged: (value) {
visitorBloc
.add(ToggleRepeatEvent());
visitorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
),
@ -442,8 +426,7 @@ class VisitorPasswordDialog extends StatelessWidget {
},
).then((listDevice) {
if (listDevice != null) {
visitorBloc.selectedDevices =
listDevice;
visitorBloc.selectedDevices = listDevice;
}
});
},
@ -455,8 +438,7 @@ class VisitorPasswordDialog extends StatelessWidget {
.bodySmall!
.copyWith(
fontWeight: FontWeight.w400,
color:
ColorsManager.whiteColors,
color: ColorsManager.whiteColors,
fontSize: 12),
),
),
@ -495,37 +477,30 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () {
if (visitorBloc.forgetFormKey.currentState!.validate()) {
if (visitorBloc.selectedDevices.isNotEmpty) {
if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
setPasswordFunction(context, size, visitorBloc);
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time') {
setPasswordFunction(context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else if (visitorBloc.endTimeAccess.toString() !=
'End Time' &&
visitorBloc.startTimeAccess.toString() !=
'Start Time') {
} else if (visitorBloc.endTimeAccess != 'End Time' &&
visitorBloc.startTimeAccess != 'Start Time') {
if (visitorBloc.effectiveTimeTimeStamp != null &&
visitorBloc.expirationTimeTimeStamp != null) {
if (isRepeat == true) {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time' &&
visitorBloc.selectedDays.isNotEmpty) {
setPasswordFunction(
context, size, visitorBloc);
setPasswordFunction(context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
@ -539,15 +514,13 @@ class VisitorPasswordDialog extends StatelessWidget {
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else {
@ -593,17 +566,17 @@ class VisitorPasswordDialog extends StatelessWidget {
alignment: Alignment.center,
content: SizedBox(
height: size.height * 0.25,
child: Center(
child:
CircularProgressIndicator(), // Display a loading spinner
child: const Center(
child: CircularProgressIndicator(), // Display a loading spinner
),
),
);
} else {
return AlertDialog(
alignment: Alignment.center,
backgroundColor: Colors.white,
content: SizedBox(
height: size.height * 0.25,
height: size.height * 0.13,
child: Column(
children: [
Column(
@ -617,13 +590,16 @@ class VisitorPasswordDialog extends StatelessWidget {
width: 35,
),
),
const SizedBox(
height: 20,
),
Text(
'Set Password',
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(
fontSize: 30,
fontSize: 24,
fontWeight: FontWeight.w400,
color: Colors.black,
),
@ -631,15 +607,6 @@ class VisitorPasswordDialog extends StatelessWidget {
],
),
const SizedBox(width: 15),
Text(
'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400,
fontSize: 18,
),
),
],
),
),
@ -668,12 +635,12 @@ class VisitorPasswordDialog extends StatelessWidget {
decoration: containerDecoration,
width: size.width * 0.1,
child: DefaultButton(
backgroundColor: Color(0xff023DFE),
borderRadius: 8,
onPressed: () {
Navigator.pop(context);
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add(OnlineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -681,8 +648,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add(OnlineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,
@ -693,8 +659,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add(OfflineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -702,8 +667,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add(OfflineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,
@ -715,7 +679,7 @@ class VisitorPasswordDialog extends StatelessWidget {
}
},
child: Text(
'Ok',
'Confirm',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,

View File

@ -13,13 +13,15 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
class DevicesManagementApi {
Future<List<AllDevicesModel>> fetchDevices(
String projectId, {
List<String>? spacesId,
}) async {
String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().get(
queryParameters: {if (spacesId != null) 'spaces': spacesId},
path: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
path: communityId.isNotEmpty && spaceId.isNotEmpty
? ApiEndpoints.getSpaceDevices
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{communityUuid}', communityId)
.replaceAll('{projectId}', projectId)
: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json['data'];
@ -414,4 +416,5 @@ class DevicesManagementApi {
);
return response;
}
}

View File

@ -83,7 +83,5 @@ abstract class ColorsManager {
static const Color maxPurpleDot = Color(0xFF5F00BD);
static const Color minBlue = Color(0xFF93AAFD);
static const Color minBlueDot = Color(0xFF023DFE);
static const Color grey25 = Color(0xFFF9F9F9);
static const Color grey25 = Color(0xFFF9F9F9);
}

View File

@ -18,7 +18,7 @@ abstract class ApiEndpoints {
static const String getAllDevices = '/projects/{projectId}/devices';
static const String getSpaceDevices =
'/projects/{projectId}/devices';
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
static const String getDeviceStatus = '/devices/{uuid}/functions/status';
static const String getBatchStatus = '/devices/batch';

View File

@ -394,6 +394,7 @@ class Assets {
static const String emptyBox = 'assets/icons/empty_box.png';
static const String completeProcessIcon =
'assets/icons/compleate_process_icon.svg';
static const String completedDoneIcon = 'assets/images/completed_done.svg';
static const String currentProcessIcon =
'assets/icons/current_process_icon.svg';
static const String uncomplete_ProcessIcon =
@ -505,5 +506,6 @@ class Assets {
static const String aqiAirQuality = 'assets/icons/aqi_air_quality.svg';
static const String temperatureAqiSidebar = 'assets/icons/thermometer.svg';
static const String humidityAqiSidebar = 'assets/icons/humidity.svg';
static const String autocadOccupancyImage = 'assets/images/autocad_occupancy_image.png';
static const String autocadOccupancyImage =
'assets/images/autocad_occupancy_image.png';
}