diff --git a/src/common/variables.js b/src/common/variables.js index 2a9ab80c2..c3e9f258b 100644 --- a/src/common/variables.js +++ b/src/common/variables.js @@ -10,6 +10,11 @@ export const tokenizationStates = { TOKENIZED: 'Tokenized', NOT_TOKENIZED: 'Not Tokenized', }; +export const verificationStatesArr = [ + verificationStates.APPROVED, + verificationStates.AWAITING, + verificationStates.REJECTED, +]; // These are the default min/max dates for the MUI KeyboardDatePicker component // See https://material-ui-pickers.dev/api/KeyboardDatePicker diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 42e805b83..1c41ab443 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -4,6 +4,9 @@ import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; import MenuItem from '@material-ui/core/MenuItem'; +import Checkbox from '@material-ui/core/Checkbox'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; import Autocomplete from '@material-ui/lab/Autocomplete'; import FilterModel, { ALL_SPECIES, @@ -25,9 +28,9 @@ import { import { verificationStates, tokenizationStates, + verificationStatesArr, datePickerDefaultMinDate, } from '../common/variables'; -import { getVerificationStatus } from '../common/utils'; import { AppContext } from '../context/AppContext'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; @@ -72,7 +75,6 @@ const styles = (theme) => { }; function Filter(props) { - // console.log('render: filter top'); const speciesContext = useContext(SpeciesContext); const tagsContext = useContext(TagsContext); const { orgList, userHasOrg } = useContext(AppContext); @@ -85,21 +87,30 @@ function Filter(props) { const [growerId, setGrowerId] = useState(filter?.planterId || ''); const [deviceId, setDeviceId] = useState(filter?.deviceIdentifier || ''); const [growerIdentifier, setGrowerIdentifier] = useState( - filter?.planterIdentifier || '', + filter?.planterIdentifier || '' ); - const [approved, setApproved] = useState(filter?.approved); - const [active, setActive] = useState(filter?.active); + const [verifyStatus, setVerifyStatus] = useState([ + { active: true, approved: true }, + { active: true, approved: false }, + ]); const [dateStart, setDateStart] = useState( - filter?.dateStart || dateStartDefault, + filter?.dateStart || dateStartDefault ); const [dateEnd, setDateEnd] = useState(filter?.dateEnd || dateEndDefault); const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); const [tag, setTag] = useState(null); const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - filter.organizationId || ALL_ORGANIZATIONS, + filter.organizationId || ALL_ORGANIZATIONS ); const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); + const [verificationStatus, setVerificationStatus] = useState([ + verificationStates.APPROVED, + verificationStates.AWAITING, + ]); + const isAllVerification = + verificationStatus.length && + verificationStatus.length === verificationStatesArr.length; const handleDateStartChange = (date) => { setDateStart(date); @@ -113,6 +124,34 @@ function Filter(props) { return convertDateToDefaultSqlDate(date); }; + const handleVerificationStatusChange = (event) => { + let value = event.target.value; + let status = []; + if ( + value[value.length - 1] === 'all' || + (value.length === 3 && value[value.length - 1] !== 'all') + ) { + setVerificationStatus( + verificationStatus.length === verificationStatesArr.length + ? [] + : verificationStatesArr + ); + value = verificationStatesArr; + } else { + setVerificationStatus(value); + } + value.forEach((val) => { + if (val === verificationStates.APPROVED) { + status.push({ active: true, approved: true }); + } else if (val === verificationStates.AWAITING) { + status.push({ active: true, approved: false }); + } else if (val === verificationStates.REJECTED) { + status.push({ active: false, approved: false }); + } + }); + setVerifyStatus(status); + }; + function handleSubmit(e) { e.preventDefault(); // save the filer to context for editing & submit @@ -124,12 +163,11 @@ function Filter(props) { filter.planterIdentifier = growerIdentifier; filter.dateStart = dateStart ? formatDate(dateStart) : undefined; filter.dateEnd = dateEnd ? formatDate(dateEnd) : undefined; - filter.approved = approved; - filter.active = active; filter.speciesId = speciesId; filter.tagId = tag ? tag.id : 0; filter.organizationId = organizationId; filter.tokenId = tokenId; + filter.verifyStatus = verifyStatus; props.onSubmit && props.onSubmit(filter); } @@ -147,10 +185,11 @@ function Filter(props) { setTagSearchString(''); setOrganizationId(ALL_ORGANIZATIONS); setTokenId(filterOptionAll); - + setVerifyStatus([ + { active: true, approved: true }, + { active: true, approved: false }, + ]); const filter = new FilterModel(); - filter.approved = approved; // keeps last value set - filter.active = active; // keeps last value set props.onSubmit && props.onSubmit(filter); } @@ -186,38 +225,36 @@ function Filter(props) { htmlFor="verification-status" id="verification-status" label="Verification Status" - value={ - active === undefined && approved === undefined - ? filterOptionAll - : getVerificationStatus(active, approved) - } - onChange={(e) => { - setApproved( - e.target.value === filterOptionAll - ? undefined - : e.target.value === verificationStates.AWAITING || - e.target.value === verificationStates.REJECTED - ? false - : true, - ); - setActive( - e.target.value === filterOptionAll - ? undefined - : e.target.value === verificationStates.AWAITING || - e.target.value === verificationStates.APPROVED - ? true - : false, - ); + SelectProps={{ + multiple: true, + value: verificationStatus, + onChange: handleVerificationStatusChange, + renderValue: (verificationStatus) => + verificationStatus.join(', '), }} > - {[ - filterOptionAll, - verificationStates.APPROVED, - verificationStates.AWAITING, - verificationStates.REJECTED, - ].map((name) => ( + + + 0 && + verificationStatus.length < verificationStatesArr.length + } + /> + + + + {verificationStatesArr.map((name) => ( - {name} + + -1} + /> + + ))} @@ -357,7 +394,7 @@ function Filter(props) { ...tagsContext.tagList.filter((t) => t.tagName .toLowerCase() - .startsWith(tagSearchString.toLowerCase()), + .startsWith(tagSearchString.toLowerCase()) ), ]} value={tag} @@ -370,7 +407,6 @@ function Filter(props) { }} onChange={(_oldVal, newVal) => { //triggered by onInputChange - console.log('newVal -- ', newVal); if (newVal && newVal.tagName === 'Not set') { setTag('Not set'); } else { diff --git a/src/components/Captures/CaptureTable.js b/src/components/Captures/CaptureTable.js index 4fba74c89..e916ddadb 100644 --- a/src/components/Captures/CaptureTable.js +++ b/src/components/Captures/CaptureTable.js @@ -226,6 +226,7 @@ const CaptureTable = () => { columns={columns} filter={filter} speciesLookup={speciesLookup} + captureTagLookup={captureTagLookup} /> {tablePagination()} @@ -265,7 +266,7 @@ const CaptureTable = () => { speciesLookup, captureTagLookup[capture.id] || [], attr, - renderer, + renderer )} ))} @@ -290,7 +291,7 @@ export const formatCell = ( speciesLookup, additionalTags, attr, - renderer, + renderer ) => { if (attr === 'id' || attr === 'planterId') { return ( diff --git a/src/components/ExportCaptures.js b/src/components/ExportCaptures.js index c5392e6f0..51cc1f770 100644 --- a/src/components/ExportCaptures.js +++ b/src/components/ExportCaptures.js @@ -32,7 +32,14 @@ const useStyle = makeStyles((theme) => ({ })); const ExportCaptures = (props) => { - const { isOpen, handleClose, columns, filter, speciesState } = props; + const { + isOpen, + handleClose, + columns, + filter, + speciesLookup, + captureTagLookup, + } = props; const classes = useStyle(); let nameColumns = {}; columns.forEach(({ attr, renderer }) => { @@ -63,9 +70,10 @@ const ExportCaptures = (props) => { const renderer = selectedColumns[attr].renderer; formatCapture[attr] = formatCell( capture, - speciesState, + speciesLookup, + captureTagLookup[capture.id] || [], attr, - renderer, + renderer ); } }); @@ -76,7 +84,7 @@ const ExportCaptures = (props) => { async function downloadCaptures() { setLoading(true); const filterColumns = Object.entries(checkedColumns).filter( - (val) => val[1].status === true, + (val) => val[1].status === true ); const selectedColumns = Object.fromEntries(filterColumns); await capturesContext.getAllCaptures({ filter }).then((response) => { diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index a6b9f7618..4036c00fc 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -42,9 +42,11 @@ export function CapturesProvider(props) { // const [byId, setById] = useState({}); const [filter, setFilter] = useState( new FilterModel({ - approved: true, - active: true, - }), + verifyStatus: [ + { active: true, approved: true }, + { active: true, approved: false }, + ], + }) ); useEffect(() => { @@ -70,6 +72,29 @@ export function CapturesProvider(props) { // }; // EVENT HANDLERS + const getWhereCondition = () => { + const { verifyStatus, ...restFilter } = filter ? filter.getWhereObj() : {}; + let orCondition = false; + let where; + if (verifyStatus.length == 1) { + where = { + ...restFilter, + active: verifyStatus[0].active, + approved: verifyStatus[0].approved, + }; + } else { + orCondition = true; + where = []; + verifyStatus.forEach((status) => { + where.push({ + ...restFilter, + active: status.active, + approved: status.approved, + }); + }); + } + return orCondition ? { or: where } : { ...where }; + }; const queryCapturesApi = ({ id = null, count = false, paramString = null }) => // abortController, @@ -93,9 +118,7 @@ export function CapturesProvider(props) { const getCaptureCount = async () => { log.debug('load capture count'); - const paramString = `where=${JSON.stringify( - filter ? filter.getWhereObj() : {}, - )}`; + const paramString = `where=${JSON.stringify(getWhereCondition())}`; const response = await queryCapturesApi({ count: true, paramString, @@ -106,11 +129,9 @@ export function CapturesProvider(props) { }; const getCapturesAsync = async () => { - // log.debug('4 - load captures'); - const where = filter ? filter.getWhereObj() : {}; - + log.debug('4 - load captures'); const lbFilter = { - where: { ...where }, + where: getWhereCondition(), order: [`${orderBy} ${order}`], limit: rowsPerPage, skip: page * rowsPerPage, @@ -142,12 +163,9 @@ export function CapturesProvider(props) { console.log('captures filterInfo -- ', filterInfo); // if filterInfo contains new values override the defaults in state hooks - const { filter = new FilterModel() } = filterInfo; - - const where = filter ? filter.getWhereObj() : {}; const lbFilter = { - where: { ...where }, + where: getWhereCondition(), order: [`${orderBy} ${order}`], limit: 20000, fields: { @@ -179,7 +197,7 @@ export function CapturesProvider(props) { setCapture(res.data); }) .catch((err) => - console.error(`ERROR: FAILED TO GET SELECTED TREE ${err}`), + console.error(`ERROR: FAILED TO GET SELECTED TREE ${err}`) ); }; diff --git a/src/models/Filter.js b/src/models/Filter.js index 3e639ff04..214a2b117 100644 --- a/src/models/Filter.js +++ b/src/models/Filter.js @@ -24,6 +24,7 @@ export default class Filter { tagId; organizationId; tokenId; + verifyStatus; constructor(options) { Object.assign(this, options); @@ -100,6 +101,10 @@ export default class Filter { where.tokenId = { eq: null }; } + if (this.verifyStatus) { + where.verifyStatus = this.verifyStatus; + } + return where; }