diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index e91a266be..2cba7d66f 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -37,6 +37,14 @@ import { CircularProgress } from '@material-ui/core'; export const FILTER_WIDTH = 330; +const isNumericValue = (value) => { + return !value.trim() || /^\d+$/.test(value.trim()); +}; + +const isNumericList = (value) => { + return !value.trim() || /^\d+(\s*,\s*\d+)*$/.test(value.trim()); +}; + const styles = (theme) => { return { root: {}, @@ -110,6 +118,7 @@ function Filter(props) { verificationStates.APPROVED, verificationStates.AWAITING, ]); + const [filterErrors, setFilterErrors] = useState({}); const isAllVerification = verificationStatus.length && verificationStatus.length === verificationStatesArr.length; @@ -156,6 +165,17 @@ function Filter(props) { function handleSubmit(e) { e.preventDefault(); + const errors = { + captureId: isNumericValue(captureId) ? '' : 'Enter a numeric Capture ID', + growerId: isNumericList(growerId) + ? '' + : 'Enter numeric Grower IDs separated by commas', + }; + setFilterErrors(errors); + if (errors.captureId || errors.growerId) { + return; + } + // save the filer to context for editing & submit const filter = new FilterModel(); filter.uuid = uuid; @@ -193,6 +213,7 @@ function Filter(props) { { active: true, approved: true }, { active: true, approved: false }, ]); + setFilterErrors({}); const filter = new FilterModel(); props.onSubmit && props.onSubmit(filter); } @@ -299,7 +320,16 @@ function Filter(props) { label="Grower ID" placeholder="e.g. 2, 7" value={growerId} - onChange={(e) => setGrowerId(e.target.value)} + error={Boolean(filterErrors.growerId)} + helperText={filterErrors.growerId} + inputProps={{ inputMode: 'numeric' }} + onChange={(e) => { + setGrowerId(e.target.value); + setFilterErrors({ + ...filterErrors, + growerId: '', + }); + }} /> setCaptureId(e.target.value)} + error={Boolean(filterErrors.captureId)} + helperText={filterErrors.captureId} + inputProps={{ inputMode: 'numeric' }} + onChange={(e) => { + setCaptureId(e.target.value); + setFilterErrors({ + ...filterErrors, + captureId: '', + }); + }} /> { + return !value.trim() || /^\d+$/.test(value.trim()); +}; + +const isNumericList = (value) => { + return !value.trim() || /^\d+(\s*,\s*\d+)*$/.test(value.trim()); +}; + const styles = (theme) => { return { root: {}, @@ -101,6 +109,7 @@ function Filter(props) { filter.stakeholderUUID || ALL_ORGANIZATIONS ); const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); + const [filterErrors, setFilterErrors] = useState({}); const handleDateStartChange = (date) => { setDateStart(date); @@ -116,6 +125,17 @@ function Filter(props) { function handleSubmit(e) { e.preventDefault(); + const errors = { + captureId: isNumericValue(captureId) ? '' : 'Enter a numeric Capture ID', + growerId: isNumericList(growerId) + ? '' + : 'Enter numeric Grower IDs separated by commas', + }; + setFilterErrors(errors); + if (errors.captureId || errors.growerId) { + return; + } + // save the filer to context for editing & submit const filter = new FilterModel(); filter.uuid = uuid; @@ -150,6 +170,7 @@ function Filter(props) { setOrganizationId(ALL_ORGANIZATIONS); setStakeholderUUID(ALL_ORGANIZATIONS); setTokenId(filterOptionAll); + setFilterErrors({}); const filter = new FilterModel(); filter.approved = approved; // keeps last value set @@ -261,7 +282,16 @@ function Filter(props) { label="Grower ID" placeholder="e.g. 7" value={growerId} - onChange={(e) => setGrowerId(e.target.value)} + error={Boolean(filterErrors.growerId)} + helperText={filterErrors.growerId} + inputProps={{ inputMode: 'numeric' }} + onChange={(e) => { + setGrowerId(e.target.value); + setFilterErrors({ + ...filterErrors, + growerId: '', + }); + }} /> setCaptureId(e.target.value)} + error={Boolean(filterErrors.captureId)} + helperText={filterErrors.captureId} + inputProps={{ inputMode: 'numeric' }} + onChange={(e) => { + setCaptureId(e.target.value); + setFilterErrors({ + ...filterErrors, + captureId: '', + }); + }} /> { log.debug('load capture count'); const paramString = `where=${JSON.stringify(filter.getWhereObj())}`; - const response = await queryCapturesApi({ - count: true, - paramString, - }); - const { count } = response.data; - setCaptureCount(Number(count)); + try { + const response = await queryCapturesApi({ + count: true, + paramString, + }); + const { count } = response.data; + setCaptureCount(Number(count)); + } catch (e) { + log.warn('load capture count error:', e); + setCaptureCount(0); + } }; const getCapturesAsync = async () => { @@ -90,9 +95,15 @@ export function CapturesProvider(props) { }; const paramString = `filter=${JSON.stringify(filterData)}`; setIsLoading(true); - const response = await queryCapturesApi({ paramString }); - setIsLoading(false); - setCaptures(response.data); + try { + const response = await queryCapturesApi({ paramString }); + setCaptures(response.data); + } catch (e) { + log.warn('load captures error:', e); + setCaptures([]); + } finally { + setIsLoading(false); + } }; // GET CAPTURES FOR EXPORT diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 1833e7ec1..bc314fd1c 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -145,10 +145,17 @@ export function VerifyProvider(props) { filter: filter, }; log.debug('load page with params:', pageParams); - const result = await api.getCaptureImages(pageParams, abortController); - setCaptureImages(result || []); - //restore loading status - setIsLoading(false); + try { + const result = await api.getCaptureImages(pageParams, abortController); + setCaptureImages(result || []); + } catch (e) { + if (e.name !== 'AbortError') { + log.warn('get error:', e); + } + setCaptureImages([]); + } finally { + setIsLoading(false); + } }; const getCaptureSelectedArr = () => { @@ -286,11 +293,18 @@ export function VerifyProvider(props) { }; const getCaptureCount = async (newfilter = filter) => { - // console.log('-- verify getCaptureCount'); - // setInvalidateCaptureCount(false); - const result = await api.getCaptureCount(newfilter); - setCaptureCount(Number(result.count)); - setInvalidateCaptureCount(false); + try { + // console.log('-- verify getCaptureCount'); + // setInvalidateCaptureCount(false); + const result = await api.getCaptureCount(newfilter); + setCaptureCount(Number(result.count)); + setInvalidateCaptureCount(false); + } catch (e) { + log.warn('get capture count error:', e); + setCaptureCount(0); + } finally { + setInvalidateCaptureCount(false); + } }; const value = {