mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00

Update CreateNewRoutineView to use const constructor Add SubSpaceModel class for device settings Add DefaultContainer widget for web layout Add events and states for device settings bloc Update API endpoints for device settings
390 lines
12 KiB
Dart
390 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.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';
|
|
|
|
class DynamicTable extends StatefulWidget {
|
|
final List<String> headers;
|
|
final String? tableName;
|
|
final List<List<dynamic>> data;
|
|
final BoxDecoration? headerDecoration;
|
|
final BoxDecoration? cellDecoration;
|
|
final Size size;
|
|
final bool withCheckBox;
|
|
final bool withSelectAll;
|
|
final bool isEmpty;
|
|
final void Function(bool?)? selectAll;
|
|
final void Function(int, bool, dynamic)? onRowSelected;
|
|
final List<String>? initialSelectedIds;
|
|
final int uuidIndex;
|
|
final Function(dynamic selectedRows)? onSelectionChanged;
|
|
final Function(int rowIndex)? onSettingsPressed;
|
|
const DynamicTable({
|
|
super.key,
|
|
required this.headers,
|
|
required this.data,
|
|
required this.size,
|
|
this.tableName,
|
|
required this.isEmpty,
|
|
required this.withCheckBox,
|
|
required this.withSelectAll,
|
|
this.headerDecoration,
|
|
this.cellDecoration,
|
|
this.selectAll,
|
|
this.onRowSelected,
|
|
this.initialSelectedIds,
|
|
required this.uuidIndex,
|
|
this.onSelectionChanged,
|
|
this.onSettingsPressed,
|
|
});
|
|
|
|
@override
|
|
_DynamicTableState createState() => _DynamicTableState();
|
|
}
|
|
|
|
class _DynamicTableState extends State<DynamicTable> {
|
|
late List<bool> _selectedRows;
|
|
bool _selectAll = false;
|
|
final ScrollController _verticalScrollController = ScrollController();
|
|
final ScrollController _horizontalScrollController = ScrollController();
|
|
late ScrollController _horizontalHeaderScrollController;
|
|
late ScrollController _horizontalBodyScrollController;
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeSelection();
|
|
_horizontalHeaderScrollController = ScrollController();
|
|
_horizontalBodyScrollController = ScrollController();
|
|
|
|
// Synchronize horizontal scrolling
|
|
_horizontalBodyScrollController.addListener(() {
|
|
_horizontalHeaderScrollController
|
|
.jumpTo(_horizontalBodyScrollController.offset);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(DynamicTable oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
if (!_compareListOfLists(oldWidget.data, widget.data)) {
|
|
_initializeSelection();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
for (int i = 0; i < oldList.length; i++) {
|
|
if (oldList[i].length != newList[i].length) return false;
|
|
|
|
for (int j = 0; j < oldList[i].length; j++) {
|
|
if (oldList[i][j] != newList[i][j]) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void _initializeSelection() {
|
|
_selectedRows = List<bool>.filled(widget.data.length, false);
|
|
_selectAll = false;
|
|
}
|
|
|
|
void _toggleRowSelection(int index) {
|
|
setState(() {
|
|
_selectedRows[index] = !_selectedRows[index];
|
|
_selectAll = _selectedRows.every((isSelected) => isSelected);
|
|
});
|
|
widget.onSelectionChanged?.call(_selectedRows);
|
|
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
|
}
|
|
|
|
void _toggleSelectAll(bool? value) {
|
|
setState(() {
|
|
_selectAll = value ?? false;
|
|
_selectedRows = List<bool>.filled(widget.data.length, _selectAll);
|
|
});
|
|
widget.onSelectionChanged?.call(_selectedRows);
|
|
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_horizontalHeaderScrollController.dispose();
|
|
_horizontalBodyScrollController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
decoration: widget.cellDecoration,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
decoration: widget.headerDecoration ??
|
|
const BoxDecoration(color: ColorsManager.boxColor),
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
controller: _horizontalHeaderScrollController,
|
|
child: SizedBox(
|
|
width: widget.size.width,
|
|
child: Row(
|
|
children: [
|
|
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
|
...List.generate(widget.headers.length, (index) {
|
|
return _buildTableHeaderCell(
|
|
widget.headers[index], index);
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Scrollbar(
|
|
controller: _verticalScrollController,
|
|
thumbVisibility: true,
|
|
trackVisibility: true,
|
|
child: SingleChildScrollView(
|
|
controller: _verticalScrollController,
|
|
child: Scrollbar(
|
|
controller: _horizontalBodyScrollController,
|
|
thumbVisibility: false,
|
|
trackVisibility: false,
|
|
notificationPredicate: (notif) => notif.depth == 1,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
controller: _horizontalBodyScrollController,
|
|
child: SizedBox(
|
|
width: widget.size.width,
|
|
child: widget.isEmpty
|
|
? _buildEmptyState()
|
|
: Column(
|
|
children:
|
|
List.generate(widget.data.length, (rowIndex) {
|
|
final row = widget.data[rowIndex];
|
|
return Row(
|
|
children: [
|
|
if (widget.withCheckBox)
|
|
_buildRowCheckbox(
|
|
rowIndex, widget.size.height * 0.08),
|
|
...row.asMap().entries.map((entry) {
|
|
return _buildTableCell(
|
|
entry.value.toString(),
|
|
widget.size.height * 0.08,
|
|
rowIndex: rowIndex,
|
|
columnIndex: entry.key,
|
|
);
|
|
}).toList(),
|
|
],
|
|
);
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSelectAllCheckbox() {
|
|
return Container(
|
|
width: 50,
|
|
decoration: const BoxDecoration(
|
|
border: Border.symmetric(
|
|
vertical: BorderSide(color: ColorsManager.boxDivider),
|
|
),
|
|
),
|
|
child: Checkbox(
|
|
value: _selectAll,
|
|
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
|
? _toggleSelectAll
|
|
: null,
|
|
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildEmptyState() => Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Column(
|
|
children: [
|
|
SvgPicture.asset(Assets.emptyTable),
|
|
const SizedBox(height: 15),
|
|
Text(
|
|
widget.tableName == 'AccessManagement'
|
|
? 'No Password '
|
|
: 'No Devices',
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.bodySmall!
|
|
.copyWith(color: ColorsManager.grayColor),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
Widget _buildRowCheckbox(int index, double size) {
|
|
return Container(
|
|
width: 50,
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: size,
|
|
decoration: const BoxDecoration(
|
|
border: Border(
|
|
bottom: BorderSide(
|
|
color: ColorsManager.boxDivider,
|
|
width: 1.0,
|
|
),
|
|
),
|
|
color: ColorsManager.whiteColors,
|
|
),
|
|
alignment: Alignment.centerLeft,
|
|
child: Center(
|
|
child: Checkbox(
|
|
value: _selectedRows[index],
|
|
onChanged: (bool? value) {
|
|
_toggleRowSelection(index);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTableHeaderCell(String title, int index) {
|
|
return Expanded(
|
|
child: Container(
|
|
decoration: const BoxDecoration(
|
|
border: Border.symmetric(
|
|
vertical: BorderSide(color: ColorsManager.boxDivider),
|
|
),
|
|
),
|
|
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),
|
|
|
|
child: Text(
|
|
title,
|
|
style: context.textTheme.titleSmall!.copyWith(
|
|
color: ColorsManager.grayColor,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
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) {
|
|
case 'Effective':
|
|
statusColor = ColorsManager.textGreen;
|
|
break;
|
|
case 'Expired':
|
|
statusColor = ColorsManager.red;
|
|
break;
|
|
case 'To be effective':
|
|
statusColor = ColorsManager.yaGreen;
|
|
break;
|
|
case 'Online':
|
|
statusColor = ColorsManager.green;
|
|
break;
|
|
case 'Offline':
|
|
statusColor = ColorsManager.red;
|
|
break;
|
|
default:
|
|
statusColor = Colors.black;
|
|
}
|
|
|
|
return Expanded(
|
|
child: 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.centerLeft,
|
|
child: Text(
|
|
content,
|
|
style: TextStyle(
|
|
color: (batteryLevel != null && batteryLevel < 20)
|
|
? ColorsManager.red
|
|
: (batteryLevel != null && batteryLevel > 20)
|
|
? ColorsManager.green
|
|
: statusColor,
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w400),
|
|
maxLines: 2,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSettingsIcon(int rowIndex, double size) {
|
|
return Container(
|
|
height: size,
|
|
width: 120,
|
|
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),
|
|
),
|
|
);
|
|
}
|
|
}
|