fix parameter limit for river and marine
This commit is contained in:
parent
31b64fc203
commit
18c2bf3ec0
@ -50,7 +50,12 @@ class AuthProvider with ChangeNotifier {
|
|||||||
List<Map<String, dynamic>>? _airManualStations;
|
List<Map<String, dynamic>>? _airManualStations;
|
||||||
List<Map<String, dynamic>>? _states;
|
List<Map<String, dynamic>>? _states;
|
||||||
List<Map<String, dynamic>>? _appSettings;
|
List<Map<String, dynamic>>? _appSettings;
|
||||||
List<Map<String, dynamic>>? _parameterLimits;
|
// --- START: MODIFIED PARAMETER LIMITS PROPERTIES ---
|
||||||
|
// The old generic list has been removed and replaced with three specific lists.
|
||||||
|
List<Map<String, dynamic>>? _npeParameterLimits;
|
||||||
|
List<Map<String, dynamic>>? _marineParameterLimits;
|
||||||
|
List<Map<String, dynamic>>? _riverParameterLimits;
|
||||||
|
// --- END: MODIFIED PARAMETER LIMITS PROPERTIES ---
|
||||||
List<Map<String, dynamic>>? _apiConfigs;
|
List<Map<String, dynamic>>? _apiConfigs;
|
||||||
List<Map<String, dynamic>>? _ftpConfigs;
|
List<Map<String, dynamic>>? _ftpConfigs;
|
||||||
List<Map<String, dynamic>>? _documents;
|
List<Map<String, dynamic>>? _documents;
|
||||||
@ -70,7 +75,11 @@ class AuthProvider with ChangeNotifier {
|
|||||||
List<Map<String, dynamic>>? get airManualStations => _airManualStations;
|
List<Map<String, dynamic>>? get airManualStations => _airManualStations;
|
||||||
List<Map<String, dynamic>>? get states => _states;
|
List<Map<String, dynamic>>? get states => _states;
|
||||||
List<Map<String, dynamic>>? get appSettings => _appSettings;
|
List<Map<String, dynamic>>? get appSettings => _appSettings;
|
||||||
List<Map<String, dynamic>>? get parameterLimits => _parameterLimits;
|
// --- START: GETTERS FOR NEW PARAMETER LIMITS ---
|
||||||
|
List<Map<String, dynamic>>? get npeParameterLimits => _npeParameterLimits;
|
||||||
|
List<Map<String, dynamic>>? get marineParameterLimits => _marineParameterLimits;
|
||||||
|
List<Map<String, dynamic>>? get riverParameterLimits => _riverParameterLimits;
|
||||||
|
// --- END: GETTERS FOR NEW PARAMETER LIMITS ---
|
||||||
List<Map<String, dynamic>>? get apiConfigs => _apiConfigs;
|
List<Map<String, dynamic>>? get apiConfigs => _apiConfigs;
|
||||||
List<Map<String, dynamic>>? get ftpConfigs => _ftpConfigs;
|
List<Map<String, dynamic>>? get ftpConfigs => _ftpConfigs;
|
||||||
List<Map<String, dynamic>>? get documents => _documents;
|
List<Map<String, dynamic>>? get documents => _documents;
|
||||||
@ -326,7 +335,13 @@ class AuthProvider with ChangeNotifier {
|
|||||||
_airManualStations = await _dbHelper.loadAirManualStations();
|
_airManualStations = await _dbHelper.loadAirManualStations();
|
||||||
_states = await _dbHelper.loadStates();
|
_states = await _dbHelper.loadStates();
|
||||||
_appSettings = await _dbHelper.loadAppSettings();
|
_appSettings = await _dbHelper.loadAppSettings();
|
||||||
_parameterLimits = await _dbHelper.loadParameterLimits();
|
|
||||||
|
// --- START: LOAD DATA FROM NEW PARAMETER LIMIT TABLES ---
|
||||||
|
_npeParameterLimits = await _dbHelper.loadNpeParameterLimits();
|
||||||
|
_marineParameterLimits = await _dbHelper.loadMarineParameterLimits();
|
||||||
|
_riverParameterLimits = await _dbHelper.loadRiverParameterLimits();
|
||||||
|
// --- END: LOAD DATA FROM NEW PARAMETER LIMIT TABLES ---
|
||||||
|
|
||||||
_documents = await _dbHelper.loadDocuments();
|
_documents = await _dbHelper.loadDocuments();
|
||||||
_apiConfigs = await _dbHelper.loadApiConfigs();
|
_apiConfigs = await _dbHelper.loadApiConfigs();
|
||||||
_ftpConfigs = await _dbHelper.loadFtpConfigs();
|
_ftpConfigs = await _dbHelper.loadFtpConfigs();
|
||||||
@ -468,7 +483,13 @@ class AuthProvider with ChangeNotifier {
|
|||||||
_airManualStations = null;
|
_airManualStations = null;
|
||||||
_states = null;
|
_states = null;
|
||||||
_appSettings = null;
|
_appSettings = null;
|
||||||
_parameterLimits = null;
|
|
||||||
|
// --- START: Clear new parameter limit lists ---
|
||||||
|
_npeParameterLimits = null;
|
||||||
|
_marineParameterLimits = null;
|
||||||
|
_riverParameterLimits = null;
|
||||||
|
// --- END: Clear new parameter limit lists ---
|
||||||
|
|
||||||
_documents = null;
|
_documents = null;
|
||||||
_apiConfigs = null;
|
_apiConfigs = null;
|
||||||
_ftpConfigs = null;
|
_ftpConfigs = null;
|
||||||
|
|||||||
@ -315,7 +315,11 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
|
|||||||
|
|
||||||
final currentReadings = _captureReadingsToMap();
|
final currentReadings = _captureReadingsToMap();
|
||||||
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||||
final marineLimits = (authProvider.parameterLimits ?? []).where((limit) => limit['department_id'] == 4).toList();
|
// --- START: MODIFICATION ---
|
||||||
|
// The `parameterLimits` getter was removed from AuthProvider.
|
||||||
|
// This now correctly uses the new `marineParameterLimits` getter.
|
||||||
|
final marineLimits = authProvider.marineParameterLimits ?? [];
|
||||||
|
// --- END: MODIFICATION ---
|
||||||
final outOfBoundsParams = _validateParameters(currentReadings, marineLimits);
|
final outOfBoundsParams = _validateParameters(currentReadings, marineLimits);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@ -39,7 +39,11 @@ class InSituStep4Summary extends StatelessWidget {
|
|||||||
/// Re-validates the final parameters against the defined limits.
|
/// Re-validates the final parameters against the defined limits.
|
||||||
Set<String> _getOutOfBoundsKeys(BuildContext context) {
|
Set<String> _getOutOfBoundsKeys(BuildContext context) {
|
||||||
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||||
final marineLimits = (authProvider.parameterLimits ?? []).where((limit) => limit['department_id'] == 4).toList();
|
// --- START MODIFICATION ---
|
||||||
|
// The `parameterLimits` getter was removed from AuthProvider.
|
||||||
|
// This now correctly uses the new `marineParameterLimits` getter.
|
||||||
|
final marineLimits = authProvider.marineParameterLimits ?? [];
|
||||||
|
// --- END MODIFICATION ---
|
||||||
final Set<String> invalidKeys = {};
|
final Set<String> invalidKeys = {};
|
||||||
|
|
||||||
final readings = {
|
final readings = {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:intl/intl.dart';
|
|||||||
|
|
||||||
import '../../../../auth_provider.dart';
|
import '../../../../auth_provider.dart';
|
||||||
import '../../../../models/river_in_situ_sampling_data.dart';
|
import '../../../../models/river_in_situ_sampling_data.dart';
|
||||||
|
import '../../../../services/api_service.dart'; // Import to access DatabaseHelper
|
||||||
import '../../../../services/river_in_situ_sampling_service.dart';
|
import '../../../../services/river_in_situ_sampling_service.dart';
|
||||||
import '../../../../bluetooth/bluetooth_manager.dart';
|
import '../../../../bluetooth/bluetooth_manager.dart';
|
||||||
import '../../../../serial/serial_manager.dart';
|
import '../../../../serial/serial_manager.dart';
|
||||||
@ -35,11 +36,12 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
bool _isAutoReading = false;
|
bool _isAutoReading = false;
|
||||||
StreamSubscription? _dataSubscription;
|
StreamSubscription? _dataSubscription;
|
||||||
|
|
||||||
// --- START FIX: Declare service variable for safe disposal ---
|
|
||||||
late final RiverInSituSamplingService _samplingService;
|
late final RiverInSituSamplingService _samplingService;
|
||||||
// --- END FIX ---
|
|
||||||
|
|
||||||
// --- START: Added for Parameter Validation Feature ---
|
// --- START: Added for direct database access ---
|
||||||
|
final DatabaseHelper _dbHelper = DatabaseHelper();
|
||||||
|
// --- END: Added for direct database access ---
|
||||||
|
|
||||||
Map<String, double>? _previousReadingsForComparison;
|
Map<String, double>? _previousReadingsForComparison;
|
||||||
Set<String> _outOfBoundsKeys = {};
|
Set<String> _outOfBoundsKeys = {};
|
||||||
|
|
||||||
@ -55,7 +57,6 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
'ammonia': 'Ammonia',
|
'ammonia': 'Ammonia',
|
||||||
'batteryVoltage': 'Battery',
|
'batteryVoltage': 'Battery',
|
||||||
};
|
};
|
||||||
// --- END: Added for Parameter Validation Feature ---
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> _parameters = [];
|
final List<Map<String, dynamic>> _parameters = [];
|
||||||
|
|
||||||
@ -85,9 +86,7 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// --- START FIX: Initialize service variable safely ---
|
|
||||||
_samplingService = Provider.of<RiverInSituSamplingService>(context, listen: false);
|
_samplingService = Provider.of<RiverInSituSamplingService>(context, listen: false);
|
||||||
// --- END FIX ---
|
|
||||||
_initializeControllers();
|
_initializeControllers();
|
||||||
_initializeFlowrateControllers();
|
_initializeFlowrateControllers();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
@ -97,14 +96,12 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_dataSubscription?.cancel();
|
_dataSubscription?.cancel();
|
||||||
|
|
||||||
// --- START FIX: Properly disconnect from active connections on dispose ---
|
|
||||||
if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) {
|
if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) {
|
||||||
_samplingService.disconnectFromBluetooth();
|
_samplingService.disconnectFromBluetooth();
|
||||||
}
|
}
|
||||||
if (_samplingService.serialConnectionState.value != SerialConnectionState.disconnected) {
|
if (_samplingService.serialConnectionState.value != SerialConnectionState.disconnected) {
|
||||||
_samplingService.disconnectFromSerial();
|
_samplingService.disconnectFromSerial();
|
||||||
}
|
}
|
||||||
// --- END FIX ---
|
|
||||||
|
|
||||||
_disposeControllers();
|
_disposeControllers();
|
||||||
_disposeFlowrateControllers();
|
_disposeFlowrateControllers();
|
||||||
@ -354,8 +351,8 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- START: New Validation Flow ---
|
// --- START: MODIFIED VALIDATION FLOW ---
|
||||||
void _validateAndProceed() {
|
void _validateAndProceed() async {
|
||||||
if (_isAutoReading) {
|
if (_isAutoReading) {
|
||||||
_showStopReadingDialog();
|
_showStopReadingDialog();
|
||||||
return;
|
return;
|
||||||
@ -367,11 +364,10 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
_formKey.currentState!.save();
|
_formKey.currentState!.save();
|
||||||
|
|
||||||
final currentReadings = _captureReadingsToMap();
|
final currentReadings = _captureReadingsToMap();
|
||||||
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
|
||||||
final allLimits = authProvider.parameterLimits ?? [];
|
|
||||||
|
|
||||||
// Use department_id 3 for River
|
// Directly load river-specific limits from the new table via DatabaseHelper.
|
||||||
final riverLimits = allLimits.where((limit) => limit['department_id'] == 3).toList();
|
final List<Map<String, dynamic>> riverLimits = await _dbHelper.loadRiverParameterLimits() ?? [];
|
||||||
|
|
||||||
final outOfBoundsParams = _validateParameters(currentReadings, riverLimits);
|
final outOfBoundsParams = _validateParameters(currentReadings, riverLimits);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -384,6 +380,7 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
_saveDataAndMoveOn(currentReadings);
|
_saveDataAndMoveOn(currentReadings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- END: MODIFIED VALIDATION FLOW ---
|
||||||
|
|
||||||
Map<String, double> _captureReadingsToMap() {
|
Map<String, double> _captureReadingsToMap() {
|
||||||
final Map<String, double> readings = {};
|
final Map<String, double> readings = {};
|
||||||
@ -475,7 +472,6 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
|
|
||||||
widget.onNext();
|
widget.onNext();
|
||||||
}
|
}
|
||||||
// --- END: New Validation Flow ---
|
|
||||||
|
|
||||||
void _showSnackBar(String message, {bool isError = false}) {
|
void _showSnackBar(String message, {bool isError = false}) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -670,7 +666,6 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- START: New UI Widgets for Validation Feature ---
|
|
||||||
Widget _buildComparisonView() {
|
Widget _buildComparisonView() {
|
||||||
final previousReadings = _previousReadingsForComparison!;
|
final previousReadings = _previousReadingsForComparison!;
|
||||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||||
@ -833,7 +828,6 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// --- END: New UI Widgets for Validation Feature ---
|
|
||||||
|
|
||||||
Widget _buildFlowrateSection() {
|
Widget _buildFlowrateSection() {
|
||||||
return Card(
|
return Card(
|
||||||
|
|||||||
@ -39,8 +39,9 @@ class RiverInSituStep5Summary extends StatelessWidget {
|
|||||||
/// Re-validates the final parameters against the defined limits.
|
/// Re-validates the final parameters against the defined limits.
|
||||||
Set<String> _getOutOfBoundsKeys(BuildContext context) {
|
Set<String> _getOutOfBoundsKeys(BuildContext context) {
|
||||||
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||||
// Filter for River department (id: 3)
|
// --- MODIFICATION: Use the new river-specific parameter limits list ---
|
||||||
final riverLimits = (authProvider.parameterLimits ?? []).where((limit) => limit['department_id'] == 3).toList();
|
final riverLimits = authProvider.riverParameterLimits ?? [];
|
||||||
|
// --- END MODIFICATION ---
|
||||||
final Set<String> invalidKeys = {};
|
final Set<String> invalidKeys = {};
|
||||||
|
|
||||||
final readings = {
|
final readings = {
|
||||||
|
|||||||
@ -5,11 +5,8 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:environment_monitoring_app/auth_provider.dart';
|
import 'package:environment_monitoring_app/auth_provider.dart';
|
||||||
import 'package:environment_monitoring_app/services/settings_service.dart';
|
import 'package:environment_monitoring_app/services/settings_service.dart';
|
||||||
// START CHANGE: Import the new UserPreferencesService to manage submission settings
|
|
||||||
import 'package:environment_monitoring_app/services/user_preferences_service.dart';
|
import 'package:environment_monitoring_app/services/user_preferences_service.dart';
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
// START CHANGE: A helper class to manage the state of each module's settings in the UI
|
|
||||||
class _ModuleSettings {
|
class _ModuleSettings {
|
||||||
bool isApiEnabled;
|
bool isApiEnabled;
|
||||||
bool isFtpEnabled;
|
bool isFtpEnabled;
|
||||||
@ -23,7 +20,6 @@ class _ModuleSettings {
|
|||||||
required this.ftpConfigs,
|
required this.ftpConfigs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
@ -37,15 +33,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final SettingsService _settingsService = SettingsService();
|
final SettingsService _settingsService = SettingsService();
|
||||||
bool _isSyncingData = false;
|
bool _isSyncingData = false;
|
||||||
|
|
||||||
// START CHANGE: New state variables for managing submission preferences UI
|
|
||||||
final UserPreferencesService _preferencesService = UserPreferencesService();
|
final UserPreferencesService _preferencesService = UserPreferencesService();
|
||||||
bool _isLoadingSettings = true;
|
bool _isLoadingSettings = true;
|
||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
// This map holds the live state of the settings UI for each module
|
|
||||||
final Map<String, _ModuleSettings> _moduleSettings = {};
|
final Map<String, _ModuleSettings> _moduleSettings = {};
|
||||||
|
|
||||||
// This list defines which modules will appear in the new settings section
|
|
||||||
final List<Map<String, String>> _configurableModules = [
|
final List<Map<String, String>> _configurableModules = [
|
||||||
{'key': 'marine_tarball', 'name': 'Marine Tarball'},
|
{'key': 'marine_tarball', 'name': 'Marine Tarball'},
|
||||||
{'key': 'marine_in_situ', 'name': 'Marine In-Situ'},
|
{'key': 'marine_in_situ', 'name': 'Marine In-Situ'},
|
||||||
@ -53,7 +46,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
{'key': 'air_installation', 'name': 'Air Installation'},
|
{'key': 'air_installation', 'name': 'Air Installation'},
|
||||||
{'key': 'air_collection', 'name': 'Air Collection'},
|
{'key': 'air_collection', 'name': 'Air Collection'},
|
||||||
];
|
];
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
final TextEditingController _tarballSearchController = TextEditingController();
|
final TextEditingController _tarballSearchController = TextEditingController();
|
||||||
String _tarballSearchQuery = '';
|
String _tarballSearchQuery = '';
|
||||||
@ -68,16 +60,33 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final TextEditingController _airClientSearchController = TextEditingController();
|
final TextEditingController _airClientSearchController = TextEditingController();
|
||||||
String _airClientSearchQuery = '';
|
String _airClientSearchQuery = '';
|
||||||
|
|
||||||
|
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 = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadAllModuleSettings(); // Load the new submission preferences on init
|
_loadAllModuleSettings();
|
||||||
_tarballSearchController.addListener(_onTarballSearchChanged);
|
_tarballSearchController.addListener(_onTarballSearchChanged);
|
||||||
_manualSearchController.addListener(_onManualSearchChanged);
|
_manualSearchController.addListener(_onManualSearchChanged);
|
||||||
_riverManualSearchController.addListener(_onRiverManualSearchChanged);
|
_riverManualSearchController.addListener(_onRiverManualSearchChanged);
|
||||||
_riverTriennialSearchController.addListener(_onRiverTriennialSearchChanged);
|
_riverTriennialSearchController.addListener(_onRiverTriennialSearchChanged);
|
||||||
_airStationSearchController.addListener(_onAirStationSearchChanged);
|
_airStationSearchController.addListener(_onAirStationSearchChanged);
|
||||||
_airClientSearchController.addListener(_onAirClientSearchChanged);
|
_airClientSearchController.addListener(_onAirClientSearchChanged);
|
||||||
|
|
||||||
|
_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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -88,6 +97,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_riverTriennialSearchController.dispose();
|
_riverTriennialSearchController.dispose();
|
||||||
_airStationSearchController.dispose();
|
_airStationSearchController.dispose();
|
||||||
_airClientSearchController.dispose();
|
_airClientSearchController.dispose();
|
||||||
|
|
||||||
|
_npeRiverLimitsSearchController.dispose();
|
||||||
|
_npeMarineLimitsSearchController.dispose();
|
||||||
|
_airLimitsSearchController.dispose();
|
||||||
|
_riverLimitsSearchController.dispose();
|
||||||
|
_marineLimitsSearchController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +142,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// START CHANGE: New methods for loading and saving the submission preferences
|
|
||||||
Future<void> _loadAllModuleSettings() async {
|
Future<void> _loadAllModuleSettings() async {
|
||||||
setState(() => _isLoadingSettings = true);
|
setState(() => _isLoadingSettings = true);
|
||||||
for (var module in _configurableModules) {
|
for (var module in _configurableModules) {
|
||||||
@ -136,26 +150,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final apiConfigsWithPrefs = await _preferencesService.getAllApiConfigsWithModulePreferences(moduleKey);
|
final apiConfigsWithPrefs = await _preferencesService.getAllApiConfigsWithModulePreferences(moduleKey);
|
||||||
final ftpConfigsWithPrefs = await _preferencesService.getAllFtpConfigsWithModulePreferences(moduleKey);
|
final ftpConfigsWithPrefs = await _preferencesService.getAllFtpConfigsWithModulePreferences(moduleKey);
|
||||||
|
|
||||||
// START MODIFICATION: Apply default settings for submission preferences
|
|
||||||
// This logic checks if the main toggle for a submission type is on but no
|
|
||||||
// specific destination is checked. If so, it applies a default selection.
|
|
||||||
// This ensures a default configuration without overriding saved user choices.
|
|
||||||
|
|
||||||
// Check if any API config is already enabled from preferences.
|
|
||||||
final bool isAnyApiConfigEnabled = apiConfigsWithPrefs.any((c) => c['is_enabled'] == true);
|
final bool isAnyApiConfigEnabled = apiConfigsWithPrefs.any((c) => c['is_enabled'] == true);
|
||||||
|
|
||||||
// If the main API toggle is on but no specific API is selected, apply the default.
|
|
||||||
if (prefs['is_api_enabled'] == true && !isAnyApiConfigEnabled) {
|
if (prefs['is_api_enabled'] == true && !isAnyApiConfigEnabled) {
|
||||||
final pstwHqApi = apiConfigsWithPrefs.firstWhere((c) => c['config_name'] == 'pstw_hq', orElse: () => {});
|
final pstwHqApi = apiConfigsWithPrefs.firstWhere((c) => c['config_name'] == 'PSTW_HQ', orElse: () => {});
|
||||||
if (pstwHqApi.isNotEmpty) {
|
if (pstwHqApi.isNotEmpty) {
|
||||||
pstwHqApi['is_enabled'] = true;
|
pstwHqApi['is_enabled'] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any FTP config is already enabled from preferences.
|
|
||||||
final bool isAnyFtpConfigEnabled = ftpConfigsWithPrefs.any((c) => c['is_enabled'] == true);
|
final bool isAnyFtpConfigEnabled = ftpConfigsWithPrefs.any((c) => c['is_enabled'] == true);
|
||||||
|
|
||||||
// If the main FTP toggle is on but no specific FTP is selected, apply the defaults for the module.
|
|
||||||
if (prefs['is_ftp_enabled'] == true && !isAnyFtpConfigEnabled) {
|
if (prefs['is_ftp_enabled'] == true && !isAnyFtpConfigEnabled) {
|
||||||
switch (moduleKey) {
|
switch (moduleKey) {
|
||||||
case 'marine_tarball':
|
case 'marine_tarball':
|
||||||
@ -195,7 +200,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// END MODIFICATION
|
|
||||||
|
|
||||||
_moduleSettings[moduleKey] = _ModuleSettings(
|
_moduleSettings[moduleKey] = _ModuleSettings(
|
||||||
isApiEnabled: prefs['is_api_enabled'],
|
isApiEnabled: prefs['is_api_enabled'],
|
||||||
@ -235,7 +239,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
Future<void> _manualDataSync() async {
|
Future<void> _manualDataSync() async {
|
||||||
if (_isSyncingData) return;
|
if (_isSyncingData) return;
|
||||||
@ -245,7 +248,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await auth.syncAllData(forceRefresh: true);
|
await auth.syncAllData(forceRefresh: true);
|
||||||
// MODIFIED: After syncing, also reload module settings to reflect any new server configurations.
|
|
||||||
await _loadAllModuleSettings();
|
await _loadAllModuleSettings();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -278,25 +280,69 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final auth = Provider.of<AuthProvider>(context);
|
final auth = Provider.of<AuthProvider>(context);
|
||||||
final lastSync = auth.lastSyncTimestamp;
|
final lastSync = auth.lastSyncTimestamp;
|
||||||
|
|
||||||
// Get the synced data from the provider.
|
|
||||||
final appSettings = auth.appSettings;
|
final appSettings = auth.appSettings;
|
||||||
final parameterLimits = auth.parameterLimits;
|
final npeParameterLimits = auth.npeParameterLimits;
|
||||||
|
final marineParameterLimits = auth.marineParameterLimits;
|
||||||
|
final riverParameterLimits = auth.riverParameterLimits;
|
||||||
final apiConfigs = auth.apiConfigs;
|
final apiConfigs = auth.apiConfigs;
|
||||||
final ftpConfigs = auth.ftpConfigs;
|
final ftpConfigs = auth.ftpConfigs;
|
||||||
final airClients = auth.airClients;
|
final airClients = auth.airClients;
|
||||||
final departments = auth.departments;
|
final departments = auth.departments;
|
||||||
|
final allManualStations = auth.manualStations;
|
||||||
|
|
||||||
// Find Department IDs
|
|
||||||
final int? airDepartmentId = departments?.firstWhere((d) => d['department_name'] == 'Air', orElse: () => {})?['department_id'];
|
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? 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 int? marineDepartmentId = departments?.firstWhere((d) => d['department_name'] == 'Marine', orElse: () => {})?['department_id'];
|
||||||
|
|
||||||
// Filter Parameter Limits by Department ID
|
final filteredNpeRiverLimits = npeParameterLimits?.where((limit) {
|
||||||
final filteredAirLimits = parameterLimits?.where((limit) => limit['department_id'] == airDepartmentId).toList();
|
final isRiverNpe = riverDepartmentId != null && limit['department_id'] == riverDepartmentId;
|
||||||
final filteredRiverLimits = parameterLimits?.where((limit) => limit['department_id'] == riverDepartmentId).toList();
|
if (!isRiverNpe) return false;
|
||||||
final filteredMarineLimits = parameterLimits?.where((limit) => limit['department_id'] == marineDepartmentId).toList();
|
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();
|
||||||
|
|
||||||
// Filter Marine Stations
|
|
||||||
final filteredTarballStations = (auth.tarballStations?.where((station) {
|
final filteredTarballStations = (auth.tarballStations?.where((station) {
|
||||||
final stationName = station['tbl_station_name']?.toLowerCase() ?? '';
|
final stationName = station['tbl_station_name']?.toLowerCase() ?? '';
|
||||||
final stationCode = station['tbl_station_code']?.toLowerCase() ?? '';
|
final stationCode = station['tbl_station_code']?.toLowerCase() ?? '';
|
||||||
@ -311,7 +357,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return stationName.contains(query) || stationCode.contains(query);
|
return stationName.contains(query) || stationCode.contains(query);
|
||||||
}).toList())?.cast<Map<String, dynamic>>();
|
}).toList())?.cast<Map<String, dynamic>>();
|
||||||
|
|
||||||
// Filter River Stations
|
|
||||||
final filteredRiverManualStations = (auth.riverManualStations?.where((station) {
|
final filteredRiverManualStations = (auth.riverManualStations?.where((station) {
|
||||||
final riverName = station['sampling_river']?.toLowerCase() ?? '';
|
final riverName = station['sampling_river']?.toLowerCase() ?? '';
|
||||||
final stationCode = station['sampling_station_code']?.toLowerCase() ?? '';
|
final stationCode = station['sampling_station_code']?.toLowerCase() ?? '';
|
||||||
@ -328,7 +373,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return riverName.contains(query) || stationCode.contains(query) || basinName.contains(query);
|
return riverName.contains(query) || stationCode.contains(query) || basinName.contains(query);
|
||||||
}).toList())?.cast<Map<String, dynamic>>();
|
}).toList())?.cast<Map<String, dynamic>>();
|
||||||
|
|
||||||
// Filter Air Stations
|
|
||||||
final filteredAirStations = (auth.airManualStations?.where((station) {
|
final filteredAirStations = (auth.airManualStations?.where((station) {
|
||||||
final stationName = station['station_name']?.toLowerCase() ?? '';
|
final stationName = station['station_name']?.toLowerCase() ?? '';
|
||||||
final stationCode = station['station_code']?.toLowerCase() ?? '';
|
final stationCode = station['station_code']?.toLowerCase() ?? '';
|
||||||
@ -336,7 +380,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return stationName.contains(query) || stationCode.contains(query);
|
return stationName.contains(query) || stationCode.contains(query);
|
||||||
}).toList())?.cast<Map<String, dynamic>>();
|
}).toList())?.cast<Map<String, dynamic>>();
|
||||||
|
|
||||||
// Filter Air Clients
|
|
||||||
final filteredAirClients = (auth.airClients?.where((client) {
|
final filteredAirClients = (auth.airClients?.where((client) {
|
||||||
final clientName = client['client_name']?.toLowerCase() ?? '';
|
final clientName = client['client_name']?.toLowerCase() ?? '';
|
||||||
final clientId = client['client_id']?.toString().toLowerCase() ?? '';
|
final clientId = client['client_id']?.toString().toLowerCase() ?? '';
|
||||||
@ -347,7 +390,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Settings"),
|
title: const Text("Settings"),
|
||||||
// START CHANGE: Add a save button to the AppBar
|
|
||||||
actions: [
|
actions: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
@ -360,7 +402,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
// END CHANGE
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(24.0),
|
||||||
@ -391,7 +432,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
// START CHANGE: Insert the new Submission Preferences section
|
|
||||||
_buildSectionHeader(context, "Submission Preferences"),
|
_buildSectionHeader(context, "Submission Preferences"),
|
||||||
_isLoadingSettings
|
_isLoadingSettings
|
||||||
? const Center(child: Padding(padding: EdgeInsets.all(16.0), child: CircularProgressIndicator()))
|
? const Center(child: Padding(padding: EdgeInsets.all(16.0), child: CircularProgressIndicator()))
|
||||||
@ -410,7 +450,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
Text("Telegram Alert Settings", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)),
|
Text("Telegram Alert Settings", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@ -457,20 +496,75 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
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(
|
_buildExpansionTile(
|
||||||
title: 'Air Parameter Limits',
|
title: 'Air Parameter Limits',
|
||||||
leadingIcon: Icons.poll,
|
leadingIcon: Icons.air,
|
||||||
child: _buildInfoList(filteredAirLimits, (item) => _buildParameterLimitEntry(item)),
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildSearchBar(
|
||||||
|
controller: _airLimitsSearchController,
|
||||||
|
labelText: 'Search Air Limits',
|
||||||
|
hintText: 'Search by parameter name',
|
||||||
|
),
|
||||||
|
_buildInfoList(filteredAirLimits, (item) => _buildParameterLimitEntry(item, departments: departments)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_buildExpansionTile(
|
_buildExpansionTile(
|
||||||
title: 'River Parameter Limits',
|
title: 'River Parameter Limits',
|
||||||
leadingIcon: Icons.poll,
|
leadingIcon: Icons.water,
|
||||||
child: _buildInfoList(filteredRiverLimits, (item) => _buildParameterLimitEntry(item)),
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildSearchBar(
|
||||||
|
controller: _riverLimitsSearchController,
|
||||||
|
labelText: 'Search River Limits',
|
||||||
|
hintText: 'Search by parameter name',
|
||||||
|
),
|
||||||
|
_buildInfoList(filteredRiverLimits, (item) => _buildParameterLimitEntry(item)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_buildExpansionTile(
|
_buildExpansionTile(
|
||||||
title: 'Marine Parameter Limits',
|
title: 'Marine Parameter Limits',
|
||||||
leadingIcon: Icons.poll,
|
leadingIcon: Icons.waves,
|
||||||
child: _buildInfoList(filteredMarineLimits, (item) => _buildParameterLimitEntry(item)),
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildSearchBar(
|
||||||
|
controller: _marineLimitsSearchController,
|
||||||
|
labelText: 'Search Marine Limits',
|
||||||
|
hintText: 'Search by parameter or station',
|
||||||
|
),
|
||||||
|
_buildInfoList(filteredMarineLimits, (item) => _buildParameterLimitEntry(item, stations: allManualStations)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_buildExpansionTile(
|
_buildExpansionTile(
|
||||||
title: 'API Configurations',
|
title: 'API Configurations',
|
||||||
@ -668,7 +762,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// START CHANGE: New helper widgets for the preferences UI
|
|
||||||
Widget _buildModulePreferenceTile(String title, String moduleKey, _ModuleSettings settings) {
|
Widget _buildModulePreferenceTile(String title, String moduleKey, _ModuleSettings settings) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
@ -727,7 +820,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// END CHANGE
|
|
||||||
|
|
||||||
Widget _buildSectionHeader(BuildContext context, String title) {
|
Widget _buildSectionHeader(BuildContext context, String title) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -748,7 +840,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
leading: Icon(leadingIcon),
|
leading: Icon(leadingIcon),
|
||||||
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
children: [
|
children: [
|
||||||
child,
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -784,13 +879,37 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildParameterLimitEntry(Map<String, dynamic> item) {
|
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 paramName = item['param_parameter_list']?.toString() ?? 'N/A';
|
||||||
final upperLimit = item['param_upper_limit']?.toString() ?? 'N/A';
|
final upperLimit = item['param_upper_limit']?.toString() ?? 'N/A';
|
||||||
final lowerLimit = item['param_lower_limit']?.toString() ?? 'N/A';
|
final lowerLimit = item['param_lower_limit']?.toString() ?? 'N/A';
|
||||||
String unit = '';
|
String contextSubtitle = '';
|
||||||
|
|
||||||
// Hardcoded units as they are not available in the provided data
|
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) {
|
||||||
|
// --- START: MODIFICATION ---
|
||||||
|
final stationCode = station['man_station_code'] ?? 'N/A';
|
||||||
|
final stationName = station['man_station_name'] ?? 'N/A';
|
||||||
|
contextSubtitle = 'Station: $stationCode - $stationName';
|
||||||
|
// --- END: MODIFICATION ---
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String unit = '';
|
||||||
if (paramName.toLowerCase() == 'ph') {
|
if (paramName.toLowerCase() == 'ph') {
|
||||||
unit = 'pH units';
|
unit = 'pH units';
|
||||||
} else if (paramName.toLowerCase() == 'temp') {
|
} else if (paramName.toLowerCase() == 'temp') {
|
||||||
@ -798,7 +917,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -819,16 +938,24 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.science_outlined, size: 16),
|
const Icon(Icons.science_outlined, size: 16),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Flexible(
|
||||||
paramName,
|
child: Text(
|
||||||
style: const TextStyle(fontSize: 14),
|
paramName,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
if (contextSubtitle.isNotEmpty)
|
||||||
unit,
|
Padding(
|
||||||
style: const TextStyle(fontSize: 12, fontStyle: FontStyle.italic, color: Colors.grey),
|
padding: const EdgeInsets.only(top: 2.0),
|
||||||
),
|
child: Text(
|
||||||
|
contextSubtitle,
|
||||||
|
style: const TextStyle(fontSize: 12, fontStyle: FontStyle.italic, color: Colors.grey),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -868,18 +995,21 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
required String labelText,
|
required String labelText,
|
||||||
required String hintText,
|
required String hintText,
|
||||||
}) {
|
}) {
|
||||||
return TextField(
|
return Padding(
|
||||||
controller: controller,
|
padding: const EdgeInsets.only(bottom: 8.0, top: 8.0),
|
||||||
decoration: InputDecoration(
|
child: TextField(
|
||||||
labelText: labelText,
|
controller: controller,
|
||||||
hintText: hintText,
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.search),
|
labelText: labelText,
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
|
hintText: hintText,
|
||||||
suffixIcon: controller.text.isNotEmpty
|
prefixIcon: const Icon(Icons.search),
|
||||||
? IconButton(icon: const Icon(Icons.clear), onPressed: () => controller.clear())
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||||
: null,
|
suffixIcon: controller.text.isNotEmpty
|
||||||
|
? IconButton(icon: const Icon(Icons.clear), onPressed: () => controller.clear())
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
),
|
),
|
||||||
style: const TextStyle(fontSize: 14),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -276,13 +276,29 @@ class ApiService {
|
|||||||
await dbHelper.deleteAppSettings(id);
|
await dbHelper.deleteAppSettings(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'parameterLimits': {
|
// --- START: REPLACED GENERIC LIMITS WITH SPECIFIC SYNC TASKS ---
|
||||||
'endpoint': 'parameter-limits',
|
'npeParameterLimits': {
|
||||||
|
'endpoint': 'npe-parameter-limits',
|
||||||
'handler': (d, id) async {
|
'handler': (d, id) async {
|
||||||
await dbHelper.upsertParameterLimits(d);
|
await dbHelper.upsertNpeParameterLimits(d);
|
||||||
await dbHelper.deleteParameterLimits(id);
|
await dbHelper.deleteNpeParameterLimits(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'marineParameterLimits': {
|
||||||
|
'endpoint': 'marine-parameter-limits',
|
||||||
|
'handler': (d, id) async {
|
||||||
|
await dbHelper.upsertMarineParameterLimits(d);
|
||||||
|
await dbHelper.deleteMarineParameterLimits(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'riverParameterLimits': {
|
||||||
|
'endpoint': 'river-parameter-limits',
|
||||||
|
'handler': (d, id) async {
|
||||||
|
await dbHelper.upsertRiverParameterLimits(d);
|
||||||
|
await dbHelper.deleteRiverParameterLimits(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// --- END: REPLACED GENERIC LIMITS WITH SPECIFIC SYNC TASKS ---
|
||||||
'apiConfigs': {
|
'apiConfigs': {
|
||||||
'endpoint': 'api-configs',
|
'endpoint': 'api-configs',
|
||||||
'handler': (d, id) async {
|
'handler': (d, id) async {
|
||||||
@ -781,7 +797,9 @@ class RiverApiService {
|
|||||||
class DatabaseHelper {
|
class DatabaseHelper {
|
||||||
static Database? _database;
|
static Database? _database;
|
||||||
static const String _dbName = 'app_data.db';
|
static const String _dbName = 'app_data.db';
|
||||||
static const int _dbVersion = 21;
|
// --- START: INCREMENTED DB VERSION ---
|
||||||
|
static const int _dbVersion = 23;
|
||||||
|
// --- END: INCREMENTED DB VERSION ---
|
||||||
|
|
||||||
static const String _profileTable = 'user_profile';
|
static const String _profileTable = 'user_profile';
|
||||||
static const String _usersTable = 'all_users';
|
static const String _usersTable = 'all_users';
|
||||||
@ -799,6 +817,11 @@ class DatabaseHelper {
|
|||||||
static const String _statesTable = 'states';
|
static const String _statesTable = 'states';
|
||||||
static const String _appSettingsTable = 'app_settings';
|
static const String _appSettingsTable = 'app_settings';
|
||||||
static const String _parameterLimitsTable = 'manual_parameter_limits';
|
static const String _parameterLimitsTable = 'manual_parameter_limits';
|
||||||
|
// --- START: ADDED NEW TABLE CONSTANTS ---
|
||||||
|
static const String _npeParameterLimitsTable = 'npe_parameter_limits';
|
||||||
|
static const String _marineParameterLimitsTable = 'marine_parameter_limits';
|
||||||
|
static const String _riverParameterLimitsTable = 'river_parameter_limits';
|
||||||
|
// --- END: ADDED NEW TABLE CONSTANTS ---
|
||||||
static const String _apiConfigsTable = 'api_configurations';
|
static const String _apiConfigsTable = 'api_configurations';
|
||||||
static const String _ftpConfigsTable = 'ftp_configurations';
|
static const String _ftpConfigsTable = 'ftp_configurations';
|
||||||
static const String _retryQueueTable = 'retry_queue';
|
static const String _retryQueueTable = 'retry_queue';
|
||||||
@ -844,6 +867,11 @@ class DatabaseHelper {
|
|||||||
await db.execute('CREATE TABLE $_statesTable(state_id INTEGER PRIMARY KEY, state_json TEXT)');
|
await db.execute('CREATE TABLE $_statesTable(state_id INTEGER PRIMARY KEY, state_json TEXT)');
|
||||||
await db.execute('CREATE TABLE $_appSettingsTable(setting_id INTEGER PRIMARY KEY, setting_json TEXT)');
|
await db.execute('CREATE TABLE $_appSettingsTable(setting_id INTEGER PRIMARY KEY, setting_json TEXT)');
|
||||||
await db.execute('CREATE TABLE $_parameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
await db.execute('CREATE TABLE $_parameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
// --- START: ADDED CREATE TABLE FOR NEW LIMITS ---
|
||||||
|
await db.execute('CREATE TABLE $_npeParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
await db.execute('CREATE TABLE $_marineParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
await db.execute('CREATE TABLE $_riverParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
// --- END: ADDED CREATE TABLE FOR NEW LIMITS ---
|
||||||
await db.execute('CREATE TABLE $_apiConfigsTable(api_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
await db.execute('CREATE TABLE $_apiConfigsTable(api_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
||||||
await db.execute('CREATE TABLE $_ftpConfigsTable(ftp_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
await db.execute('CREATE TABLE $_ftpConfigsTable(ftp_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
||||||
await db.execute('''
|
await db.execute('''
|
||||||
@ -987,6 +1015,13 @@ class DatabaseHelper {
|
|||||||
debugPrint("Upgrade warning: Failed to add password_hash column to users table (may already exist): $e");
|
debugPrint("Upgrade warning: Failed to add password_hash column to users table (may already exist): $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- START: ADDED UPGRADE LOGIC FOR NEW TABLES ---
|
||||||
|
if (oldVersion < 23) {
|
||||||
|
await db.execute('CREATE TABLE IF NOT EXISTS $_npeParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
await db.execute('CREATE TABLE IF NOT EXISTS $_marineParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
await db.execute('CREATE TABLE IF NOT EXISTS $_riverParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
||||||
|
}
|
||||||
|
// --- END: ADDED UPGRADE LOGIC FOR NEW TABLES ---
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs an "upsert": inserts new records or replaces existing ones.
|
/// Performs an "upsert": inserts new records or replaces existing ones.
|
||||||
@ -1199,6 +1234,20 @@ class DatabaseHelper {
|
|||||||
Future<void> deleteParameterLimits(List<dynamic> ids) => _deleteData(_parameterLimitsTable, 'param_autoid', ids);
|
Future<void> deleteParameterLimits(List<dynamic> ids) => _deleteData(_parameterLimitsTable, 'param_autoid', ids);
|
||||||
Future<List<Map<String, dynamic>>?> loadParameterLimits() => _loadData(_parameterLimitsTable, 'limit');
|
Future<List<Map<String, dynamic>>?> loadParameterLimits() => _loadData(_parameterLimitsTable, 'limit');
|
||||||
|
|
||||||
|
// --- START: ADDED NEW DB METHODS FOR PARAMETER LIMITS ---
|
||||||
|
Future<void> upsertNpeParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_npeParameterLimitsTable, 'param_autoid', data, 'limit');
|
||||||
|
Future<void> deleteNpeParameterLimits(List<dynamic> ids) => _deleteData(_npeParameterLimitsTable, 'param_autoid', ids);
|
||||||
|
Future<List<Map<String, dynamic>>?> loadNpeParameterLimits() => _loadData(_npeParameterLimitsTable, 'limit');
|
||||||
|
|
||||||
|
Future<void> upsertMarineParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_marineParameterLimitsTable, 'param_autoid', data, 'limit');
|
||||||
|
Future<void> deleteMarineParameterLimits(List<dynamic> ids) => _deleteData(_marineParameterLimitsTable, 'param_autoid', ids);
|
||||||
|
Future<List<Map<String, dynamic>>?> loadMarineParameterLimits() => _loadData(_marineParameterLimitsTable, 'limit');
|
||||||
|
|
||||||
|
Future<void> upsertRiverParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_riverParameterLimitsTable, 'param_autoid', data, 'limit');
|
||||||
|
Future<void> deleteRiverParameterLimits(List<dynamic> ids) => _deleteData(_riverParameterLimitsTable, 'param_autoid', ids);
|
||||||
|
Future<List<Map<String, dynamic>>?> loadRiverParameterLimits() => _loadData(_riverParameterLimitsTable, 'limit');
|
||||||
|
// --- END: ADDED NEW DB METHODS FOR PARAMETER LIMITS ---
|
||||||
|
|
||||||
Future<void> upsertApiConfigs(List<Map<String, dynamic>> data) => _upsertData(_apiConfigsTable, 'api_config_id', data, 'config');
|
Future<void> upsertApiConfigs(List<Map<String, dynamic>> data) => _upsertData(_apiConfigsTable, 'api_config_id', data, 'config');
|
||||||
Future<void> deleteApiConfigs(List<dynamic> ids) => _deleteData(_apiConfigsTable, 'api_config_id', ids);
|
Future<void> deleteApiConfigs(List<dynamic> ids) => _deleteData(_apiConfigsTable, 'api_config_id', ids);
|
||||||
Future<List<Map<String, dynamic>>?> loadApiConfigs() => _loadData(_apiConfigsTable, 'config');
|
Future<List<Map<String, dynamic>>?> loadApiConfigs() => _loadData(_apiConfigsTable, 'config');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user