Skip to content
2 changes: 2 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7285,12 +7285,14 @@ const CONST = {
UNAPPROVED_CARD: 'unapprovedCard',
RECONCILIATION: 'reconciliation',
TOP_SPENDERS: 'topSpenders',
TOP_CATEGORIES: 'topCategories',
},
GROUP_PREFIX: 'group_',
ANIMATION: {
FADE_DURATION: 200,
},
TODO_BADGE_MAX_COUNT: 50,
TOP_SEARCH_LIMIT: 10,
},
SEARCH_SELECTOR: {
SELECTION_MODE_SINGLE: 'single',
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6859,6 +6859,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
deleteSavedSearchConfirm: 'Möchten Sie diese Suche wirklich löschen?',
searchName: 'Name suchen',
savedSearchesMenuItemTitle: 'Gespeichert',
topCategories: 'Top-Kategorien',
groupedExpenses: 'gruppierte Ausgaben',
bulkActions: {
approve: 'Genehmigen',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -6745,6 +6745,7 @@ const translations = {
deleteSavedSearchConfirm: 'Are you sure you want to delete this search?',
searchName: 'Search name',
savedSearchesMenuItemTitle: 'Saved',
topCategories: 'Top categories',
groupedExpenses: 'grouped expenses',
bulkActions: {
approve: 'Approve',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6492,6 +6492,7 @@ ${amount} para ${merchant} - ${date}`,
topSpenders: 'Mayores gastadores',
saveSearch: 'Guardar búsqueda',
savedSearchesMenuItemTitle: 'Guardadas',
topCategories: 'Categorías principales',
searchName: 'Nombre de la búsqueda',
deleteSavedSearch: 'Eliminar búsqueda guardada',
deleteSavedSearchConfirm: '¿Estás seguro de que quieres eliminar esta búsqueda?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6870,6 +6870,7 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
deleteSavedSearchConfirm: 'Voulez-vous vraiment supprimer cette recherche ?',
searchName: 'Rechercher un nom',
savedSearchesMenuItemTitle: 'Enregistré',
topCategories: 'Catégories principales',
groupedExpenses: 'dépenses groupées',
bulkActions: {
approve: 'Approuver',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6849,6 +6849,7 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
deleteSavedSearchConfirm: 'Sei sicuro di voler eliminare questa ricerca?',
searchName: 'Cerca nome',
savedSearchesMenuItemTitle: 'Salvato',
topCategories: 'Categorie principali',
groupedExpenses: 'spese raggruppate',
bulkActions: {
approve: 'Approva',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6790,6 +6790,7 @@ ${reportName}
deleteSavedSearchConfirm: 'この検索を削除してもよろしいですか?',
searchName: '名前を検索',
savedSearchesMenuItemTitle: '保存済み',
topCategories: 'トップカテゴリ',
groupedExpenses: 'グループ化された経費',
bulkActions: {
approve: '承認',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6831,6 +6831,7 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
deleteSavedSearchConfirm: 'Weet je zeker dat je deze zoekopdracht wilt verwijderen?',
searchName: 'Naam zoeken',
savedSearchesMenuItemTitle: 'Opgeslagen',
topCategories: 'Topcategorieën',
groupedExpenses: 'gegroepeerde uitgaven',
bulkActions: {
approve: 'Goedkeuren',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6820,6 +6820,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
deleteSavedSearchConfirm: 'Czy na pewno chcesz usunąć to wyszukiwanie?',
searchName: 'Wyszukaj nazwę',
savedSearchesMenuItemTitle: 'Zapisano',
topCategories: 'Najlepsze kategorie',
groupedExpenses: 'pogrupowane wydatki',
bulkActions: {
approve: 'Zatwierdź',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6822,6 +6822,7 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
deleteSavedSearchConfirm: 'Você tem certeza de que deseja excluir esta pesquisa?',
searchName: 'Pesquisar nome',
savedSearchesMenuItemTitle: 'Salvo',
topCategories: 'Principais categorias',
groupedExpenses: 'despesas agrupadas',
bulkActions: {
approve: 'Aprovar',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6671,6 +6671,7 @@ ${reportName}
deleteSavedSearchConfirm: '您确定要删除此搜索吗?',
searchName: '搜索名称',
savedSearchesMenuItemTitle: '已保存',
topCategories: '热门类别',
groupedExpenses: '已分组报销费用',
bulkActions: {
approve: '批准',
Expand Down
92 changes: 63 additions & 29 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,47 @@ type GetSectionsParams = {
allTransactionViolations?: OnyxCollection<OnyxTypes.TransactionViolation[]>;
};

/**
* Creates a top search menu item with common structure for TOP_SPENDERS and TOP_CATEGORIES
*/
function createTopSearchMenuItem(
key: SearchKey,
translationPath: TranslationPaths,
icon: IconAsset | Extract<ExpensifyIconName, 'Receipt' | 'ChatBubbles' | 'MoneyBag' | 'CreditCard' | 'MoneyHourglass' | 'CreditCardHourglass' | 'Bank'>,
groupBy: ValueOf<typeof CONST.SEARCH.GROUP_BY>,
limit?: number,
): SearchTypeMenuItem {
const searchQuery = buildQueryStringFromFilterFormValues(
{
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
groupBy,
dateOn: CONST.SEARCH.DATE_PRESETS.LAST_MONTH,
},
{
sortBy: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL,
sortOrder: CONST.SEARCH.SORT_ORDER.DESC,
...(limit && {limit}),
},
);

return {
key,
translationPath,
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
icon,
searchQuery,
get searchQueryJSON() {
return buildSearchQueryJSON(this.searchQuery);
},
get hash() {
return this.searchQueryJSON?.hash ?? CONST.DEFAULT_NUMBER_ID;
},
get similarSearchHash() {
return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
};
}

/**
* Returns a list of all possible searches in the LHN, along with their query & hash.
* *NOTE* When rendering the LHN, you should use the "createTypeMenuSections" method, which
Expand Down Expand Up @@ -623,32 +664,14 @@ function getSuggestedSearches(
return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
},
[CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS]: {
key: CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS,
translationPath: 'search.topSpenders',
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
icon: Expensicons.User,
searchQuery: buildQueryStringFromFilterFormValues(
{
type: CONST.SEARCH.DATA_TYPES.EXPENSE,
groupBy: CONST.SEARCH.GROUP_BY.FROM,
dateOn: CONST.SEARCH.DATE_PRESETS.LAST_MONTH,
},
{
sortBy: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL,
sortOrder: CONST.SEARCH.SORT_ORDER.DESC,
},
),
get searchQueryJSON() {
return buildSearchQueryJSON(this.searchQuery);
},
get hash() {
return this.searchQueryJSON?.hash ?? CONST.DEFAULT_NUMBER_ID;
},
get similarSearchHash() {
return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
},
},
[CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS]: createTopSearchMenuItem(CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS, 'search.topSpenders', Expensicons.User, CONST.SEARCH.GROUP_BY.FROM),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We do not need to add the createTopSearchMenuItem util function here. Therefore, you only need to add the TOP_CATEGORIES case and follow the existing code pattern above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

please check this ai comment #80102 (comment)

[CONST.SEARCH.SEARCH_KEYS.TOP_CATEGORIES]: createTopSearchMenuItem(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add TOP_CATEGORIES definition in getSuggestedSearches() with query: type:expense group-by:category date:last-month sort-by:group-total sort-order:desc limit:10

Image

Pease double-check the search query. It looks like it’s missing limit 10

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this will be part of #80035, limit filter not exist yet

CONST.SEARCH.SEARCH_KEYS.TOP_CATEGORIES,
'search.topCategories',
Expensicons.Folder,
Comment thread
ashu75575 marked this conversation as resolved.
CONST.SEARCH.GROUP_BY.CATEGORY,
CONST.SEARCH.TOP_SEARCH_LIMIT,
),
};
}

Expand All @@ -671,6 +694,7 @@ function getSuggestedSearchesVisibility(
let shouldShowUnapprovedCardSuggestion = false;
let shouldShowReconciliationSuggestion = false;
let shouldShowTopSpendersSuggestion = false;
let shouldShowTopCategoriesSuggestion = false;

const hasCardFeed = Object.values(cardFeedsByPolicy ?? {}).some((feeds) => feeds.length > 0);

Expand Down Expand Up @@ -707,6 +731,7 @@ function getSuggestedSearchesVisibility(
const isEligibleForReconciliationSuggestion = isPaidPolicy && isAdmin && ((isPaymentEnabled && hasVBBA && hasReimburser) || isECardEnabled);
const isAuditor = policy.role === CONST.POLICY.ROLE.AUDITOR;
const isEligibleForTopSpendersSuggestion = isPaidPolicy && (isAdmin || isAuditor || isApprover);
Comment thread
ashu75575 marked this conversation as resolved.
const isEligibleForTopCategoriesSuggestion = isPaidPolicy;

shouldShowSubmitSuggestion ||= isEligibleForSubmitSuggestion;
shouldShowPaySuggestion ||= isEligibleForPaySuggestion;
Expand All @@ -717,6 +742,7 @@ function getSuggestedSearchesVisibility(
shouldShowUnapprovedCardSuggestion ||= isEligibleForUnapprovedCardSuggestion;
shouldShowReconciliationSuggestion ||= isEligibleForReconciliationSuggestion;
shouldShowTopSpendersSuggestion ||= isEligibleForTopSpendersSuggestion;
shouldShowTopCategoriesSuggestion ||= isEligibleForTopCategoriesSuggestion;

// We don't need to check the rest of the policies if we already determined that all suggestions should be displayed
return (
Expand All @@ -728,7 +754,8 @@ function getSuggestedSearchesVisibility(
shouldShowUnapprovedCashSuggestion &&
shouldShowUnapprovedCardSuggestion &&
shouldShowReconciliationSuggestion &&
shouldShowTopSpendersSuggestion
shouldShowTopSpendersSuggestion &&
shouldShowTopCategoriesSuggestion
);
});

Expand All @@ -745,6 +772,7 @@ function getSuggestedSearchesVisibility(
[CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CARD]: shouldShowUnapprovedCardSuggestion,
[CONST.SEARCH.SEARCH_KEYS.RECONCILIATION]: shouldShowReconciliationSuggestion,
[CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS]: shouldShowTopSpendersSuggestion,
[CONST.SEARCH.SEARCH_KEYS.TOP_CATEGORIES]: shouldShowTopCategoriesSuggestion,
};
}

Expand Down Expand Up @@ -3019,9 +3047,15 @@ function createTypeMenuSections(
menuItems: [],
};

if (suggestedSearchesVisibility[CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS]) {
const insightsSearchKeys = [CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS, CONST.SEARCH.SEARCH_KEYS.TOP_CATEGORIES];

for (const key of insightsSearchKeys) {
if (!suggestedSearchesVisibility[key]) {
continue;
}

insightsSection.menuItems.push({
...suggestedSearches[CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS],
...suggestedSearches[key],
emptyState: {
title: 'search.searchResults.emptyResults.title',
subtitle: 'search.searchResults.emptyResults.subtitle',
Expand Down
Loading