Skip to content
1 change: 1 addition & 0 deletions src/hooks/useSearchTypeMenuSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const policySelector = (policy: OnyxEntry<Policy>): OnyxEntry<Policy> =>
areCompanyCardsEnabled: policy.areCompanyCardsEnabled,
areExpensifyCardsEnabled: policy.areExpensifyCardsEnabled,
achAccount: policy.achAccount,
areCategoriesEnabled: policy.areCategoriesEnabled,
};

const policiesSelector = (policies: OnyxCollection<Policy>) => createPoliciesSelector(policies, policySelector);
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SearchParser/searchParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4410,7 +4410,7 @@ function peg$parse(input, options) {
from: "from",
card: "card",
"withdrawal-id": "withdrawn",
category: "category",
category: "groupCategory",
tag: "tag",
merchant: "groupMerchant",
month: "groupmonth",
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SearchParser/searchParser.peggy
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from: "from",
card: "card",
"withdrawal-id": "withdrawn",
category: "category",
category: "groupCategory",
tag: "tag",
merchant: "groupMerchant",
month: "groupmonth",
Expand Down
14 changes: 9 additions & 5 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,15 +450,19 @@ function createTopSearchMenuItem(
groupBy: ValueOf<typeof CONST.SEARCH.GROUP_BY>,
limit?: number,
): SearchTypeMenuItem {
const isCategory = groupBy === CONST.SEARCH.GROUP_BY.CATEGORY;
const defaultSortBy = isCategory ? CONST.SEARCH.TABLE_COLUMNS.GROUP_CATEGORY : CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL;
const defaultSortOrder = isCategory ? CONST.SEARCH.SORT_ORDER.ASC : CONST.SEARCH.SORT_ORDER.DESC;

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,
sortBy: defaultSortBy,
sortOrder: defaultSortOrder,
...(limit && {limit}),
},
);
Expand Down Expand Up @@ -790,7 +794,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);
const isEligibleForTopCategoriesSuggestion = isPaidPolicy;
const isEligibleForTopCategoriesSuggestion = isPaidPolicy && policy.areCategoriesEnabled === true;
const isEligibleForTopMerchantsSuggestion = isPaidPolicy;

shouldShowSubmitSuggestion ||= isEligibleForSubmitSuggestion;
Expand Down Expand Up @@ -2258,8 +2262,8 @@ function getCategorySections(data: OnyxTypes.SearchResults['data'], queryJSON: S

let transactionsQueryJSON: SearchQueryJSON | undefined;
if (queryJSON && categoryGroup.category !== undefined) {
// Normalize empty category to CATEGORY_EMPTY_VALUE to avoid invalid query like "category:"
const categoryValue = categoryGroup.category === '' ? CONST.SEARCH.CATEGORY_EMPTY_VALUE : categoryGroup.category;
const isEmptyCategory = !categoryGroup.category;
const categoryValue = isEmptyCategory ? CONST.SEARCH.CATEGORY_EMPTY_VALUE : categoryGroup.category;

const newFlatFilters = queryJSON.flatFilters.filter((filter) => filter.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY);
newFlatFilters.push({key: CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY, filters: [{operator: CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO, value: categoryValue}]});
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ function search({
...(limit !== undefined && {maximumResults: limit}),
};
const jsonQuery = JSON.stringify(query);

saveLastSearchParams({
queryJSON,
offset,
Expand Down
104 changes: 104 additions & 0 deletions tests/unit/Search/SearchUIUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4713,6 +4713,110 @@ describe('SearchUIUtils', () => {
const response2 = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response2.export).toBe(true);
});

test('Should show Top Categories when areCategoriesEnabled is true', () => {
const policyKey = `policy_${policyID}`;

const policies: OnyxCollection<OnyxTypes.Policy> = {
[policyKey]: {
id: policyID,
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: true,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(true);
});

test('Should hide Top Categories when areCategoriesEnabled is false', () => {
const policyKey = `policy_${policyID}`;

const policies: OnyxCollection<OnyxTypes.Policy> = {
[policyKey]: {
id: policyID,
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: false,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(false);
});

test('Should not show Top Categories when areCategoriesEnabled is undefined', () => {
const policyKey = `policy_${policyID}`;

const policies: OnyxCollection<OnyxTypes.Policy> = {
[policyKey]: {
id: policyID,
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: undefined,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(false);
});

test('Should not show Top Categories for free policies even if categories are enabled', () => {
const policyKey = `policy_${policyID}`;

const policies: OnyxCollection<OnyxTypes.Policy> = {
[policyKey]: {
id: policyID,
type: CONST.POLICY.TYPE.PERSONAL,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: true,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(false);
});

test('Should show Top Categories if at least one policy has categories enabled', () => {
const policies: OnyxCollection<OnyxTypes.Policy> = {
policyOne: {
id: 'policyOne',
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: false,
} as OnyxTypes.Policy,
policyTwo: {
id: 'policyTwo',
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: true,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(true);
});

test('Should hide Top Categories if all policies have categories disabled', () => {
const policies: OnyxCollection<OnyxTypes.Policy> = {
policyOne: {
id: 'policyOne',
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: false,
} as OnyxTypes.Policy,
policyTwo: {
id: 'policyTwo',
type: CONST.POLICY.TYPE.TEAM,
role: CONST.POLICY.ROLE.ADMIN,
areCategoriesEnabled: false,
} as OnyxTypes.Policy,
};

const response = SearchUIUtils.getSuggestedSearchesVisibility(adminEmail, {}, policies, undefined);
expect(response.topCategories).toBe(false);
});
});

describe('Test getColumnsToShow', () => {
Expand Down
Loading