mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Compare commits
1 Commits
6534bfae5b
...
SP-1579-FE
Author | SHA1 | Date | |
---|---|---|---|
ad4a0fc2ed |
3
assets/icons/close_settings_icon.svg
Normal file
3
assets/icons/close_settings_icon.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 24C8.79469 24 5.78123 22.7518 3.51469 20.4853C1.24823 18.2188 0 15.2053 0 12C0 8.79469 1.24823 5.78123 3.51469 3.51469C5.78123 1.24823 8.79469 0 12 0C15.2053 0 18.2188 1.24823 20.4853 3.51469C22.7518 5.78123 24 8.79469 24 12C24 15.2053 22.7518 18.2188 20.4853 20.4853C18.2188 22.7518 15.2053 24 12 24ZM12 1.875C9.2955 1.875 6.75291 2.92819 4.84055 4.84055C2.92819 6.75291 1.875 9.2955 1.875 12C1.875 14.7045 2.92819 17.2471 4.84055 19.1595C6.75291 21.0718 9.2955 22.125 12 22.125C14.7045 22.125 17.2471 21.0718 19.1595 19.1595C21.0718 17.2471 22.125 14.7045 22.125 12C22.125 9.2955 21.0718 6.75291 19.1595 4.84055C17.2471 2.92819 14.7045 1.875 12 1.875ZM15.9775 17.3033L12 13.3258L8.02252 17.3033L6.6967 15.9775L10.6742 12L6.6967 8.02252L8.02252 6.6967L12 10.6742L15.9775 6.6967L17.3033 8.02252L13.3258 12L17.3033 15.9775L15.9775 17.3033Z" fill="#999999" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 992 B |
3
assets/icons/edit_name_icon_settings.svg
Normal file
3
assets/icons/edit_name_icon_settings.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.4854 3.1332L9.8668 0.515228C9.64704 0.295536 9.34903 0.172119 9.03829 0.172119C8.72755 0.172119 8.42953 0.295536 8.20977 0.515228L0.983989 7.74042C0.874797 7.84895 0.788225 7.97806 0.729285 8.12027C0.670346 8.26249 0.640212 8.41499 0.640629 8.56894V11.1875C0.640629 11.4983 0.764094 11.7964 0.983864 12.0161C1.20363 12.2359 1.5017 12.3594 1.8125 12.3594H11.6563C11.8427 12.3594 12.0216 12.2853 12.1534 12.1534C12.2853 12.0216 12.3594 11.8427 12.3594 11.6562C12.3594 11.4698 12.2853 11.2909 12.1534 11.1591C12.0216 11.0272 11.8427 10.9531 11.6563 10.9531H6.32422L12.4854 4.79081C12.5942 4.68199 12.6806 4.55278 12.7395 4.41057C12.7984 4.26836 12.8288 4.11594 12.8288 3.96201C12.8288 3.80807 12.7984 3.65565 12.7395 3.51344C12.6806 3.37123 12.5942 3.24202 12.4854 3.1332ZM4.33204 10.9531H2.04688V8.66796L6.96875 3.74609L9.25391 6.03124L4.33204 10.9531ZM10.25 5.03515L7.96485 2.74999L9.03946 1.67538L11.3246 3.96054L10.25 5.03515Z" fill="#D5D5D5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -21,6 +21,7 @@ class DynamicTable extends StatefulWidget {
|
||||
final List<String>? initialSelectedIds;
|
||||
final int uuidIndex;
|
||||
final Function(dynamic selectedRows)? onSelectionChanged;
|
||||
final Function(int rowIndex)? onSettingsPressed;
|
||||
const DynamicTable({
|
||||
super.key,
|
||||
required this.headers,
|
||||
@ -37,6 +38,7 @@ class DynamicTable extends StatefulWidget {
|
||||
this.initialSelectedIds,
|
||||
required this.uuidIndex,
|
||||
this.onSelectionChanged,
|
||||
this.onSettingsPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -63,7 +65,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
}
|
||||
}
|
||||
|
||||
bool _compareListOfLists(List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
bool _compareListOfLists(
|
||||
List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
// Check if the old and new lists are the same
|
||||
if (oldList.length != newList.length) return false;
|
||||
|
||||
@ -132,7 +135,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||
...List.generate(widget.headers.length, (index) {
|
||||
return _buildTableHeaderCell(widget.headers[index], index);
|
||||
return _buildTableHeaderCell(
|
||||
widget.headers[index], index);
|
||||
})
|
||||
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
||||
],
|
||||
@ -153,11 +157,14 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
height: 15,
|
||||
),
|
||||
Text(
|
||||
widget.tableName == 'AccessManagement' ? 'No Password ' : 'No Devices',
|
||||
widget.tableName == 'AccessManagement'
|
||||
? 'No Password '
|
||||
: 'No Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -166,16 +173,28 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: List.generate(widget.data.length, (index) {
|
||||
final row = widget.data[index];
|
||||
children:
|
||||
List.generate(widget.data.length, (rowIndex) {
|
||||
final row = widget.data[rowIndex];
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildRowCheckbox(index, widget.size.height * 0.08),
|
||||
...row.map((cell) => _buildTableCell(cell.toString(), widget.size.height * 0.08)),
|
||||
if (widget.withCheckBox)
|
||||
_buildRowCheckbox(
|
||||
rowIndex, widget.size.height * 0.08),
|
||||
...row.asMap().entries.map((entry) {
|
||||
int columnIndex = entry.key;
|
||||
dynamic cell = entry.value;
|
||||
return _buildTableCell(
|
||||
cell.toString(),
|
||||
widget.size.height * 0.08,
|
||||
rowIndex: rowIndex,
|
||||
columnIndex: columnIndex,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -196,7 +215,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
child: Checkbox(
|
||||
value: _selectAll,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -238,7 +259,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
constraints: const BoxConstraints.expand(height: 40),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: index == widget.headers.length - 1 ? 12 : 8.0, vertical: 4),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
||||
vertical: 4),
|
||||
child: Text(
|
||||
title,
|
||||
style: context.textTheme.titleSmall!.copyWith(
|
||||
@ -253,13 +276,23 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableCell(String content, double size) {
|
||||
Widget _buildTableCell(
|
||||
String content,
|
||||
double size, {
|
||||
required int rowIndex,
|
||||
required int columnIndex,
|
||||
}) {
|
||||
bool isBatteryLevel = content.endsWith('%');
|
||||
double? batteryLevel;
|
||||
|
||||
if (isBatteryLevel) {
|
||||
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
||||
}
|
||||
bool isSettingsColumn = widget.headers[columnIndex] == 'Settings';
|
||||
|
||||
if (isSettingsColumn) {
|
||||
return _buildSettingsIcon(rowIndex, size);
|
||||
}
|
||||
|
||||
Color? statusColor;
|
||||
switch (content) {
|
||||
@ -311,4 +344,22 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingsIcon(int rowIndex, double size) {
|
||||
return Container(
|
||||
height: size,
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: ColorsManager.boxDivider, width: 1.0),
|
||||
),
|
||||
color: Colors.white,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: IconButton(
|
||||
icon: SvgPicture.asset(Assets.settings),
|
||||
onPressed: () => widget.onSettingsPressed?.call(rowIndex),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
return const RoutinesView();
|
||||
}
|
||||
if (state.createRoutineView) {
|
||||
return CreateNewRoutineView();
|
||||
return const CreateNewRoutineView();
|
||||
}
|
||||
|
||||
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||
|
@ -6,9 +6,11 @@ import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/device_settings_panel.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
@ -58,7 +60,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Low Battery ($lowBatteryCount)',
|
||||
];
|
||||
|
||||
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
final buttonLabel =
|
||||
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
@ -105,18 +108,23 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
if (selectedDevices.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => DeviceControlDialog(
|
||||
builder: (context) =>
|
||||
DeviceControlDialog(
|
||||
device: selectedDevices.first,
|
||||
),
|
||||
);
|
||||
} else if (selectedDevices.length > 1) {
|
||||
final productTypes = selectedDevices
|
||||
.map((device) => device.productType)
|
||||
.toSet();
|
||||
} else if (selectedDevices.length >
|
||||
1) {
|
||||
final productTypes =
|
||||
selectedDevices
|
||||
.map((device) =>
|
||||
device.productType)
|
||||
.toSet();
|
||||
if (productTypes.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => DeviceBatchControlDialog(
|
||||
builder: (context) =>
|
||||
DeviceBatchControlDialog(
|
||||
devices: selectedDevices,
|
||||
),
|
||||
);
|
||||
@ -130,7 +138,9 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isControlButtonEnabled ? Colors.white : Colors.grey,
|
||||
color: isControlButtonEnabled
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -166,29 +176,40 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Installation Date and Time',
|
||||
'Status',
|
||||
'Last Offline Date and Time',
|
||||
'Settings'
|
||||
],
|
||||
data: devicesToShow.map((device) {
|
||||
final combinedSpaceNames = device.spaces != null
|
||||
? device.spaces!.map((space) => space.spaceName).join(' > ') +
|
||||
? device.spaces!
|
||||
.map((space) => space.spaceName)
|
||||
.join(' > ') +
|
||||
(device.community != null
|
||||
? ' > ${device.community!.name}'
|
||||
: '')
|
||||
: (device.community != null ? device.community!.name : '');
|
||||
: (device.community != null
|
||||
? device.community!.name
|
||||
: '');
|
||||
|
||||
return [
|
||||
device.name ?? '',
|
||||
device.productName ?? '',
|
||||
device.uuid ?? '',
|
||||
(device.spaces != null && device.spaces!.isNotEmpty)
|
||||
(device.spaces != null &&
|
||||
device.spaces!.isNotEmpty)
|
||||
? device.spaces![0].spaceName
|
||||
: '',
|
||||
combinedSpaceNames,
|
||||
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.batteryLevel != null
|
||||
? '${device.batteryLevel}%'
|
||||
: '-',
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.online == true ? 'Online' : 'Offline',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
'Settings',
|
||||
];
|
||||
}).toList(),
|
||||
onSelectionChanged: (selectedRows) {
|
||||
@ -202,6 +223,10 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
.map((device) => device.uuid!)
|
||||
.toList(),
|
||||
isEmpty: devicesToShow.isEmpty,
|
||||
onSettingsPressed: (rowIndex) {
|
||||
final device = devicesToShow[rowIndex];
|
||||
showDeviceSettingsSidebar(context, device);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
@ -213,4 +238,37 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void showDeviceSettingsSidebar(BuildContext context, AllDevicesModel device) {
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: "Device Settings",
|
||||
transitionDuration: const Duration(milliseconds: 300),
|
||||
pageBuilder: (context, anim1, anim2) {
|
||||
return Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Material(
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
color: ColorsManager.whiteColors,
|
||||
child: DeviceSettingsPanel(
|
||||
device: device,
|
||||
onClose: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
transitionBuilder: (context, anim1, anim2, child) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1, 0),
|
||||
end: Offset.zero,
|
||||
).animate(anim1),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,182 @@
|
||||
class DeviceInfoModel {
|
||||
final int activeTime;
|
||||
final String category;
|
||||
final String categoryName;
|
||||
final int createTime;
|
||||
final String gatewayId;
|
||||
final String icon;
|
||||
final String ip;
|
||||
final String lat;
|
||||
final String localKey;
|
||||
final String lon;
|
||||
final String model;
|
||||
final String name;
|
||||
final String nodeId;
|
||||
final bool online;
|
||||
final String ownerId;
|
||||
final String productName;
|
||||
final bool sub;
|
||||
final String timeZone;
|
||||
final int updateTime;
|
||||
final String uuid;
|
||||
final String productUuid;
|
||||
final String productType;
|
||||
final String permissionType;
|
||||
final String macAddress;
|
||||
final Subspace subspace;
|
||||
|
||||
DeviceInfoModel({
|
||||
required this.activeTime,
|
||||
required this.category,
|
||||
required this.categoryName,
|
||||
required this.createTime,
|
||||
required this.gatewayId,
|
||||
required this.icon,
|
||||
required this.ip,
|
||||
required this.lat,
|
||||
required this.localKey,
|
||||
required this.lon,
|
||||
required this.model,
|
||||
required this.name,
|
||||
required this.nodeId,
|
||||
required this.online,
|
||||
required this.ownerId,
|
||||
required this.productName,
|
||||
required this.sub,
|
||||
required this.timeZone,
|
||||
required this.updateTime,
|
||||
required this.uuid,
|
||||
required this.productUuid,
|
||||
required this.productType,
|
||||
required this.permissionType,
|
||||
required this.macAddress,
|
||||
required this.subspace,
|
||||
});
|
||||
|
||||
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
|
||||
return DeviceInfoModel(
|
||||
activeTime: json['activeTime'],
|
||||
category: json['category'],
|
||||
categoryName: json['categoryName'],
|
||||
createTime: json['createTime'],
|
||||
gatewayId: json['gatewayId'],
|
||||
icon: json['icon'],
|
||||
ip: json['ip'] ?? "",
|
||||
lat: json['lat'],
|
||||
localKey: json['localKey'],
|
||||
lon: json['lon'],
|
||||
model: json['model'],
|
||||
name: json['name'],
|
||||
nodeId: json['nodeId'],
|
||||
online: json['online'],
|
||||
ownerId: json['ownerId'],
|
||||
productName: json['productName'],
|
||||
sub: json['sub'],
|
||||
timeZone: json['timeZone'],
|
||||
updateTime: json['updateTime'],
|
||||
uuid: json['uuid'],
|
||||
productUuid: json['productUuid'],
|
||||
productType: json['productType'],
|
||||
permissionType: json['permissionType'] ?? '',
|
||||
macAddress: json['macAddress'],
|
||||
subspace: Subspace.fromJson(json['subspace']),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'activeTime': activeTime,
|
||||
'category': category,
|
||||
'categoryName': categoryName,
|
||||
'createTime': createTime,
|
||||
'gatewayId': gatewayId,
|
||||
'icon': icon,
|
||||
'ip': ip,
|
||||
'lat': lat,
|
||||
'localKey': localKey,
|
||||
'lon': lon,
|
||||
'model': model,
|
||||
'name': name,
|
||||
'nodeId': nodeId,
|
||||
'online': online,
|
||||
'ownerId': ownerId,
|
||||
'productName': productName,
|
||||
'sub': sub,
|
||||
'timeZone': timeZone,
|
||||
'updateTime': updateTime,
|
||||
'uuid': uuid,
|
||||
'productUuid': productUuid,
|
||||
'productType': productType,
|
||||
'permissionType': permissionType,
|
||||
'macAddress': macAddress,
|
||||
'subspace': subspace.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static DeviceInfoModel empty() {
|
||||
return DeviceInfoModel(
|
||||
activeTime: 0,
|
||||
category: '',
|
||||
categoryName: '',
|
||||
createTime: 0,
|
||||
gatewayId: '',
|
||||
icon: '',
|
||||
ip: '',
|
||||
lat: '',
|
||||
localKey: '',
|
||||
lon: '',
|
||||
model: '',
|
||||
name: '',
|
||||
nodeId: '',
|
||||
online: false,
|
||||
ownerId: '',
|
||||
productName: '',
|
||||
sub: false,
|
||||
timeZone: '',
|
||||
updateTime: 0,
|
||||
uuid: '',
|
||||
productUuid: '',
|
||||
productType: '',
|
||||
permissionType: '',
|
||||
macAddress: '',
|
||||
subspace: Subspace(
|
||||
uuid: '',
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
subspaceName: '',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Subspace {
|
||||
final String uuid;
|
||||
final String createdAt;
|
||||
final String updatedAt;
|
||||
final String subspaceName;
|
||||
|
||||
Subspace({
|
||||
required this.uuid,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.subspaceName,
|
||||
});
|
||||
|
||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||
return Subspace(
|
||||
uuid: json['uuid'],
|
||||
createdAt: json['createdAt'],
|
||||
updatedAt: json['updatedAt'],
|
||||
subspaceName: json['subspaceName'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'createdAt': createdAt,
|
||||
'updatedAt': updatedAt,
|
||||
'subspaceName': subspaceName,
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/sub_space_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||
part 'setting_bloc_event.dart';
|
||||
|
||||
class SettingBlocBloc extends Bloc<SettingBlocEvent, DeviceSettingsState> {
|
||||
final String deviceId;
|
||||
SettingBlocBloc({
|
||||
required this.deviceId,
|
||||
}) : super(const SettingBlocInitial()) {
|
||||
on<DeviceSettingInitialInfo>(fetchDeviceInfo);
|
||||
on<SaveNameEvent>(saveName);
|
||||
on<ChangeNameEvent>(_changeName);
|
||||
on<DeleteDeviceEvent>(deleteDevice);
|
||||
//on<FetchRoomsEvent>(_fetchRoomsAndDevices);
|
||||
}
|
||||
static String deviceName = '';
|
||||
final TextEditingController nameController =
|
||||
TextEditingController(text: deviceName);
|
||||
List<SubSpaceModel> roomsList = [];
|
||||
bool isEditingName = false;
|
||||
|
||||
bool _validateInputs() {
|
||||
final nameError = fullNameValidator(nameController.text);
|
||||
if (nameError != null) {
|
||||
CustomSnackBar.displaySnackBar(nameError);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String? fullNameValidator(String? value) {
|
||||
if (value == null) return 'name is required';
|
||||
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
|
||||
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
|
||||
return 'name must be between 2 and 30 characters long';
|
||||
}
|
||||
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
|
||||
return 'Only alphanumeric characters, space, dash and single quote are allowed';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> saveName(
|
||||
SaveNameEvent event, Emitter<DeviceSettingsState> emit) async {
|
||||
if (_validateInputs()) return;
|
||||
try {
|
||||
emit(SettingLoadingState());
|
||||
var response = await DevicesManagementApi.putDeviceName(
|
||||
deviceId: deviceId, deviceName: nameController.text);
|
||||
add(DeviceSettingInitialInfo());
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
emit(UpdateSettingState(deviceName: nameController.text));
|
||||
} catch (e) {
|
||||
emit(ErrorState(message: e.toString()));
|
||||
} finally {
|
||||
// isSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceInfoModel deviceInfo = DeviceInfoModel(
|
||||
activeTime: 0,
|
||||
category: "",
|
||||
categoryName: "",
|
||||
createTime: 0,
|
||||
gatewayId: "",
|
||||
icon: "",
|
||||
ip: "",
|
||||
lat: "",
|
||||
localKey: "",
|
||||
lon: "",
|
||||
model: "",
|
||||
name: "",
|
||||
nodeId: "",
|
||||
online: false,
|
||||
ownerId: "",
|
||||
productName: "",
|
||||
sub: false,
|
||||
timeZone: "",
|
||||
updateTime: 0,
|
||||
uuid: "",
|
||||
productUuid: "",
|
||||
productType: "",
|
||||
permissionType: "",
|
||||
macAddress: "",
|
||||
subspace: Subspace(
|
||||
uuid: "",
|
||||
createdAt: "",
|
||||
updatedAt: "",
|
||||
subspaceName: "",
|
||||
),
|
||||
);
|
||||
|
||||
Future fetchDeviceInfo(
|
||||
DeviceSettingInitialInfo event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(SettingLoadingState());
|
||||
var response = await DevicesManagementApi.getDeviceInfo(deviceId);
|
||||
deviceInfo = DeviceInfoModel.fromJson(response);
|
||||
nameController.text = deviceInfo.name;
|
||||
|
||||
emit(UpdateSettingState(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: deviceInfo,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(ErrorState(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
bool editName = false;
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
void _changeName(ChangeNameEvent event, Emitter<DeviceSettingsState> emit) {
|
||||
emit(SettingLoadingState());
|
||||
editName = event.value!;
|
||||
if (editName) {
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
focusNode.requestFocus();
|
||||
});
|
||||
} else {
|
||||
add(const SaveNameEvent());
|
||||
focusNode.unfocus();
|
||||
}
|
||||
emit(UpdateSettingState(deviceName: deviceName, deviceInfo: deviceInfo));
|
||||
}
|
||||
|
||||
void deleteDevice(
|
||||
DeleteDeviceEvent event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(SettingLoadingState());
|
||||
var response =
|
||||
await DevicesManagementApi.resetDevise(devicesUuid: deviceId);
|
||||
CustomSnackBar.displaySnackBar('Reset Successfully');
|
||||
emit(UpdateSettingState(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: deviceInfo,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(ErrorState(message: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
part of 'setting_bloc_bloc.dart';
|
||||
|
||||
abstract class SettingBlocEvent extends Equatable {
|
||||
const SettingBlocEvent();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SaveDeviceName extends SettingBlocEvent {
|
||||
final String deviceName;
|
||||
final String deviceId;
|
||||
|
||||
const SaveDeviceName({required this.deviceName, required this.deviceId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceName, deviceId];
|
||||
}
|
||||
|
||||
class StartEditingName extends SettingBlocEvent {}
|
||||
|
||||
class CancelEditingName extends SettingBlocEvent {}
|
||||
|
||||
class ChangeEditingNameValue extends SettingBlocEvent {
|
||||
final String value;
|
||||
const ChangeEditingNameValue(this.value);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [value];
|
||||
}
|
||||
|
||||
class FetchRoomsEvent extends SettingBlocEvent {
|
||||
final String deviceId;
|
||||
|
||||
const FetchRoomsEvent(this.deviceId);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId];
|
||||
}
|
||||
|
||||
class SaveNameEvent extends SettingBlocEvent {
|
||||
const SaveNameEvent();
|
||||
}
|
||||
|
||||
class DeviceSettingInitialInfo extends SettingBlocEvent {}
|
||||
|
||||
class ChangeNameEvent extends SettingBlocEvent {
|
||||
final bool? value;
|
||||
const ChangeNameEvent({this.value});
|
||||
}
|
||||
class DeleteDeviceEvent extends SettingBlocEvent {}
|
@ -0,0 +1,69 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/sub_space_model.dart';
|
||||
|
||||
abstract class DeviceSettingsState extends Equatable {
|
||||
const DeviceSettingsState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SettingBlocInitial extends DeviceSettingsState {
|
||||
final String deviceName;
|
||||
final String deviceId;
|
||||
final bool isEditingName;
|
||||
final String editingNameValue;
|
||||
|
||||
const SettingBlocInitial({
|
||||
this.deviceName = '',
|
||||
this.deviceId = '',
|
||||
this.isEditingName = false,
|
||||
this.editingNameValue = '',
|
||||
});
|
||||
|
||||
SettingBlocInitial copyWith({
|
||||
String? deviceName,
|
||||
String? deviceId,
|
||||
bool? isEditingName,
|
||||
String? editingNameValue,
|
||||
}) =>
|
||||
SettingBlocInitial(
|
||||
deviceName: deviceName ?? this.deviceName,
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
isEditingName: isEditingName ?? this.isEditingName,
|
||||
editingNameValue: editingNameValue ?? this.editingNameValue,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[deviceName, deviceId, isEditingName, editingNameValue];
|
||||
}
|
||||
|
||||
class SettingLoadingState extends DeviceSettingsState {}
|
||||
|
||||
class UpdateSettingState extends DeviceSettingsState {
|
||||
final String deviceName;
|
||||
final DeviceInfoModel? deviceInfo;
|
||||
const UpdateSettingState({required this.deviceName, this.deviceInfo});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceName, deviceInfo];
|
||||
}
|
||||
|
||||
class ErrorState extends DeviceSettingsState {
|
||||
final String message;
|
||||
|
||||
const ErrorState({required this.message});
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class FetchRoomsState extends DeviceSettingsState {
|
||||
final List<SubSpaceModel> roomsList;
|
||||
|
||||
const FetchRoomsState({required this.roomsList});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [roomsList];
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||
|
||||
class SubSpaceModel {
|
||||
final String? id;
|
||||
final String? name;
|
||||
List<DeviceModel>? devices;
|
||||
|
||||
SubSpaceModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.devices,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'devices': devices?.map((device) => device.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
factory SubSpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
List<DeviceModel> devices = [];
|
||||
if (json['devices'] != null) {
|
||||
for (var device in json['devices']) {
|
||||
devices.add(DeviceModel.fromJson(device));
|
||||
}
|
||||
}
|
||||
return SubSpaceModel(
|
||||
id: json['uuid'],
|
||||
name: json['subspaceName'],
|
||||
devices: devices,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/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/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||
|
||||
class DeviceSettingsPanel extends StatelessWidget {
|
||||
final VoidCallback? onClose;
|
||||
final AllDevicesModel device;
|
||||
|
||||
const DeviceSettingsPanel({this.onClose, super.key, required this.device});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sectionTitle = context.theme.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.grayColor,
|
||||
);
|
||||
Widget infoRow(
|
||||
{required String label, required String value, Widget? trailing}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: context.theme.textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.end,
|
||||
style: context.theme.textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
trailing ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => SettingBlocBloc(
|
||||
deviceId: device.uuid ?? '',
|
||||
)..add(DeviceSettingInitialInfo()),
|
||||
child: BlocBuilder<SettingBlocBloc, DeviceSettingsState>(
|
||||
builder: (context, state) {
|
||||
final iconPath =
|
||||
DeviceTypeHelper.getDeviceIconByTypeCode(device.productType);
|
||||
final _bloc = BlocProvider.of<SettingBlocBloc>(context);
|
||||
DeviceInfoModel deviceInfo = DeviceInfoModel.empty();
|
||||
if (state is UpdateSettingState) {
|
||||
deviceInfo = state.deviceInfo!;
|
||||
}
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
color: ColorsManager.grey25,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
||||
child: ListView(
|
||||
children: [
|
||||
/// Header
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: SvgPicture.asset(Assets.closeSettingsIcon),
|
||||
onPressed: onClose ?? () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Device Settings',
|
||||
style: context.theme.textTheme.titleLarge!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.primaryColor)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
/// Device Name + Icon
|
||||
DefaultContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor:
|
||||
const Color.fromARGB(177, 213, 213, 213),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
radius: 36,
|
||||
child: SvgPicture.asset(
|
||||
iconPath,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
maxLength: 30,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
focusNode: _bloc.focusNode,
|
||||
controller: _bloc.nameController,
|
||||
enabled: _bloc.editName,
|
||||
onFieldSubmitted: (value) {
|
||||
_bloc.add(const ChangeNameEvent(value: false));
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
fillColor: Colors.white10,
|
||||
counterText: '',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_bloc.editName == true
|
||||
? const SizedBox()
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
_bloc.add(const ChangeNameEvent(value: true));
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.editNameIconSettings,
|
||||
color: ColorsManager.grayColor,
|
||||
height: 20,
|
||||
width: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
/// Device Management
|
||||
Text('Device Management', style: sectionTitle),
|
||||
DefaultContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: infoRow(
|
||||
label: 'Sub-Space:',
|
||||
value: device.subspace!.subspaceName,
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(color: ColorsManager.dividerColor),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: infoRow(
|
||||
label: 'Virtual Address:',
|
||||
value: deviceInfo.productUuid,
|
||||
trailing: InkWell(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: device.productUuid ?? ''),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Virtual Address copied to clipboard'),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.copy,
|
||||
size: 16,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(color: ColorsManager.dividerColor),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: infoRow(
|
||||
label: 'MAC Address:',
|
||||
value: deviceInfo.macAddress),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
/// Remove Device Button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_bloc.add(DeleteDeviceEvent());
|
||||
},
|
||||
child: const DefaultContainer(
|
||||
padding: EdgeInsets.all(25),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Remove Device',
|
||||
style: TextStyle(color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceTypeHelper {
|
||||
static const Map<String, String> _iconMap = {
|
||||
'AC': Assets.ac,
|
||||
'GW': Assets.gateway,
|
||||
'CPS': Assets.sensors,
|
||||
'DL': Assets.doorLock,
|
||||
'WPS': Assets.sensors,
|
||||
'3G': Assets.gangSwitch,
|
||||
'2G': Assets.twoGang,
|
||||
'1G': Assets.oneGang,
|
||||
'CUR': Assets.curtain,
|
||||
'WH': Assets.waterHeater,
|
||||
'DS': Assets.doorSensor,
|
||||
'1GT': Assets.oneTouchSwitch,
|
||||
'2GT': Assets.twoTouchSwitch,
|
||||
'3GT': Assets.threeTouchSwitch,
|
||||
'GD': Assets.garageDoor,
|
||||
'WL': Assets.waterLeakNormal,
|
||||
'NCPS': Assets.sensors,
|
||||
};
|
||||
|
||||
static String getDeviceIconByTypeCode(String? typeCode) {
|
||||
if (typeCode == null) return Assets.logoHorizontal;
|
||||
return _iconMap[typeCode] ?? Assets.logoHorizontal;
|
||||
}
|
||||
}
|
@ -91,7 +91,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deviceBatchControl(List<String> uuids, String code, dynamic value) async {
|
||||
Future<bool> deviceBatchControl(
|
||||
List<String> uuids, String code, dynamic value) async {
|
||||
try {
|
||||
final body = {
|
||||
'devicesUuid': uuids,
|
||||
@ -116,7 +117,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async {
|
||||
static Future<List<DeviceModel>> getDevicesByGatewayId(
|
||||
String gatewayId) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
|
||||
showServerMessage: false,
|
||||
@ -150,7 +152,9 @@ class DevicesManagementApi {
|
||||
String code,
|
||||
) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
|
||||
path: ApiEndpoints.getDeviceLogs
|
||||
.replaceAll('{uuid}', uuid)
|
||||
.replaceAll('{code}', code),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
return DeviceReport.fromJson(json['data']);
|
||||
@ -223,7 +227,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> addScheduleRecord(ScheduleEntry sendSchedule, String uuid) async {
|
||||
Future<bool> addScheduleRecord(
|
||||
ScheduleEntry sendSchedule, String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
@ -240,7 +245,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ScheduleModel>> getDeviceSchedules(String uuid, String category) async {
|
||||
Future<List<ScheduleModel>> getDeviceSchedules(
|
||||
String uuid, String category) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getScheduleByDeviceId
|
||||
@ -263,7 +269,9 @@ class DevicesManagementApi {
|
||||
}
|
||||
|
||||
Future<bool> updateScheduleRecord(
|
||||
{required bool enable, required String uuid, required String scheduleId}) async {
|
||||
{required bool enable,
|
||||
required String uuid,
|
||||
required String scheduleId}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateScheduleByDeviceId
|
||||
@ -284,7 +292,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> editScheduleRecord(String uuid, ScheduleEntry newSchedule) async {
|
||||
Future<bool> editScheduleRecord(
|
||||
String uuid, ScheduleEntry newSchedule) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
@ -335,4 +344,46 @@ class DevicesManagementApi {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> putDeviceName(
|
||||
{required String deviceId, required String deviceName}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||
body: {"deviceName": deviceName},
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'];
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future getDeviceInfo(String deviceId) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'] as Map<String, dynamic>;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
static Future resetDevise({
|
||||
String? devicesUuid,
|
||||
}) async {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.resetDevice.replaceAll('{deviceUuid}', devicesUuid!),
|
||||
showServerMessage: false,
|
||||
body: {
|
||||
"devicesUuid": [devicesUuid]
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/sub_space_model.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/model/pagination_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||
@ -12,14 +13,16 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class CommunitySpaceManagementApi {
|
||||
// Community Management APIs
|
||||
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
|
||||
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
||||
{int page = 1}) async {
|
||||
try {
|
||||
List<CommunityModel> allCommunities = [];
|
||||
bool hasNext = true;
|
||||
|
||||
while (hasNext) {
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
path: ApiEndpoints.getCommunityList
|
||||
.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
},
|
||||
@ -55,8 +58,14 @@ class CommunitySpaceManagementApi {
|
||||
try {
|
||||
bool hasNext = false;
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
|
||||
path:
|
||||
ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'includeSpaces': true,
|
||||
'size': 25,
|
||||
'search': search
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
try {
|
||||
List<dynamic> jsonData = json['data'] ?? [];
|
||||
@ -68,7 +77,10 @@ class CommunitySpaceManagementApi {
|
||||
|
||||
page = currentPage + 1;
|
||||
paginationModel = PaginationModel(
|
||||
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
|
||||
pageNum: page,
|
||||
hasNext: hasNext,
|
||||
size: 25,
|
||||
communities: communityList);
|
||||
return paginationModel;
|
||||
} catch (_) {
|
||||
hasNext = false;
|
||||
@ -83,7 +95,8 @@ class CommunitySpaceManagementApi {
|
||||
Future<CommunityModel?> getCommunityById(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.getCommunityById
|
||||
.replaceAll('{communityId}', communityId),
|
||||
expectedResponseModel: (json) {
|
||||
return CommunityModel.fromJson(json['data']);
|
||||
},
|
||||
@ -95,7 +108,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
|
||||
Future<CommunityModel?> createCommunity(
|
||||
String name, String description, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
||||
@ -114,7 +128,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
|
||||
Future<bool> updateCommunity(
|
||||
String communityId, String name, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateCommunity
|
||||
@ -151,7 +166,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
|
||||
Future<SpacesResponse> fetchSpaces(
|
||||
String communityId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.listSpaces
|
||||
@ -177,7 +193,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
|
||||
Future<SpaceModel?> getSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpace
|
||||
@ -289,7 +306,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
|
||||
Future<bool> deleteSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.deleteSpace
|
||||
@ -307,15 +325,17 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(
|
||||
String communityId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpaceHierarchy
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', projectId),
|
||||
expectedResponseModel: (json) {
|
||||
final spaceModels =
|
||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
|
||||
return spaceModels;
|
||||
},
|
||||
@ -327,15 +347,17 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
|
||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
|
||||
{String? communityId, String? projectId}) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.spaceOnlyWithDevices
|
||||
.replaceAll('{communityId}', communityId!)
|
||||
.replaceAll('{projectId}', projectId!),
|
||||
expectedResponseModel: (json) {
|
||||
final spaceModels =
|
||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
return spaceModels;
|
||||
},
|
||||
);
|
||||
@ -345,4 +367,36 @@ class CommunitySpaceManagementApi {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<SubSpaceModel>> getSubSpaceBySpaceId(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
// Construct the API path
|
||||
final path = ApiEndpoints.listSubspace
|
||||
.replaceFirst('{communityUuid}', communityId)
|
||||
.replaceFirst('{spaceUuid}', spaceId)
|
||||
.replaceAll('{projectUuid}', projectId);
|
||||
|
||||
final response = await HTTPService().get(
|
||||
path: path,
|
||||
queryParameters: {"page": 1, "pageSize": 10},
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
List<SubSpaceModel> rooms = [];
|
||||
if (json['data'] != null) {
|
||||
for (var subspace in json['data']) {
|
||||
rooms.add(SubSpaceModel.fromJson(subspace));
|
||||
}
|
||||
} else {
|
||||
print("Warning: 'data' key is missing or null in response JSON.");
|
||||
}
|
||||
return rooms;
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error, stackTrace) {
|
||||
return []; // Return an empty list if there's an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,4 +73,6 @@ abstract class ColorsManager {
|
||||
static const Color vividBlue = Color(0xFF023DFE);
|
||||
static const Color semiTransparentRed = Color(0x99FF0000);
|
||||
static const Color grey700 = Color(0xFF2D3748);
|
||||
static const Color grey25 = Color(0xFFF9F9F9);
|
||||
|
||||
}
|
||||
|
@ -60,9 +60,12 @@ abstract class ApiEndpoints {
|
||||
'/devices/{uuid}/report-logs?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId =
|
||||
'/schedule/enable/{deviceUuid}';
|
||||
static const String factoryReset = '/devices/batch';
|
||||
|
||||
//product
|
||||
@ -124,4 +127,10 @@ abstract class ApiEndpoints {
|
||||
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
|
||||
static const String spaceOnlyWithDevices =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces?onlyWithDevices=true';
|
||||
|
||||
static const String listSubspace =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
|
||||
static const String deviceByUuid = '/devices/{deviceUuid}';
|
||||
|
||||
static const String resetDevice = '/factory/reset/{deviceUuid}';
|
||||
}
|
||||
|
@ -481,4 +481,9 @@ class Assets {
|
||||
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
||||
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
||||
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
|
||||
static const String closeSettingsIcon =
|
||||
'assets/icons/close_settings_icon.svg';
|
||||
|
||||
static const String editNameIconSettings =
|
||||
'assets/icons/edit_name_icon_settings.svg';
|
||||
}
|
||||
|
45
lib/web_layout/default_container.dart
Normal file
45
lib/web_layout/default_container.dart
Normal file
@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DefaultContainer extends StatelessWidget {
|
||||
const DefaultContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.height,
|
||||
this.width,
|
||||
this.color,
|
||||
this.boxConstraints,
|
||||
this.margin,
|
||||
this.padding,
|
||||
this.onTap,
|
||||
this.borderRadius,
|
||||
});
|
||||
|
||||
final double? height;
|
||||
final double? width;
|
||||
final Widget child;
|
||||
final BoxConstraints? boxConstraints;
|
||||
final EdgeInsets? margin;
|
||||
final EdgeInsets? padding;
|
||||
final Color? color;
|
||||
final Function()? onTap;
|
||||
final BorderRadius? borderRadius;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Container(
|
||||
height: height,
|
||||
width: width,
|
||||
margin: margin ?? const EdgeInsets.only(right: 3, bottom: 3),
|
||||
constraints: boxConstraints,
|
||||
decoration: BoxDecoration(
|
||||
color: color ?? Colors.white,
|
||||
borderRadius: borderRadius ?? BorderRadius.circular(20),
|
||||
),
|
||||
padding: padding ?? const EdgeInsets.all(10),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user