From 673d87914748f62c2337adfe6af2017a6a517f75 Mon Sep 17 00:00:00 2001 From: SuspenseFallback Date: Mon, 7 Nov 2022 06:11:30 +0800 Subject: [PATCH 01/32] feat: initial filter test --- src/api/treeTrackerApi.js | 2 +- src/components/FilterTop.js | 91 ++++++++++++++++++++++++------------ src/context/VerifyContext.js | 5 ++ 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/api/treeTrackerApi.js b/src/api/treeTrackerApi.js index 05fce190e..83cf76882 100644 --- a/src/api/treeTrackerApi.js +++ b/src/api/treeTrackerApi.js @@ -5,8 +5,8 @@ import { makeQueryString, } from './apiUtils'; import { getVerificationStatus } from '../common/utils'; -import { session } from '../models/auth'; import log from 'loglevel'; +import { session } from '../models/auth'; // Set API as a variable const API_ROOT = process.env.REACT_APP_API_ROOT; diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 5ebcf309d..71883a9e3 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -1,36 +1,32 @@ -import React, { useState, useContext } from 'react'; -import { withStyles } from '@material-ui/core/styles'; -import { Grid, Button, TextField, MenuItem } from '@material-ui/core'; -import Autocomplete from '@material-ui/lab/Autocomplete'; -import SelectOrg from './common/SelectOrg'; +import { Button, Grid, MenuItem, TextField } from '@material-ui/core'; import FilterModel, { - ALL_SPECIES, - // SPECIES_ANY_SET, - // SPECIES_NOT_SET, ALL_ORGANIZATIONS, - ALL_TAGS, - TAG_NOT_SET, + ALL_SPECIES, ANY_TAG_SET, + TAG_NOT_SET, } from '../models/Filter'; -import DateFnsUtils from '@date-io/date-fns'; import { - MuiPickersUtilsProvider, KeyboardDatePicker, + MuiPickersUtilsProvider, } from '@material-ui/pickers'; +import React, { useContext, useEffect, useState } from 'react'; import { - getDatePickerLocale, - getDateFormatLocale, convertDateToDefaultSqlDate, + getDateFormatLocale, + getDatePickerLocale, } from '../common/locale'; import { verificationStates, captureStatus, datePickerDefaultMinDate, - // tokenizationStates, + verificationStates, } from '../common/variables'; // import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; +import { getVerificationStatus } from '../common/utils'; +import { withStyles } from '@material-ui/core/styles'; + // import { CircularProgress } from '@material-ui/core'; export const FILTER_WIDTH = 330; @@ -74,28 +70,63 @@ const styles = (theme) => { function Filter(props) { // const speciesContext = useContext(SpeciesContext); + const url = new URL(window.location.href); + const params = new URLSearchParams(url.search); const tagsContext = useContext(TagsContext); const { classes, filter } = props; const filterOptionAll = 'All'; - const startDateDefault = null; - const endDateDefault = null; - const [uuid, setUUID] = useState(filter?.uuid || ''); - const [captureId, setCaptureId] = useState(filter?.captureId || ''); - const [growerId, setGrowerId] = useState(filter?.grower_account_id || ''); - const [deviceId, setDeviceId] = useState(filter?.device_identifier || ''); - const [wallet, setWallet] = useState(filter?.wallet || ''); - const [status, setStatus] = useState(filter?.status); - const [startDate, setStartDate] = useState( - filter?.startDate || startDateDefault + const dateStartDefault = null; + const dateEndDefault = null; + const [uuid, setUUID] = useState( + url.searchParams.get('uuid') || filter?.uuid || '' + ); + const [captureId, setCaptureId] = useState( + url.searchParams.get('captureId') || filter?.captureId || '' + ); + const [growerId, setGrowerId] = useState( + url.searchParams.get('growerId') || filter?.planterId || '' + ); + const [deviceId, setDeviceId] = useState( + url.searchParams.get('deviceId') || filter?.deviceIdentifier || '' + ); + const [growerIdentifier, setGrowerIdentifier] = useState( + url.searchParams.get('growerIdentifier') || filter?.planterIdentifier || '' + ); + const [approved, setApproved] = useState(filter?.approved); + const [active, setActive] = useState(filter?.active); + const [dateStart, setDateStart] = useState( + url.searchParams.get('dateStart') || filter?.dateStart || dateStartDefault + ); + const [dateEnd, setDateEnd] = useState( + url.searchParams.get('dateEnd') || filter?.dateEnd || dateEndDefault + ); + const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); + const [tag, setTag] = useState(url.searchParams.get('tag') || null); + const [tagSearchString, setTagSearchString] = useState( + url.searchParams.get('tagSearchString') || '' ); - const [endDate, setEndDate] = useState(filter?.endDate || endDateDefault); - const [speciesId, setSpeciesId] = useState(filter?.species_id || ALL_SPECIES); - const [tag, setTag] = useState(null); - const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - filter?.organizationId || ALL_ORGANIZATIONS + url.searchParams.get('organizationId') || + filter?.organizationId || + ALL_ORGANIZATIONS ); // const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); + useEffect(() => { + handleQuerySearchParams('deviceId', deviceId); + }, []); + + const handleQuerySearchParams = (name, value) => { + if (!params.get(name)) { + params.append(name, value); + if (window.location.href.includes('?')) { + window.location.href += '&' + name + '=' + value; + } else { + window.location.href += '?' + name + '=' + value; + } + } else { + params.set(name, value); + } + }; const handleStartDateChange = (date) => { setStartDate(date); diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 60dbe5517..daf69ac38 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -6,6 +6,11 @@ import { captureStatus } from 'common/variables'; import { AppContext } from './AppContext.js'; import { setOrganizationFilter } from '../common/utils'; +import React, { createContext, useEffect, useState } from 'react'; + +import FilterModel from '../models/Filter'; +import api from '../api/treeTrackerApi'; + const log = loglevel.getLogger('../context/VerifyContext'); export const VerifyContext = createContext({ From fee2813bde6efb1d1c766439fe6c0e75111d4e12 Mon Sep 17 00:00:00 2001 From: Krithin Jay Pakshootra Date: Mon, 23 Jan 2023 10:31:08 +0800 Subject: [PATCH 02/32] chore: fix merge error --- src/common/locale.js | 4 +- src/components/FilterTop.js | 523 +++++++++++++++++------------- src/components/FilterTopGrower.js | 9 +- src/components/Verify.js | 60 ++-- src/views/VerifyView.js | 7 +- 5 files changed, 342 insertions(+), 261 deletions(-) diff --git a/src/common/locale.js b/src/common/locale.js index cffb3a476..92cb56736 100644 --- a/src/common/locale.js +++ b/src/common/locale.js @@ -1,5 +1,7 @@ -import enLocale from 'date-fns/locale/en-US'; import { format, formatDistanceToNow, getYear } from 'date-fns'; + +import enLocale from 'date-fns/locale/en-US'; + // Per default set to EN var localeLanguage = 'en'; // default date pattern when converting dates diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 71883a9e3..769512128 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -31,6 +31,18 @@ import { withStyles } from '@material-ui/core/styles'; export const FILTER_WIDTH = 330; +const stringToDate = (string) => { + if (!string) { + return false; + } else { + return new Date( + parseInt(string.substring(0, 4)), + parseInt(string.substring(5, 7)) - 1, + parseInt(string.substring(8, 10)) + ); + } +}; + const styles = (theme) => { return { root: {}, @@ -95,10 +107,14 @@ function Filter(props) { const [approved, setApproved] = useState(filter?.approved); const [active, setActive] = useState(filter?.active); const [dateStart, setDateStart] = useState( - url.searchParams.get('dateStart') || filter?.dateStart || dateStartDefault + stringToDate(url.searchParams.get('dateStart')) || + filter?.dateStart || + dateStartDefault ); const [dateEnd, setDateEnd] = useState( - url.searchParams.get('dateEnd') || filter?.dateEnd || dateEndDefault + stringToDate(url.searchParams.get('dateEnd')) || + filter?.dateEnd || + dateEndDefault ); const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); const [tag, setTag] = useState(url.searchParams.get('tag') || null); @@ -110,23 +126,45 @@ function Filter(props) { filter?.organizationId || ALL_ORGANIZATIONS ); + let open = props.open; // const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); + useEffect(() => { + handleQuerySearchParams('growerId', growerId); + handleQuerySearchParams('captureId', captureId); + handleQuerySearchParams('uuid', uuid); handleQuerySearchParams('deviceId', deviceId); - }, []); + handleQuerySearchParams( + 'dateStart', + dateStart ? formatDate(dateStart) : '' + ); + handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); + handleQuerySearchParams('speciesId', speciesId); + handleQuerySearchParams('growerIdentifier', growerIdentifier); + handleQuerySearchParams('approved', approved); + handleQuerySearchParams('active', active); + handleQuerySearchParams('tag', tag); + handleQuerySearchParams('tagSearchString', tagSearchString); + handleQuerySearchParams('organizationId', organizationId); + handleQuerySearchParams('stakeholderUUID', stakeholderUUID); - const handleQuerySearchParams = (name, value) => { - if (!params.get(name)) { - params.append(name, value); - if (window.location.href.includes('?')) { - window.location.href += '&' + name + '=' + value; - } else { - window.location.href += '?' + name + '=' + value; - } - } else { - params.set(name, value); - } - }; + const filter = new FilterModel(); + filter.uuid = uuid; + filter.captureId = captureId; + filter.planterId = growerId; + filter.deviceIdentifier = deviceId; + 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.stakeholderUUID = stakeholderUUID; + + props.onSubmit && props.onSubmit(filter); + }, []); const handleStartDateChange = (date) => { setStartDate(date); @@ -143,6 +181,31 @@ function Filter(props) { function handleSubmit(e) { e.preventDefault(); // save the filer to context for editing & submit + + handleQuerySearchParams('growerId', growerId); + handleQuerySearchParams('captureId', captureId); + handleQuerySearchParams('uuid', uuid); + handleQuerySearchParams('deviceId', deviceId); + handleQuerySearchParams( + 'dateStart', + dateStart ? formatDate(dateStart) : '' + ); + handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); + handleQuerySearchParams('speciesId', speciesId); + handleQuerySearchParams('growerIdentifier', growerIdentifier); + handleQuerySearchParams('approved', approved); + handleQuerySearchParams('active', active); + handleQuerySearchParams('tag', tag); + handleQuerySearchParams('tagSearchString', tagSearchString); + handleQuerySearchParams('organizationId', organizationId); + handleQuerySearchParams('stakeholderUUID', stakeholderUUID); + // filter.tokenId = tokenId; + + handleFilter(); + } + + function handleFilter() { + console.log(dateStart); const filter = new FilterModel(); filter.uuid = uuid; filter.captureId = captureId.trim(); @@ -155,6 +218,7 @@ function Filter(props) { filter.species_id = speciesId; filter.tag_id = tag ? tag.id : undefined; filter.organization_id = organizationId; + filter.stakeholderUUID = stakeholderUUID; // filter.tokenId = tokenId; props.onSubmit && props.onSubmit(filter); } @@ -172,6 +236,7 @@ function Filter(props) { setTag(null); setTagSearchString(''); setOrganizationId(ALL_ORGANIZATIONS); + setStakeholderUUID(ALL_ORGANIZATIONS); // setTokenId(filterOptionAll); const filter = new FilterModel(); @@ -179,55 +244,70 @@ function Filter(props) { props.onSubmit && props.onSubmit(filter); } + const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } + }; + return ( <> - { -
- - - { - setStatus( - e.target.value === filterOptionAll - ? undefined - : e.target.value === verificationStates.APPROVED - ? captureStatus.APPROVED - : e.target.value === verificationStates.REJECTED - ? captureStatus.REJECTED - : captureStatus.UNPROCESSED - ); - }} - > - {[ - filterOptionAll, - verificationStates.APPROVED, - verificationStates.AWAITING, - verificationStates.REJECTED, - ].map((name) => ( - - {name} - - ))} - - {/* + + + { + 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 + ); + }} + > + {[ + filterOptionAll, + verificationStates.APPROVED, + verificationStates.AWAITING, + verificationStates.REJECTED, + ].map((name) => ( + + {name} + + ))} + + {/* ))} */} - - - - - setGrowerId(e.target.value)} - /> - setCaptureId(e.target.value)} - /> - setUUID(e.target.value)} - /> - setDeviceId(e.target.value)} - /> - setWallet(e.target.value)} + + - {/* setSpeciesId(e.target.value)} - > - {speciesContext.isLoading ? ( - - ) : ( - [ - { id: ALL_SPECIES, name: 'All' }, - { id: SPECIES_ANY_SET, name: 'Any set' }, - { - id: SPECIES_NOT_SET, - name: 'Not set', - }, - ...speciesContext.speciesList, - ].map((species) => ( - - {species.name} - - )) - )} - */} - + + setGrowerId(e.target.value)} + /> + setCaptureId(e.target.value)} + /> + setUUID(e.target.value)} + /> + setDeviceId(e.target.value)} + /> + setGrowerIdentifier(e.target.value)} + /> + {/* setSpeciesId(e.target.value)} + > + {speciesContext.isLoading ? ( + + ) : ( + [ + { id: ALL_SPECIES, name: 'All' }, + { id: SPECIES_ANY_SET, name: 'Any set' }, { - id: TAG_NOT_SET, + id: SPECIES_NOT_SET, name: 'Not set', - isPublic: true, - status: 'active', - owner_id: null, - }, - { - id: ANY_TAG_SET, - name: 'Any tag set', - isPublic: true, - status: 'active', - owner_id: null, }, - ...tagsContext.tagList.filter((t) => - t.name - .toLowerCase() - .startsWith(tagSearchString.toLowerCase()) - ), - ]} - value={tag} - defaultValue={'All'} - getOptionLabel={(tag) => tag.name} - onChange={(_oldVal, newVal) => { - //triggered by onInputChange - setTag(newVal); - }} - onInputChange={(_oldVal, newVal) => { - setTagSearchString(newVal); - }} - renderInput={(params) => { - return ; - }} - getOptionSelected={(option, value) => option.id === value.id} - /> - { - setOrganizationId(org.stakeholder_uuid); - }} - /> + ...speciesContext.speciesList, + ].map((species) => ( + + {species.name} + + )) + )} + */} + + t.name + .toLowerCase() + .startsWith(tagSearchString.toLowerCase()) + ), + ]} + value={tag} + defaultValue={'All'} + getOptionLabel={(tag) => tag.name} + onChange={(_oldVal, newVal) => { + //triggered by onInputChange + setTag(newVal); + }} + onInputChange={(_oldVal, newVal) => { + setTagSearchString(newVal); + }} + renderInput={(params) => { + return ; + }} + getOptionSelected={(option, value) => option.id === value.id} + /> + { + setOrganizationId(org.id); + }} + /> - + Reset + - - } + + ); } diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index 4ed4e9a73..d28060a80 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -1,11 +1,12 @@ import React, { useState } from 'react'; -import { withStyles } from '@material-ui/core/styles'; -import Grid from '@material-ui/core/Grid'; + +import { ALL_ORGANIZATIONS } from '../models/Filter'; import Button from '@material-ui/core/Button'; -import TextField from '@material-ui/core/TextField'; import FilterModel from '../models/FilterGrower'; +import Grid from '@material-ui/core/Grid'; import SelectOrg from './common/SelectOrg'; -import { ALL_ORGANIZATIONS } from '../models/Filter'; +import TextField from '@material-ui/core/TextField'; +import { withStyles } from '@material-ui/core/styles'; export const FILTER_WIDTH = 330; diff --git a/src/components/Verify.js b/src/components/Verify.js index 5f50d17c6..9d57d990b 100644 --- a/src/components/Verify.js +++ b/src/components/Verify.js @@ -1,42 +1,41 @@ -import React, { useEffect, useState, useContext, useRef } from 'react'; -import clsx from 'clsx'; -import { makeStyles } from '@material-ui/core/styles'; import { - Typography, + AppBar, + Avatar, + Box, + Button, Card, - Button, // replace with icons down the line + Divider, Grid, - LinearProgress, IconButton, - Snackbar, - Avatar, + LinearProgress, Paper, - Box, + Snackbar, TablePagination, - Divider, - AppBar, + Typography, } from '@material-ui/core'; +import { Image, LocationOn, Map, Nature, Person } from '@material-ui/icons'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import { Skeleton, ToggleButton, ToggleButtonGroup } from '@material-ui/lab'; -import IconFilter from '@material-ui/icons/FilterList'; +import CaptureDetailDialog from './CaptureDetailDialog'; +import { CaptureDetailProvider } from 'context/CaptureDetailContext'; +import CaptureDetailTooltip from './CaptureDetailTooltip'; import CheckIcon from '@material-ui/icons/Check'; -import { LocationOn, Person, Nature, Map, Image } from '@material-ui/icons'; -import Navbar from './Navbar'; +import FilterTop from './FilterTop'; import GrowerDetail from './GrowerDetail'; +import IconFilter from '@material-ui/icons/FilterList'; +import Navbar from './Navbar'; +import OptimizedImage from './OptimizedImage'; // import CaptureTags from './CaptureTags'; import SidePanel from './SidePanel'; -import FilterTop from './FilterTop'; -import CaptureDetailDialog from './CaptureDetailDialog'; -import OptimizedImage from './OptimizedImage'; -import CaptureDetailTooltip from './CaptureDetailTooltip'; -import Spinner from './common/Spinner'; -import { documentTitle } from 'common/variables'; -import { countToLocaleString } from 'common/numbers'; -import { VerifyContext } from 'context/VerifyContext'; import { SpeciesContext } from 'context/SpeciesContext'; +import Spinner from './common/Spinner'; import { TagsContext } from 'context/TagsContext'; -import { CaptureDetailProvider } from 'context/CaptureDetailContext'; -import { pathType } from './common/LinkToWebmap'; +import { VerifyContext } from 'context/VerifyContext'; +import clsx from 'clsx'; +import { countToLocaleString } from 'common/numbers'; +import { documentTitle } from 'common/variables'; +import { makeStyles } from '@material-ui/core/styles'; const log = require('loglevel').getLogger('components/Verify'); const EMPTY_ARRAY = new Array(16).fill(); @@ -574,13 +573,12 @@ const Verify = (props) => { , ]} > - {isFilterShown && ( - - )} + diff --git a/src/views/VerifyView.js b/src/views/VerifyView.js index 1df12c593..dd3fbb17c 100644 --- a/src/views/VerifyView.js +++ b/src/views/VerifyView.js @@ -1,9 +1,10 @@ import React, { useEffect } from 'react'; -import { documentTitle } from '../common/variables'; -import Verify from '../components/Verify'; -import { VerifyProvider } from '../context/VerifyContext'; + import { SpeciesProvider } from '../context/SpeciesContext'; import { TagsProvider } from '../context/TagsContext'; +import Verify from '../components/Verify'; +import { VerifyProvider } from '../context/VerifyContext'; +import { documentTitle } from '../common/variables'; function VerifyView() { useEffect(() => { From fc286cbb6a192867adb721a49c3cd3b0d6417d06 Mon Sep 17 00:00:00 2001 From: Krithin Jay Pakshootra Date: Mon, 23 Jan 2023 10:33:02 +0800 Subject: [PATCH 03/32] fix: more merge errors --- src/components/CaptureFilter.js | 129 +++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 9825864da..4ad2b72a0 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -7,31 +7,40 @@ import MenuItem from '@material-ui/core/MenuItem'; import Autocomplete from '@material-ui/lab/Autocomplete'; import SelectOrg from './common/SelectOrg'; import FilterModel, { + ALL_ORGANIZATIONS, ALL_SPECIES, SPECIES_ANY_SET, SPECIES_NOT_SET, - ALL_ORGANIZATIONS, ALL_TAGS, TAG_NOT_SET, ANY_TAG_SET, } from '../models/Filter'; -import DateFnsUtils from '@date-io/date-fns'; import { - MuiPickersUtilsProvider, KeyboardDatePicker, + MuiPickersUtilsProvider, } from '@material-ui/pickers'; +import React, { useContext, useEffect, useState } from 'react'; import { - getDatePickerLocale, - getDateFormatLocale, convertDateToDefaultSqlDate, + getDateFormatLocale, + getDatePickerLocale, } from '../common/locale'; import { - tokenizationStates, datePickerDefaultMinDate, + tokenizationStates, } from '../common/variables'; + +import Autocomplete from '@material-ui/lab/Autocomplete'; +import Button from '@material-ui/core/Button'; +import { CircularProgress } from '@material-ui/core'; +import DateFnsUtils from '@date-io/date-fns'; +import Grid from '@material-ui/core/Grid'; +import MenuItem from '@material-ui/core/MenuItem'; +import SelectOrg from './common/SelectOrg'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; -import { CircularProgress } from '@material-ui/core'; +import TextField from '@material-ui/core/TextField'; +import { withStyles } from '@material-ui/core/styles'; export const FILTER_WIDTH = 330; @@ -72,30 +81,77 @@ const styles = (theme) => { }; }; +const stringToDate = (string) => { + if (!string) { + return false; + } else { + return new Date( + parseInt(string.substring(0, 4)), + parseInt(string.substring(5, 7)) - 1, + parseInt(string.substring(8, 10)) + ); + } +}; + function Filter(props) { + const url = new URL(window.location.href); + const params = new URLSearchParams(url.search); + const speciesContext = useContext(SpeciesContext); const tagsContext = useContext(TagsContext); const { classes, filter } = props; const filterOptionAll = 'All'; - const startDateDefault = null; - const endDateDefault = null; - const [uuid, setUUID] = useState(filter?.uuid || ''); - const [captureId, setCaptureId] = useState(filter?.captureId || ''); - const [wallet, setWallet] = useState(filter?.wallet || ''); - const [growerId, setGrowerId] = useState(filter?.grower_account_id || ''); - const [deviceId, setDeviceId] = useState(filter?.device_identifier || ''); - const [startDate, setStartDate] = useState( - filter?.startDate || startDateDefault + const dateStartDefault = null; + const dateEndDefault = null; + + const [uuid, setUUID] = useState( + url.searchParams.get('uuid') || filter?.uuid || '' + ); + const [captureId, setCaptureId] = useState( + url.searchParams.get('captureId') || filter?.captureId || '' + ); + const [growerId, setGrowerId] = useState( + url.searchParams.get('growerId') || filter?.planterId || '' + ); + const [deviceId, setDeviceId] = useState( + url.searchParams.get('deviceId') || filter?.deviceIdentifier || '' + ); + const [growerIdentifier, setGrowerIdentifier] = useState( + url.searchParams.get('growerIdentifier') || filter?.planterIdentifier || '' ); const [endDate, setEndDate] = useState(filter?.endDate || endDateDefault); const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); const [tag, setTag] = useState(null); const [tagSearchString, setTagSearchString] = useState(''); - const [organizationId, setOrganizationId] = useState(ALL_ORGANIZATIONS); - const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); + const [organizationId, setOrganizationId] = useState( + url.searchParams.get('organizationId') || + filter.organizationId || + ALL_ORGANIZATIONS + ); + const [tokenId, setTokenId] = useState( + url.searchParams.get('tokenId') || filter?.tokenId || filterOptionAll + ); - const handleStartDateChange = (date) => { - setStartDate(date); + useEffect(() => { + const filter = new FilterModel(); + filter.uuid = uuid; + filter.captureId = captureId; + filter.planterId = growerId; + filter.deviceIdentifier = deviceId; + filter.planterIdentifier = growerIdentifier; + filter.dateStart = dateStart ? formatDate(dateStart) : undefined; + filter.dateEnd = dateEnd ? formatDate(dateEnd) : undefined; + filter.speciesId = speciesId; + filter.tagId = tag ? tag.id : 0; + filter.organizationId = organizationId; + filter.stakeholderUUID = stakeholderUUID; + filter.tokenId = tokenId; + filter.verifyStatus = verifyStatus; + props.onSubmit && props.onSubmit(filter); + }, []); + + const handleDateStartChange = (date) => { + setDateStart(date); }; const handleEndDateChange = (date) => { @@ -125,6 +181,24 @@ function Filter(props) { const filter = new FilterModel(test); props.onSubmit && props.onSubmit(filter); + + handleQuerySearchParams('uuid', uuid); + handleQuerySearchParams('captureId', captureId); + handleQuerySearchParams('deviceId', deviceId); + handleQuerySearchParams('growerIdentifier', growerIdentifier); + handleQuerySearchParams('growerId', growerId); + handleQuerySearchParams( + 'dateStart', + dateStart ? formatDate(dateStart) : '' + ); + handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); + handleQuerySearchParams('speciesId', speciesId); + handleQuerySearchParams('growerIdentifier', growerIdentifier); + handleQuerySearchParams('tag', tag); + handleQuerySearchParams('tagSearchString', tagSearchString); + handleQuerySearchParams('organizationId', organizationId); + handleQuerySearchParams('stakeholderUUID', stakeholderUUID); + handleQuerySearchParams('tokenId', tokenId); } function handleReset() { @@ -145,6 +219,21 @@ function Filter(props) { props.onSubmit && props.onSubmit(filter); } + const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } + }; + return ( <> { From 6b990a2b9e3c91bfb6d34cb9835e9064d2b522c6 Mon Sep 17 00:00:00 2001 From: Krithin Jay Pakshootra Date: Mon, 23 Jan 2023 10:35:19 +0800 Subject: [PATCH 04/32] fix: more merge errors --- src/components/CaptureDetailDialog.js | 31 +++-- .../CaptureMatching/CaptureMatchingView.js | 111 ++++++++++++------ 2 files changed, 93 insertions(+), 49 deletions(-) diff --git a/src/components/CaptureDetailDialog.js b/src/components/CaptureDetailDialog.js index 5e1f1ed4e..111b9b12f 100644 --- a/src/components/CaptureDetailDialog.js +++ b/src/components/CaptureDetailDialog.js @@ -1,34 +1,33 @@ -import React, { useState, useEffect, useContext, useMemo } from 'react'; - -import { makeStyles } from '@material-ui/core/styles'; import { - Typography, + Box, + Button, + Chip, + CircularProgress, + Container, Dialog, - Grid, Divider, - Chip, - IconButton, Drawer, - Box, + Grid, + IconButton, Link, - CircularProgress, - Container, - Button, + Typography, } from '@material-ui/core'; -import Close from '@material-ui/icons/Close'; -import OptimizedImage from './OptimizedImage'; -import LinkToWebmap, { pathType } from './common/LinkToWebmap'; -import { verificationStates } from '../common/variables'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; + import { CaptureDetailContext } from '../context/CaptureDetailContext'; -import CopyNotification from './common/CopyNotification'; +import Close from '@material-ui/icons/Close'; import { CopyButton } from './common/CopyButton'; +import CopyNotification from './common/CopyNotification'; import Country from './common/Country'; +import LinkToWebmap from './common/LinkToWebmap'; +import OptimizedImage from './OptimizedImage'; import Skeleton from '@material-ui/lab/Skeleton'; import { captureStatus } from '../common/variables'; import { hasPermission, POLICIES } from '../models/auth'; import { AppContext } from '../context/AppContext'; import theme from './common/theme'; import log from 'loglevel'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme) => ({ chipRoot: { diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index 4ba0e87ca..329f387fc 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -3,47 +3,49 @@ import { getDistance } from 'geolib'; import { makeStyles } from '@material-ui/core/styles'; import { - Button, AppBar, + Avatar, + Box, + Button, Chip, Divider, - Grid, - Box, - Paper, - LinearProgress, Drawer, - Typography, FormControl, + Grid, + IconButton, + LinearProgress, + Paper, TextField, - Avatar, Tooltip, - IconButton, + Typography, } from '@material-ui/core'; -import NatureOutlinedIcon from '@material-ui/icons/NatureOutlined'; -import CloseIcon from '@material-ui/icons/Close'; -import FilterListIcon from '@material-ui/icons/FilterList'; -import AccessTimeIcon from '@material-ui/icons/AccessTime'; -import LocationOnOutlinedIcon from '@material-ui/icons/LocationOnOutlined'; -import SkipNextIcon from '@material-ui/icons/SkipNext'; -import QuestionMarkIcon from '@material-ui/icons/HelpOutlineOutlined'; -import PhotoCameraOutlinedIcon from '@material-ui/icons/PhotoCameraOutlined'; -import Pagination from '@material-ui/lab/Pagination'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; -import { documentTitle } from 'common/variables'; -import { getDateTimeStringLocale } from 'common/locale'; +import AccessTimeIcon from '@material-ui/icons/AccessTime'; import { AppContext } from 'context/AppContext'; -import { MatchingToolContext } from 'context/MatchingToolContext'; +import CandidateImages from './CandidateImages'; +import CaptureDetailDialog from 'components/CaptureDetailDialog'; import { CaptureDetailProvider } from 'context/CaptureDetailContext'; +import CloseIcon from '@material-ui/icons/Close'; +import Country from '../common/Country'; +import FilterListIcon from '@material-ui/icons/FilterList'; +import GrowerDetail from 'components/GrowerDetail'; import { GrowerProvider } from 'context/GrowerContext'; -import CaptureDetailDialog from 'components/CaptureDetailDialog'; +import LocationOnOutlinedIcon from '@material-ui/icons/LocationOnOutlined'; +import { MatchingToolContext } from 'context/MatchingToolContext'; +import NatureOutlinedIcon from '@material-ui/icons/NatureOutlined'; +import Navbar from '../Navbar'; import OptimizedImage from 'components/OptimizedImage'; -import GrowerDetail from 'components/GrowerDetail'; -import Country from '../common/Country'; +import Pagination from '@material-ui/lab/Pagination'; +import PhotoCameraOutlinedIcon from '@material-ui/icons/PhotoCameraOutlined'; +import QuestionMarkIcon from '@material-ui/icons/HelpOutlineOutlined'; import SelectOrg from '../common/SelectOrg'; -import CandidateImages from './CandidateImages'; -import Navbar from '../Navbar'; +import SkipNextIcon from '@material-ui/icons/SkipNext'; import api from 'api/treeTrackerApi'; +import { documentTitle } from 'common/variables'; +import { getDateTimeStringLocale } from 'common/locale'; import log from 'loglevel'; +import { makeStyles } from '@material-ui/core/styles'; const useStyle = makeStyles((theme) => ({ container: { @@ -310,6 +312,8 @@ function CaptureMatchingView() { endDate: now.toISOString().split('T')[0], stakeholderUUID: null, }; + const url = new URL(window.location.href); + const params = new URLSearchParams(url.search); const classes = useStyle(); const appContext = useContext(AppContext); @@ -321,9 +325,13 @@ function CaptureMatchingView() { const [noOfPages, setNoOfPages] = useState(null); //for pagination const [imgCount, setImgCount] = useState(null); //for header icon const [treesCount, setTreesCount] = useState(0); - const [startDate, setStartDate] = useState(initialFilter.startDate); - const [endDate, setEndDate] = useState(initialFilter.endDate); - const [organizationId, setOrganizationId] = useState(null); + const [startDate, setStartDate] = useState( + url.searchParams.get('startDate') || '' + ); + const [endDate, setEndDate] = useState(url.searchParams.get('endDate') || ''); + const [organizationId, setOrganizationId] = useState( + url.searchParams.get('organizationId') || null + ); const [stakeholderUUID, setStakeholderUUID] = useState(null); const [filter, setFilter] = useState(initialFilter); const [growerAccount, setGrowerAccount] = useState({}); @@ -427,6 +435,17 @@ function CaptureMatchingView() { return () => abortController.abort(); }, [captureImage]); + useEffect(() => { + handleQuerySearchParams('startDate', startDate); + handleQuerySearchParams('endDate', endDate); + handleQuerySearchParams('organizationId', organizationId); + setFilter({ + startDate, + endDate, + stakeholderUUID, + }); + }, []); + // Capture Image Pagination function const handleChange = (e, value) => { setCurrentPage(value); @@ -468,8 +487,27 @@ function CaptureMatchingView() { setEndDate(e.target.value); } + const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + console.log(url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } + }; + function handleFilterSubmit() { // log.debug('filter submit -----> ', organizationId, stakeholderUUID); + handleQuerySearchParams('startDate', startDate); + handleQuerySearchParams('endDate', endDate); + handleQuerySearchParams('organizationId', organizationId); setFilter({ startDate, endDate, @@ -506,8 +544,11 @@ function CaptureMatchingView() { const getGrowerRegDate = () => { if (Object.keys(growerAccount).length !== 0) { - return 'Joined at ' + getDateTimeStringLocale( - growerAccount.first_registration_at || growerAccount.created_at + return ( + 'Joined at ' + + getDateTimeStringLocale( + growerAccount.first_registration_at || growerAccount.created_at + ) ); } return ''; @@ -566,13 +607,17 @@ function CaptureMatchingView() { filter.endDate || 'End Date' }`} className={classes.currentHeaderChip} - onDelete={() => + onDelete={() => { setFilter({ ...filter, startDate: undefined, endDate: undefined, - }) - } + }); + handleQuerySearchParams('startDate', ''); + handleQuerySearchParams('endDate', ''); + setStartDate(''); + setEndDate(''); + }} /> )} {filter.stakeholderUUID && ( From 9de70ebefe9bd80f1e6506676769e94e09861aca Mon Sep 17 00:00:00 2001 From: SuspenseFallback Date: Mon, 21 Nov 2022 10:09:40 +0800 Subject: [PATCH 05/32] fix: ear --- .../CustomTableFilter/CustomTableFilter.js | 93 +++++++++++++++---- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/src/components/common/CustomTableFilter/CustomTableFilter.js b/src/components/common/CustomTableFilter/CustomTableFilter.js index dccb9eb07..ab673020f 100644 --- a/src/components/common/CustomTableFilter/CustomTableFilter.js +++ b/src/components/common/CustomTableFilter/CustomTableFilter.js @@ -1,21 +1,21 @@ -import React, { useState, useContext } from 'react'; -import PropTypes from 'prop-types'; -import Grid from '@material-ui/core/Grid'; -import Drawer from '@material-ui/core/Drawer'; -import TextField from '@material-ui/core/TextField'; -import Select from '@material-ui/core/Select'; -import InputLabel from '@material-ui/core/InputLabel'; -import MenuItem from '@material-ui/core/MenuItem'; +import React, { useContext, useState } from 'react'; + +import { ALL_ORGANIZATIONS } from '../../../models/Filter'; +import { AppContext } from '../../../context/AppContext'; import Button from '@material-ui/core/Button'; -import Typography from '@material-ui/core/Typography'; -import Divider from '@material-ui/core/Divider'; import CloseIcon from '@material-ui/icons/Close'; +import Divider from '@material-ui/core/Divider'; +import Drawer from '@material-ui/core/Drawer'; import FormControl from '@material-ui/core/FormControl'; - +import Grid from '@material-ui/core/Grid'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import PropTypes from 'prop-types'; +import Select from '@material-ui/core/Select'; import SelectOrg from '../SelectOrg'; +import TextField from '@material-ui/core/TextField'; +import Typography from '@material-ui/core/Typography'; import useStyles from './CustomTableFilter.styles'; -import { AppContext } from '../../../context/AppContext'; -import { ALL_ORGANIZATIONS } from '../../../models/Filter'; const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; @@ -35,11 +35,13 @@ const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; function CustomTableFilter(props) { // console.warn('orgList', orgList); const initialFilter = { - // organization_id: '', - grower: '', - payment_status: 'all', - earnings_status: 'all', - phone: '', + organization_id: url.searchParams.get('organization_id') || '', + grower: url.searchParams.get('grower') || '', + payment_status: url.searchParams.get('payment_status') || 'all', + earnings_status: url.searchParams.get('earnings_status') || 'all', + phone: url.searchParams.get('phone') || '', + start_date: url.searchParams.get('start_date') || '', + end_date: url.searchParams.get('end_date') || '', }; const [localFilter, setLocalFilter] = useState(initialFilter); const { @@ -54,8 +56,63 @@ function CustomTableFilter(props) { const classes = useStyles(); const { updateSelectedFilter } = useContext(AppContext); + useEffect(() => { + const filtersToSubmit = { + ...filter, + ...localFilter, + }; + console.log( + 'useEffect - CustomTableFilter.js:66 - localFilter --', + localFilter + ); + + if (filtersToSubmit.organization_id === ALL_ORGANIZATIONS) { + const modifiedFiltersToSubmit = { + ...filtersToSubmit, + organization_id: '', + sub_organization: '', + }; + setFilter(modifiedFiltersToSubmit); + setIsFilterOpen(false); + console.log(modifiedFiltersToSubmit); + updateSelectedFilter({ + modifiedFiltersToSubmit, + }); + mapFiltersToQueries(modifiedFiltersToSubmit); + } else { + setFilter(filtersToSubmit); + setIsFilterOpen(false); + console.log(filtersToSubmit); + updateSelectedFilter(filtersToSubmit); + mapFiltersToQueries(filtersToSubmit); + } + }, []); + + const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } + }; + + const mapFiltersToQueries = (filter) => { + console.log(filter); + for (var key in filter) { + handleQuerySearchParams(key, filter[key]); + } + }; + const handleOnFormControlChange = (e) => { let updatedFilter = { ...localFilter }; + console.log('updatedFilter --', updatedFilter); if (e?.target) { e.preventDefault(); const { name, value } = e.target; From 895e3cf2fb0afb3a89b604970fb62d5a89f4ac31 Mon Sep 17 00:00:00 2001 From: SuspenseFallback Date: Sun, 4 Dec 2022 18:46:27 +0800 Subject: [PATCH 06/32] feat: working on payments filter --- src/components/FilterTopGrower.js | 2 +- src/components/GrowerFilterHeader.js | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index d28060a80..45c71c509 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -94,7 +94,7 @@ function FilterTopGrower(props) { return ( <> { -
+ ({ activeFilters: { @@ -50,14 +51,12 @@ function GrowersFilterHeader(props) { , ]} > - {isFilterShown && ( - - )} + ); From 5514d005d211e4a6f96f29df608d289e3c204633 Mon Sep 17 00:00:00 2001 From: SuspenseFallback Date: Wed, 14 Dec 2022 21:41:02 +0800 Subject: [PATCH 07/32] feat: filter persists on growers --- src/components/FilterTopGrower.js | 80 ++++++++++++++++++++++++++----- src/models/FilterGrower.js | 11 +++++ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index 45c71c509..92b4588a4 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -1,8 +1,7 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; -import { ALL_ORGANIZATIONS } from '../models/Filter'; import Button from '@material-ui/core/Button'; -import FilterModel from '../models/FilterGrower'; +import FilterModel, { ALL_ORGANIZATIONS, FILTER_FIELDS } from '../models/FilterGrower'; import Grid from '@material-ui/core/Grid'; import SelectOrg from './common/SelectOrg'; import TextField from '@material-ui/core/TextField'; @@ -44,20 +43,75 @@ const styles = (theme) => { function FilterTopGrower(props) { const { classes, filter } = props; - const [id, setId] = useState(filter?.id || ''); - const [personId, setPersonId] = useState(filter?.personId || ''); - const [firstName, setFirstName] = useState(filter?.firstName || ''); - const [lastName, setLastName] = useState(filter?.lastName || ''); - const [organizationId, setOrganizationId] = useState(ALL_ORGANIZATIONS); - const [email, setEmail] = useState(filter?.email || ''); - const [phone, setPhone] = useState(filter?.phone || ''); - const [wallet, setWallet] = useState(filter?.wallet || ''); + const url = new URL(window.location.href); + const params = new URLSearchParams(url.search); + + const [id, setId] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.id)); + const [personId, setPersonId] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.personId)); + const [firstName, setFirstName] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.firstName)); + const [lastName, setLastName] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.lastName)); + const [email, setEmail] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.email)); + const [phone, setPhone] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.phone)); + const [wallet, setWallet] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.wallet)); const [deviceIdentifier, setDeviceIdentifier] = useState( - filter?.device_identifier || '' + getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.deviceIdentifier + )); + const [organizationId, setOrganizationId] = useState( + getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.organizationId, ALL_ORGANIZATIONS) ); + + const getValueFromUrlOrFilter = (url, filter, attr, defaultVal = '') => { + return (url?.searchParams.get(attr) || filter?.[attr] || defaultVal); + } + + const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } + }; + + handleAllQuerySearchParams() { + handleQuerySearchParams(FILTER_FIELDS.id, id); + handleQuerySearchParams(FILTER_FIELDS.personId, personId); + handleQuerySearchParams(FILTER_FIELDS.firstName, firstName); + handleQuerySearchParams(FILTER_FIELDS.lastName, lastName); + handleQuerySearchParams(FILTER_FIELDS.organizationId, organizationId); + handleQuerySearchParams(FILTER_FIELDS.email, email); + handleQuerySearchParams(FILTER_FIELDS.phone, phone); + handleQuerySearchParams(FILTER_FIELDS.deviceIdentifier, deviceIdentifier); + handleQuerySearchParams(FILTER_FIELDS.wallet, wallet); + } + + useEffect(() => { + handleAllQuerySearchParams(); + + const filter = new FilterModel({ + personId, + id, + firstName, + lastName, + organizationId, + stakeholderUUID, + email, + phone, + deviceIdentifier, + }); + props.onSubmit && props.onSubmit(filter); + }, []); function handleSubmit(e) { e.preventDefault(); + handleAllQuerySearchParams(); + const filter = new FilterModel({ personId, id, @@ -83,6 +137,8 @@ function FilterTopGrower(props) { setDeviceIdentifier(''); setWallet(''); + handleAllQuerySearchParams(); + const filter = new FilterModel(); props.onSubmit && props.onSubmit(filter); }; diff --git a/src/models/FilterGrower.js b/src/models/FilterGrower.js index 1a84cd0e8..57769afd7 100644 --- a/src/models/FilterGrower.js +++ b/src/models/FilterGrower.js @@ -4,6 +4,17 @@ export const ALL_ORGANIZATIONS = 'ALL_ORGANIZATIONS'; export const ORGANIZATION_NOT_SET = 'ORGANIZATION_NOT_SET'; +export const FILTER_FIELDS = { + personId: 'personId', + wallet: 'wallet', + id: 'id', + firstName: 'firstName', + lastName: 'lastName', + organizationId: 'organization_id', + deviceIdentifier: 'device_identifier', + email: 'email', + phone: 'phone', +}; // import log from 'loglevel'; export default class Filter { From 862e316d58fbc51e7feb80d3611d15fbe8c99dae Mon Sep 17 00:00:00 2001 From: Krithin Jay Pakshootra Date: Mon, 23 Jan 2023 10:15:56 +0800 Subject: [PATCH 08/32] chore: draft pr --- src/components/Stakeholders/Filter.js | 15 ++++++++------- src/components/Stakeholders/FilterBar.js | 4 ++-- src/views/StakeholdersView.js | 11 ++++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/Stakeholders/Filter.js b/src/components/Stakeholders/Filter.js index c6843b684..79f43e9ab 100644 --- a/src/components/Stakeholders/Filter.js +++ b/src/components/Stakeholders/Filter.js @@ -1,22 +1,23 @@ -import React, { useState, useContext, useEffect } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; import { Button, Dialog, - DialogTitle, - DialogContent, DialogActions, - TextField, + DialogContent, + DialogTitle, FormControl, MenuItem, + TextField, } from '@material-ui/core'; +import React, { useContext, useEffect, useState } from 'react'; + +import { ALL_ORGANIZATIONS } from 'models/Filter'; +import { AppContext } from '../../context/AppContext'; import FilterIcon from '@material-ui/icons/FilterList'; import Autocomplete from '@material-ui/lab/Autocomplete'; import FilterModel from '../../models/FilterStakeholder'; -import { AppContext } from '../../context/AppContext'; import { StakeholdersContext } from '../../context/StakeholdersContext'; import { localeSort } from '../../common/utils'; -import { ALL_ORGANIZATIONS } from 'models/Filter'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles({ root: { diff --git a/src/components/Stakeholders/FilterBar.js b/src/components/Stakeholders/FilterBar.js index 22de51877..6cad5e252 100644 --- a/src/components/Stakeholders/FilterBar.js +++ b/src/components/Stakeholders/FilterBar.js @@ -1,7 +1,7 @@ +import Add from './Add'; import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; import StakeholderFilter from './Filter'; -import Add from './Add'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles({ flex: { diff --git a/src/views/StakeholdersView.js b/src/views/StakeholdersView.js index 641b64946..30b4553a9 100644 --- a/src/views/StakeholdersView.js +++ b/src/views/StakeholdersView.js @@ -1,10 +1,11 @@ -import React from 'react'; -import { GrowerProvider } from '../context/GrowerContext'; -import { StakeholdersProvider } from '../context/StakeholdersContext'; -import StakeholderTable from '../components/Stakeholders/Table'; +import { Container, Grid } from '@material-ui/core'; + import FilterBar from '../components/Stakeholders/FilterBar'; +import { GrowerProvider } from '../context/GrowerContext'; import Navbar from '../components/Navbar'; -import { Grid, Container } from '@material-ui/core'; +import React from 'react'; +import StakeholderTable from '../components/Stakeholders/Table'; +import { StakeholdersProvider } from '../context/StakeholdersContext'; export default function Stakeholders() { return ( From d9da2458a8a432221bbaf0208007dad5dc5ff412 Mon Sep 17 00:00:00 2001 From: Krithin Jay Pakshootra Date: Mon, 23 Jan 2023 11:29:11 +0800 Subject: [PATCH 09/32] fix: multiple errors in code after merge --- src/components/CaptureDetailDialog.js | 6 +++++- src/components/CaptureFilter.js | 1 - src/components/CaptureMatching/CaptureMatchingView.js | 1 - src/components/FilterTop.js | 11 +++++------ src/components/Verify.js | 1 + .../common/CustomTableFilter/CustomTableFilter.js | 5 ++++- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/CaptureDetailDialog.js b/src/components/CaptureDetailDialog.js index 111b9b12f..5b92033a0 100644 --- a/src/components/CaptureDetailDialog.js +++ b/src/components/CaptureDetailDialog.js @@ -12,14 +12,17 @@ import { Link, Typography, } from '@material-ui/core'; +import LinkToWebmap, { pathType } from './common/LinkToWebmap'; +import { POLICIES, hasPermission } from 'models/auth'; import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { captureStatus, verificationStates } from '../common/variables'; +import { AppContext } from 'context/AppContext'; import { CaptureDetailContext } from '../context/CaptureDetailContext'; import Close from '@material-ui/icons/Close'; import { CopyButton } from './common/CopyButton'; import CopyNotification from './common/CopyNotification'; import Country from './common/Country'; -import LinkToWebmap from './common/LinkToWebmap'; import OptimizedImage from './OptimizedImage'; import Skeleton from '@material-ui/lab/Skeleton'; import { captureStatus } from '../common/variables'; @@ -28,6 +31,7 @@ import { AppContext } from '../context/AppContext'; import theme from './common/theme'; import log from 'loglevel'; import { makeStyles } from '@material-ui/core/styles'; +import theme from './common/theme'; const useStyles = makeStyles((theme) => ({ chipRoot: { diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 4ad2b72a0..063e06305 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -1,4 +1,3 @@ -import React, { useState, useContext } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index 329f387fc..459a0681c 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -1,4 +1,3 @@ -import React, { useState, useEffect, useContext, useMemo } from 'react'; import { getDistance } from 'geolib'; import { makeStyles } from '@material-ui/core/styles'; diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 769512128..7692fa1d5 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -11,16 +11,15 @@ import { } from '@material-ui/pickers'; import React, { useContext, useEffect, useState } from 'react'; import { - convertDateToDefaultSqlDate, - getDateFormatLocale, - getDatePickerLocale, -} from '../common/locale'; -import { - verificationStates, captureStatus, datePickerDefaultMinDate, verificationStates, } from '../common/variables'; +import { + convertDateToDefaultSqlDate, + getDateFormatLocale, + getDatePickerLocale, +} from '../common/locale'; // import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; diff --git a/src/components/Verify.js b/src/components/Verify.js index 9d57d990b..18a5ca629 100644 --- a/src/components/Verify.js +++ b/src/components/Verify.js @@ -36,6 +36,7 @@ import clsx from 'clsx'; import { countToLocaleString } from 'common/numbers'; import { documentTitle } from 'common/variables'; import { makeStyles } from '@material-ui/core/styles'; +import { pathType } from './common/LinkToWebmap'; const log = require('loglevel').getLogger('components/Verify'); const EMPTY_ARRAY = new Array(16).fill(); diff --git a/src/components/common/CustomTableFilter/CustomTableFilter.js b/src/components/common/CustomTableFilter/CustomTableFilter.js index ab673020f..5f8156da3 100644 --- a/src/components/common/CustomTableFilter/CustomTableFilter.js +++ b/src/components/common/CustomTableFilter/CustomTableFilter.js @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { ALL_ORGANIZATIONS } from '../../../models/Filter'; import { AppContext } from '../../../context/AppContext'; @@ -34,6 +34,9 @@ const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; */ function CustomTableFilter(props) { // console.warn('orgList', orgList); + const url = new URL(window.location.href); + const params = new URLSearchParams(url.search); + const initialFilter = { organization_id: url.searchParams.get('organization_id') || '', grower: url.searchParams.get('grower') || '', From 76c92d164cb49d6524e8cf73494619ea8b217234 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 29 Jan 2023 17:34:02 +0000 Subject: [PATCH 10/32] chore: revert import reordering to reduce merge complexity --- src/api/treeTrackerApi.js | 2 +- src/common/locale.js | 4 +- src/components/CaptureDetailDialog.js | 32 ++++++----- src/components/CaptureFilter.js | 16 +++--- .../CaptureMatching/CaptureMatchingView.js | 53 +++++++++---------- src/components/FilterTop.js | 35 ++++++------ src/components/FilterTopGrower.js | 10 ++-- src/components/GrowerFilterHeader.js | 11 ++-- src/components/Stakeholders/Filter.js | 15 +++--- src/components/Stakeholders/FilterBar.js | 4 +- src/components/Verify.js | 46 ++++++++-------- .../CustomTableFilter/CustomTableFilter.js | 26 ++++----- 12 files changed, 123 insertions(+), 131 deletions(-) diff --git a/src/api/treeTrackerApi.js b/src/api/treeTrackerApi.js index 83cf76882..05fce190e 100644 --- a/src/api/treeTrackerApi.js +++ b/src/api/treeTrackerApi.js @@ -5,8 +5,8 @@ import { makeQueryString, } from './apiUtils'; import { getVerificationStatus } from '../common/utils'; -import log from 'loglevel'; import { session } from '../models/auth'; +import log from 'loglevel'; // Set API as a variable const API_ROOT = process.env.REACT_APP_API_ROOT; diff --git a/src/common/locale.js b/src/common/locale.js index 92cb56736..cffb3a476 100644 --- a/src/common/locale.js +++ b/src/common/locale.js @@ -1,7 +1,5 @@ -import { format, formatDistanceToNow, getYear } from 'date-fns'; - import enLocale from 'date-fns/locale/en-US'; - +import { format, formatDistanceToNow, getYear } from 'date-fns'; // Per default set to EN var localeLanguage = 'en'; // default date pattern when converting dates diff --git a/src/components/CaptureDetailDialog.js b/src/components/CaptureDetailDialog.js index 5b92033a0..58fc56c59 100644 --- a/src/components/CaptureDetailDialog.js +++ b/src/components/CaptureDetailDialog.js @@ -1,29 +1,28 @@ +import React, { useState, useEffect, useContext, useMemo } from 'react'; + +import { makeStyles } from '@material-ui/core/styles'; import { - Box, - Button, - Chip, - CircularProgress, - Container, + Typography, Dialog, - Divider, - Drawer, Grid, + Divider, + Chip, IconButton, + Drawer, + Box, Link, - Typography, + CircularProgress, + Container, + Button, } from '@material-ui/core'; +import Close from '@material-ui/icons/Close'; +import OptimizedImage from './OptimizedImage'; import LinkToWebmap, { pathType } from './common/LinkToWebmap'; -import { POLICIES, hasPermission } from 'models/auth'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; -import { captureStatus, verificationStates } from '../common/variables'; - -import { AppContext } from 'context/AppContext'; +import { verificationStates } from '../common/variables'; import { CaptureDetailContext } from '../context/CaptureDetailContext'; -import Close from '@material-ui/icons/Close'; -import { CopyButton } from './common/CopyButton'; import CopyNotification from './common/CopyNotification'; +import { CopyButton } from './common/CopyButton'; import Country from './common/Country'; -import OptimizedImage from './OptimizedImage'; import Skeleton from '@material-ui/lab/Skeleton'; import { captureStatus } from '../common/variables'; import { hasPermission, POLICIES } from '../models/auth'; @@ -31,7 +30,6 @@ import { AppContext } from '../context/AppContext'; import theme from './common/theme'; import log from 'loglevel'; import { makeStyles } from '@material-ui/core/styles'; -import theme from './common/theme'; const useStyles = makeStyles((theme) => ({ chipRoot: { diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 063e06305..2ca88b422 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -1,3 +1,4 @@ +import React, { useEffect, useState, useContext } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; @@ -6,27 +7,27 @@ import MenuItem from '@material-ui/core/MenuItem'; import Autocomplete from '@material-ui/lab/Autocomplete'; import SelectOrg from './common/SelectOrg'; import FilterModel, { - ALL_ORGANIZATIONS, ALL_SPECIES, SPECIES_ANY_SET, SPECIES_NOT_SET, ALL_TAGS, TAG_NOT_SET, ANY_TAG_SET, + ALL_ORGANIZATIONS, } from '../models/Filter'; +import DateFnsUtils from '@date-io/date-fns'; import { - KeyboardDatePicker, MuiPickersUtilsProvider, + KeyboardDatePicker, } from '@material-ui/pickers'; -import React, { useContext, useEffect, useState } from 'react'; import { - convertDateToDefaultSqlDate, - getDateFormatLocale, getDatePickerLocale, + getDateFormatLocale, + convertDateToDefaultSqlDate, } from '../common/locale'; import { - datePickerDefaultMinDate, tokenizationStates, + datePickerDefaultMinDate, } from '../common/variables'; import Autocomplete from '@material-ui/lab/Autocomplete'; @@ -38,8 +39,7 @@ import MenuItem from '@material-ui/core/MenuItem'; import SelectOrg from './common/SelectOrg'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; -import TextField from '@material-ui/core/TextField'; -import { withStyles } from '@material-ui/core/styles'; +import { CircularProgress } from '@material-ui/core'; export const FILTER_WIDTH = 330; diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index 459a0681c..a97d7c6f5 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -1,50 +1,49 @@ +import React, { useState, useEffect, useContext, useMemo } from 'react'; import { getDistance } from 'geolib'; import { makeStyles } from '@material-ui/core/styles'; import { - AppBar, - Avatar, - Box, Button, + AppBar, Chip, Divider, - Drawer, - FormControl, Grid, - IconButton, - LinearProgress, + Box, Paper, + LinearProgress, + Drawer, + Typography, + FormControl, TextField, + Avatar, Tooltip, - Typography, + IconButton, } from '@material-ui/core'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; - -import AccessTimeIcon from '@material-ui/icons/AccessTime'; -import { AppContext } from 'context/AppContext'; -import CandidateImages from './CandidateImages'; -import CaptureDetailDialog from 'components/CaptureDetailDialog'; -import { CaptureDetailProvider } from 'context/CaptureDetailContext'; +import NatureOutlinedIcon from '@material-ui/icons/NatureOutlined'; import CloseIcon from '@material-ui/icons/Close'; -import Country from '../common/Country'; import FilterListIcon from '@material-ui/icons/FilterList'; -import GrowerDetail from 'components/GrowerDetail'; -import { GrowerProvider } from 'context/GrowerContext'; +import AccessTimeIcon from '@material-ui/icons/AccessTime'; import LocationOnOutlinedIcon from '@material-ui/icons/LocationOnOutlined'; +import SkipNextIcon from '@material-ui/icons/SkipNext'; +import QuestionMarkIcon from '@material-ui/icons/HelpOutlineOutlined'; +import PhotoCameraOutlinedIcon from '@material-ui/icons/PhotoCameraOutlined'; +import Pagination from '@material-ui/lab/Pagination'; + +import { documentTitle } from 'common/variables'; +import { getDateTimeStringLocale } from 'common/locale'; +import { AppContext } from 'context/AppContext'; import { MatchingToolContext } from 'context/MatchingToolContext'; -import NatureOutlinedIcon from '@material-ui/icons/NatureOutlined'; -import Navbar from '../Navbar'; +import { CaptureDetailProvider } from 'context/CaptureDetailContext'; +import { GrowerProvider } from 'context/GrowerContext'; +import CaptureDetailDialog from 'components/CaptureDetailDialog'; import OptimizedImage from 'components/OptimizedImage'; -import Pagination from '@material-ui/lab/Pagination'; -import PhotoCameraOutlinedIcon from '@material-ui/icons/PhotoCameraOutlined'; -import QuestionMarkIcon from '@material-ui/icons/HelpOutlineOutlined'; +import GrowerDetail from 'components/GrowerDetail'; +import Country from '../common/Country'; import SelectOrg from '../common/SelectOrg'; -import SkipNextIcon from '@material-ui/icons/SkipNext'; +import CandidateImages from './CandidateImages'; +import Navbar from '../Navbar'; import api from 'api/treeTrackerApi'; -import { documentTitle } from 'common/variables'; -import { getDateTimeStringLocale } from 'common/locale'; import log from 'loglevel'; -import { makeStyles } from '@material-ui/core/styles'; const useStyle = makeStyles((theme) => ({ container: { diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 7692fa1d5..951e19625 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -1,32 +1,31 @@ -import { Button, Grid, MenuItem, TextField } from '@material-ui/core'; +import React, { useState, useContext, useEffect } from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import { Grid, Button, TextField, MenuItem } from '@material-ui/core'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import SelectOrg from './common/SelectOrg'; import FilterModel, { - ALL_ORGANIZATIONS, ALL_SPECIES, - ANY_TAG_SET, + // SPECIES_ANY_SET, + // SPECIES_NOT_SET, + ALL_ORGANIZATIONS, TAG_NOT_SET, + ANY_TAG_SET, } from '../models/Filter'; +import DateFnsUtils from '@date-io/date-fns'; import { - KeyboardDatePicker, MuiPickersUtilsProvider, + KeyboardDatePicker, } from '@material-ui/pickers'; -import React, { useContext, useEffect, useState } from 'react'; import { + getDatePickerLocale, + getDateFormatLocale, + convertDateToDefaultSqlDate, +} from '../common/locale'; +import { + verificationStates, captureStatus, datePickerDefaultMinDate, - verificationStates, } from '../common/variables'; -import { - convertDateToDefaultSqlDate, - getDateFormatLocale, - getDatePickerLocale, -} from '../common/locale'; - -// import { SpeciesContext } from '../context/SpeciesContext'; -import { TagsContext } from '../context/TagsContext'; -import { getVerificationStatus } from '../common/utils'; -import { withStyles } from '@material-ui/core/styles'; - -// import { CircularProgress } from '@material-ui/core'; export const FILTER_WIDTH = 330; diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index 92b4588a4..e46c9a8de 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -1,11 +1,11 @@ import React, { useEffect, useState } from 'react'; - -import Button from '@material-ui/core/Button'; -import FilterModel, { ALL_ORGANIZATIONS, FILTER_FIELDS } from '../models/FilterGrower'; +import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; -import SelectOrg from './common/SelectOrg'; +import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; -import { withStyles } from '@material-ui/core/styles'; +import FilterModel from '../models/FilterGrower'; +import SelectOrg from './common/SelectOrg'; +import { ALL_ORGANIZATIONS } from '../models/Filter'; export const FILTER_WIDTH = 330; diff --git a/src/components/GrowerFilterHeader.js b/src/components/GrowerFilterHeader.js index 59bd1ac79..8ac0f6d8d 100644 --- a/src/components/GrowerFilterHeader.js +++ b/src/components/GrowerFilterHeader.js @@ -1,12 +1,11 @@ -import { Button, Grid } from '@material-ui/core'; -import React, { useContext, useState } from 'react'; - +import React, { useState, useContext } from 'react'; +import { Grid, Button } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import Avatar from '@material-ui/core/Avatar'; -import FilterTopGrower from './FilterTopGrower'; -import { GrowerContext } from '../context/GrowerContext'; import IconFilter from '@material-ui/icons/FilterList'; +import { GrowerContext } from '../context/GrowerContext'; import Navbar from './Navbar'; -import { makeStyles } from '@material-ui/core/styles'; +import FilterTopGrower from './FilterTopGrower'; const useStyle = makeStyles((theme) => ({ activeFilters: { diff --git a/src/components/Stakeholders/Filter.js b/src/components/Stakeholders/Filter.js index 79f43e9ab..c6843b684 100644 --- a/src/components/Stakeholders/Filter.js +++ b/src/components/Stakeholders/Filter.js @@ -1,23 +1,22 @@ +import React, { useState, useContext, useEffect } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; import { Button, Dialog, - DialogActions, - DialogContent, DialogTitle, + DialogContent, + DialogActions, + TextField, FormControl, MenuItem, - TextField, } from '@material-ui/core'; -import React, { useContext, useEffect, useState } from 'react'; - -import { ALL_ORGANIZATIONS } from 'models/Filter'; -import { AppContext } from '../../context/AppContext'; import FilterIcon from '@material-ui/icons/FilterList'; import Autocomplete from '@material-ui/lab/Autocomplete'; import FilterModel from '../../models/FilterStakeholder'; +import { AppContext } from '../../context/AppContext'; import { StakeholdersContext } from '../../context/StakeholdersContext'; import { localeSort } from '../../common/utils'; -import { makeStyles } from '@material-ui/core/styles'; +import { ALL_ORGANIZATIONS } from 'models/Filter'; const useStyles = makeStyles({ root: { diff --git a/src/components/Stakeholders/FilterBar.js b/src/components/Stakeholders/FilterBar.js index 6cad5e252..22de51877 100644 --- a/src/components/Stakeholders/FilterBar.js +++ b/src/components/Stakeholders/FilterBar.js @@ -1,7 +1,7 @@ -import Add from './Add'; import React from 'react'; -import StakeholderFilter from './Filter'; import { makeStyles } from '@material-ui/core/styles'; +import StakeholderFilter from './Filter'; +import Add from './Add'; const useStyles = makeStyles({ flex: { diff --git a/src/components/Verify.js b/src/components/Verify.js index 18a5ca629..3e35afbc3 100644 --- a/src/components/Verify.js +++ b/src/components/Verify.js @@ -1,41 +1,41 @@ +import React, { useEffect, useState, useContext, useRef } from 'react'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; import { - AppBar, - Avatar, - Box, - Button, + Typography, Card, - Divider, + Button, // replace with icons down the line Grid, - IconButton, LinearProgress, - Paper, + IconButton, Snackbar, + Avatar, + Paper, + Box, TablePagination, - Typography, + Divider, + AppBar, } from '@material-ui/core'; -import { Image, LocationOn, Map, Nature, Person } from '@material-ui/icons'; -import React, { useContext, useEffect, useRef, useState } from 'react'; import { Skeleton, ToggleButton, ToggleButtonGroup } from '@material-ui/lab'; -import CaptureDetailDialog from './CaptureDetailDialog'; -import { CaptureDetailProvider } from 'context/CaptureDetailContext'; -import CaptureDetailTooltip from './CaptureDetailTooltip'; -import CheckIcon from '@material-ui/icons/Check'; -import FilterTop from './FilterTop'; -import GrowerDetail from './GrowerDetail'; import IconFilter from '@material-ui/icons/FilterList'; +import CheckIcon from '@material-ui/icons/Check'; +import { LocationOn, Person, Nature, Map, Image } from '@material-ui/icons'; import Navbar from './Navbar'; -import OptimizedImage from './OptimizedImage'; +import GrowerDetail from './GrowerDetail'; // import CaptureTags from './CaptureTags'; import SidePanel from './SidePanel'; -import { SpeciesContext } from 'context/SpeciesContext'; +import FilterTop from './FilterTop'; +import CaptureDetailDialog from './CaptureDetailDialog'; +import OptimizedImage from './OptimizedImage'; +import CaptureDetailTooltip from './CaptureDetailTooltip'; import Spinner from './common/Spinner'; -import { TagsContext } from 'context/TagsContext'; -import { VerifyContext } from 'context/VerifyContext'; -import clsx from 'clsx'; -import { countToLocaleString } from 'common/numbers'; import { documentTitle } from 'common/variables'; -import { makeStyles } from '@material-ui/core/styles'; +import { countToLocaleString } from 'common/numbers'; +import { VerifyContext } from 'context/VerifyContext'; +import { SpeciesContext } from 'context/SpeciesContext'; +import { TagsContext } from 'context/TagsContext'; +import { CaptureDetailProvider } from 'context/CaptureDetailContext'; import { pathType } from './common/LinkToWebmap'; const log = require('loglevel').getLogger('components/Verify'); diff --git a/src/components/common/CustomTableFilter/CustomTableFilter.js b/src/components/common/CustomTableFilter/CustomTableFilter.js index 5f8156da3..881d0e1ac 100644 --- a/src/components/common/CustomTableFilter/CustomTableFilter.js +++ b/src/components/common/CustomTableFilter/CustomTableFilter.js @@ -1,21 +1,21 @@ -import React, { useContext, useEffect, useState } from 'react'; - -import { ALL_ORGANIZATIONS } from '../../../models/Filter'; -import { AppContext } from '../../../context/AppContext'; -import Button from '@material-ui/core/Button'; -import CloseIcon from '@material-ui/icons/Close'; -import Divider from '@material-ui/core/Divider'; -import Drawer from '@material-ui/core/Drawer'; -import FormControl from '@material-ui/core/FormControl'; +import React, { useState, useContext } from 'react'; +import PropTypes from 'prop-types'; import Grid from '@material-ui/core/Grid'; +import Drawer from '@material-ui/core/Drawer'; +import TextField from '@material-ui/core/TextField'; +import Select from '@material-ui/core/Select'; import InputLabel from '@material-ui/core/InputLabel'; import MenuItem from '@material-ui/core/MenuItem'; -import PropTypes from 'prop-types'; -import Select from '@material-ui/core/Select'; -import SelectOrg from '../SelectOrg'; -import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import CloseIcon from '@material-ui/icons/Close'; +import FormControl from '@material-ui/core/FormControl'; + +import SelectOrg from '../SelectOrg'; import useStyles from './CustomTableFilter.styles'; +import { AppContext } from '../../../context/AppContext'; +import { ALL_ORGANIZATIONS } from '../../../models/Filter'; const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; From a92442d365ccc48e96a4e4db4d204e18eba46279 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 29 Jan 2023 17:36:39 +0000 Subject: [PATCH 11/32] chore: revert import reordering to reduce merge complexity --- src/context/VerifyContext.js | 5 ----- src/views/StakeholdersView.js | 11 +++++------ src/views/VerifyView.js | 7 +++---- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index daf69ac38..60dbe5517 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -6,11 +6,6 @@ import { captureStatus } from 'common/variables'; import { AppContext } from './AppContext.js'; import { setOrganizationFilter } from '../common/utils'; -import React, { createContext, useEffect, useState } from 'react'; - -import FilterModel from '../models/Filter'; -import api from '../api/treeTrackerApi'; - const log = loglevel.getLogger('../context/VerifyContext'); export const VerifyContext = createContext({ diff --git a/src/views/StakeholdersView.js b/src/views/StakeholdersView.js index 30b4553a9..641b64946 100644 --- a/src/views/StakeholdersView.js +++ b/src/views/StakeholdersView.js @@ -1,11 +1,10 @@ -import { Container, Grid } from '@material-ui/core'; - -import FilterBar from '../components/Stakeholders/FilterBar'; -import { GrowerProvider } from '../context/GrowerContext'; -import Navbar from '../components/Navbar'; import React from 'react'; -import StakeholderTable from '../components/Stakeholders/Table'; +import { GrowerProvider } from '../context/GrowerContext'; import { StakeholdersProvider } from '../context/StakeholdersContext'; +import StakeholderTable from '../components/Stakeholders/Table'; +import FilterBar from '../components/Stakeholders/FilterBar'; +import Navbar from '../components/Navbar'; +import { Grid, Container } from '@material-ui/core'; export default function Stakeholders() { return ( diff --git a/src/views/VerifyView.js b/src/views/VerifyView.js index dd3fbb17c..1df12c593 100644 --- a/src/views/VerifyView.js +++ b/src/views/VerifyView.js @@ -1,10 +1,9 @@ import React, { useEffect } from 'react'; - -import { SpeciesProvider } from '../context/SpeciesContext'; -import { TagsProvider } from '../context/TagsContext'; +import { documentTitle } from '../common/variables'; import Verify from '../components/Verify'; import { VerifyProvider } from '../context/VerifyContext'; -import { documentTitle } from '../common/variables'; +import { SpeciesProvider } from '../context/SpeciesContext'; +import { TagsProvider } from '../context/TagsContext'; function VerifyView() { useEffect(() => { From 1db7230c03e3b79b837b77bd039c5f9e2c6f86f3 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 29 Jan 2023 17:42:52 +0000 Subject: [PATCH 12/32] refactor: consolidate duplicate handleQuerySearchParams implementations --- src/common/utils.js | 15 +++++++++++++++ src/components/CaptureFilter.js | 16 +--------------- .../CaptureMatching/CaptureMatchingView.js | 17 +---------------- src/components/FilterTop.js | 16 +--------------- src/components/FilterTopGrower.js | 18 ++---------------- .../CustomTableFilter/CustomTableFilter.js | 16 +--------------- 6 files changed, 21 insertions(+), 77 deletions(-) diff --git a/src/common/utils.js b/src/common/utils.js index c2f5f43b3..f2b4e06bf 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -56,3 +56,18 @@ export const localeSort = (arr, order) => { return sortVal * orderVal; }); }; + +export const handleQuerySearchParams = (name, value) => { + if (params.has(name) && value == '') { + url.searchParams.delete(name); + window.history.pushState({}, '', url.search); + } else if (!params.has(name) && value == '') { + return; + } else if (!params.get(name)) { + url.searchParams.append(name, value); + window.history.pushState({}, '', url.search); + } else if (params.get(name)) { + url.searchParams.set(name, value); + window.history.pushState({}, '', url.search); + } +}; diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 2ca88b422..fd61db005 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -40,6 +40,7 @@ import SelectOrg from './common/SelectOrg'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; import { CircularProgress } from '@material-ui/core'; +import { handleQuerySearchParams } from '../common/utils'; export const FILTER_WIDTH = 330; @@ -218,21 +219,6 @@ function Filter(props) { props.onSubmit && props.onSubmit(filter); } - const handleQuerySearchParams = (name, value) => { - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); - window.history.pushState({}, '', url.search); - } - }; - return ( <> { diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index a97d7c6f5..aa0ffa903 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -44,6 +44,7 @@ import CandidateImages from './CandidateImages'; import Navbar from '../Navbar'; import api from 'api/treeTrackerApi'; import log from 'loglevel'; +import { handleQuerySearchParams } from '../common/utils'; const useStyle = makeStyles((theme) => ({ container: { @@ -485,22 +486,6 @@ function CaptureMatchingView() { setEndDate(e.target.value); } - const handleQuerySearchParams = (name, value) => { - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - console.log(url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); - window.history.pushState({}, '', url.search); - } - }; - function handleFilterSubmit() { // log.debug('filter submit -----> ', organizationId, stakeholderUUID); handleQuerySearchParams('startDate', startDate); diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 951e19625..2ae032390 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -26,6 +26,7 @@ import { captureStatus, datePickerDefaultMinDate, } from '../common/variables'; +import { handleQuerySearchParams } from '../common/utils'; export const FILTER_WIDTH = 330; @@ -242,21 +243,6 @@ function Filter(props) { props.onSubmit && props.onSubmit(filter); } - const handleQuerySearchParams = (name, value) => { - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); - window.history.pushState({}, '', url.search); - } - }; - return ( <> { - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); - window.history.pushState({}, '', url.search); - } - }; - - handleAllQuerySearchParams() { + const handleAllQuerySearchParams = () => { handleQuerySearchParams(FILTER_FIELDS.id, id); handleQuerySearchParams(FILTER_FIELDS.personId, personId); handleQuerySearchParams(FILTER_FIELDS.firstName, firstName); diff --git a/src/components/common/CustomTableFilter/CustomTableFilter.js b/src/components/common/CustomTableFilter/CustomTableFilter.js index 881d0e1ac..9abdbcaf4 100644 --- a/src/components/common/CustomTableFilter/CustomTableFilter.js +++ b/src/components/common/CustomTableFilter/CustomTableFilter.js @@ -16,6 +16,7 @@ import SelectOrg from '../SelectOrg'; import useStyles from './CustomTableFilter.styles'; import { AppContext } from '../../../context/AppContext'; import { ALL_ORGANIZATIONS } from '../../../models/Filter'; +import { handleQuerySearchParams } from '../common/utils'; const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; @@ -91,21 +92,6 @@ function CustomTableFilter(props) { } }, []); - const handleQuerySearchParams = (name, value) => { - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); - window.history.pushState({}, '', url.search); - } - }; - const mapFiltersToQueries = (filter) => { console.log(filter); for (var key in filter) { From aa97c722dc68c52f0d179b4c371b372921e80499 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 29 Jan 2023 17:44:30 +0000 Subject: [PATCH 13/32] fix: give handleQuerySearchParams access to current params --- src/common/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/utils.js b/src/common/utils.js index f2b4e06bf..1f2823200 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -58,6 +58,7 @@ export const localeSort = (arr, order) => { }; export const handleQuerySearchParams = (name, value) => { + const params = new URLSearchParams(url.search); if (params.has(name) && value == '') { url.searchParams.delete(name); window.history.pushState({}, '', url.search); From d5841d739c2f83d3e4074e60bcb7731b8c7f0054 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 29 Jan 2023 17:45:25 +0000 Subject: [PATCH 14/32] fix: add missing url var --- src/common/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/utils.js b/src/common/utils.js index 1f2823200..a2a52264c 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -58,6 +58,7 @@ export const localeSort = (arr, order) => { }; export const handleQuerySearchParams = (name, value) => { + const url = new URL(window.location.href); const params = new URLSearchParams(url.search); if (params.has(name) && value == '') { url.searchParams.delete(name); From 6d855311140c92fb474b32c5c45f1d97d81d84c9 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 08:51:16 +0000 Subject: [PATCH 15/32] feat: move searchParams handling to context components --- package-lock.json | 5 - src/common/utils.js | 36 +- src/components/CaptureFilter.js | 92 +-- .../CaptureMatching/CaptureMatchingView.js | 3 +- src/components/FilterTop.js | 545 ++++++++---------- src/components/FilterTopGrower.js | 67 +-- src/components/PrivateRoute.js | 8 +- src/components/Routers.js | 4 +- .../CustomTableFilter/CustomTableFilter.js | 56 +- src/context/GrowerContext.js | 50 +- src/views/GrowersView.js | 4 +- src/views/VerifyView.js | 4 +- 12 files changed, 338 insertions(+), 536 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5453b92fd..a129771f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43334,11 +43334,6 @@ } } }, - "caniuse-lite": { - "version": "1.0.30001359", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001359.tgz", - "integrity": "sha512-Xln/BAsPzEuiVLgJ2/45IaqD9jShtk3Y33anKb4+yLwQzws3+v6odKfpgES/cDEaZMLzSChpIGdbOYtH9MyuHw==" - }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", diff --git a/src/common/utils.js b/src/common/utils.js index a2a52264c..3f80181b1 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -57,19 +57,29 @@ export const localeSort = (arr, order) => { }); }; -export const handleQuerySearchParams = (name, value) => { - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - if (params.has(name) && value == '') { - url.searchParams.delete(name); - window.history.pushState({}, '', url.search); - } else if (!params.has(name) && value == '') { - return; - } else if (!params.get(name)) { - url.searchParams.append(name, value); - window.history.pushState({}, '', url.search); - } else if (params.get(name)) { - url.searchParams.set(name, value); +export const handleQuerySearchParams = (newParams) => { + let changed = false; + let url = new URL(window.location.href); + const oldParams = url.searchParams; + Object.keys(newParams).forEach((key) => { + const value = newParams[key]?.toString(); + if (typeof value === 'function') { + return; + } + if (oldParams.has(key) && (value === undefined || value === '')) { + url.searchParams.delete(key); + changed = true; + } else if (!oldParams.has(key) && (value === undefined || value === '')) { + return; + } else if (!oldParams.get(key)) { + url.searchParams.append(key, value); + changed = true; + } else if (oldParams.get(key) && oldParams.get(key) !== value) { + url.searchParams.set(key, value); + changed = true; + } + }); + if (changed) { window.history.pushState({}, '', url.search); } }; diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index fd61db005..05c81d028 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useContext } from 'react'; +import React, { useState, useContext } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; @@ -40,7 +40,6 @@ import SelectOrg from './common/SelectOrg'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; import { CircularProgress } from '@material-ui/core'; -import { handleQuerySearchParams } from '../common/utils'; export const FILTER_WIDTH = 330; @@ -81,77 +80,32 @@ const styles = (theme) => { }; }; -const stringToDate = (string) => { - if (!string) { - return false; - } else { - return new Date( - parseInt(string.substring(0, 4)), - parseInt(string.substring(5, 7)) - 1, - parseInt(string.substring(8, 10)) - ); - } -}; - function Filter(props) { - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - const speciesContext = useContext(SpeciesContext); const tagsContext = useContext(TagsContext); const { classes, filter } = props; const filterOptionAll = 'All'; - const dateStartDefault = null; - const dateEndDefault = null; - - const [uuid, setUUID] = useState( - url.searchParams.get('uuid') || filter?.uuid || '' - ); - const [captureId, setCaptureId] = useState( - url.searchParams.get('captureId') || filter?.captureId || '' - ); - const [growerId, setGrowerId] = useState( - url.searchParams.get('growerId') || filter?.planterId || '' - ); - const [deviceId, setDeviceId] = useState( - url.searchParams.get('deviceId') || filter?.deviceIdentifier || '' - ); - const [growerIdentifier, setGrowerIdentifier] = useState( - url.searchParams.get('growerIdentifier') || filter?.planterIdentifier || '' + const startDateDefault = null; + const endDateDefault = null; + const [uuid, setUUID] = useState(filter?.uuid || ''); + const [captureId, setCaptureId] = useState(filter?.captureId || ''); + const [wallet, setWallet] = useState(filter?.wallet || ''); + const [growerId, setGrowerId] = useState(filter?.grower_account_id || ''); + const [deviceId, setDeviceId] = useState(filter?.device_identifier || ''); + const [startDate, setStartDate] = useState( + filter?.startDate || startDateDefault ); const [endDate, setEndDate] = useState(filter?.endDate || endDateDefault); const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); const [tag, setTag] = useState(null); const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - url.searchParams.get('organizationId') || - filter.organizationId || - ALL_ORGANIZATIONS - ); - const [tokenId, setTokenId] = useState( - url.searchParams.get('tokenId') || filter?.tokenId || filterOptionAll + filter.organizationId || ALL_ORGANIZATIONS ); + const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); - useEffect(() => { - const filter = new FilterModel(); - filter.uuid = uuid; - filter.captureId = captureId; - filter.planterId = growerId; - filter.deviceIdentifier = deviceId; - filter.planterIdentifier = growerIdentifier; - filter.dateStart = dateStart ? formatDate(dateStart) : undefined; - filter.dateEnd = dateEnd ? formatDate(dateEnd) : undefined; - filter.speciesId = speciesId; - filter.tagId = tag ? tag.id : 0; - filter.organizationId = organizationId; - filter.stakeholderUUID = stakeholderUUID; - filter.tokenId = tokenId; - filter.verifyStatus = verifyStatus; - props.onSubmit && props.onSubmit(filter); - }, []); - - const handleDateStartChange = (date) => { - setDateStart(date); + const handleStartDateChange = (date) => { + setStartDate(date); }; const handleEndDateChange = (date) => { @@ -181,24 +135,6 @@ function Filter(props) { const filter = new FilterModel(test); props.onSubmit && props.onSubmit(filter); - - handleQuerySearchParams('uuid', uuid); - handleQuerySearchParams('captureId', captureId); - handleQuerySearchParams('deviceId', deviceId); - handleQuerySearchParams('growerIdentifier', growerIdentifier); - handleQuerySearchParams('growerId', growerId); - handleQuerySearchParams( - 'dateStart', - dateStart ? formatDate(dateStart) : '' - ); - handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); - handleQuerySearchParams('speciesId', speciesId); - handleQuerySearchParams('growerIdentifier', growerIdentifier); - handleQuerySearchParams('tag', tag); - handleQuerySearchParams('tagSearchString', tagSearchString); - handleQuerySearchParams('organizationId', organizationId); - handleQuerySearchParams('stakeholderUUID', stakeholderUUID); - handleQuerySearchParams('tokenId', tokenId); } function handleReset() { diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index aa0ffa903..0c072dd4e 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -44,7 +44,7 @@ import CandidateImages from './CandidateImages'; import Navbar from '../Navbar'; import api from 'api/treeTrackerApi'; import log from 'loglevel'; -import { handleQuerySearchParams } from '../common/utils'; +import { handleQuerySearchParams } from '../../common/utils'; const useStyle = makeStyles((theme) => ({ container: { @@ -312,7 +312,6 @@ function CaptureMatchingView() { stakeholderUUID: null, }; const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); const classes = useStyle(); const appContext = useContext(AppContext); diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 2ae032390..d4603fca2 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -1,4 +1,4 @@ -import React, { useState, useContext, useEffect } from 'react'; +import React, { useState, useContext } from 'react'; import { withStyles } from '@material-ui/core/styles'; import { Grid, Button, TextField, MenuItem } from '@material-ui/core'; import Autocomplete from '@material-ui/lab/Autocomplete'; @@ -26,21 +26,12 @@ import { captureStatus, datePickerDefaultMinDate, } from '../common/variables'; -import { handleQuerySearchParams } from '../common/utils'; -export const FILTER_WIDTH = 330; +// import { SpeciesContext } from '../context/SpeciesContext'; +import { TagsContext } from '../context/TagsContext'; +// import { CircularProgress } from '@material-ui/core'; -const stringToDate = (string) => { - if (!string) { - return false; - } else { - return new Date( - parseInt(string.substring(0, 4)), - parseInt(string.substring(5, 7)) - 1, - parseInt(string.substring(8, 10)) - ); - } -}; +export const FILTER_WIDTH = 330; const styles = (theme) => { return { @@ -81,90 +72,30 @@ const styles = (theme) => { function Filter(props) { // const speciesContext = useContext(SpeciesContext); - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); const tagsContext = useContext(TagsContext); const { classes, filter } = props; const filterOptionAll = 'All'; - const dateStartDefault = null; - const dateEndDefault = null; - const [uuid, setUUID] = useState( - url.searchParams.get('uuid') || filter?.uuid || '' - ); - const [captureId, setCaptureId] = useState( - url.searchParams.get('captureId') || filter?.captureId || '' - ); - const [growerId, setGrowerId] = useState( - url.searchParams.get('growerId') || filter?.planterId || '' - ); - const [deviceId, setDeviceId] = useState( - url.searchParams.get('deviceId') || filter?.deviceIdentifier || '' - ); - const [growerIdentifier, setGrowerIdentifier] = useState( - url.searchParams.get('growerIdentifier') || filter?.planterIdentifier || '' - ); - const [approved, setApproved] = useState(filter?.approved); - const [active, setActive] = useState(filter?.active); - const [dateStart, setDateStart] = useState( - stringToDate(url.searchParams.get('dateStart')) || - filter?.dateStart || - dateStartDefault - ); - const [dateEnd, setDateEnd] = useState( - stringToDate(url.searchParams.get('dateEnd')) || - filter?.dateEnd || - dateEndDefault - ); - const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES); - const [tag, setTag] = useState(url.searchParams.get('tag') || null); - const [tagSearchString, setTagSearchString] = useState( - url.searchParams.get('tagSearchString') || '' + const startDateDefault = null; + const endDateDefault = null; + const [uuid, setUUID] = useState(filter?.uuid || ''); + const [captureId, setCaptureId] = useState(filter?.captureId || ''); + const [growerId, setGrowerId] = useState(filter?.grower_account_id || ''); + const [deviceId, setDeviceId] = useState(filter?.device_identifier || ''); + const [wallet, setWallet] = useState(filter?.wallet || ''); + const [status, setStatus] = useState(filter?.status); + const [startDate, setStartDate] = useState( + filter?.startDate || startDateDefault ); + const [endDate, setEndDate] = useState(filter?.endDate || endDateDefault); + const [speciesId, setSpeciesId] = useState(filter?.species_id || ALL_SPECIES); + const [tag, setTag] = useState(null); + const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - url.searchParams.get('organizationId') || filter?.organizationId || ALL_ORGANIZATIONS ); - let open = props.open; // const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); - useEffect(() => { - handleQuerySearchParams('growerId', growerId); - handleQuerySearchParams('captureId', captureId); - handleQuerySearchParams('uuid', uuid); - handleQuerySearchParams('deviceId', deviceId); - handleQuerySearchParams( - 'dateStart', - dateStart ? formatDate(dateStart) : '' - ); - handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); - handleQuerySearchParams('speciesId', speciesId); - handleQuerySearchParams('growerIdentifier', growerIdentifier); - handleQuerySearchParams('approved', approved); - handleQuerySearchParams('active', active); - handleQuerySearchParams('tag', tag); - handleQuerySearchParams('tagSearchString', tagSearchString); - handleQuerySearchParams('organizationId', organizationId); - handleQuerySearchParams('stakeholderUUID', stakeholderUUID); - - const filter = new FilterModel(); - filter.uuid = uuid; - filter.captureId = captureId; - filter.planterId = growerId; - filter.deviceIdentifier = deviceId; - 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.stakeholderUUID = stakeholderUUID; - - props.onSubmit && props.onSubmit(filter); - }, []); - const handleStartDateChange = (date) => { setStartDate(date); }; @@ -180,31 +111,6 @@ function Filter(props) { function handleSubmit(e) { e.preventDefault(); // save the filer to context for editing & submit - - handleQuerySearchParams('growerId', growerId); - handleQuerySearchParams('captureId', captureId); - handleQuerySearchParams('uuid', uuid); - handleQuerySearchParams('deviceId', deviceId); - handleQuerySearchParams( - 'dateStart', - dateStart ? formatDate(dateStart) : '' - ); - handleQuerySearchParams('dateEnd', dateEnd ? formatDate(dateEnd) : ''); - handleQuerySearchParams('speciesId', speciesId); - handleQuerySearchParams('growerIdentifier', growerIdentifier); - handleQuerySearchParams('approved', approved); - handleQuerySearchParams('active', active); - handleQuerySearchParams('tag', tag); - handleQuerySearchParams('tagSearchString', tagSearchString); - handleQuerySearchParams('organizationId', organizationId); - handleQuerySearchParams('stakeholderUUID', stakeholderUUID); - // filter.tokenId = tokenId; - - handleFilter(); - } - - function handleFilter() { - console.log(dateStart); const filter = new FilterModel(); filter.uuid = uuid; filter.captureId = captureId.trim(); @@ -245,53 +151,53 @@ function Filter(props) { return ( <> - - - - { - 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 - ); - }} - > - {[ - filterOptionAll, - verificationStates.APPROVED, - verificationStates.AWAITING, - verificationStates.REJECTED, - ].map((name) => ( - - {name} - - ))} - - {/* + + + { + setStatus( + e.target.value === filterOptionAll + ? undefined + : e.target.value === verificationStates.APPROVED + ? captureStatus.APPROVED + : e.target.value === verificationStates.REJECTED + ? captureStatus.REJECTED + : captureStatus.UNPROCESSED + ); + }} + > + {[ + filterOptionAll, + verificationStates.APPROVED, + verificationStates.AWAITING, + verificationStates.REJECTED, + ].map((name) => ( + + {name} + + ))} + + {/* ))} */} - - + + + + setGrowerId(e.target.value)} /> - setCaptureId(e.target.value)} /> - - setGrowerId(e.target.value)} - /> - setCaptureId(e.target.value)} - /> - setUUID(e.target.value)} - /> - setDeviceId(e.target.value)} - /> - setGrowerIdentifier(e.target.value)} - /> - {/* setSpeciesId(e.target.value)} - > - {speciesContext.isLoading ? ( - - ) : ( - [ - { id: ALL_SPECIES, name: 'All' }, - { id: SPECIES_ANY_SET, name: 'Any set' }, + setUUID(e.target.value)} + /> + setDeviceId(e.target.value)} + /> + setGrowerIdentifier(e.target.value)} + /> + {/* setSpeciesId(e.target.value)} + > + {speciesContext.isLoading ? ( + + ) : ( + [ + { id: ALL_SPECIES, name: 'All' }, + { id: SPECIES_ANY_SET, name: 'Any set' }, + { + id: SPECIES_NOT_SET, + name: 'Not set', + }, + ...speciesContext.speciesList, + ].map((species) => ( + + {species.name} + + )) + )} + */} + ( - - {species.name} - - )) - )} - */} - - t.name - .toLowerCase() - .startsWith(tagSearchString.toLowerCase()) - ), - ]} - value={tag} - defaultValue={'All'} - getOptionLabel={(tag) => tag.name} - onChange={(_oldVal, newVal) => { - //triggered by onInputChange - setTag(newVal); - }} - onInputChange={(_oldVal, newVal) => { - setTagSearchString(newVal); - }} - renderInput={(params) => { - return ; - }} - getOptionSelected={(option, value) => option.id === value.id} - /> - { - setOrganizationId(org.id); - }} - /> + { + id: ANY_TAG_SET, + name: 'Any tag set', + isPublic: true, + status: 'active', + owner_id: null, + }, + ...tagsContext.tagList.filter((t) => + t.name + .toLowerCase() + .startsWith(tagSearchString.toLowerCase()) + ), + ]} + value={tag} + defaultValue={'All'} + getOptionLabel={(tag) => tag.name} + onChange={(_oldVal, newVal) => { + //triggered by onInputChange + setTag(newVal); + }} + onInputChange={(_oldVal, newVal) => { + setTagSearchString(newVal); + }} + renderInput={(params) => { + return ; + }} + getOptionSelected={(option, value) => option.id === value.id} + /> + { + setOrganizationId(org.id); + }} + /> + Reset + + - - + + } ); } diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index 03dfd1c7c..d84d1da8d 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; @@ -6,7 +6,6 @@ import TextField from '@material-ui/core/TextField'; import FilterModel from '../models/FilterGrower'; import SelectOrg from './common/SelectOrg'; import { ALL_ORGANIZATIONS } from '../models/Filter'; -import { handleQuerySearchParams } from './common/utils'; export const FILTER_WIDTH = 330; @@ -44,60 +43,22 @@ const styles = (theme) => { function FilterTopGrower(props) { const { classes, filter } = props; - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - - const [id, setId] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.id)); - const [personId, setPersonId] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.personId)); - const [firstName, setFirstName] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.firstName)); - const [lastName, setLastName] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.lastName)); - const [email, setEmail] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.email)); - const [phone, setPhone] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.phone)); - const [wallet, setWallet] = useState(getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.wallet)); - const [deviceIdentifier, setDeviceIdentifier] = useState( - getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.deviceIdentifier - )); + const [id, setId] = useState(filter?.id || ''); + const [personId, setPersonId] = useState(filter?.personId || ''); + const [firstName, setFirstName] = useState(filter?.firstName || ''); + const [lastName, setLastName] = useState(filter?.lastName || ''); const [organizationId, setOrganizationId] = useState( - getValueFromUrlOrFilter(url, filter, FILTER_FIELDS.organizationId, ALL_ORGANIZATIONS) + filter?.organizationId || ALL_ORGANIZATIONS + ); + const [email, setEmail] = useState(filter?.email || ''); + const [phone, setPhone] = useState(filter?.phone || ''); + const [wallet, setWallet] = useState(filter?.wallet || ''); + const [deviceIdentifier, setDeviceIdentifier] = useState( + filter?.device_identifier || '' ); - - const getValueFromUrlOrFilter = (url, filter, attr, defaultVal = '') => { - return (url?.searchParams.get(attr) || filter?.[attr] || defaultVal); - } - - const handleAllQuerySearchParams = () => { - handleQuerySearchParams(FILTER_FIELDS.id, id); - handleQuerySearchParams(FILTER_FIELDS.personId, personId); - handleQuerySearchParams(FILTER_FIELDS.firstName, firstName); - handleQuerySearchParams(FILTER_FIELDS.lastName, lastName); - handleQuerySearchParams(FILTER_FIELDS.organizationId, organizationId); - handleQuerySearchParams(FILTER_FIELDS.email, email); - handleQuerySearchParams(FILTER_FIELDS.phone, phone); - handleQuerySearchParams(FILTER_FIELDS.deviceIdentifier, deviceIdentifier); - handleQuerySearchParams(FILTER_FIELDS.wallet, wallet); - } - - useEffect(() => { - handleAllQuerySearchParams(); - - const filter = new FilterModel({ - personId, - id, - firstName, - lastName, - organizationId, - stakeholderUUID, - email, - phone, - deviceIdentifier, - }); - props.onSubmit && props.onSubmit(filter); - }, []); function handleSubmit(e) { e.preventDefault(); - handleAllQuerySearchParams(); - const filter = new FilterModel({ personId, id, @@ -123,8 +84,6 @@ function FilterTopGrower(props) { setDeviceIdentifier(''); setWallet(''); - handleAllQuerySearchParams(); - const filter = new FilterModel(); props.onSubmit && props.onSubmit(filter); }; @@ -136,7 +95,7 @@ function FilterTopGrower(props) { return ( <> { -
+ appContext.user ? ( - + ) : ( )) ) : ( @@ -81,6 +82,7 @@ export default function Routers() { : undefined } key={`route_${idx}`} + search={search} /> ) )} @@ -142,5 +144,5 @@ export default function Routers() { ); - }, [appContext.routes, appContext.user]); + }, [appContext.routes, appContext.user, search]); } diff --git a/src/components/common/CustomTableFilter/CustomTableFilter.js b/src/components/common/CustomTableFilter/CustomTableFilter.js index 9abdbcaf4..dccb9eb07 100644 --- a/src/components/common/CustomTableFilter/CustomTableFilter.js +++ b/src/components/common/CustomTableFilter/CustomTableFilter.js @@ -16,7 +16,6 @@ import SelectOrg from '../SelectOrg'; import useStyles from './CustomTableFilter.styles'; import { AppContext } from '../../../context/AppContext'; import { ALL_ORGANIZATIONS } from '../../../models/Filter'; -import { handleQuerySearchParams } from '../common/utils'; const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; @@ -35,17 +34,12 @@ const PAYMENT_STATUS = ['calculated', 'cancelled', 'paid', 'all']; */ function CustomTableFilter(props) { // console.warn('orgList', orgList); - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - const initialFilter = { - organization_id: url.searchParams.get('organization_id') || '', - grower: url.searchParams.get('grower') || '', - payment_status: url.searchParams.get('payment_status') || 'all', - earnings_status: url.searchParams.get('earnings_status') || 'all', - phone: url.searchParams.get('phone') || '', - start_date: url.searchParams.get('start_date') || '', - end_date: url.searchParams.get('end_date') || '', + // organization_id: '', + grower: '', + payment_status: 'all', + earnings_status: 'all', + phone: '', }; const [localFilter, setLocalFilter] = useState(initialFilter); const { @@ -60,48 +54,8 @@ function CustomTableFilter(props) { const classes = useStyles(); const { updateSelectedFilter } = useContext(AppContext); - useEffect(() => { - const filtersToSubmit = { - ...filter, - ...localFilter, - }; - console.log( - 'useEffect - CustomTableFilter.js:66 - localFilter --', - localFilter - ); - - if (filtersToSubmit.organization_id === ALL_ORGANIZATIONS) { - const modifiedFiltersToSubmit = { - ...filtersToSubmit, - organization_id: '', - sub_organization: '', - }; - setFilter(modifiedFiltersToSubmit); - setIsFilterOpen(false); - console.log(modifiedFiltersToSubmit); - updateSelectedFilter({ - modifiedFiltersToSubmit, - }); - mapFiltersToQueries(modifiedFiltersToSubmit); - } else { - setFilter(filtersToSubmit); - setIsFilterOpen(false); - console.log(filtersToSubmit); - updateSelectedFilter(filtersToSubmit); - mapFiltersToQueries(filtersToSubmit); - } - }, []); - - const mapFiltersToQueries = (filter) => { - console.log(filter); - for (var key in filter) { - handleQuerySearchParams(key, filter[key]); - } - }; - const handleOnFormControlChange = (e) => { let updatedFilter = { ...localFilter }; - console.log('updatedFilter --', updatedFilter); if (e?.target) { e.preventDefault(); const { name, value } = e.target; diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index 2b184537b..6902e55db 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -2,16 +2,20 @@ import React, { useContext, useState, useEffect, createContext } from 'react'; import { AppContext } from './AppContext.js'; import FilterGrower, { ALL_ORGANIZATIONS } from 'models/FilterGrower'; import api from 'api/growers'; -import { setOrganizationFilter } from '../common/utils'; +import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; import * as loglevel from 'loglevel'; +import { useLocation } from 'react-router-dom'; const log = loglevel.getLogger('context/GrowerContext'); +const DEFAULT_PAGE_SIZE = 24; +const DEFAULT_CURRENT_PAGE = 0; + export const GrowerContext = createContext({ growers: [], - pageSize: 24, + pageSize: DEFAULT_PAGE_SIZE, count: null, - currentPage: 0, + currentPage: DEFAULT_CURRENT_PAGE, filter: new FilterGrower(), isLoading: false, totalGrowerCount: null, @@ -29,15 +33,27 @@ export const GrowerContext = createContext({ export function GrowerProvider(props) { const { orgId, orgList } = useContext(AppContext); + const { search } = props; + + const searchParams = Object.fromEntries(new URLSearchParams(search)); + const { + pageSize: pageSizeParam = undefined, + currentPage: currentPageParam = undefined, + ...filterParams + } = searchParams; + const [growers, setGrowers] = useState([]); - const [pageSize, setPageSize] = useState(24); + const [pageSize, setPageSize] = useState( + Number(pageSizeParam) || DEFAULT_PAGE_SIZE + ); const [count, setCount] = useState(null); - const [currentPage, setCurrentPage] = useState(0); - const [filter, setFilter] = useState( - new FilterGrower({ organization_id: ALL_ORGANIZATIONS }) + const [currentPage, setCurrentPage] = useState( + Number(currentPageParam) || DEFAULT_CURRENT_PAGE ); + const [filter, setFilter] = useState(new FilterGrower(filterParams)); const [isLoading, setIsLoading] = useState(false); const [totalGrowerCount, setTotalGrowerCount] = useState(null); + const location = useLocation(); useEffect(() => { const abortController = new AbortController(); @@ -49,6 +65,26 @@ export function GrowerProvider(props) { return () => abortController.abort(); }, [filter, pageSize, currentPage, orgId]); + useEffect(() => { + handleQuerySearchParams({ + pageSize, + currentPage, + ...filter, + }); + }, [filter, pageSize, currentPage]); + + useEffect(() => { + const searchParams = Object.fromEntries(new URLSearchParams(search)); + const { + pageSize: pageSizeParam = undefined, + currentPage: currentPageParam = undefined, + ...filterParams + } = searchParams; + setFilter(new FilterGrower(filterParams)); + setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE); + setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE); + }, [search, location]); + // EVENT HANDLERS const changePageSize = async (pageSize) => { diff --git a/src/views/GrowersView.js b/src/views/GrowersView.js index 3d44dc5bb..7473aaccd 100644 --- a/src/views/GrowersView.js +++ b/src/views/GrowersView.js @@ -7,7 +7,7 @@ import { SpeciesProvider } from '../context/SpeciesContext'; import { TagsProvider } from '../context/TagsContext'; import GrowerFilterHeader from '../components/GrowerFilterHeader'; -function GrowersView() { +function GrowersView({ search }) { useEffect(() => { document.title = `Growers - ${documentTitle}`; }, []); @@ -18,7 +18,7 @@ function GrowersView() { direction="column" style={{ flexWrap: 'nowrap', height: '100%', overflow: 'hidden' }} > - + diff --git a/src/views/VerifyView.js b/src/views/VerifyView.js index 1df12c593..3095cb22c 100644 --- a/src/views/VerifyView.js +++ b/src/views/VerifyView.js @@ -5,13 +5,13 @@ import { VerifyProvider } from '../context/VerifyContext'; import { SpeciesProvider } from '../context/SpeciesContext'; import { TagsProvider } from '../context/TagsContext'; -function VerifyView() { +function VerifyView({ search }) { useEffect(() => { document.title = `Verify Captures - ${documentTitle}`; }, []); return ( - + From 52e539493026e9db7665028c1cf7d77a8733aa7b Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 21:28:33 +0000 Subject: [PATCH 16/32] feat: rework searchParams filter lifecycle --- src/common/utils.js | 29 +++++--------- src/components/Routers.js | 4 +- src/context/CapturesContext.js | 69 +++++++++++++++++++++++++++++----- src/context/GrowerContext.js | 36 +++++++++--------- src/context/VerifyContext.js | 53 ++++++++++++++++++++++---- src/models/Filter.js | 25 +++++++++++- src/models/FilterGrower.js | 23 +++++++++++- src/views/CapturesView.js | 6 ++- src/views/GrowersView.js | 8 +++- src/views/VerifyView.js | 8 +++- 10 files changed, 198 insertions(+), 63 deletions(-) diff --git a/src/common/utils.js b/src/common/utils.js index 3f80181b1..67dfcef49 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -58,28 +58,19 @@ export const localeSort = (arr, order) => { }; export const handleQuerySearchParams = (newParams) => { - let changed = false; - let url = new URL(window.location.href); - const oldParams = url.searchParams; + const oldSearch = window.location.search; + let searchParams = new URLSearchParams(); Object.keys(newParams).forEach((key) => { - const value = newParams[key]?.toString(); - if (typeof value === 'function') { - return; - } - if (oldParams.has(key) && (value === undefined || value === '')) { - url.searchParams.delete(key); - changed = true; - } else if (!oldParams.has(key) && (value === undefined || value === '')) { - return; - } else if (!oldParams.get(key)) { - url.searchParams.append(key, value); - changed = true; - } else if (oldParams.get(key) && oldParams.get(key) !== value) { - url.searchParams.set(key, value); - changed = true; + const value = newParams[key]; + if (value) { + searchParams.set(key, value.toString()); } }); - if (changed) { + + let url = new URL(window.location.href); + url.search = searchParams.toString(); + + if (url.search !== oldSearch) { window.history.pushState({}, '', url.search); } }; diff --git a/src/components/Routers.js b/src/components/Routers.js index 1a5100acb..dac2d6b0d 100644 --- a/src/components/Routers.js +++ b/src/components/Routers.js @@ -66,7 +66,6 @@ export default function Routers() { : undefined } key={`route_${i}`} - search={search} /> )) ) : ( @@ -82,7 +81,6 @@ export default function Routers() { : undefined } key={`route_${idx}`} - search={search} /> ) )} @@ -144,5 +142,5 @@ export default function Routers() { ); - }, [appContext.routes, appContext.user, search]); + }, [appContext.routes, appContext.user]); } diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index f94d2cde6..7485ff051 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -3,20 +3,25 @@ import FilterModel, { ALL_ORGANIZATIONS } from '../models/Filter'; import api from '../api/treeTrackerApi'; // import { captureStatus } from '../common/variables'; import { AppContext } from './AppContext.js'; -import { setOrganizationFilter } from '../common/utils'; +import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; import * as loglevel from 'loglevel'; const log = loglevel.getLogger('../context/CapturesContext'); +const DEFAULT_ROWS_PER_PAGE = 25; +const DEFAULT_PAGE = 0; +const DEFAULT_ORDER = 'desc'; +const DEFAULT_ORDER_BY = 'created_at'; + export const CapturesContext = createContext({ isLoading: false, captures: [], captureCount: 0, capture: {}, - page: 0, - rowsPerPage: 25, - order: 'desc', - orderBy: 'timeCreated', + page: DEFAULT_PAGE, + rowsPerPage: DEFAULT_ROWS_PER_PAGE, + order: DEFAULT_ORDER, + orderBy: DEFAULT_ORDER_BY, filter: new FilterModel(), setRowsPerPage: () => {}, setPage: () => {}, @@ -31,16 +36,31 @@ export const CapturesContext = createContext({ export function CapturesProvider(props) { const { orgId, orgList } = useContext(AppContext); + const { searchParams } = props; + + const { + rowsPerPage: rowsPerPageParam = undefined, + page: pageParam = undefined, + order: orderParam = undefined, + orderBy: orderByParam = undefined, + ...filterParams + } = Object.fromEntries(searchParams); + const [captures, setCaptures] = useState([]); const [captureCount, setCaptureCount] = useState(0); const [capture, setCapture] = useState({}); - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(25); - const [order, setOrder] = useState('desc'); - const [orderBy, setOrderBy] = useState('created_at'); + const [page, setPage] = useState(Number(pageParam) || DEFAULT_PAGE); + const [rowsPerPage, setRowsPerPage] = useState( + Number(rowsPerPageParam) || DEFAULT_ROWS_PER_PAGE + ); + const [order, setOrder] = useState(orderParam || DEFAULT_ORDER); + const [orderBy, setOrderBy] = useState(orderByParam || DEFAULT_ORDER_BY); const [isLoading, setIsLoading] = useState(false); const [filter, setFilter] = useState( - new FilterModel({ organization_id: ALL_ORGANIZATIONS }) + FilterModel.fromSearchParams({ + organization_id: ALL_ORGANIZATIONS, + ...filterParams + }) ); useEffect(() => { @@ -52,6 +72,35 @@ export function CapturesProvider(props) { return () => abortController.abort(); }, [filter, rowsPerPage, page, order, orderBy, orgId]); + useEffect(() => { + handleQuerySearchParams({ + rowsPerPage, + page, + order, + orderBy, + ...filter.toSearchParams(), + }); + + getCaptures(); + }, [filter, rowsPerPage, page, order, orderBy]); + + useEffect(() => { + const { + rowsPerPage: rowsPerPageParam = undefined, + page: pageParam = undefined, + order: orderParam = undefined, + orderBy: orderByParam = undefined, + ...filterParams + } = Object.fromEntries(searchParams); + + setFilter(FilterModel.fromSearchParams(filterParams)); + + setPage(Number(pageParam) || DEFAULT_PAGE); + setRowsPerPage(Number(rowsPerPageParam) || DEFAULT_ROWS_PER_PAGE); + setOrder(orderParam || DEFAULT_ORDER); + setOrderBy(orderByParam || DEFAULT_ORDER_BY); + }, [searchParams, location]); + const getCaptures = async (abortController) => { log.debug('4 - load captures', filter); diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index 6902e55db..520b56e9d 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -33,14 +33,13 @@ export const GrowerContext = createContext({ export function GrowerProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { search } = props; + const { searchParams } = props; - const searchParams = Object.fromEntries(new URLSearchParams(search)); const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, ...filterParams - } = searchParams; + } = Object.fromEntries(searchParams); const [growers, setGrowers] = useState([]); const [pageSize, setPageSize] = useState( @@ -50,40 +49,43 @@ export function GrowerProvider(props) { const [currentPage, setCurrentPage] = useState( Number(currentPageParam) || DEFAULT_CURRENT_PAGE ); - const [filter, setFilter] = useState(new FilterGrower(filterParams)); + const [filter, setFilter] = useState( + FilterGrower.fromSearchParams({ + organization_id: ALL_ORGANIZATIONS, + ...filterParams + }) + ); const [isLoading, setIsLoading] = useState(false); const [totalGrowerCount, setTotalGrowerCount] = useState(null); const location = useLocation(); useEffect(() => { const abortController = new AbortController(); + + handleQuerySearchParams({ + pageSize, + currentPage, + ...filter.toSearchParams(), + }); + // orgId can be either null or an [] of uuids if (orgId !== undefined) { - load({ signal: abortController.signal }); + load(); // getCount(); } return () => abortController.abort(); }, [filter, pageSize, currentPage, orgId]); useEffect(() => { - handleQuerySearchParams({ - pageSize, - currentPage, - ...filter, - }); - }, [filter, pageSize, currentPage]); - - useEffect(() => { - const searchParams = Object.fromEntries(new URLSearchParams(search)); const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, ...filterParams - } = searchParams; - setFilter(new FilterGrower(filterParams)); + } = Object.fromEntries(searchParams); + setFilter(FilterGrower.fromSearchParams(filterParams)); setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE); setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE); - }, [search, location]); + }, [searchParams, location]); // EVENT HANDLERS diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 60dbe5517..5bfae13be 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -4,10 +4,13 @@ import FilterModel, { ALL_ORGANIZATIONS } from '../models/Filter'; import * as loglevel from 'loglevel'; import { captureStatus } from 'common/variables'; import { AppContext } from './AppContext.js'; -import { setOrganizationFilter } from '../common/utils'; +import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; const log = loglevel.getLogger('../context/VerifyContext'); +const DEFAULT_PAGE_SIZE = 24; +const DEFAULT_CURRENT_PAGE = 0; + export const VerifyContext = createContext({ captureImages: [], captureImagesSelected: [], @@ -16,8 +19,8 @@ export const VerifyContext = createContext({ isLoading: false, isProcessing: false, percentComplete: 0, - pageSize: 24, - currentPage: 0, + pageSize: DEFAULT_PAGE_SIZE, + currentPage: DEFAULT_CURRENT_PAGE, filter: new FilterModel(), invalidateCaptureCount: true, captureCount: null, @@ -36,6 +39,14 @@ export const VerifyContext = createContext({ export function VerifyProvider(props) { const { orgId, orgList } = useContext(AppContext); + const { searchParams } = props; + + const { + pageSize: pageSizeParam = undefined, + currentPage: currentPageParam = undefined, + ...filterParams + } = Object.fromEntries(searchParams); + const [captureImages, setCaptureImages] = useState([]); const [captureImagesUndo, setCaptureImagesUndo] = useState([]); const [captureImagesSelected, setCaptureImagesSelected] = useState({}); @@ -43,12 +54,17 @@ export function VerifyProvider(props) { const [isLoading, setIsLoading] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [percentComplete, setPercentComplete] = useState(0); - const [pageSize, setPageSize] = useState(24); - const [currentPage, setCurrentPage] = useState(0); + const [pageSize, setPageSize] = useState( + Number(pageSizeParam) || DEFAULT_PAGE_SIZE + ); + const [currentPage, setCurrentPage] = useState( + Number(currentPageParam) || DEFAULT_CURRENT_PAGE + ); const [filter, setFilter] = useState( - new FilterModel({ - organization_id: ALL_ORGANIZATIONS, + FilterModel.fromSearchParams({ status: captureStatus.UNPROCESSED, + organization_id: ALL_ORGANIZATIONS, + ...filterParams, }) ); const [invalidateCaptureCount, setInvalidateCaptureCount] = useState(true); @@ -60,6 +76,12 @@ export function VerifyProvider(props) { /* load captures when the page or page size changes */ useEffect(() => { + handleQuerySearchParams({ + pageSize, + currentPage, + ...filter.toSearchParams(), + }); + const abortController = new AbortController(); // orgId can be either null or an [] of uuids if (orgId !== undefined) { @@ -69,6 +91,23 @@ export function VerifyProvider(props) { return () => abortController.abort(); }, [filter, pageSize, currentPage, orgId]); + useEffect(() => { + const { + pageSize: pageSizeParam = undefined, + currentPage: currentPageParam = undefined, + ...filterParams + } = Object.fromEntries(searchParams); + + setFilter( + FilterModel.fromSearchParams({ + status: captureStatus.UNPROCESSED, + ...filterParams, + }) + ); + setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE); + setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE); + }, [searchParams, location]); + // STATE HELPER FUNCTIONS // /* diff --git a/src/models/Filter.js b/src/models/Filter.js index 2d20caa5a..9a64e794c 100644 --- a/src/models/Filter.js +++ b/src/models/Filter.js @@ -13,7 +13,7 @@ export const ANY_TAG_SET = 'ANY_TAG_SET'; import { tokenizationStates } from '../common/variables'; // import log from 'loglevel'; -export default class Filter { +class Filter { uuid; captureId; startDate; @@ -198,4 +198,27 @@ export default class Filter { return numFilters; } + + toSearchParams() { + return { + uuid: this.uuid, + captureId: this.captureId, + startDate: this.startDate, + endDate: this.endDate, + grower_account_id: this.grower_account_id, + device_identifier: this.device_identifier, + wallet: this.wallet, + species_id: this.species_id, + tagId: this.tagId, + organization_id: this.organization_id, + tokenId: this.tokenId, + status: this.status, + }; + } } + +Filter.fromSearchParams = (searchParams) => { + return new Filter(searchParams); +}; + +export default Filter; diff --git a/src/models/FilterGrower.js b/src/models/FilterGrower.js index 57769afd7..a6572546e 100644 --- a/src/models/FilterGrower.js +++ b/src/models/FilterGrower.js @@ -17,7 +17,7 @@ export const FILTER_FIELDS = { }; // import log from 'loglevel'; -export default class Filter { +class Filter { constructor(options) { Object.assign(this, options); } @@ -129,4 +129,25 @@ export default class Filter { return numFilters; } + + toSearchParams() { + return { + personId: this.personId, + wallet: this.wallet, + id: this.id, + grower_account_id: this.grower_account_id, + firstName: this.firstName, + lastName: this.lastName, + organization_id: this.organization_id, + device_identifier: this.device_identifier, + email: this.email, + phone: this.phone, + }; + } } + +Filter.fromSearchParams = (searchParams) => { + return new Filter(searchParams); +}; + +export default Filter; diff --git a/src/views/CapturesView.js b/src/views/CapturesView.js index c0512a259..653913f77 100644 --- a/src/views/CapturesView.js +++ b/src/views/CapturesView.js @@ -1,4 +1,5 @@ import React, { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; import { documentTitle } from '../common/variables'; import { Grid } from '@material-ui/core'; import CaptureTable from '../components/Captures/CaptureTable'; @@ -12,13 +13,16 @@ function CapturesView() { document.title = `Capture Data - ${documentTitle}`; }, []); + const location = useLocation(); + const searchParams = new URLSearchParams(location.search); + return ( - + diff --git a/src/views/GrowersView.js b/src/views/GrowersView.js index 7473aaccd..872138445 100644 --- a/src/views/GrowersView.js +++ b/src/views/GrowersView.js @@ -1,4 +1,5 @@ import React, { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; import { documentTitle } from '../common/variables'; import { Grid } from '@material-ui/core'; import Growers from '../components/Growers/Growers.js'; @@ -7,18 +8,21 @@ import { SpeciesProvider } from '../context/SpeciesContext'; import { TagsProvider } from '../context/TagsContext'; import GrowerFilterHeader from '../components/GrowerFilterHeader'; -function GrowersView({ search }) { +function GrowersView() { useEffect(() => { document.title = `Growers - ${documentTitle}`; }, []); + const location = useLocation(); + const searchParams = new URLSearchParams(location.search); + return ( - + diff --git a/src/views/VerifyView.js b/src/views/VerifyView.js index 3095cb22c..f1a3a34c8 100644 --- a/src/views/VerifyView.js +++ b/src/views/VerifyView.js @@ -1,17 +1,21 @@ import React, { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; import { documentTitle } from '../common/variables'; import Verify from '../components/Verify'; import { VerifyProvider } from '../context/VerifyContext'; import { SpeciesProvider } from '../context/SpeciesContext'; import { TagsProvider } from '../context/TagsContext'; -function VerifyView({ search }) { +function VerifyView() { useEffect(() => { document.title = `Verify Captures - ${documentTitle}`; }, []); + const location = useLocation(); + const searchParams = new URLSearchParams(location.search); + return ( - + From f13acd3e4248219a8e4c1a67f82a9cd4d312b4d3 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 21:39:43 +0000 Subject: [PATCH 17/32] feat: add abort controllers to prevent duplicate queries --- src/api/growers.js | 3 ++- src/api/treeTrackerApi.js | 4 ++-- src/context/CapturesContext.js | 4 +++- src/context/GrowerContext.js | 11 +++++------ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/api/growers.js b/src/api/growers.js index b563701e7..15601c39d 100644 --- a/src/api/growers.js +++ b/src/api/growers.js @@ -48,7 +48,7 @@ export default { } }, - getCount(filter) { + getCount(filter, abortController) { try { const filterObj = filter?.getWhereObj ? filter.getWhereObj() : {}; const query = `${QUERY_API}/v2/growers/count${ @@ -59,6 +59,7 @@ export default { 'content-type': 'application/json', Authorization: session.token, }, + signal: abortController?.signal, }).then(handleResponse); } catch (error) { handleError(error); diff --git a/src/api/treeTrackerApi.js b/src/api/treeTrackerApi.js index 05fce190e..1f6a36f6c 100644 --- a/src/api/treeTrackerApi.js +++ b/src/api/treeTrackerApi.js @@ -137,8 +137,7 @@ export default { /** * Captures */ - - getCaptures({ limit = 25, offset = 0, order, filter = {} }) { + getCaptures({ limit = 25, offset = 0, order, filter = {} }, abortController) { try { const where = filter.getWhereObj ? filter.getWhereObj() : {}; let filterObj = { ...where, limit, offset, order }; @@ -152,6 +151,7 @@ export default { 'content-type': 'application/json', Authorization: session.token, }, + signal: abortController?.signal, }).then(handleResponse); } catch (error) { handleError(error); diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index 7485ff051..09ed58374 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -81,7 +81,9 @@ export function CapturesProvider(props) { ...filter.toSearchParams(), }); - getCaptures(); + const abortController = new AbortController(); + getCaptures({ signal: abortController.signal }); + return () => abortController.abort(); }, [filter, rowsPerPage, page, order, orderBy]); useEffect(() => { diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index 520b56e9d..bf679a0b6 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -60,18 +60,16 @@ export function GrowerProvider(props) { const location = useLocation(); useEffect(() => { - const abortController = new AbortController(); - handleQuerySearchParams({ pageSize, currentPage, ...filter.toSearchParams(), }); - // orgId can be either null or an [] of uuids + const abortController = new AbortController(); if (orgId !== undefined) { - load(); - // getCount(); + load({ signal: abortController.signal }); + // getCount({ signal: abortController.signal }); } return () => abortController.abort(); }, [filter, pageSize, currentPage, orgId]); @@ -128,7 +126,7 @@ export function GrowerProvider(props) { setIsLoading(false); }; - const getCount = async () => { + const getCount = async (abortController) => { //set correct values for organization_id, an array of uuids for ALL_ORGANIZATIONS or a uuid string if provided const finalFilter = setOrganizationFilter( filter.getWhereObj(), @@ -140,6 +138,7 @@ export function GrowerProvider(props) { const { count } = await api.getCount({ filter: new FilterGrower(finalFilter), + abortController }); setCount(Number(count)); }; From ad2da29eca17abb14fb73e70c32d26cee600d413 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 21:45:16 +0000 Subject: [PATCH 18/32] fix: provide default empty searchParams --- src/context/CapturesContext.js | 2 +- src/context/GrowerContext.js | 2 +- src/context/VerifyContext.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index 09ed58374..dcb4da79d 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -36,7 +36,7 @@ export const CapturesContext = createContext({ export function CapturesProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams } = props; + const { searchParams = [] } = props; const { rowsPerPage: rowsPerPageParam = undefined, diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index bf679a0b6..32fe706b6 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -33,7 +33,7 @@ export const GrowerContext = createContext({ export function GrowerProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams } = props; + const { searchParams = [] } = props; const { pageSize: pageSizeParam = undefined, diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 5bfae13be..70812ac49 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -39,7 +39,7 @@ export const VerifyContext = createContext({ export function VerifyProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams } = props; + const { searchParams = [] } = props; const { pageSize: pageSizeParam = undefined, From 629f2c4a1d6a0ad590bd1b62a2c94f9b7d4d5467 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 22:06:11 +0000 Subject: [PATCH 19/32] feat: make searchParams handling more robust --- src/context/CapturesContext.js | 10 +++++++--- src/context/GrowerContext.js | 24 ++++++++++++++---------- src/context/VerifyContext.js | 22 ++++++++++++++-------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index dcb4da79d..b5afe5236 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -36,7 +36,7 @@ export const CapturesContext = createContext({ export function CapturesProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams = [] } = props; + const { searchParams } = props; const { rowsPerPage: rowsPerPageParam = undefined, @@ -44,7 +44,7 @@ export function CapturesProvider(props) { order: orderParam = undefined, orderBy: orderByParam = undefined, ...filterParams - } = Object.fromEntries(searchParams); + } = Object.fromEntries(searchParams || []); const [captures, setCaptures] = useState([]); const [captureCount, setCaptureCount] = useState(0); @@ -87,6 +87,10 @@ export function CapturesProvider(props) { }, [filter, rowsPerPage, page, order, orderBy]); useEffect(() => { + if (!searchParams) { + return; + } + const { rowsPerPage: rowsPerPageParam = undefined, page: pageParam = undefined, @@ -101,7 +105,7 @@ export function CapturesProvider(props) { setRowsPerPage(Number(rowsPerPageParam) || DEFAULT_ROWS_PER_PAGE); setOrder(orderParam || DEFAULT_ORDER); setOrderBy(orderByParam || DEFAULT_ORDER_BY); - }, [searchParams, location]); + }, [searchParams]); const getCaptures = async (abortController) => { log.debug('4 - load captures', filter); diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index 32fe706b6..e66adca78 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -4,7 +4,6 @@ import FilterGrower, { ALL_ORGANIZATIONS } from 'models/FilterGrower'; import api from 'api/growers'; import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; import * as loglevel from 'loglevel'; -import { useLocation } from 'react-router-dom'; const log = loglevel.getLogger('context/GrowerContext'); @@ -33,13 +32,13 @@ export const GrowerContext = createContext({ export function GrowerProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams = [] } = props; + const { searchParams } = props; const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, ...filterParams - } = Object.fromEntries(searchParams); + } = Object.fromEntries(searchParams || []); const [growers, setGrowers] = useState([]); const [pageSize, setPageSize] = useState( @@ -57,14 +56,15 @@ export function GrowerProvider(props) { ); const [isLoading, setIsLoading] = useState(false); const [totalGrowerCount, setTotalGrowerCount] = useState(null); - const location = useLocation(); useEffect(() => { - handleQuerySearchParams({ - pageSize, - currentPage, - ...filter.toSearchParams(), - }); + if (searchParams) { + handleQuerySearchParams({ + pageSize, + currentPage, + ...filter.toSearchParams(), + }); + } const abortController = new AbortController(); if (orgId !== undefined) { @@ -75,6 +75,10 @@ export function GrowerProvider(props) { }, [filter, pageSize, currentPage, orgId]); useEffect(() => { + if (!searchParams) { + return; + } + const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, @@ -83,7 +87,7 @@ export function GrowerProvider(props) { setFilter(FilterGrower.fromSearchParams(filterParams)); setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE); setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE); - }, [searchParams, location]); + }, [searchParams]); // EVENT HANDLERS diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 70812ac49..79cc3b266 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -39,13 +39,13 @@ export const VerifyContext = createContext({ export function VerifyProvider(props) { const { orgId, orgList } = useContext(AppContext); - const { searchParams = [] } = props; + const { searchParams } = props; const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, ...filterParams - } = Object.fromEntries(searchParams); + } = Object.fromEntries(searchParams || []); const [captureImages, setCaptureImages] = useState([]); const [captureImagesUndo, setCaptureImagesUndo] = useState([]); @@ -76,11 +76,13 @@ export function VerifyProvider(props) { /* load captures when the page or page size changes */ useEffect(() => { - handleQuerySearchParams({ - pageSize, - currentPage, - ...filter.toSearchParams(), - }); + if (searchParams) { + handleQuerySearchParams({ + pageSize, + currentPage, + ...filter.toSearchParams(), + }); + } const abortController = new AbortController(); // orgId can be either null or an [] of uuids @@ -92,6 +94,10 @@ export function VerifyProvider(props) { }, [filter, pageSize, currentPage, orgId]); useEffect(() => { + if (!searchParams) { + return; + } + const { pageSize: pageSizeParam = undefined, currentPage: currentPageParam = undefined, @@ -106,7 +112,7 @@ export function VerifyProvider(props) { ); setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE); setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE); - }, [searchParams, location]); + }, [searchParams]); // STATE HELPER FUNCTIONS From 7c01348c136e297a7f7ff5614d2ea2c09ecea0c2 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 22:24:16 +0000 Subject: [PATCH 20/32] feat: omit default values from searchParams --- src/context/CapturesContext.js | 9 +++++---- src/context/GrowerContext.js | 5 +++-- src/context/VerifyContext.js | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index b5afe5236..da3ff14b3 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -74,10 +74,11 @@ export function CapturesProvider(props) { useEffect(() => { handleQuerySearchParams({ - rowsPerPage, - page, - order, - orderBy, + rowsPerPage: + rowsPerPage === DEFAULT_ROWS_PER_PAGE ? undefined : rowsPerPage, + page: page === DEFAULT_PAGE ? undefined : page, + order: order === DEFAULT_ORDER ? undefined : order, + orderBy: porderByage === DEFAULT_ORDER_BY ? undefined : orderBy, ...filter.toSearchParams(), }); diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index e66adca78..972542475 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -60,8 +60,9 @@ export function GrowerProvider(props) { useEffect(() => { if (searchParams) { handleQuerySearchParams({ - pageSize, - currentPage, + pageSize: pageSize === DEFAULT_PAGE_SIZE ? undefined : pageSize, + currentPage: + currentPage === DEFAULT_CURRENT_PAGE ? undefined : currentPage, ...filter.toSearchParams(), }); } diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 79cc3b266..926e13236 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -78,8 +78,9 @@ export function VerifyProvider(props) { useEffect(() => { if (searchParams) { handleQuerySearchParams({ - pageSize, - currentPage, + pageSize: pageSize === DEFAULT_PAGE_SIZE ? undefined : pageSize, + currentPage: + currentPage === DEFAULT_CURRENT_PAGE ? undefined : currentPage, ...filter.toSearchParams(), }); } From c765dec8a783e2f690637da96ef34b327af54b17 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 30 Jan 2023 22:24:35 +0000 Subject: [PATCH 21/32] fix: typo --- src/context/CapturesContext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index da3ff14b3..5cf8f13be 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -78,7 +78,7 @@ export function CapturesProvider(props) { rowsPerPage === DEFAULT_ROWS_PER_PAGE ? undefined : rowsPerPage, page: page === DEFAULT_PAGE ? undefined : page, order: order === DEFAULT_ORDER ? undefined : order, - orderBy: porderByage === DEFAULT_ORDER_BY ? undefined : orderBy, + orderBy: orderBy === DEFAULT_ORDER_BY ? undefined : orderBy, ...filter.toSearchParams(), }); From 13f2d8740c35edb45a68de68e989b19aca89c8e5 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 20:55:59 +0000 Subject: [PATCH 22/32] chore: fix merge errors --- src/components/CaptureDetailDialog.js | 1 - src/components/CaptureFilter.js | 7 ------- src/components/FilterTop.js | 18 ++++++++---------- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/components/CaptureDetailDialog.js b/src/components/CaptureDetailDialog.js index 58fc56c59..5e1f1ed4e 100644 --- a/src/components/CaptureDetailDialog.js +++ b/src/components/CaptureDetailDialog.js @@ -29,7 +29,6 @@ import { hasPermission, POLICIES } from '../models/auth'; import { AppContext } from '../context/AppContext'; import theme from './common/theme'; import log from 'loglevel'; -import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme) => ({ chipRoot: { diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 05c81d028..7189d6869 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -30,13 +30,6 @@ import { datePickerDefaultMinDate, } from '../common/variables'; -import Autocomplete from '@material-ui/lab/Autocomplete'; -import Button from '@material-ui/core/Button'; -import { CircularProgress } from '@material-ui/core'; -import DateFnsUtils from '@date-io/date-fns'; -import Grid from '@material-ui/core/Grid'; -import MenuItem from '@material-ui/core/MenuItem'; -import SelectOrg from './common/SelectOrg'; import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; import { CircularProgress } from '@material-ui/core'; diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index d4603fca2..839ee8316 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -8,6 +8,7 @@ import FilterModel, { // SPECIES_ANY_SET, // SPECIES_NOT_SET, ALL_ORGANIZATIONS, + ALL_TAGS, TAG_NOT_SET, ANY_TAG_SET, } from '../models/Filter'; @@ -91,8 +92,7 @@ function Filter(props) { 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); @@ -123,7 +123,6 @@ function Filter(props) { filter.species_id = speciesId; filter.tag_id = tag ? tag.id : undefined; filter.organization_id = organizationId; - filter.stakeholderUUID = stakeholderUUID; // filter.tokenId = tokenId; props.onSubmit && props.onSubmit(filter); } @@ -141,7 +140,6 @@ function Filter(props) { setTag(null); setTagSearchString(''); setOrganizationId(ALL_ORGANIZATIONS); - setStakeholderUUID(ALL_ORGANIZATIONS); // setTokenId(filterOptionAll); const filter = new FilterModel(); @@ -286,8 +284,8 @@ function Filter(props) { id="grower-identifier" label="Wallet/Grower Identifier" placeholder="e.g. grower@example.com" - value={growerIdentifier} - onChange={(e) => setGrowerIdentifier(e.target.value)} + value={wallet} + onChange={(e) => setWallet(e.target.value)} /> {/* )) - )} + )} */} t.name .toLowerCase() @@ -374,7 +372,7 @@ function Filter(props) { { - setOrganizationId(org.id); + setOrganizationId(org.stakeholder_uuid); }} /> From ad0707c5546d04c940f5bcc0dbc9d0eeb5ce12f1 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:04:35 +0000 Subject: [PATCH 23/32] chore: revert unnecessary merge changes --- src/components/CaptureFilter.js | 3 +-- src/components/FilterTop.js | 1 + src/components/GrowerFilterHeader.js | 14 ++++++++------ src/components/Verify.js | 13 +++++++------ src/context/GrowerContext.js | 10 +++++++--- src/context/VerifyContext.js | 6 +++++- src/models/FilterGrower.js | 11 ----------- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 7189d6869..6d80a0d7b 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -10,10 +10,10 @@ import FilterModel, { ALL_SPECIES, SPECIES_ANY_SET, SPECIES_NOT_SET, + ALL_ORGANIZATIONS, ALL_TAGS, TAG_NOT_SET, ANY_TAG_SET, - ALL_ORGANIZATIONS, } from '../models/Filter'; import DateFnsUtils from '@date-io/date-fns'; import { @@ -29,7 +29,6 @@ import { tokenizationStates, datePickerDefaultMinDate, } from '../common/variables'; - import { SpeciesContext } from '../context/SpeciesContext'; import { TagsContext } from '../context/TagsContext'; import { CircularProgress } from '@material-ui/core'; diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 839ee8316..5ebcf309d 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -26,6 +26,7 @@ import { verificationStates, captureStatus, datePickerDefaultMinDate, + // tokenizationStates, } from '../common/variables'; // import { SpeciesContext } from '../context/SpeciesContext'; diff --git a/src/components/GrowerFilterHeader.js b/src/components/GrowerFilterHeader.js index 8ac0f6d8d..2f5ba6bd8 100644 --- a/src/components/GrowerFilterHeader.js +++ b/src/components/GrowerFilterHeader.js @@ -50,12 +50,14 @@ function GrowersFilterHeader(props) { , ]} > - + {isFilterShown && ( + + )} ); diff --git a/src/components/Verify.js b/src/components/Verify.js index 3e35afbc3..5f50d17c6 100644 --- a/src/components/Verify.js +++ b/src/components/Verify.js @@ -574,12 +574,13 @@ const Verify = (props) => { , ]} > - + {isFilterShown && ( + + )} diff --git a/src/context/GrowerContext.js b/src/context/GrowerContext.js index 972542475..b9f4f289d 100644 --- a/src/context/GrowerContext.js +++ b/src/context/GrowerContext.js @@ -2,7 +2,10 @@ import React, { useContext, useState, useEffect, createContext } from 'react'; import { AppContext } from './AppContext.js'; import FilterGrower, { ALL_ORGANIZATIONS } from 'models/FilterGrower'; import api from 'api/growers'; -import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; +import { + setOrganizationFilter, + handleQuerySearchParams, +} from '../common/utils'; import * as loglevel from 'loglevel'; const log = loglevel.getLogger('context/GrowerContext'); @@ -51,7 +54,7 @@ export function GrowerProvider(props) { const [filter, setFilter] = useState( FilterGrower.fromSearchParams({ organization_id: ALL_ORGANIZATIONS, - ...filterParams + ...filterParams, }) ); const [isLoading, setIsLoading] = useState(false); @@ -68,6 +71,7 @@ export function GrowerProvider(props) { } const abortController = new AbortController(); + // orgId can be either null or an [] of uuids if (orgId !== undefined) { load({ signal: abortController.signal }); // getCount({ signal: abortController.signal }); @@ -143,7 +147,7 @@ export function GrowerProvider(props) { const { count } = await api.getCount({ filter: new FilterGrower(finalFilter), - abortController + abortController, }); setCount(Number(count)); }; diff --git a/src/context/VerifyContext.js b/src/context/VerifyContext.js index 926e13236..cfa86b1e9 100644 --- a/src/context/VerifyContext.js +++ b/src/context/VerifyContext.js @@ -4,7 +4,10 @@ import FilterModel, { ALL_ORGANIZATIONS } from '../models/Filter'; import * as loglevel from 'loglevel'; import { captureStatus } from 'common/variables'; import { AppContext } from './AppContext.js'; -import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; +import { + setOrganizationFilter, + handleQuerySearchParams, +} from '../common/utils'; const log = loglevel.getLogger('../context/VerifyContext'); @@ -108,6 +111,7 @@ export function VerifyProvider(props) { setFilter( FilterModel.fromSearchParams({ status: captureStatus.UNPROCESSED, + organization_id: ALL_ORGANIZATIONS, ...filterParams, }) ); diff --git a/src/models/FilterGrower.js b/src/models/FilterGrower.js index a6572546e..029da8623 100644 --- a/src/models/FilterGrower.js +++ b/src/models/FilterGrower.js @@ -4,17 +4,6 @@ export const ALL_ORGANIZATIONS = 'ALL_ORGANIZATIONS'; export const ORGANIZATION_NOT_SET = 'ORGANIZATION_NOT_SET'; -export const FILTER_FIELDS = { - personId: 'personId', - wallet: 'wallet', - id: 'id', - firstName: 'firstName', - lastName: 'lastName', - organizationId: 'organization_id', - deviceIdentifier: 'device_identifier', - email: 'email', - phone: 'phone', -}; // import log from 'loglevel'; class Filter { From 11cbe86d64d4f8e988cfe71a20cb97c2adaadec7 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:26:57 +0000 Subject: [PATCH 24/32] fix: handle string and Date formats for date filter --- src/components/FilterTop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 5ebcf309d..91811cb0e 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -106,7 +106,7 @@ function Filter(props) { }; const formatDate = (date) => { - return convertDateToDefaultSqlDate(date); + return convertDateToDefaultSqlDate(new Date(date)); }; function handleSubmit(e) { From 5444c7e8c08106c544abf80fe89d23b042329f47 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:28:07 +0000 Subject: [PATCH 25/32] fix: handle string and Date format dates in filter --- src/components/Filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Filter.js b/src/components/Filter.js index 60812d57a..105ab08f4 100644 --- a/src/components/Filter.js +++ b/src/components/Filter.js @@ -83,7 +83,7 @@ function Filter(props) { }; const formatDate = (date) => { - return convertDateToDefaultSqlDate(date); + return convertDateToDefaultSqlDate(new Date(date)); }; function handleCloseClick() { From f82e895d970536fd8ae1cc1e62158e0179937b8f Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:40:11 +0000 Subject: [PATCH 26/32] fix: use correct name for filter.organization_id --- src/components/CaptureFilter.js | 2 +- src/components/FilterTop.js | 2 +- src/components/FilterTopGrower.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/CaptureFilter.js b/src/components/CaptureFilter.js index 6d80a0d7b..4672d602f 100644 --- a/src/components/CaptureFilter.js +++ b/src/components/CaptureFilter.js @@ -92,7 +92,7 @@ function Filter(props) { const [tag, setTag] = useState(null); const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - filter.organizationId || ALL_ORGANIZATIONS + filter?.organization_id || ALL_ORGANIZATIONS ); const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); diff --git a/src/components/FilterTop.js b/src/components/FilterTop.js index 91811cb0e..df3c06a05 100644 --- a/src/components/FilterTop.js +++ b/src/components/FilterTop.js @@ -93,7 +93,7 @@ function Filter(props) { const [tag, setTag] = useState(null); const [tagSearchString, setTagSearchString] = useState(''); const [organizationId, setOrganizationId] = useState( - filter?.organizationId || ALL_ORGANIZATIONS + filter?.organization_id || ALL_ORGANIZATIONS ); // const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll); diff --git a/src/components/FilterTopGrower.js b/src/components/FilterTopGrower.js index d84d1da8d..77464f83b 100644 --- a/src/components/FilterTopGrower.js +++ b/src/components/FilterTopGrower.js @@ -48,7 +48,7 @@ function FilterTopGrower(props) { const [firstName, setFirstName] = useState(filter?.firstName || ''); const [lastName, setLastName] = useState(filter?.lastName || ''); const [organizationId, setOrganizationId] = useState( - filter?.organizationId || ALL_ORGANIZATIONS + filter?.organization_id || ALL_ORGANIZATIONS ); const [email, setEmail] = useState(filter?.email || ''); const [phone, setPhone] = useState(filter?.phone || ''); From 755dd78377edd8dd5390074990735e8c059b0dfa Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:42:46 +0000 Subject: [PATCH 27/32] chore: revert capture matching filter --- .../CaptureMatching/CaptureMatchingView.js | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/components/CaptureMatching/CaptureMatchingView.js b/src/components/CaptureMatching/CaptureMatchingView.js index 0c072dd4e..049b5d5b4 100644 --- a/src/components/CaptureMatching/CaptureMatchingView.js +++ b/src/components/CaptureMatching/CaptureMatchingView.js @@ -44,7 +44,6 @@ import CandidateImages from './CandidateImages'; import Navbar from '../Navbar'; import api from 'api/treeTrackerApi'; import log from 'loglevel'; -import { handleQuerySearchParams } from '../../common/utils'; const useStyle = makeStyles((theme) => ({ container: { @@ -311,7 +310,6 @@ function CaptureMatchingView() { endDate: now.toISOString().split('T')[0], stakeholderUUID: null, }; - const url = new URL(window.location.href); const classes = useStyle(); const appContext = useContext(AppContext); @@ -323,13 +321,9 @@ function CaptureMatchingView() { const [noOfPages, setNoOfPages] = useState(null); //for pagination const [imgCount, setImgCount] = useState(null); //for header icon const [treesCount, setTreesCount] = useState(0); - const [startDate, setStartDate] = useState( - url.searchParams.get('startDate') || '' - ); - const [endDate, setEndDate] = useState(url.searchParams.get('endDate') || ''); - const [organizationId, setOrganizationId] = useState( - url.searchParams.get('organizationId') || null - ); + const [startDate, setStartDate] = useState(initialFilter.startDate); + const [endDate, setEndDate] = useState(initialFilter.endDate); + const [organizationId, setOrganizationId] = useState(null); const [stakeholderUUID, setStakeholderUUID] = useState(null); const [filter, setFilter] = useState(initialFilter); const [growerAccount, setGrowerAccount] = useState({}); @@ -433,17 +427,6 @@ function CaptureMatchingView() { return () => abortController.abort(); }, [captureImage]); - useEffect(() => { - handleQuerySearchParams('startDate', startDate); - handleQuerySearchParams('endDate', endDate); - handleQuerySearchParams('organizationId', organizationId); - setFilter({ - startDate, - endDate, - stakeholderUUID, - }); - }, []); - // Capture Image Pagination function const handleChange = (e, value) => { setCurrentPage(value); @@ -487,9 +470,6 @@ function CaptureMatchingView() { function handleFilterSubmit() { // log.debug('filter submit -----> ', organizationId, stakeholderUUID); - handleQuerySearchParams('startDate', startDate); - handleQuerySearchParams('endDate', endDate); - handleQuerySearchParams('organizationId', organizationId); setFilter({ startDate, endDate, @@ -589,17 +569,13 @@ function CaptureMatchingView() { filter.endDate || 'End Date' }`} className={classes.currentHeaderChip} - onDelete={() => { + onDelete={() => setFilter({ ...filter, startDate: undefined, endDate: undefined, - }); - handleQuerySearchParams('startDate', ''); - handleQuerySearchParams('endDate', ''); - setStartDate(''); - setEndDate(''); - }} + }) + } /> )} {filter.stakeholderUUID && ( From 8b28f9a870c1fa333f1bbe0e984bcd360c55566f Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:45:02 +0000 Subject: [PATCH 28/32] chore: revert capture matching changes From 2ccf498557777439059c6fecc0d2aec37b857d4e Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 21:45:58 +0000 Subject: [PATCH 29/32] chore: revert capture matching changes From d571a6e704dc54d7472aa48067fe927626ad3820 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 22:11:59 +0000 Subject: [PATCH 30/32] fix: support empty search string --- src/common/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/utils.js b/src/common/utils.js index 67dfcef49..268771bdf 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -71,6 +71,6 @@ export const handleQuerySearchParams = (newParams) => { url.search = searchParams.toString(); if (url.search !== oldSearch) { - window.history.pushState({}, '', url.search); + window.history.pushState({}, '', url.search || location.pathname); } }; From a6396502bc504d4a3dfa28f39c20865dec867d04 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Sun, 26 Feb 2023 22:12:12 +0000 Subject: [PATCH 31/32] chore: remove duplicate useEffect for captures --- src/context/CapturesContext.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/context/CapturesContext.js b/src/context/CapturesContext.js index 5cf8f13be..a662d28d2 100644 --- a/src/context/CapturesContext.js +++ b/src/context/CapturesContext.js @@ -3,7 +3,10 @@ import FilterModel, { ALL_ORGANIZATIONS } from '../models/Filter'; import api from '../api/treeTrackerApi'; // import { captureStatus } from '../common/variables'; import { AppContext } from './AppContext.js'; -import { setOrganizationFilter, handleQuerySearchParams } from '../common/utils'; +import { + setOrganizationFilter, + handleQuerySearchParams, +} from '../common/utils'; import * as loglevel from 'loglevel'; const log = loglevel.getLogger('../context/CapturesContext'); @@ -59,19 +62,10 @@ export function CapturesProvider(props) { const [filter, setFilter] = useState( FilterModel.fromSearchParams({ organization_id: ALL_ORGANIZATIONS, - ...filterParams + ...filterParams, }) ); - useEffect(() => { - const abortController = new AbortController(); - // orgId can be either null or an [] of uuids - if (orgId !== undefined) { - getCaptures({ signal: abortController.signal }); - } - return () => abortController.abort(); - }, [filter, rowsPerPage, page, order, orderBy, orgId]); - useEffect(() => { handleQuerySearchParams({ rowsPerPage: @@ -83,9 +77,12 @@ export function CapturesProvider(props) { }); const abortController = new AbortController(); - getCaptures({ signal: abortController.signal }); + // orgId can be either null or an [] of uuids + if (orgId !== undefined) { + getCaptures({ signal: abortController.signal }); + } return () => abortController.abort(); - }, [filter, rowsPerPage, page, order, orderBy]); + }, [filter, rowsPerPage, page, order, orderBy, orgId]); useEffect(() => { if (!searchParams) { From 73fcbea11fe7aea82deee51ec6bfc6f1868a8859 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Wed, 1 Mar 2023 13:30:37 +0000 Subject: [PATCH 32/32] refactor: declare `fromSearchParams` as static class method --- src/models/Filter.js | 8 ++++---- src/models/FilterGrower.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/models/Filter.js b/src/models/Filter.js index 9a64e794c..cb835911a 100644 --- a/src/models/Filter.js +++ b/src/models/Filter.js @@ -215,10 +215,10 @@ class Filter { status: this.status, }; } -} -Filter.fromSearchParams = (searchParams) => { - return new Filter(searchParams); -}; + static fromSearchParams = (searchParams) => { + return new Filter(searchParams); + }; +} export default Filter; diff --git a/src/models/FilterGrower.js b/src/models/FilterGrower.js index 029da8623..e155ca1b1 100644 --- a/src/models/FilterGrower.js +++ b/src/models/FilterGrower.js @@ -133,10 +133,10 @@ class Filter { phone: this.phone, }; } -} -Filter.fromSearchParams = (searchParams) => { - return new Filter(searchParams); -}; + static fromSearchParams = (searchParams) => { + return new Filter(searchParams); + }; +} export default Filter;