Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter bookmark #1010

Draft
wants to merge 33 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
673d879
feat: initial filter test
SuspenseFallback Nov 6, 2022
fee2813
chore: fix merge error
SuspenseFallback Jan 23, 2023
fc286cb
fix: more merge errors
SuspenseFallback Jan 23, 2023
6b990a2
fix: more merge errors
SuspenseFallback Jan 23, 2023
9de70eb
fix: ear
SuspenseFallback Nov 21, 2022
895e3cf
feat: working on payments filter
SuspenseFallback Dec 4, 2022
5514d00
feat: filter persists on growers
SuspenseFallback Dec 14, 2022
862e316
chore: draft pr
SuspenseFallback Jan 23, 2023
d9da245
fix: multiple errors in code after merge
SuspenseFallback Jan 23, 2023
76c92d1
chore: revert import reordering to reduce merge complexity
nmcharlton Jan 29, 2023
a92442d
chore: revert import reordering to reduce merge complexity
nmcharlton Jan 29, 2023
1db7230
refactor: consolidate duplicate handleQuerySearchParams implementations
nmcharlton Jan 29, 2023
aa97c72
fix: give handleQuerySearchParams access to current params
nmcharlton Jan 29, 2023
d5841d7
fix: add missing url var
nmcharlton Jan 29, 2023
6d85531
feat: move searchParams handling to context components
nmcharlton Jan 30, 2023
52e5394
feat: rework searchParams filter lifecycle
nmcharlton Jan 30, 2023
f13acd3
feat: add abort controllers to prevent duplicate queries
nmcharlton Jan 30, 2023
ad2da29
fix: provide default empty searchParams
nmcharlton Jan 30, 2023
629f2c4
feat: make searchParams handling more robust
nmcharlton Jan 30, 2023
7c01348
feat: omit default values from searchParams
nmcharlton Jan 30, 2023
c765dec
fix: typo
nmcharlton Jan 30, 2023
13f2d87
chore: fix merge errors
nmcharlton Feb 26, 2023
ad0707c
chore: revert unnecessary merge changes
nmcharlton Feb 26, 2023
11cbe86
fix: handle string and Date formats for date filter
nmcharlton Feb 26, 2023
5444c7e
fix: handle string and Date format dates in filter
nmcharlton Feb 26, 2023
f82e895
fix: use correct name for filter.organization_id
nmcharlton Feb 26, 2023
755dd78
chore: revert capture matching filter
nmcharlton Feb 26, 2023
8b28f9a
chore: revert capture matching changes
nmcharlton Feb 26, 2023
2ccf498
chore: revert capture matching changes
nmcharlton Feb 26, 2023
d571a6e
fix: support empty search string
nmcharlton Feb 26, 2023
a639650
chore: remove duplicate useEffect for captures
nmcharlton Feb 26, 2023
73fcbea
refactor: declare `fromSearchParams` as static class method
nmcharlton Mar 1, 2023
3946bba
Merge branch 'master' into filter-bookmark
nmcharlton Apr 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/api/growers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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${
Expand All @@ -59,6 +59,7 @@ export default {
'content-type': 'application/json',
Authorization: session.token,
},
signal: abortController?.signal,
}).then(handleResponse);
} catch (error) {
handleError(error);
Expand Down
4 changes: 2 additions & 2 deletions src/api/treeTrackerApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -152,6 +151,7 @@ export default {
'content-type': 'application/json',
Authorization: session.token,
},
signal: abortController?.signal,
}).then(handleResponse);
} catch (error) {
handleError(error);
Expand Down
18 changes: 18 additions & 0 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,21 @@ export const localeSort = (arr, order) => {
return sortVal * orderVal;
});
};

export const handleQuerySearchParams = (newParams) => {
const oldSearch = window.location.search;
let searchParams = new URLSearchParams();
Object.keys(newParams).forEach((key) => {
const value = newParams[key];
if (value) {
searchParams.set(key, value.toString());
}
});

let url = new URL(window.location.href);
url.search = searchParams.toString();

if (url.search !== oldSearch) {
window.history.pushState({}, '', url.search || location.pathname);
}
};
4 changes: 3 additions & 1 deletion src/components/CaptureFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ function Filter(props) {
const [speciesId, setSpeciesId] = useState(filter?.speciesId || ALL_SPECIES);
const [tag, setTag] = useState(null);
const [tagSearchString, setTagSearchString] = useState('');
const [organizationId, setOrganizationId] = useState(ALL_ORGANIZATIONS);
const [organizationId, setOrganizationId] = useState(
filter?.organization_id || ALL_ORGANIZATIONS
);
const [tokenId, setTokenId] = useState(filter?.tokenId || filterOptionAll);

const handleStartDateChange = (date) => {
Expand Down
7 changes: 5 additions & 2 deletions src/components/CaptureMatching/CaptureMatchingView.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,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 '';
Expand Down
2 changes: 1 addition & 1 deletion src/components/Filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function Filter(props) {
};

const formatDate = (date) => {
return convertDateToDefaultSqlDate(date);
return convertDateToDefaultSqlDate(new Date(date));
};

function handleCloseClick() {
Expand Down
4 changes: 2 additions & 2 deletions src/components/FilterTop.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -106,7 +106,7 @@ function Filter(props) {
};

const formatDate = (date) => {
return convertDateToDefaultSqlDate(date);
return convertDateToDefaultSqlDate(new Date(date));
};

function handleSubmit(e) {
Expand Down
4 changes: 3 additions & 1 deletion src/components/FilterTopGrower.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ function FilterTopGrower(props) {
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 [organizationId, setOrganizationId] = useState(
filter?.organization_id || ALL_ORGANIZATIONS
);
const [email, setEmail] = useState(filter?.email || '');
const [phone, setPhone] = useState(filter?.phone || '');
const [wallet, setWallet] = useState(filter?.wallet || filterOptionAll);
Expand Down
8 changes: 6 additions & 2 deletions src/components/PrivateRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { AppContext } from '../context/AppContext';

export default function PrivateRoute({ component: Component, ...rest }) {
export default function PrivateRoute({
component: Component,
search,
...rest
}) {
const appContext = useContext(AppContext);

return (
<Route
{...rest}
render={(props) =>
appContext.user ? (
<Component {...props} />
<Component search={search} {...props} />
) : (
<Redirect
to={{
Expand Down
73 changes: 63 additions & 10 deletions src/context/CapturesContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@ 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: () => {},
Expand All @@ -31,19 +39,43 @@ 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(() => {
handleQuerySearchParams({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be inside a conditional like in the Verify and Grower contexts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the others that are incorrect.
I suspect the if (searchParams) check is an artefact of a previous way of doing this.
We probably shouldn't check it as we're not using it within this useEffect. I'll do some more testing.

rowsPerPage:
rowsPerPage === DEFAULT_ROWS_PER_PAGE ? undefined : rowsPerPage,
page: page === DEFAULT_PAGE ? undefined : page,
order: order === DEFAULT_ORDER ? undefined : order,
orderBy: orderBy === DEFAULT_ORDER_BY ? undefined : orderBy,
...filter.toSearchParams(),
});

const abortController = new AbortController();
// orgId can be either null or an [] of uuids
if (orgId !== undefined) {
Expand All @@ -52,6 +84,27 @@ export function CapturesProvider(props) {
return () => abortController.abort();
}, [filter, rowsPerPage, page, order, orderBy, orgId]);

useEffect(() => {
if (!searchParams) {
return;
}

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]);

const getCaptures = async (abortController) => {
log.debug('4 - load captures', filter);

Expand Down
62 changes: 54 additions & 8 deletions src/context/GrowerContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ 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';

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,
Expand All @@ -30,26 +36,65 @@ export const GrowerContext = createContext({

export function GrowerProvider(props) {
const { orgId, orgList } = useContext(AppContext);
const { searchParams } = props;

const {
pageSize: pageSizeParam = undefined,
currentPage: currentPageParam = undefined,
...filterParams
} = Object.fromEntries(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 [currentPage, setCurrentPage] = useState(
Number(currentPageParam) || DEFAULT_CURRENT_PAGE
);
const [filter, setFilter] = useState(
new FilterGrower({ organization_id: ALL_ORGANIZATIONS })
FilterGrower.fromSearchParams({
organization_id: ALL_ORGANIZATIONS,
...filterParams,
})
);
const [isLoading, setIsLoading] = useState(false);
const [totalGrowerCount, setTotalGrowerCount] = useState(null);

useEffect(() => {
if (searchParams) {
handleQuerySearchParams({
pageSize: pageSize === DEFAULT_PAGE_SIZE ? undefined : pageSize,
currentPage:
currentPage === DEFAULT_CURRENT_PAGE ? undefined : currentPage,
...filter.toSearchParams(),
});
}

const abortController = new AbortController();
// orgId can be either null or an [] of uuids
if (orgId !== undefined) {
load({ signal: abortController.signal });
// getCount();
// getCount({ signal: abortController.signal });
}
return () => abortController.abort();
}, [filter, pageSize, currentPage, orgId]);

useEffect(() => {
if (!searchParams) {
gwynndp marked this conversation as resolved.
Show resolved Hide resolved
return;
}

const {
pageSize: pageSizeParam = undefined,
currentPage: currentPageParam = undefined,
...filterParams
} = Object.fromEntries(searchParams);
setFilter(FilterGrower.fromSearchParams(filterParams));
setPageSize(Number(pageSizeParam) || DEFAULT_PAGE_SIZE);
setCurrentPage(Number(currentPageParam) || DEFAULT_CURRENT_PAGE);
}, [searchParams]);

// EVENT HANDLERS

const changePageSize = async (pageSize) => {
Expand Down Expand Up @@ -100,7 +145,7 @@ export function GrowerProvider(props) {
};
};

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(),
Expand All @@ -112,6 +157,7 @@ export function GrowerProvider(props) {

const { count } = await api.getCount({
filter: new FilterGrower(finalFilter),
abortController,
});
setCount(Number(count));
};
Expand Down
Loading