From 147025af9f29f20be656f0120312df0126e7705f Mon Sep 17 00:00:00 2001 From: logan Date: Tue, 14 Apr 2026 12:28:47 -0700 Subject: [PATCH 1/3] Enhance input handling and validation in various components - Updated `PathGenerationOptions` to use local state for number inputs, improving user experience by allowing direct input without native steppers. - Added validation for numeric inputs in `PesticidePrescriptionsView`, ensuring all thresholds are valid before enabling the save button. - Refactored `UploadSettingsModal` to manage drafts for input fields, implementing validation checks for various settings to ensure correct values are provided. - Updated CSS to remove native number steppers for a cleaner input experience. --- .../src/components/PathGenerationOptions.tsx | 45 +++- .../components/PesticidePrescriptionsView.tsx | 16 +- .../src/components/UploadSettingsModal.tsx | 246 +++++++++++++++--- code/frontend/src/index.css | 11 + 4 files changed, 274 insertions(+), 44 deletions(-) diff --git a/code/frontend/src/components/PathGenerationOptions.tsx b/code/frontend/src/components/PathGenerationOptions.tsx index b852342..e7f64c2 100644 --- a/code/frontend/src/components/PathGenerationOptions.tsx +++ b/code/frontend/src/components/PathGenerationOptions.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import type { UploadSystemSettings } from '../types/upload'; interface PathGenerationOptionsProps { @@ -28,6 +28,22 @@ const PathGenerationOptions: React.FC = ({ onCoverageWidthOverrideChange, settings, }) => { + const [headingDraft, setHeadingDraft] = useState(String(pathHeading)); + const [robotWidthDraft, setRobotWidthDraft] = useState(String(robotWidthOverride)); + const [coverageWidthDraft, setCoverageWidthDraft] = useState(String(coverageWidthOverride)); + + useEffect(() => { + setHeadingDraft(String(pathHeading)); + }, [pathHeading]); + + useEffect(() => { + setRobotWidthDraft(String(robotWidthOverride)); + }, [robotWidthOverride]); + + useEffect(() => { + setCoverageWidthDraft(String(coverageWidthOverride)); + }, [coverageWidthOverride]); + return (
diff --git a/code/frontend/src/components/PesticidePrescriptionsView.tsx b/code/frontend/src/components/PesticidePrescriptionsView.tsx index 295c65c..0f6ea83 100644 --- a/code/frontend/src/components/PesticidePrescriptionsView.tsx +++ b/code/frontend/src/components/PesticidePrescriptionsView.tsx @@ -284,6 +284,17 @@ const PesticidePrescriptionsView: React.FC = () => { return computePrescriptionTotalGallons(detailGeojson, getSprayForFeature, override); }, [detailGeojson, getSprayForFeature, detailConfig, draftGpaNone, draftGpaLow, draftGpaHigh]); + const parsedDraftGpaNone = draftGpaNone.trim() === '' ? Number.NaN : Number.parseFloat(draftGpaNone); + const parsedDraftGpaLow = draftGpaLow.trim() === '' ? Number.NaN : Number.parseFloat(draftGpaLow); + const parsedDraftGpaHigh = draftGpaHigh.trim() === '' ? Number.NaN : Number.parseFloat(draftGpaHigh); + const thresholdsAreValid = + Number.isFinite(parsedDraftGpaNone) && + parsedDraftGpaNone >= 0 && + Number.isFinite(parsedDraftGpaLow) && + parsedDraftGpaLow >= 0 && + Number.isFinite(parsedDraftGpaHigh) && + parsedDraftGpaHigh >= 0; + const saveSprayUpdates = useCallback(async () => { if (!selectedTaskId) return; const updates = Object.entries(sprayUpdates).map(([featureId, spray]) => ({ featureId, spray })); @@ -921,6 +932,9 @@ const PesticidePrescriptionsView: React.FC = () => { /> + {!thresholdsAreValid && ( +

All spray thresholds must be numeric values greater than or equal to 0.

+ )} {previewGallonsInModal != null && (

Preview total spray:{' '} @@ -938,7 +952,7 @@ const PesticidePrescriptionsView: React.FC = () => {