485 lines
18 KiB
Dart
485 lines
18 KiB
Dart
// 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<ParameterLimitsSettingsScreen> createState() =>
|
|
_ParameterLimitsSettingsScreenState();
|
|
}
|
|
|
|
class _ParameterLimitsSettingsScreenState
|
|
extends State<ParameterLimitsSettingsScreen> {
|
|
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<Map<String, dynamic>>? items,
|
|
Widget Function(Map<String, dynamic>) 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<Map<String, dynamic>>? filteredList,
|
|
List<Map<String, dynamic>>? allStations,
|
|
) {
|
|
if (filteredList == null || filteredList.isEmpty) {
|
|
final originalList = context.read<AuthProvider>().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<String, dynamic> item, {
|
|
List<Map<String, dynamic>>? departments,
|
|
List<Map<String, dynamic>>? 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<AuthProvider>(
|
|
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),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
} |