Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d15fbc7
allow workspace and status filter to be negatable
bernhardoj Jul 1, 2026
58cc820
make it undefinedable
bernhardoj Jul 1, 2026
a607e7e
simplify
bernhardoj Jul 1, 2026
74c6b78
lint and test
bernhardoj Jul 1, 2026
7687dcd
improvement
bernhardoj Jul 1, 2026
857b5d3
lint
bernhardoj Jul 1, 2026
ff3db0b
prettier
bernhardoj Jul 1, 2026
5afd48b
remove unused export
bernhardoj Jul 1, 2026
f405bfa
remove unused export
bernhardoj Jul 1, 2026
6f543cd
fix test
bernhardoj Jul 1, 2026
122541b
revert unintended change
bernhardoj Jul 1, 2026
4396bb0
make it undefinedable
bernhardoj Jul 1, 2026
d21f3c5
add more test
bernhardoj Jul 1, 2026
075a7e0
lint
bernhardoj Jul 1, 2026
2f2b057
Merge branch 'main' into feat/93208-negatable-workspace-and-status
bernhardoj Jul 8, 2026
ab98fc5
fix test
bernhardoj Jul 8, 2026
850354e
fix test
bernhardoj Jul 8, 2026
b3a9037
remove unnecessary wrapper
bernhardoj Jul 8, 2026
def4f71
lint
bernhardoj Jul 8, 2026
0fe5753
lint
bernhardoj Jul 8, 2026
8c2b8e4
Merge branch 'main' into feat/93208-negatable-workspace-and-status
bernhardoj Jul 9, 2026
945769a
rename
bernhardoj Jul 9, 2026
9446de4
check the length
bernhardoj Jul 9, 2026
f875d35
combine into 1 if
bernhardoj Jul 9, 2026
e155d74
extract logic to s reusable function
bernhardoj Jul 9, 2026
cb80450
simplify
bernhardoj Jul 9, 2026
6ca99da
remove status ALL usages
bernhardoj Jul 9, 2026
f921061
make it more reusable
bernhardoj Jul 9, 2026
7ec6659
preserve the query hashes
bernhardoj Jul 9, 2026
da45e90
fix typecheck
bernhardoj Jul 9, 2026
04e1251
fix test
bernhardoj Jul 9, 2026
b75495e
Merge branch 'main' into feat/93208-negatable-workspace-and-status
bernhardoj Jul 9, 2026
5c0c385
remove unused import
bernhardoj Jul 9, 2026
fedacb2
remove unused export
bernhardoj Jul 9, 2026
0e43105
lint
bernhardoj Jul 9, 2026
55f9009
Merge branch 'main' into feat/93208-negatable-workspace-and-status
bernhardoj Jul 13, 2026
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
6 changes: 0 additions & 6 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6837,7 +6837,6 @@ const CONST = {
},
STATUS: {
EXPENSE: {
ALL: '',
UNREPORTED: 'unreported',
DRAFTS: 'drafts',
OUTSTANDING: 'outstanding',
Expand All @@ -6847,26 +6846,22 @@ const CONST = {
DELETED: 'deleted',
},
EXPENSE_REPORT: {
ALL: '',
DRAFTS: 'drafts',
OUTSTANDING: 'outstanding',
APPROVED: 'approved',
DONE: 'done',
PAID: 'paid',
},
INVOICE: {
ALL: '',
OUTSTANDING: 'outstanding',
PAID: 'paid',
},
TRIP: {
ALL: '',
CURRENT: 'current',
PAST: 'past',
},
CHAT: {},
TASK: {
ALL: '',
OUTSTANDING: 'outstanding',
COMPLETED: 'completed',
},
Expand Down Expand Up @@ -6956,7 +6951,6 @@ const CONST = {
},
SYNTAX_ROOT_KEYS: {
TYPE: 'type',
STATUS: 'status',
SORT_BY: 'sortBy',
SORT_ORDER: 'sortOrder',
VIEW: 'view',
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Icon from '@components/Icon';
import {PressableWithFeedback} from '@components/Pressable';
import ScrollView from '@components/ScrollView';
import type {Filter} from '@components/Search/types';
import SpacerView from '@components/SpacerView';
import Text from '@components/Text';

Expand Down Expand Up @@ -33,7 +34,7 @@ type FilterItemCallbacks = {

type FilterListProps = FilterItemCallbacks & {
type: SearchDataTypes | undefined;
policyID: string[] | undefined;
policyID: Filter;
selectedFilter?: SearchFilter['key'];
style?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isAmountFilterKey, isDateFilterKey, isTextFilterKey} from '@libs/SearchUIUtils';
import {getFilterNegatableValue, isAmountFilterKey, isDateFilterKey, isTextFilterKey} from '@libs/SearchUIUtils';
import type {SearchFilter} from '@libs/SearchUIUtils';

import CONST from '@src/CONST';
Expand All @@ -8,7 +8,6 @@ import React from 'react';

import type {FilterComponentsProps} from '..';
import type {AmountFilterContentProps} from './AmountFilterContent';
import type {CommonFilterContentProps} from './CommonFilterContent';
import type {DateFilterContentProps} from './DateFilterContent';
import type {ReportFieldFilterContentProps} from './ReportFieldFilterContent';
import type {TextInputFilterContentProps} from './TextInputFilterContent';
Expand All @@ -17,11 +16,10 @@ type TextInputFilterContentWrapperProps = Pick<TextInputFilterContentProps, 'fil
type AmountFilterContentWrapperProps = Pick<AmountFilterContentProps, 'filterKey' | 'value' | 'onChange'>;
type DateFilterContentWrapperProps = Pick<DateFilterContentProps, 'filterKey' | 'value' | 'hasFeed' | 'onChange'>;
type ReportFieldFilterContentWrapperProps = Pick<ReportFieldFilterContentProps, 'values' | 'onChange'>;
type CommonFilterContentWrapperProps = Omit<CommonFilterContentProps, 'selectionListTextInputStyle' | 'selectionListStyle' | 'autoFocus' | 'footer'>;
type CommonFilterContentWrapperProps = Omit<FilterComponentsProps, 'selectionListTextInputStyle' | 'selectionListStyle' | 'autoFocus' | 'footer'>;
type SearchAdvancedFiltersContentProps = {
filterKey: SearchFilter['key'];
values: Partial<SearchAdvancedFiltersForm> | undefined;
policyIDQuery: string[] | undefined;
ready?: boolean;
components: {
Text: React.ComponentType<TextInputFilterContentWrapperProps>;
Expand All @@ -39,7 +37,7 @@ function getFilterFormValue<K extends FilterComponentsProps['filterKey']>(filter
return update;
}

function SearchAdvancedFiltersContent({filterKey, values, policyIDQuery, ready, components, onChange}: SearchAdvancedFiltersContentProps) {
function SearchAdvancedFiltersContent({filterKey, values, ready, components, onChange}: SearchAdvancedFiltersContentProps) {
const {Text: TextFilter, Amount: AmountFilter, Date: DateFilter, ReportField: ReportFieldFilter, Common: CommonFilter} = components;

if (isTextFilterKey(filterKey)) {
Expand Down Expand Up @@ -112,8 +110,7 @@ function SearchAdvancedFiltersContent({filterKey, values, policyIDQuery, ready,
filterKey={filterKey}
value={values?.[filterKey]}
type={values?.type}
policyIDs={values?.policyID}
policyIDQuery={policyIDQuery}
policyID={getFilterNegatableValue(CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID, values)}
ready={ready}
onChange={(newValue) => onChange(getFilterFormValue(filterKey, newValue))}
/>
Expand Down
30 changes: 10 additions & 20 deletions src/components/Search/FilterComponents/CategorySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type {SearchFilterCommonProps} from '@components/Search/types';
import type {Filter, SearchFilterCommonProps} from '@components/Search/types';

import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';

import {getDecodedCategoryName} from '@libs/CategoryUtils';
import {sortOptionsWithEmptyValue} from '@libs/SearchQueryUtils';
import {getAllPolicyValues, sortOptionsWithEmptyValue} from '@libs/SearchQueryUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PolicyCategories, PolicyCategory} from '@src/types/onyx';
import type {PolicyCategories} from '@src/types/onyx';
import {getEmptyObject} from '@src/types/utils/EmptyObject';

import type {OnyxCollection} from 'react-native-onyx';
Expand All @@ -18,10 +18,10 @@ import React from 'react';
import MultiSelect from './MultiSelect';

type CategorySelectorProps = SearchFilterCommonProps<string[] | undefined> & {
policyIDs: string[] | undefined;
policyID: Filter | undefined;
};

function CategorySelector({value = [], policyIDs = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: CategorySelectorProps) {
function CategorySelector({value = [], policyID, selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: CategorySelectorProps) {
const {translate, localeCompare} = useLocalize();
const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID);

Expand Down Expand Up @@ -50,23 +50,13 @@ function CategorySelector({value = [], policyIDs = [], selectionListTextInputSty
},
[availableNonPersonalPolicyCategoriesSelector],
);
const selectedPoliciesCategories: PolicyCategory[] = Object.keys(allPolicyCategories ?? {})
.filter((key) => policyIDs.map((policyID) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`)?.includes(key))
.map((key) => Object.values(allPolicyCategories?.[key] ?? {}))
.flat();

const categoryItems = [{text: translate('search.noCategory'), value: CONST.SEARCH.CATEGORY_EMPTY_VALUE as string}];
const uniqueCategoryNames = new Set<string>();
if (policyIDs.length === 0) {
const categories = Object.values(allPolicyCategories ?? {}).flatMap((policyCategories) => Object.values(policyCategories ?? {}));
for (const category of categories) {
uniqueCategoryNames.add(category.name);
}
} else if (selectedPoliciesCategories.length > 0) {
for (const category of selectedPoliciesCategories) {
uniqueCategoryNames.add(category.name);
}
}
const uniqueCategoryNames = new Set<string>(
getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_CATEGORIES, allPolicyCategories).flatMap((policyCategories) =>
Object.values(policyCategories ?? {}).map((category) => category.name),
),
);
categoryItems.push(
...Array.from(uniqueCategoryNames)
.filter(Boolean)
Expand Down
12 changes: 6 additions & 6 deletions src/components/Search/FilterComponents/ExportedToSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Icon from '@components/Icon';
import type {SearchFilterCommonProps} from '@components/Search/types';
import type {Filter, SearchFilterCommonProps} from '@components/Search/types';

import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -10,8 +10,8 @@ import useThemeStyles from '@hooks/useThemeStyles';

import {getSearchValueForConnection} from '@libs/AccountingUtils';
import {getExportTemplates} from '@libs/actions/Search';
import {getConnectedIntegrationNamesForPolicies} from '@libs/PolicyUtils';
import {getIntegrationIcon} from '@libs/ReportUtils';
import {getAllPolicyValues, getConnectedIntegrationNamesForPolicies} from '@libs/SearchQueryUtils';

import variables from '@styles/variables';

Expand All @@ -27,15 +27,15 @@ import {View} from 'react-native';
import MultiSelect from './MultiSelect';

type ExportedToSelectorProps = SearchFilterCommonProps<string[] | undefined> & {
policyIDs: string[] | undefined;
policyID: Filter | undefined;
};

const STANDARD_EXPORT_TEMPLATE_ID_TO_DISPLAY_LABEL: Record<string, string> = {
[CONST.REPORT.EXPORT_OPTIONS.REPORT_LEVEL_EXPORT]: CONST.REPORT.EXPORT_OPTION_LABELS.REPORT_LEVEL_EXPORT,
[CONST.REPORT.EXPORT_OPTIONS.EXPENSE_LEVEL_EXPORT]: CONST.REPORT.EXPORT_OPTION_LABELS.EXPENSE_LEVEL_EXPORT,
};

function ExportedToSelector({value = [], policyIDs = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: ExportedToSelectorProps) {
function ExportedToSelector({value = [], policyID, selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: ExportedToSelectorProps) {
const styles = useThemeStyles();
const {translate, localeCompare} = useLocalize();
const StyleUtils = useStyleUtils();
Expand All @@ -56,7 +56,7 @@ function ExportedToSelector({value = [], policyIDs = [], selectionListTextInputS
const [csvExportLayouts] = useOnyx(ONYXKEYS.NVP_CSV_EXPORT_LAYOUTS);
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);

const connectedIntegrationNames = getConnectedIntegrationNamesForPolicies(policies, policyIDs.length > 0 ? policyIDs : undefined);
const connectedIntegrationNames = getConnectedIntegrationNamesForPolicies(policies, policyID);

const integrationConnectionNames = CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES;

Expand Down Expand Up @@ -97,7 +97,7 @@ function ExportedToSelector({value = [], policyIDs = [], selectionListTextInputS
});

const usedPickerValueKeys = new Set(connectedIntegrationPickerItems.map((item) => item.value));
const policiesToLoadTemplatesFrom = policyIDs.length > 0 ? policyIDs.map((id) => policies?.[`${ONYXKEYS.COLLECTION.POLICY}${id}`]).filter(Boolean) : Object.values(policies ?? {});
const policiesToLoadTemplatesFrom = getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY, policies);
const exportTemplatesFromPolicies = policiesToLoadTemplatesFrom.flatMap((policy) => getExportTemplates([], {}, translate, policy, false));
const exportTemplatesFromAccount = getExportTemplates(integrationsExportTemplates ?? [], csvExportLayouts ?? {}, translate, undefined, true);
const allExportTemplates = [...exportTemplatesFromAccount, ...exportTemplatesFromPolicies];
Expand Down
27 changes: 7 additions & 20 deletions src/components/Search/FilterComponents/TagSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {SearchFilterCommonProps} from '@components/Search/types';
import type {Filter, SearchFilterCommonProps} from '@components/Search/types';

import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';

import {getCleanedTagName, getTagNamesFromTagsLists} from '@libs/PolicyUtils';
import {sortOptionsWithEmptyValue} from '@libs/SearchQueryUtils';
import {getAllPolicyValues, sortOptionsWithEmptyValue} from '@libs/SearchQueryUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -19,30 +19,17 @@ import React from 'react';
import MultiSelect from './MultiSelect';

type TagSelectorProps = SearchFilterCommonProps<string[] | undefined> & {
policyIDs: string[] | undefined;
policyID: Filter | undefined;
};

function TagSelector({value = [], policyIDs = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TagSelectorProps) {
function TagSelector({value = [], policyID, selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TagSelectorProps) {
const {translate, localeCompare} = useLocalize();
const [allPolicyTagLists = getEmptyObject<NonNullable<OnyxCollection<PolicyTagLists>>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {selector: passthroughPolicyTagListSelector});

const selectedPoliciesTagLists = Object.keys(allPolicyTagLists ?? {})
.filter((key) => policyIDs.map((policyID) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`)?.includes(key))
?.map((key) => getTagNamesFromTagsLists(allPolicyTagLists?.[key] ?? {}))
.flat();

const tagItems = [{text: translate('search.noTag'), value: CONST.SEARCH.TAG_EMPTY_VALUE as string}];
const uniqueTagNames = new Set<string>();
if (policyIDs.length === 0) {
const tagListsUnpacked = Object.values(allPolicyTagLists ?? {}).filter((item) => !!item);
for (const tag of tagListsUnpacked.map(getTagNamesFromTagsLists).flat()) {
uniqueTagNames.add(tag);
}
} else if (selectedPoliciesTagLists.length > 0) {
for (const tag of selectedPoliciesTagLists) {
uniqueTagNames.add(tag);
}
}
const uniqueTagNames = new Set<string>(
getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_TAGS, allPolicyTagLists).flatMap((policyTags) => getTagNamesFromTagsLists(policyTags ?? {})),
);
tagItems.push(
...Array.from(uniqueTagNames)
.map((tagName) => ({text: getCleanedTagName(tagName), value: tagName}))
Expand Down
17 changes: 5 additions & 12 deletions src/components/Search/FilterComponents/TaxRateSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
import type {SearchFilterCommonProps} from '@components/Search/types';
import type {Filter, SearchFilterCommonProps} from '@components/Search/types';

import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';

import {getAllTaxRates} from '@libs/PolicyUtils';
import {getAllPolicyValuesMap} from '@libs/SearchQueryUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy} from '@src/types/onyx';

import React from 'react';

import MultiSelect from './MultiSelect';

type TaxRateSelectorProps = SearchFilterCommonProps<string[] | undefined> & {
policyIDs: string[] | undefined;
policyID: Filter | undefined;
};

function TaxRateSelector({value = [], policyIDs = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TaxRateSelectorProps) {
function TaxRateSelector({value = [], policyID, selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TaxRateSelectorProps) {
const {localeCompare} = useLocalize();
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);

const allTaxRates = getAllTaxRates(policies);
const selectedPoliciesMap = policyIDs?.reduce<Record<string, Policy>>((acc, policyID) => {
const key = `${ONYXKEYS.COLLECTION.POLICY}${policyID}`;
const policy = policies?.[key];
if (policy) {
acc[key] = policy;
}
return acc;
}, {});
const selectedPoliciesMap = getAllPolicyValuesMap(policyID, ONYXKEYS.COLLECTION.POLICY, policies);
const scopedTaxRates = !selectedPoliciesMap || Object.keys(selectedPoliciesMap).length === 0 ? allTaxRates : getAllTaxRates(selectedPoliciesMap);
const taxItems = Object.entries(scopedTaxRates)
.map(([taxRateName, taxRateKeys]) => ({
Expand Down
3 changes: 2 additions & 1 deletion src/components/Search/FilterComponents/TypeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {emailSelector} from '@src/selectors/Session';
import type {Policy} from '@src/types/onyx';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';

import type {OnyxCollection} from 'react-native-onyx';

import React from 'react';

import SingleSelect from './SingleSelect';

type TypeSelectorProps = SearchFilterCommonProps<string | undefined>;
type TypeSelectorProps = SearchFilterCommonProps<SearchDataTypes | undefined>;

/**
* Extracts only the fields needed by getTypeOptions (canSendInvoice check).
Expand Down
Loading
Loading