Skip to content

Commit

Permalink
Add autocomplete for cardIDs and correctly compute backend query
Browse files Browse the repository at this point in the history
  • Loading branch information
Kicu committed Oct 30, 2024
1 parent 39ef57b commit 533131a
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 138 deletions.
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5799,7 +5799,7 @@ const CONST = {
CATEGORY: 'category',
TAG: 'tag',
TAX_RATE: 'taxRate',
CARD_ID: 'cardID', // Fixme substitute bank id?
CARD_ID: 'cardID',
REPORT_ID: 'reportID',
KEYWORD: 'keyword',
IN: 'in',
Expand Down
5 changes: 4 additions & 1 deletion src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,10 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) {
}
const inputQueryJSON = SearchQueryUtils.buildSearchQueryJSON(inputValue);
if (inputQueryJSON) {
const standardizedQuery = SearchQueryUtils.standardizeQueryJSON(inputQueryJSON, cardList, taxRates);
// Todo traverse the tree to update all the display values into id values; this is only temporary until autocomplete code from SearchRouter is implement here
// After https://github.com/Expensify/App/pull/51633 is merged, autocomplete functionality will be included into this component, and `getFindIDFromDisplayValue` can be removed
const computeNodeValueFn = SearchQueryUtils.getFindIDFromDisplayValue(cardList, taxRates);
const standardizedQuery = SearchQueryUtils.traverseAndUpdatedQuery(inputQueryJSON, computeNodeValueFn);
const query = SearchQueryUtils.buildSearchQueryString(standardizedQuery);
SearchActions.clearAllFilters();
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));
Expand Down
39 changes: 20 additions & 19 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {ValueOf} from 'type-fest';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {usePersonalDetails} from '@components/OnyxProvider';
import {useOptionsList} from '@components/OptionListContextProvider';
import type {AutocompleteRange, SearchQueryJSON} from '@components/Search/types';
import type {SearchAutocompleteQueryRange, SearchQueryString} from '@components/Search/types';
import type {SelectionListHandle} from '@components/SelectionList/types';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useDebouncedState from '@hooks/useDebouncedState';
Expand Down Expand Up @@ -63,18 +63,14 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
return state?.routes.at(-1)?.params?.reportID;
});

const cleanQuery = useMemo(() => {
return getQueryWithSubstitutions(textInputValue, autocompleteSubstitutions);
}, [autocompleteSubstitutions, textInputValue]);

const {activeWorkspaceID} = useActiveWorkspace();
const policy = usePolicy(activeWorkspaceID);

const typeAutocompleteList = Object.values(CONST.SEARCH.DATA_TYPES);
const statusAutocompleteList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP});
const expenseTypes = Object.values(CONST.SEARCH.TRANSACTION_TYPE);
const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST);
const cardAutocompleteList = Object.values(cardList ?? {}).map((card) => card.bank);
const cardAutocompleteList = Object.values(cardList);
const personalDetailsForParticipants = usePersonalDetails();
const participantsAutocompleteList = Object.values(personalDetailsForParticipants)
.filter((details): details is NonNullable<PersonalDetails> => !!(details && details?.login))
Expand Down Expand Up @@ -155,7 +151,7 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
const contextualReportData = contextualReportID ? searchOptions.recentReports?.find((option) => option.reportID === contextualReportID) : undefined;

const updateAutocomplete = useCallback(
(autocompleteValue: string, ranges: AutocompleteRange[], autocompleteType?: ValueOf<typeof CONST.SEARCH.SYNTAX_ROOT_KEYS & typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>) => {
(autocompleteValue: string, ranges: SearchAutocompleteQueryRange[], autocompleteType?: ValueOf<typeof CONST.SEARCH.SYNTAX_ROOT_KEYS & typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>) => {
const alreadyAutocompletedKeys: string[] = [];
ranges.forEach((range) => {
if (!autocompleteType || range.key !== autocompleteType) {
Expand Down Expand Up @@ -282,15 +278,16 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
}));
break;
}
// Fixme implement card autocomplete ids
case CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID: {
const filteredCards = cardAutocompleteList
.filter((card) => card.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(card.toLowerCase()))
.filter((card) => card.bank.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(card.bank.toLowerCase()))
.sort()
.slice(0, 10);

filteredAutocompleteSuggestions = filteredCards.map((card) => ({
filterKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID,
text: card,
text: card.bank,
autocompleteID: card.cardID.toString(),
}));
break;
}
Expand Down Expand Up @@ -340,17 +337,23 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
);

const onSearchSubmit = useCallback(
(query: SearchQueryJSON | undefined) => {
if (!query) {
(queryString: SearchQueryString) => {
const cleanedQueryString = getQueryWithSubstitutions(queryString, autocompleteSubstitutions);
const queryJSON = SearchQueryUtils.buildSearchQueryJSON(cleanedQueryString);
if (!queryJSON) {
return;
}

onRouterClose();
const standardizedQuery = SearchQueryUtils.standardizeQueryJSON(query, cardList, allTaxRates);
const queryString = SearchQueryUtils.buildSearchQueryString(standardizedQuery);
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: queryString}));

const computeNodeValueFn = SearchQueryUtils.getUpdatedAmountValue;
const standardizedQuery = SearchQueryUtils.traverseAndUpdatedQuery(queryJSON, computeNodeValueFn);
const query = SearchQueryUtils.buildSearchQueryString(standardizedQuery);
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));

setTextInputValue('');
},
[allTaxRates, cardList, onRouterClose, setTextInputValue],
[autocompleteSubstitutions, onRouterClose, setTextInputValue],
);

const updateSubstitutionsMap = (key: string, value: string) => {
Expand All @@ -365,8 +368,6 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {

const modalWidth = shouldUseNarrowLayout ? styles.w100 : {width: variables.searchRouterPopoverWidth};

// console.log('[ROUTER]', {user: textInputValue, cleanQuery, autocompleteSubstitutions});

return (
<View
style={[styles.flex1, modalWidth, styles.h100, !shouldUseNarrowLayout && styles.mh85vh]}
Expand All @@ -383,7 +384,7 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
isFullWidth={shouldUseNarrowLayout}
updateSearch={onSearchChange}
onSubmit={() => {
onSearchSubmit(SearchQueryUtils.buildSearchQueryJSON(cleanQuery));
onSearchSubmit(textInputValue);
}}
routerListRef={listRef}
shouldShowOfflineMessage
Expand Down
6 changes: 3 additions & 3 deletions src/components/Search/SearchRouter/SearchRouterList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {ForwardedRef} from 'react';
import {useOnyx} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import {usePersonalDetails} from '@components/OnyxProvider';
import type {SearchQueryJSON} from '@components/Search/types';
import type {SearchQueryString} from '@components/Search/types';
import SelectionList from '@components/SelectionList';
import SearchQueryListItem from '@components/SelectionList/Search/SearchQueryListItem';
import type {SearchQueryItem, SearchQueryListItemProps} from '@components/SelectionList/Search/SearchQueryListItem';
Expand Down Expand Up @@ -56,7 +56,7 @@ type SearchRouterListProps = {
autocompleteItems: AutocompleteItemData[] | undefined;

/** Callback to submit query when selecting a list item */
onSearchSubmit: (query: SearchQueryJSON | undefined) => void;
onSearchSubmit: (query: SearchQueryString) => void;

/** Context present when opening SearchRouter from a report, invoice or workspace page */
reportForContextualSearch?: OptionData;
Expand Down Expand Up @@ -213,7 +213,7 @@ function SearchRouterList(
return;
}

onSearchSubmit(SearchQueryUtils.buildSearchQueryJSON(item.searchQuery));
onSearchSubmit(item.searchQuery);
}

// Handle selection of "Recent chat"
Expand Down
19 changes: 5 additions & 14 deletions src/components/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,16 @@ type SearchQueryJSON = {
flatFilters: QueryFilters;
} & SearchQueryAST;

// Fixme [Search] remove duplicate
type AutocompleteRange = {
key: ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS & typeof CONST.SEARCH.SYNTAX_ROOT_KEYS>;
length: number;
start: number;
value: string;
};

type SearchAutocompleteResult = {
autocomplete: AutocompleteRange | null;
ranges: AutocompleteRange[];
autocomplete: SearchAutocompleteQueryRange | null;
ranges: SearchAutocompleteQueryRange[];
};

type SearchAutocompleteQueryRange = {
key: ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>;
value: string;
start: number;
key: ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS & typeof CONST.SEARCH.SYNTAX_ROOT_KEYS>;
length: number;
start: number;
value: string;
};

export type {
Expand All @@ -119,6 +111,5 @@ export type {
TripSearchStatus,
ChatSearchStatus,
SearchAutocompleteResult,
AutocompleteRange,
SearchAutocompleteQueryRange,
};
Loading

0 comments on commit 533131a

Please sign in to comment.