fix issue on marine investigative and npe report
This commit is contained in:
parent
d0f9d72ebd
commit
6c4bc335b8
@ -67,7 +67,7 @@ class _MarineInvesManualStep2SiteInfoState extends State<MarineInvesManualStep2S
|
||||
setState(() => setImageCallback(file));
|
||||
} else if (mounted) {
|
||||
// Corrected snackbar message
|
||||
_showSnackBar('Image selection failed. Please ensure all photos are taken in landscape (vertical) mode.', isError: true);
|
||||
_showSnackBar('Image selection failed. Please ensure all photos are taken in landscape (horizontal) mode.', isError: true);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
@ -150,7 +150,7 @@ class _MarineInvesManualStep2SiteInfoState extends State<MarineInvesManualStep2S
|
||||
Text("Required Photos *", style: Theme.of(context).textTheme.titleLarge),
|
||||
// MODIFIED: Matched in-situ text
|
||||
const Text(
|
||||
"All photos must be in landscape (vertical) orientation. A watermark will be applied automatically.",
|
||||
"All photos must be in landscape (horizontal) orientation. A watermark will be applied automatically.",
|
||||
style: TextStyle(color: Colors.grey)
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
@ -28,12 +28,12 @@ class NPEReportFromInSitu extends StatefulWidget {
|
||||
State<NPEReportFromInSitu> createState() => _NPEReportFromInSituState();
|
||||
}
|
||||
|
||||
class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
// Modified: Added WidgetsBindingObserver to handle app lifecycle changes (USB permission dialog)
|
||||
class _NPEReportFromInSituState extends State<NPEReportFromInSitu> with WidgetsBindingObserver {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
bool _isPickingImage = false;
|
||||
|
||||
// --- START: MODIFIED STATE VARIABLES ---
|
||||
// Data handling
|
||||
bool? _useRecentSample; // To track Yes/No selection
|
||||
bool _isLoadingRecentSamples = false; // Now triggered on-demand
|
||||
@ -48,7 +48,6 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
String? _selectedState;
|
||||
String? _selectedCategory;
|
||||
Map<String, dynamic>? _selectedManualStation;
|
||||
// --- END: MODIFIED STATE VARIABLES ---
|
||||
|
||||
// Controllers
|
||||
final _stationIdController = TextEditingController();
|
||||
@ -81,6 +80,8 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Added: Register observer
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_samplingService = Provider.of<MarineInSituSamplingService>(context, listen: false);
|
||||
_loadAllStatesFromProvider(); // Load manual stations for "No" path
|
||||
_setDefaultDateTime(); // Set default time for all paths
|
||||
@ -88,6 +89,8 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Added: Remove observer
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_dataSubscription?.cancel();
|
||||
_lockoutTimer?.cancel();
|
||||
if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) {
|
||||
@ -117,7 +120,23 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// --- START: ADDED HELPER METHODS ---
|
||||
// Added: Handle App Lifecycle changes (specifically for USB permission dialog return)
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (mounted) {
|
||||
final btConnecting = _samplingService.bluetoothConnectionState.value == BluetoothConnectionState.connecting;
|
||||
final serialConnecting = _samplingService.serialConnectionState.value == SerialConnectionState.connecting;
|
||||
|
||||
// If the UI is still loading or the service thinks it's still connecting (stuck after permission dialog),
|
||||
// force a disconnect/reset so the user can try again.
|
||||
if (_isLoading || btConnecting || serialConnecting) {
|
||||
_disconnectFromAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setDefaultDateTime() {
|
||||
final now = DateTime.now();
|
||||
_eventDateTimeController.text = DateFormat('yyyy-MM-dd HH:mm').format(now);
|
||||
@ -167,19 +186,16 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
_latController.clear();
|
||||
_longController.clear();
|
||||
|
||||
// --- CHANGE: Clear measurement controllers so they are empty before reading ---
|
||||
_doPercentController.clear();
|
||||
_doMgLController.clear();
|
||||
_phController.clear();
|
||||
_condController.clear();
|
||||
_turbController.clear();
|
||||
_tempController.clear();
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
_setDefaultDateTime(); // Reset to 'now'
|
||||
});
|
||||
}
|
||||
// --- END: ADDED HELPER METHODS ---
|
||||
|
||||
Future<void> _fetchRecentNearbySamples() async {
|
||||
setState(() => _isLoadingRecentSamples = true);
|
||||
@ -269,10 +285,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
_npeData.latitude = _latController.text;
|
||||
_npeData.longitude = _longController.text;
|
||||
|
||||
// selectedStation is already set by either _populateFormFromData or the "No" path dropdown
|
||||
// _npeData.selectedStation = _selectedRecentSample?.selectedStation;
|
||||
|
||||
_npeData.locationDescription = _locationController.text; // Used by both paths
|
||||
_npeData.locationDescription = _locationController.text;
|
||||
_npeData.possibleSource = _possibleSourceController.text;
|
||||
_npeData.othersObservationRemark = _othersObservationController.text;
|
||||
_npeData.oxygenSaturation = double.tryParse(_doPercentController.text);
|
||||
@ -387,7 +400,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
if (mounted) setState(() => _isPickingImage = false);
|
||||
}
|
||||
|
||||
// --- START: IN-SITU DEVICE METHODS (Unchanged) ---
|
||||
// --- IN-SITU DEVICE METHODS ---
|
||||
void _updateTextFields(Map<String, double> readings) {
|
||||
const defaultValue = -999.0;
|
||||
setState(() {
|
||||
@ -433,7 +446,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
}
|
||||
|
||||
Future<bool> _connectToDevice(String type) async {
|
||||
setState(() => _isLoading = true); // Use main loading indicator
|
||||
setState(() => _isLoading = true);
|
||||
bool success = false;
|
||||
try {
|
||||
if (type == 'bluetooth') {
|
||||
@ -463,7 +476,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
debugPrint("Connection failed: $e");
|
||||
if (mounted) _showConnectionFailedDialog();
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false); // Stop main loading indicator
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@ -547,11 +560,10 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
setState(() {
|
||||
_isAutoReading = false;
|
||||
_isLockedOut = false;
|
||||
_isLoading = false; // Modified: Reset isLoading to unlock buttons
|
||||
});
|
||||
}
|
||||
}
|
||||
// --- END: IN-SITU DEVICE METHODS (Unchanged) ---
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -565,7 +577,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
children: [
|
||||
// --- START: SECTION 1 (NEW) ---
|
||||
// --- SECTION 1 ---
|
||||
_buildSectionTitle("1. Use Recent Sample?"),
|
||||
Row(
|
||||
children: [
|
||||
@ -577,9 +589,9 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_useRecentSample = value;
|
||||
_clearManualStationSelection(); // Clear "No" path data
|
||||
_clearManualStationSelection();
|
||||
if (value == true && _recentNearbySamples.isEmpty) {
|
||||
_fetchRecentNearbySamples(); // Fetch samples on-demand
|
||||
_fetchRecentNearbySamples();
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -593,7 +605,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_useRecentSample = value;
|
||||
_clearRecentSampleSelection(); // Clear "Yes" path data
|
||||
_clearRecentSampleSelection();
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -601,10 +613,8 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// --- END: SECTION 1 (NEW) ---
|
||||
|
||||
// --- START: SECTION 2 (CONDITIONAL) ---
|
||||
// "YES" PATH: Select from recent samples
|
||||
// --- SECTION 2 ---
|
||||
if (_useRecentSample == true) ...[
|
||||
_buildSectionTitle("2. Select Recent Sample"),
|
||||
if (_isLoadingRecentSamples)
|
||||
@ -622,7 +632,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
if (sample != null) {
|
||||
setState(() {
|
||||
_selectedRecentSample = sample;
|
||||
_npeData.selectedStation = sample.selectedStation; // CRITICAL: Set station for submission
|
||||
_npeData.selectedStation = sample.selectedStation;
|
||||
_populateFormFromData(sample);
|
||||
});
|
||||
}
|
||||
@ -631,7 +641,6 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
),
|
||||
],
|
||||
|
||||
// "NO" PATH: Select from manual station list
|
||||
if (_useRecentSample == false) ...[
|
||||
_buildSectionTitle("2. Select Manual Station"),
|
||||
DropdownSearch<String>(
|
||||
@ -687,7 +696,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
onChanged: (station) {
|
||||
setState(() {
|
||||
_selectedManualStation = station;
|
||||
_npeData.selectedStation = station; // CRITICAL: Set station for submission
|
||||
_npeData.selectedStation = station;
|
||||
_stationIdController.text = station?['man_station_code'] ?? '';
|
||||
_locationController.text = station?['man_station_name'] ?? '';
|
||||
_latController.text = station?['man_latitude']?.toString() ?? '';
|
||||
@ -697,9 +706,8 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
validator: (val) => val == null && _stationsForCategory.isNotEmpty ? "Station is required" : null,
|
||||
),
|
||||
],
|
||||
// --- END: SECTION 2 (CONDITIONAL) ---
|
||||
|
||||
// --- START: SHARED SECTIONS (NOW ALWAYS VISIBLE) ---
|
||||
// --- SHARED SECTIONS ---
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle("Station Information"),
|
||||
_buildTextFormField(controller: _stationIdController, label: "Station ID", readOnly: true),
|
||||
@ -734,12 +742,10 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15)
|
||||
),
|
||||
// Disable button if "Yes/No" hasn't been selected
|
||||
onPressed: _isLoading || _useRecentSample == null ? null : _submitNpeReport,
|
||||
child: _isLoading ? const CircularProgressIndicator(color: Colors.white) : const Text("Submit Report"),
|
||||
),
|
||||
),
|
||||
// --- END: SHARED SECTIONS ---
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -890,13 +896,10 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
);
|
||||
}
|
||||
|
||||
// --- START: WIDGET BUILDERS FOR IN-SITU (Unchanged) ---
|
||||
Widget _buildInSituSection() {
|
||||
final activeConnection = _getActiveConnectionDetails();
|
||||
final String? activeType = activeConnection?['type'] as String?;
|
||||
|
||||
// For the "No" path, the in-situ fields must be editable.
|
||||
// For the "Yes" path, they should be read-only as they come from the sample.
|
||||
final bool areFieldsReadOnly = (_useRecentSample == true);
|
||||
|
||||
return Column(
|
||||
@ -987,7 +990,7 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
required String label,
|
||||
required String unit,
|
||||
required TextEditingController controller,
|
||||
bool readOnly = false, // ADDED: readOnly parameter
|
||||
bool readOnly = false,
|
||||
}) {
|
||||
final bool isMissing = controller.text.isEmpty || controller.text.contains('-999');
|
||||
final String displayValue = isMissing ? '-.--' : (double.tryParse(controller.text)?.toStringAsFixed(5) ?? '-.--');
|
||||
@ -1001,14 +1004,12 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
trailing: SizedBox(
|
||||
width: 120,
|
||||
child: TextFormField(
|
||||
// --- START: MODIFIED to handle readOnly vs. editable ---
|
||||
controller: readOnly ? null : controller,
|
||||
initialValue: readOnly ? displayValue : null,
|
||||
key: readOnly ? ValueKey(displayValue) : null,
|
||||
// --- END: MODIFIED ---
|
||||
readOnly: readOnly,
|
||||
textAlign: TextAlign.right,
|
||||
keyboardType: readOnly ? null : const TextInputType.numberWithOptions(decimal: true), // Allow editing only if NOT readOnly
|
||||
keyboardType: readOnly ? null : const TextInputType.numberWithOptions(decimal: true),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isMissing ? Colors.grey : Theme.of(context).colorScheme.primary,
|
||||
@ -1016,16 +1017,13 @@ class _NPEReportFromInSituState extends State<NPEReportFromInSitu> {
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
isDense: true, // Helps with alignment
|
||||
// --- CHANGE: Added hint text to display when controller is empty (before start reading) ---
|
||||
isDense: true,
|
||||
hintText: '-.--',
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
// -----------------------------------------------------------------------------------------
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// --- END: WIDGET BUILDERS FOR IN-SITU ---
|
||||
}
|
||||
@ -26,7 +26,8 @@ class NPEReportFromTarball extends StatefulWidget {
|
||||
State<NPEReportFromTarball> createState() => _NPEReportFromTarballState();
|
||||
}
|
||||
|
||||
class _NPEReportFromTarballState extends State<NPEReportFromTarball> {
|
||||
// Modified: Added WidgetsBindingObserver to handle app lifecycle changes (USB permission dialog)
|
||||
class _NPEReportFromTarballState extends State<NPEReportFromTarball> with WidgetsBindingObserver {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
bool _isPickingImage = false;
|
||||
@ -70,6 +71,8 @@ class _NPEReportFromTarballState extends State<NPEReportFromTarball> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Added: Register observer
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_samplingService = Provider.of<MarineInSituSamplingService>(context, listen: false);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
@ -83,6 +86,8 @@ class _NPEReportFromTarballState extends State<NPEReportFromTarball> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Added: Remove observer
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_stationIdController.dispose();
|
||||
_locationController.dispose();
|
||||
_eventDateTimeController.dispose();
|
||||
@ -106,6 +111,23 @@ class _NPEReportFromTarballState extends State<NPEReportFromTarball> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Added: Handle App Lifecycle changes (specifically for USB permission dialog return)
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (mounted) {
|
||||
final btConnecting = _samplingService.bluetoothConnectionState.value == BluetoothConnectionState.connecting;
|
||||
final serialConnecting = _samplingService.serialConnectionState.value == SerialConnectionState.connecting;
|
||||
|
||||
// If the UI is still loading or the service thinks it's still connecting (stuck after permission dialog),
|
||||
// force a disconnect/reset so the user can try again.
|
||||
if (_isLoading || btConnecting || serialConnecting) {
|
||||
_disconnectFromAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setDefaultDateTime() {
|
||||
final now = DateTime.now();
|
||||
_eventDateTimeController.text = DateFormat('yyyy-MM-dd HH:mm').format(now);
|
||||
@ -409,6 +431,7 @@ class _NPEReportFromTarballState extends State<NPEReportFromTarball> {
|
||||
setState(() {
|
||||
_isAutoReading = false;
|
||||
_isLockedOut = false;
|
||||
_isLoading = false; // Added: Reset isLoading to unlock buttons
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,8 @@ class NPEReportNewLocation extends StatefulWidget {
|
||||
State<NPEReportNewLocation> createState() => _NPEReportNewLocationState();
|
||||
}
|
||||
|
||||
class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
// Modified: Added WidgetsBindingObserver to handle app lifecycle changes (USB permission dialog)
|
||||
class _NPEReportNewLocationState extends State<NPEReportNewLocation> with WidgetsBindingObserver {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
bool _isPickingImage = false;
|
||||
@ -67,6 +68,8 @@ class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Added: Register observer
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_samplingService = Provider.of<MarineInSituSamplingService>(context, listen: false);
|
||||
_setDefaultDateTime();
|
||||
_loadAllStatesFromProvider();
|
||||
@ -74,6 +77,8 @@ class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Added: Remove observer
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_dataSubscription?.cancel();
|
||||
_lockoutTimer?.cancel();
|
||||
if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) {
|
||||
@ -102,6 +107,23 @@ class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Added: Handle App Lifecycle changes (specifically for USB permission dialog return)
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (mounted) {
|
||||
final btConnecting = _samplingService.bluetoothConnectionState.value == BluetoothConnectionState.connecting;
|
||||
final serialConnecting = _samplingService.serialConnectionState.value == SerialConnectionState.connecting;
|
||||
|
||||
// If the UI is still loading or the service thinks it's still connecting (stuck after permission dialog),
|
||||
// force a disconnect/reset so the user can try again.
|
||||
if (_isLoading || btConnecting || serialConnecting) {
|
||||
_disconnectFromAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setDefaultDateTime() {
|
||||
final now = DateTime.now();
|
||||
_eventDateTimeController.text = DateFormat('yyyy-MM-dd HH:mm').format(now);
|
||||
@ -243,10 +265,62 @@ class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await _showImageErrorDialog(
|
||||
"Image processing failed. Please ensure the photo is taken in landscape mode."
|
||||
);
|
||||
}
|
||||
|
||||
if (mounted) setState(() => _isPickingImage = false);
|
||||
}
|
||||
|
||||
Future<void> _showImageErrorDialog(String message) async {
|
||||
if (!mounted) return;
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: const Row(
|
||||
children: [
|
||||
Icon(Icons.error_outline, color: Colors.red),
|
||||
SizedBox(width: 10),
|
||||
Text('Image Error'),
|
||||
],
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(message),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
"Please ensure your device is held horizontally:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Icon(
|
||||
Icons.stay_current_landscape,
|
||||
size: 60,
|
||||
color: Colors.blue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('OK'),
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _updateTextFields(Map<String, double> readings) {
|
||||
const defaultValue = -999.0;
|
||||
setState(() {
|
||||
@ -406,6 +480,7 @@ class _NPEReportNewLocationState extends State<NPEReportNewLocation> {
|
||||
setState(() {
|
||||
_isAutoReading = false;
|
||||
_isLockedOut = false;
|
||||
_isLoading = false; // Added: Reset isLoading to unlock buttons
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// lib/services/marine_api_service.dart
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
@ -151,8 +149,9 @@ class MarineApiService {
|
||||
// Pass the station type to the API so it knows which foreign key to check (station_id vs tbl_station_id)
|
||||
final String stationTypeParam = Uri.encodeComponent(stationType);
|
||||
|
||||
// *** FIX: Use the correct top-level resource name 'marine-investigative' ***
|
||||
final String endpoint =
|
||||
'marine/investigative/records-by-station?station_id=$stationId&date=$dateStr&station_type=$stationTypeParam';
|
||||
'marine-investigative/records-by-station?station_id=$stationId&date=$dateStr&station_type=$stationTypeParam';
|
||||
|
||||
debugPrint("MarineApiService: Calling API endpoint: $endpoint");
|
||||
final response = await _baseService.get(baseUrl, endpoint);
|
||||
@ -184,10 +183,10 @@ class MarineApiService {
|
||||
'samplingDate': samplingDate,
|
||||
};
|
||||
|
||||
// Use a new endpoint dedicated to the investigative module
|
||||
// *** FIX: Use the correct top-level resource name 'marine-investigative' ***
|
||||
return _baseService.postMultipart(
|
||||
baseUrl: baseUrl,
|
||||
endpoint: 'marine/investigative/images/send-email',
|
||||
endpoint: 'marine-investigative/images/send-email',
|
||||
fields: fields,
|
||||
files: {},
|
||||
);
|
||||
|
||||
@ -732,7 +732,10 @@ class MarineInvestigativeSamplingService {
|
||||
final allLimits = await _dbHelper.loadMarineParameterLimits() ?? [];
|
||||
if (allLimits.isEmpty) return "";
|
||||
|
||||
final dynamic stationId = data.selectedStation?['man_station_id'];
|
||||
// --- START FIX: Use correct key 'station_id' with fallback to 'man_station_id' ---
|
||||
final dynamic stationId = data.selectedStation?['station_id'] ?? data.selectedStation?['man_station_id'];
|
||||
// --- END FIX ---
|
||||
|
||||
if (stationId == null) return ""; // Cannot check limits
|
||||
|
||||
final readings = {
|
||||
@ -843,7 +846,7 @@ class MarineInvestigativeSamplingService {
|
||||
if (isHit) {
|
||||
final valueStr = value.toStringAsFixed(5);
|
||||
final lowerStr = lowerLimit?.toStringAsFixed(5) ?? 'N/A';
|
||||
final upperStr = upperLimit?.toStringAsFixed(5) ?? 'N/á';
|
||||
final upperStr = upperLimit?.toStringAsFixed(5) ?? 'N/A';
|
||||
String limitStr;
|
||||
if (lowerStr != 'N/A' && upperStr != 'N/A') {
|
||||
limitStr = '$lowerStr - $upperStr';
|
||||
@ -864,7 +867,7 @@ class MarineInvestigativeSamplingService {
|
||||
final buffer = StringBuffer()
|
||||
..writeln()
|
||||
..writeln(' ')
|
||||
..writeln('🚨 *NPE Parameter Limit Detected:*')
|
||||
..writeln('🚨 *Marine NPE Parameter Limit Detected:*')
|
||||
..writeln('The following parameters triggered an NPE alert:');
|
||||
buffer.writeAll(npeMessages, '\n');
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user