diff --git a/lib/pages/common/access_device_table.dart b/lib/pages/common/access_device_table.dart new file mode 100644 index 00000000..86d4a6b3 --- /dev/null +++ b/lib/pages/common/access_device_table.dart @@ -0,0 +1,304 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class AccessDeviceTable extends StatefulWidget { + final List headers; + final String? tableName; + final List> 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? initialSelectedIds; + final int uuidIndex; + const AccessDeviceTable({ + 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, + }); + + @override + _DynamicTableState createState() => _DynamicTableState(); +} + +class _DynamicTableState extends State { + late List _selected; + bool _selectAll = false; + + @override + void initState() { + super.initState(); + _initializeSelection(); + } + + @override + void didUpdateWidget(AccessDeviceTable oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.data != widget.data) { + _initializeSelection(); + } + } + + void _initializeSelection() { + if (widget.data.isEmpty) { + _selected = []; + _selectAll = false; + } else { + _selected = List.generate(widget.data.length, (index) { + // Check if the initialSelectedIds contains the deviceUuid + // uuidIndex is the index of the column containing the deviceUuid + final deviceUuid = widget.data[index][widget.uuidIndex]; + return widget.initialSelectedIds != null && + widget.initialSelectedIds!.contains(deviceUuid); + }); + _selectAll = _selected.every((element) => element == true); + } + } + + void _toggleRowSelection(int index) { + setState(() { + _selected[index] = !_selected[index]; + + if (widget.onRowSelected != null) { + widget.onRowSelected!(index, _selected[index], widget.data[index]); + } + }); + } + + void _toggleSelectAll(bool? value) { + setState(() { + _selectAll = value ?? false; + _selected = List.filled(widget.data.length, _selectAll); + if (widget.selectAll != null) { + widget.selectAll!(_selectAll); + } + }); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: widget.cellDecoration, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SizedBox( + width: widget.size.width, + child: Column( + children: [ + Container( + decoration: widget.headerDecoration ?? + BoxDecoration(color: Colors.grey[200]), + child: Row( + children: [ + if (widget.withCheckBox) _buildSelectAllCheckbox(), + ...widget.headers + .map((header) => _buildTableHeaderCell(header)), + ], + ), + ), + widget.isEmpty + ? Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + SvgPicture.asset(Assets.emptyTable), + const SizedBox( + height: 15, + ), + Text( + // no password + widget.tableName == 'AccessManagement' + ? 'No Password ' + : 'No Devices', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor), + ) + ], + ), + ], + ), + ], + ), + ) + : Expanded( + child: Container( + color: Colors.white, + child: ListView.builder( + shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: (context, index) { + final row = widget.data[index]; + return Row( + children: [ + if (widget.withCheckBox) + _buildRowCheckbox( + index, widget.size.height * 0.10), + ...row.map((cell) => _buildTableCell( + cell.toString(), + widget.size.height * 0.10)), + ], + ); + }, + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildSelectAllCheckbox() { + return Container( + width: 50, + padding: const EdgeInsets.all(8.0), + decoration: const BoxDecoration( + border: Border.symmetric( + vertical: BorderSide(color: ColorsManager.boxDivider), + ), + ), + child: Checkbox( + value: widget.data.isNotEmpty && + _selected.every((element) => element == true), + onChanged: widget.withSelectAll && widget.data.isNotEmpty + ? _toggleSelectAll + : null, + ), + ); + } + + 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, + ), + ), + ), + alignment: Alignment.centerLeft, + child: Center( + child: Checkbox( + value: _selected[index], + onChanged: (bool? value) { + _toggleRowSelection(index); + }, + ), + ), + ); + } + + Widget _buildTableHeaderCell(String title) { + return Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border.symmetric( + vertical: BorderSide(color: ColorsManager.boxDivider), + ), + ), + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 13, + color: Color(0xFF999999), + ), + maxLines: 2, + ), + ), + ), + ); + } + + Widget _buildTableCell(String content, double size) { + bool isBatteryLevel = content.endsWith('%'); + double? batteryLevel; + + if (isBatteryLevel) { + batteryLevel = double.tryParse(content.replaceAll('%', '').trim()); + } + + 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, + ), + ), + ), + alignment: Alignment.centerLeft, + child: Text( + content, + style: TextStyle( + color: (batteryLevel != null && batteryLevel < 20) + ? ColorsManager.red + : (batteryLevel != null && batteryLevel > 20) + ? ColorsManager.green + : statusColor, + fontSize: 10, + fontWeight: FontWeight.w400), + maxLines: 2, + ), + ), + ); + } +} diff --git a/lib/pages/visitor_password/view/add_device_dialog.dart b/lib/pages/visitor_password/view/add_device_dialog.dart index 1a116b06..5be0eb2f 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/common/access_device_table.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; @@ -35,10 +36,10 @@ class AddDeviceDialog extends StatelessWidget { backgroundColor: Colors.white, title: Text( 'Add Accessible Device', - style: Theme.of(context) - .textTheme - .headlineLarge! - .copyWith(fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black), + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + fontWeight: FontWeight.w400, + fontSize: 24, + color: Colors.black), ), content: SizedBox( height: MediaQuery.of(context).size.height / 1.7, @@ -68,10 +69,13 @@ class AddDeviceDialog extends StatelessWidget { ), Text( 'Only online accessible devices can be added', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.grayColor), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.grayColor), ), ], )), @@ -152,7 +156,8 @@ class AddDeviceDialog extends StatelessWidget { visitorBloc.deviceNameController.clear(); visitorBloc.deviceIdController.clear(); visitorBloc.unitNameController.clear(); - visitorBloc.add(FetchDevice()); // Reset to original list + visitorBloc.add( + FetchDevice()); // Reset to original list }, ), ), @@ -163,7 +168,7 @@ class AddDeviceDialog extends StatelessWidget { Expanded( flex: 3, child: state is TableLoaded - ? DynamicTable( + ? AccessDeviceTable( uuidIndex: 1, withSelectAll: true, initialSelectedIds: selectedDeviceIds, @@ -172,7 +177,8 @@ class AddDeviceDialog extends StatelessWidget { selectAll: (p0) { visitorBloc.selectedDeviceIds.clear(); for (var item in state.data) { - visitorBloc.add(SelectDeviceEvent(item.uuid)); + visitorBloc + .add(SelectDeviceEvent(item.uuid)); } }, onRowSelected: (index, isSelected, row) {