// lib/screens/settings/parameter_limits_settings.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:environment_monitoring_app/auth_provider.dart'; class ParameterLimitsSettingsScreen extends StatefulWidget { const ParameterLimitsSettingsScreen({super.key}); @override State createState() => _ParameterLimitsSettingsScreenState(); } class _ParameterLimitsSettingsScreenState extends State { final TextEditingController _npeRiverLimitsSearchController = TextEditingController(); String _npeRiverLimitsSearchQuery = ''; final TextEditingController _npeMarineLimitsSearchController = TextEditingController(); String _npeMarineLimitsSearchQuery = ''; final TextEditingController _airLimitsSearchController = TextEditingController(); String _airLimitsSearchQuery = ''; final TextEditingController _riverLimitsSearchController = TextEditingController(); String _riverLimitsSearchQuery = ''; final TextEditingController _marineLimitsSearchController = TextEditingController(); String _marineLimitsSearchQuery = ''; int _marineLimitsCurrentPage = 1; final int _marineLimitsItemsPerPage = 15; @override void initState() { super.initState(); _npeRiverLimitsSearchController.addListener(() => setState( () => _npeRiverLimitsSearchQuery = _npeRiverLimitsSearchController.text)); _npeMarineLimitsSearchController.addListener(() => setState(() => _npeMarineLimitsSearchQuery = _npeMarineLimitsSearchController.text)); _airLimitsSearchController.addListener(() => setState(() => _airLimitsSearchQuery = _airLimitsSearchController.text)); _riverLimitsSearchController.addListener(() => setState( () => _riverLimitsSearchQuery = _riverLimitsSearchController.text)); _marineLimitsSearchController.addListener(() { setState(() { _marineLimitsSearchQuery = _marineLimitsSearchController.text; _marineLimitsCurrentPage = 1; // Reset to page 1 }); }); } @override void dispose() { _npeRiverLimitsSearchController.dispose(); _npeMarineLimitsSearchController.dispose(); _airLimitsSearchController.dispose(); _riverLimitsSearchController.dispose(); _marineLimitsSearchController.dispose(); super.dispose(); } Widget _buildSectionHeader(BuildContext context, String title) { return Padding( padding: const EdgeInsets.fromLTRB(8.0, 24.0, 8.0, 16.0), child: Text( title, style: Theme.of(context) .textTheme .headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), ); } Widget _buildExpansionTile({ required String title, required IconData leadingIcon, required Widget child, }) { return ExpansionTile( leading: Icon(leadingIcon), title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: child, ), ], ); } Widget _buildInfoList(List>? items, Widget Function(Map) itemBuilder) { if (items == null || items.isEmpty) { return const ListTile( title: Text('No data available. Sync to download.'), dense: true, ); } return Column( children: items.map((item) => itemBuilder(item)).toList(), ); } Widget _buildPaginatedMarineLimitsList( List>? filteredList, List>? allStations, ) { if (filteredList == null || filteredList.isEmpty) { final originalList = context.read().marineParameterLimits; String message = 'No matching parameter limits found.'; if (originalList == null || originalList.isEmpty) { message = 'No data available. Sync to download.'; } return ListTile( title: Text(message, textAlign: TextAlign.center), dense: true, ); } final totalItems = filteredList.length; final totalPages = (totalItems / _marineLimitsItemsPerPage).ceil(); if (totalPages > 0 && _marineLimitsCurrentPage > totalPages) { _marineLimitsCurrentPage = totalPages; } final startIndex = (_marineLimitsCurrentPage - 1) * _marineLimitsItemsPerPage; final endIndex = (startIndex + _marineLimitsItemsPerPage > totalItems) ? totalItems : startIndex + _marineLimitsItemsPerPage; final paginatedItems = filteredList.sublist(startIndex, endIndex); return Column( children: [ Column( children: paginatedItems .map((item) => _buildParameterLimitEntry(item, stations: allStations)) .toList(), ), if (totalPages > 1) ...[ const Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( icon: const Icon(Icons.arrow_back_ios), iconSize: 18.0, tooltip: 'Previous Page', onPressed: _marineLimitsCurrentPage > 1 ? () { setState(() { _marineLimitsCurrentPage--; }); } : null, ), Text( 'Page $_marineLimitsCurrentPage of $totalPages', style: Theme.of(context).textTheme.bodySmall, ), IconButton( icon: const Icon(Icons.arrow_forward_ios), iconSize: 18.0, tooltip: 'Next Page', onPressed: _marineLimitsCurrentPage < totalPages ? () { setState(() { _marineLimitsCurrentPage++; }); } : null, ), ], ), ] ], ); } Widget _buildParameterLimitEntry( Map item, { List>? departments, List>? stations, }) { final paramName = item['param_parameter_list']?.toString() ?? 'N/A'; final upperLimit = item['param_upper_limit']?.toString() ?? 'N/A'; final lowerLimit = item['param_lower_limit']?.toString() ?? 'N/A'; String contextSubtitle = ''; if (item.containsKey('department_id') && item['department_id'] != null && departments != null) { final deptId = item['department_id']; final dept = departments.firstWhere((d) => d['department_id'] == deptId, orElse: () => {}); if (dept.isNotEmpty) { contextSubtitle = 'Dept: ${dept['department_name']}'; } } if (item.containsKey('station_id') && item['station_id'] != null && stations != null) { final stationId = item['station_id']; final station = stations.firstWhere((s) => s['station_id'] == stationId, orElse: () => {}); if (station.isNotEmpty) { final stationCode = station['man_station_code'] ?? 'N/A'; final stationName = station['man_station_name'] ?? 'N/A'; contextSubtitle = 'Station: $stationCode - $stationName'; } } return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 2, child: Text( lowerLimit, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), textAlign: TextAlign.start, ), ), Expanded( flex: 5, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.science_outlined, size: 16), const SizedBox(width: 8), Flexible( child: Text( paramName, style: const TextStyle(fontSize: 14), textAlign: TextAlign.center, ), ), ], ), if (contextSubtitle.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 2.0), child: Text( contextSubtitle, style: const TextStyle( fontSize: 12, fontStyle: FontStyle.italic, color: Colors.grey), textAlign: TextAlign.center, ), ), ], ), ), Expanded( flex: 2, child: Text( upperLimit, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), textAlign: TextAlign.end, ), ), ], ), ); } Widget _buildSearchBar({ required TextEditingController controller, required String labelText, required String hintText, }) { return Padding( padding: const EdgeInsets.only(bottom: 8.0, top: 8.0), child: TextField( controller: controller, decoration: InputDecoration( labelText: labelText, hintText: hintText, prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), suffixIcon: controller.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () => controller.clear()) : null, ), style: const TextStyle(fontSize: 14), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Parameter Limits"), ), body: Consumer( builder: (context, auth, child) { final npeParameterLimits = auth.npeParameterLimits; final marineParameterLimits = auth.marineParameterLimits; final riverParameterLimits = auth.riverParameterLimits; final departments = auth.departments; final allManualStations = auth.manualStations; final int? airDepartmentId = departments ?.firstWhere((d) => d['department_name'] == 'Air', orElse: () => {}) ?['department_id']; final int? riverDepartmentId = departments ?.firstWhere((d) => d['department_name'] == 'River', orElse: () => {}) ?['department_id']; final int? marineDepartmentId = departments ?.firstWhere((d) => d['department_name'] == 'Marine', orElse: () => {}) ?['department_id']; final filteredNpeRiverLimits = npeParameterLimits?.where((limit) { final isRiverNpe = riverDepartmentId != null && limit['department_id'] == riverDepartmentId; if (!isRiverNpe) return false; final paramName = limit['param_parameter_list']?.toLowerCase() ?? ''; final query = _npeRiverLimitsSearchQuery.toLowerCase(); return paramName.contains(query); }).toList(); final filteredNpeMarineLimits = npeParameterLimits?.where((limit) { final isMarineNpe = marineDepartmentId != null && limit['department_id'] == marineDepartmentId; if (!isMarineNpe) return false; final paramName = limit['param_parameter_list']?.toLowerCase() ?? ''; final query = _npeMarineLimitsSearchQuery.toLowerCase(); return paramName.contains(query); }).toList(); final filteredAirLimits = npeParameterLimits?.where((limit) { final isAirLimit = airDepartmentId != null && limit['department_id'] == airDepartmentId; if (!isAirLimit) return false; final paramName = limit['param_parameter_list']?.toLowerCase() ?? ''; final query = _airLimitsSearchQuery.toLowerCase(); return paramName.contains(query); }).toList(); final filteredRiverLimits = riverParameterLimits?.where((limit) { final paramName = limit['param_parameter_list']?.toLowerCase() ?? ''; final query = _riverLimitsSearchQuery.toLowerCase(); return paramName.contains(query); }).toList(); final filteredMarineLimits = marineParameterLimits?.where((limit) { final paramName = limit['param_parameter_list']?.toLowerCase() ?? ''; final query = _marineLimitsSearchQuery.toLowerCase(); if (paramName.contains(query)) return true; final stationId = limit['station_id']; if (stationId != null && allManualStations != null) { final station = allManualStations.firstWhere( (s) => s['station_id'] == stationId, orElse: () => {}); if (station.isNotEmpty) { final stationName = station['man_station_name']?.toLowerCase() ?? ''; final stationCode = station['man_station_code']?.toLowerCase() ?? ''; if (stationName.contains(query) || stationCode.contains(query)) { return true; } } } return false; }).toList(); return ListView( padding: const EdgeInsets.all(16.0), children: [ _buildSectionHeader(context, "Parameter Limits"), Card( margin: EdgeInsets.zero, child: Column( children: [ _buildExpansionTile( title: 'NPE River Parameter Limits', leadingIcon: Icons.science_outlined, child: Column( children: [ _buildSearchBar( controller: _npeRiverLimitsSearchController, labelText: 'Search NPE River Limits', hintText: 'Search by parameter name', ), _buildInfoList( filteredNpeRiverLimits, (item) => _buildParameterLimitEntry(item, departments: departments)), ], ), ), _buildExpansionTile( title: 'NPE Marine Parameter Limits', leadingIcon: Icons.science_outlined, child: Column( children: [ _buildSearchBar( controller: _npeMarineLimitsSearchController, labelText: 'Search NPE Marine Limits', hintText: 'Search by parameter name', ), _buildInfoList( filteredNpeMarineLimits, (item) => _buildParameterLimitEntry(item, departments: departments)), ], ), ), _buildExpansionTile( title: 'Air Parameter Limits', leadingIcon: Icons.air, child: Column( children: [ _buildSearchBar( controller: _airLimitsSearchController, labelText: 'Search Air Limits', hintText: 'Search by parameter name', ), _buildInfoList( filteredAirLimits, (item) => _buildParameterLimitEntry(item, departments: departments)), ], ), ), _buildExpansionTile( title: 'River Parameter Limits', leadingIcon: Icons.water, child: Column( children: [ _buildSearchBar( controller: _riverLimitsSearchController, labelText: 'Search River Limits', hintText: 'Search by parameter name', ), _buildInfoList(filteredRiverLimits, (item) => _buildParameterLimitEntry(item)), ], ), ), _buildExpansionTile( title: 'Marine Parameter Limits', leadingIcon: Icons.waves, child: Column( children: [ _buildSearchBar( controller: _marineLimitsSearchController, labelText: 'Search Marine Limits', hintText: 'Search by parameter or station', ), _buildPaginatedMarineLimitsList( filteredMarineLimits, allManualStations), ], ), ), ], ), ), ], ); }, ), ); } }