Skip to content

Commit

Permalink
[98] add query for curator comments (#154)
Browse files Browse the repository at this point in the history
* Allow search for curators comment

* Fix highlighting of the text in view case form

* Update DataGuideDialog.tsx

* Cleanup

* Inform user if there is any not closed quote

* Cleanup code + remove console.log

* Add cypress tests for comments and uneven quote warning

* WIP fixed issues with filters and special characters in free text search

* Update index.tsx

* Update SearchBar.tsx

* Update index.tsx

* Fix setting filters

* Fix issues with text-search not visible

* Cleanup

* Cleanup SearchBar

* Remove console.log
  • Loading branch information
stanislaw-zakrzewski authored Oct 28, 2024
1 parent 929ea2a commit 881e39e
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 69 deletions.
13 changes: 12 additions & 1 deletion data-serving/scripts/setup-db/schemas/day0cases.indexes.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"location.admin2": "text",
"location.admin3": "text",
"caseReference.sourceUrl": "text",
"caseStatus": "text"
"caseStatus": "text",
"comment": "text"
}
},
{
Expand Down Expand Up @@ -145,6 +146,16 @@
"strength": 2
}
},
{
"name": "commentIdx",
"key": {
"comment": -1
},
"collation": {
"locale": "en_US",
"strength": 2
}
},
{
"name": "countryAndDate",
"key": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ describe('Linelist table', function () {
sourceUrl: 'www.example.com',
caseStatus: CaseStatus.Confirmed,
occupation: 'Actor',
comment: 'note',
});
cy.addCase({
country: 'Germany',
Expand All @@ -524,6 +525,7 @@ describe('Linelist table', function () {
cy.intercept('GET', getDefaultQuery({ limit: 50 })).as('getCases');
cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'Argentina' })).as('getCasesWithSearch1');
cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'Doctor' })).as('getCasesWithSearch2');
cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'note' })).as('getCasesWithSearch3');

cy.visit('/cases');
cy.wait('@getCases');
Expand All @@ -549,5 +551,30 @@ describe('Linelist table', function () {
cy.contains('Argentina').should('not.exist');
cy.contains('France').should('not.exist');
cy.contains('Germany').should('exist');

cy.get('#clear-search').click();
cy.wait('@getCases');
cy.contains('Argentina').should('exist');
cy.contains('France').should('exist');
cy.contains('Germany').should('exist');

cy.get('#search-field').type('note');
cy.wait('@getCasesWithSearch3');
cy.contains('Argentina').should('not.exist');
cy.contains('France').should('exist');
cy.contains('Germany').should('not.exist');
});

it('Informs user when uneven number of quotes is present in free-text search', () => {
cy.intercept('GET', getDefaultQuery({ limit: 50, query: '"Bus driver"' })).as('getCasesWithSearch');
cy.visit('/cases');
cy.contains('Please make sure you have an even number of quotes.').should('not.exist');

cy.get('#search-field').type('"Bus driver');
cy.contains('Please make sure you have an even number of quotes.').should('exist');

cy.get('#search-field').type('"');
cy.wait('@getCasesWithSearch');
cy.contains('Please make sure you have an even number of quotes.').should('not.exist');
});
});
2 changes: 2 additions & 0 deletions verification/curator-service/ui/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface AddCaseProps {
gender?: Gender;
outcome?: Outcome;
uploadIds?: string[];
comment?: string;
}

declare global {
Expand Down Expand Up @@ -111,6 +112,7 @@ export function addCase(opts: AddCaseProps): void {
travelHistory: {},
genomeSequences: {},
vaccination: {},
comment: opts.comment || '',
},
});
}
Expand Down
6 changes: 4 additions & 2 deletions verification/curator-service/ui/src/components/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,14 @@ export default function App(): JSX.Element {
};

const onModalClose = (): void => {
const searchQueryObject = new URLSearchParams(searchQuery);
navigate(
{
pathname:
location.state && location.state.lastLocation
? location.state.lastLocation
: '/cases',
search: searchQuery,
search: searchQueryObject.toString(),
},
{ state: { lastLocation: '/case/view' } },
);
Expand Down Expand Up @@ -371,7 +372,8 @@ export default function App(): JSX.Element {
)
return;

dispatch(setSearchQuery(location.search));
const searchParams = new URLSearchParams(location.search);
dispatch(setSearchQuery(searchParams.toString()));

// Save searchQuery to local storage not to lost it when user goes through auth process
localStorage.setItem('searchQuery', location.search);
Expand Down
55 changes: 44 additions & 11 deletions verification/curator-service/ui/src/components/DataGuideDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import { Box, Portal, Theme, Typography } from '@mui/material';
import { withStyles } from 'tss-react/mui';
import CloseIcon from '@mui/icons-material/Close';
import { Close as CloseIcon } from '@mui/icons-material';
import Draggable, { ControlPosition } from 'react-draggable';

// As per this issue from react-draggable library: https://github.com/react-grid-layout/react-draggable/pull/648
Expand Down Expand Up @@ -96,16 +96,49 @@ const SearchGuideDialog = ({
the left and choose ascending or descending.
</Typography>
<Typography className={classes?.textSection}>
<strong>For full-text search</strong>, enter any combination of search terms.
<br/>
Full-text search covers: occupation, admin0, admin1, admin2, admin3, sourceUrl and caseStatus.
<br/>
Search terms must be exact (example: "German" will not match "Germany").
<br/>
Full-text search matches cases that contain any ot the search terms, not a combination.
<br/>
No special characters apart from "." are allowed and if the "." is used in a search term
given search term must be contained within quotation marks.
<strong>For full-text search</strong>, enter any
combination of search terms. Rules for full-text
search:
<br />
<ul>
<li>
Full-text search covers: occupation, admin0,
admin1, admin2, admin3, sourceUrl, comment
and caseStatus.
</li>
<li>
Search terms must be exact (example:{' '}
<b>
<i>German</i>
</b>{' '}
will not match{' '}
<b>
<i>Germany</i>
</b>
).
</li>
<li>
Full-text search matches cases that contain
any of the search terms, not a combination.
</li>
<li>
To search for a combination of terms, wrap
the combination in quotation marks (example:{' '}
<b>
<i>"Bus driver"</i>
</b>
).
</li>
<li>
No special characters apart from dot are
allowed. Search terms with dot must be
contained within quotation marks (example:{' '}
<b>
<i>"global.health"</i>
</b>
).
</li>
</ul>
</Typography>
<Typography>
You can use the icons on the right to navigate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
useMediaQuery,
} from '@mui/material';
import { Close as CloseIcon } from '@mui/icons-material';
import { filtersToURL, URLToFilters } from '../util/searchQuery';
import { URLToFilters } from '../util/searchQuery';
import { hasAnyRole } from '../util/helperFunctions';
import { useAppSelector, useAppDispatch } from '../../hooks/redux';
import { fetchCountries } from '../../redux/filters/thunk';
Expand Down Expand Up @@ -153,13 +153,19 @@ export default function FiltersDialog({
handleSetModalAlert();
dispatch(setModalOpen(false));

const searchQuery = filtersToURL(values);
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(values)) {
if (value) searchParams.set(key, value);
}
const q = (new URLSearchParams(location.search)).get('q')
if (q) searchParams.set('q', q);
const searchParamsString = searchParams.toString();

sendCustomGtmEvent('filters_applied', { query: searchQuery });
sendCustomGtmEvent('filters_applied', { query: searchParamsString });

navigate({
pathname: '/cases',
search: searchQuery,
search: searchParamsString,
});
},
});
Expand Down
57 changes: 31 additions & 26 deletions verification/curator-service/ui/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import clsx from 'clsx';
import DataGuideDialog from './DataGuideDialog';
import { useDebounce } from '../hooks/useDebounce';
import FiltersDialog from './FiltersDialog';
import { searchQueryToURL, URLToSearchQuery } from './util/searchQuery';
import { useLocation, useNavigate } from 'react-router-dom';
import { KeyboardEvent, ChangeEvent } from 'react';
import { useAppSelector, useAppDispatch } from '../hooks/redux';
Expand Down Expand Up @@ -85,9 +84,7 @@ export default function SearchBar({
const [isUserTyping, setIsUserTyping] = useState<boolean>(false);
const [isDataGuideOpen, setIsDataGuideOpen] = useState<boolean>(false);
const [searchInput, setSearchInput] = useState<string>(
location.search.includes('?q=')
? URLToSearchQuery(location.search)
: '',
new URLSearchParams(location.search).get('q') || '',
);
const [modalAlert, setModalAlert] = useState<boolean>(false);
const guideButtonRef = React.useRef<HTMLButtonElement>(null);
Expand All @@ -103,28 +100,30 @@ export default function SearchBar({
}
}, [filtersBreadcrumb]);

useEffect(() => {
const q = new URLSearchParams(location.search).get('q') || '';
if (q !== searchInput) setSearchInput(q);
}, [location.search]);

// Set search query debounce to 1000ms
const debouncedSearch = useDebounce(searchInput, 2000);

// Update search input based on search query
useEffect(() => {
if (!location.search.includes('?q=')) {
setSearchInput('');
return;
}
const handleNavigating = (q: string) => {
const searchParams = new URLSearchParams(location.search);
q !== '' ? searchParams.set('q', q) : searchParams.delete('q');

setSearchInput(URLToSearchQuery(location.search));
}, [location.search]);
navigate({
pathname: '/cases',
search: searchParams.toString(),
});
};

// Apply filter parameters after delay
useEffect(() => {
if (!isUserTyping) return;

setIsUserTyping(false);
navigate({
pathname: '/cases',
search: searchQueryToURL(debouncedSearch),
});

handleNavigating(debouncedSearch);
//eslint-disable-next-line
}, [debouncedSearch]);

Expand All @@ -136,10 +135,8 @@ export default function SearchBar({
if (ev.key === 'Enter') {
ev.preventDefault();
setIsUserTyping(false);
navigate({
pathname: '/cases',
search: searchQueryToURL(searchInput),
});

handleNavigating(searchInput);
}
};

Expand Down Expand Up @@ -174,16 +171,24 @@ export default function SearchBar({
return searchStringStrippedOutColon;
}

const renderSearchErrorMessage = () => {
if (searchError) {
return 'Incorrect entry. ":" characters have been removed. Please use filters instead.';
} else {
const quoteCount = searchInput.split('"').length - 1;
if (quoteCount % 2 !== 0) {
return 'Incorrect entry. Please make sure you have an even number of quotes.';
}
}
};

return (
<>
<div className={classes.searchRoot}>
<StyledSearchTextField
size="small"
error={searchError}
helperText={
searchError &&
'Incorrect entry. ":" characters have been removed. Please use filters instead.'
}
helperText={renderSearchErrorMessage()}
id="search-field"
data-testid="searchbar"
name="searchbar"
Expand All @@ -203,7 +208,7 @@ export default function SearchBar({
}
}}
placeholder="Fulltext search"
value={decodeURI(searchInput)}
value={searchInput}
variant="outlined"
fullWidth
InputProps={{
Expand Down
Loading

0 comments on commit 881e39e

Please sign in to comment.