Add any field filter requests (#13336)
This PR adds any field filter request generation utils with its unit test. It also calls this new util in the relevant requests for table and board. This PR also adds a new corresponding state in context store so that the filter is handled in command menu and actions. We also add this new filter to the aggregate queries. The RecordShowPage story was also fixed.
This commit is contained in:
@ -1,25 +1,25 @@
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const ObjectFilterDropdownAnyFieldSearchInput = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const [viewAnyFieldSearchValue, setViewAnyFieldSearchValue] =
|
||||
useRecoilComponentStateV2(viewAnyFieldSearchValueComponentState);
|
||||
const [anyFieldFilterSearchValue, setAnyFieldFilterSearchValue] =
|
||||
useRecoilComponentStateV2(anyFieldFilterValueComponentState);
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputValue = event.target.value;
|
||||
|
||||
setViewAnyFieldSearchValue(inputValue);
|
||||
setAnyFieldFilterSearchValue(inputValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
value={viewAnyFieldSearchValue}
|
||||
value={anyFieldFilterSearchValue}
|
||||
placeholder={t`Search any field`}
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
|
||||
@ -30,8 +30,8 @@ export const useActorFieldDisplay = (): ActorFieldDisplayValue | undefined => {
|
||||
}
|
||||
|
||||
const relatedWorkspaceMember = [
|
||||
...currentWorkspaceDeletedMembers,
|
||||
...currentWorkspaceMembers,
|
||||
...(currentWorkspaceDeletedMembers ?? []),
|
||||
...(currentWorkspaceMembers ?? []),
|
||||
].find(
|
||||
(workspaceMember) => workspaceMember.id === fieldValue.workspaceMemberId,
|
||||
);
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
|
||||
export const anyFieldFilterValueComponentState = createComponentStateV2<string>(
|
||||
{
|
||||
key: 'anyFieldFilterValueComponentState',
|
||||
defaultValue: '',
|
||||
componentInstanceContext: RecordFiltersComponentInstanceContext,
|
||||
},
|
||||
);
|
||||
@ -0,0 +1,603 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { filterSelectOptionsOfFieldMetadataItem } from '@/object-record/record-filter/utils/filterSelectOptionsOfFieldMetadataItem';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const baseFieldMetadataItem: FieldMetadataItem = {
|
||||
id: 'base-field-metadata-item-id',
|
||||
createdAt: new Date().toISOString(),
|
||||
label: 'Test',
|
||||
name: 'test',
|
||||
type: FieldMetadataType.TEXT,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const textFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'text-field-id',
|
||||
name: 'textField',
|
||||
};
|
||||
|
||||
const addressFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'address-field-id',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
name: 'addressField',
|
||||
};
|
||||
|
||||
const linksFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'links-field-id',
|
||||
type: FieldMetadataType.LINKS,
|
||||
name: 'linksField',
|
||||
};
|
||||
|
||||
const fullNameFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'full-name-field-id',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'fullNameField',
|
||||
};
|
||||
|
||||
const arrayFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'array-field-id',
|
||||
type: FieldMetadataType.ARRAY,
|
||||
name: 'arrayField',
|
||||
};
|
||||
|
||||
const emailsFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'emails-field-id',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
name: 'emailsField',
|
||||
};
|
||||
|
||||
const phonesFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'phones-field-id',
|
||||
type: FieldMetadataType.PHONES,
|
||||
name: 'phonesField',
|
||||
};
|
||||
|
||||
const numberFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'number-field-id',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
name: 'numberField',
|
||||
};
|
||||
|
||||
const currencyFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'currency-field-id',
|
||||
type: FieldMetadataType.CURRENCY,
|
||||
name: 'currencyField',
|
||||
};
|
||||
|
||||
const selectFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'select-field-id',
|
||||
type: FieldMetadataType.SELECT,
|
||||
name: 'selectField',
|
||||
options: [
|
||||
{
|
||||
color: 'blue',
|
||||
id: '1',
|
||||
label: 'blue',
|
||||
position: 1,
|
||||
value: 'BLUE',
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
id: '2',
|
||||
label: 'red',
|
||||
position: 2,
|
||||
value: 'RED',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const multiSelectFieldMetadataItem: FieldMetadataItem = {
|
||||
...baseFieldMetadataItem,
|
||||
id: 'multi-select-field-id',
|
||||
type: FieldMetadataType.MULTI_SELECT,
|
||||
name: 'multiSelect',
|
||||
options: [
|
||||
{
|
||||
color: 'blue',
|
||||
id: '1',
|
||||
label: 'blue',
|
||||
position: 1,
|
||||
value: 'BLUE',
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
id: '2',
|
||||
label: 'red',
|
||||
position: 2,
|
||||
value: 'RED',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockObjectMetadataItem: ObjectMetadataItem = {
|
||||
id: 'mock-object-metadata-item',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
indexMetadatas: [],
|
||||
isActive: true,
|
||||
isCustom: true,
|
||||
isLabelSyncedWithName: true,
|
||||
isRemote: false,
|
||||
isSearchable: true,
|
||||
isSystem: false,
|
||||
labelIdentifierFieldMetadataId: 'mock-id',
|
||||
labelPlural: 'Tests',
|
||||
labelSingular: 'Test',
|
||||
nameSingular: 'test',
|
||||
namePlural: 'tests',
|
||||
fields: [],
|
||||
};
|
||||
|
||||
const mockObjectMetadataItemWithAllFields: ObjectMetadataItem = {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [
|
||||
textFieldMetadataItem,
|
||||
addressFieldMetadataItem,
|
||||
linksFieldMetadataItem,
|
||||
fullNameFieldMetadataItem,
|
||||
arrayFieldMetadataItem,
|
||||
emailsFieldMetadataItem,
|
||||
phonesFieldMetadataItem,
|
||||
numberFieldMetadataItem,
|
||||
currencyFieldMetadataItem,
|
||||
selectFieldMetadataItem,
|
||||
multiSelectFieldMetadataItem,
|
||||
],
|
||||
};
|
||||
|
||||
describe('turnAnyFieldFilterIntoRecordGqlFilter', () => {
|
||||
describe('TEXT field type', () => {
|
||||
it('should generate correct filter for text field', () => {
|
||||
const filterValue = 'test';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [textFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[textFieldMetadataItem.name]: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ADDRESS field type', () => {
|
||||
it('should generate correct filter for address field', () => {
|
||||
const filterValue = 'New York';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [addressFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
or: [
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressStreet1: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressStreet2: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressCity: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressState: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[addressFieldMetadataItem.name]: {
|
||||
addressPostcode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('LINKS field type', () => {
|
||||
it('should generate correct filter for links field', () => {
|
||||
const filterValue = 'test';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [linksFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
or: [
|
||||
{
|
||||
[linksFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[linksFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[linksFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('FULL_NAME field type', () => {
|
||||
it('should generate correct filter for full name field', () => {
|
||||
const filterValue = 'test';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [fullNameFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
or: [
|
||||
{
|
||||
[fullNameFieldMetadataItem.name]: {
|
||||
firstName: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[fullNameFieldMetadataItem.name]: {
|
||||
lastName: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ARRAY field type', () => {
|
||||
it('should generate correct filter for array field', () => {
|
||||
const filterValue = 'test';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [arrayFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[arrayFieldMetadataItem.name]: {
|
||||
containsIlike: `%${filterValue}%`,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('EMAILS field type', () => {
|
||||
it('should generate correct filter for emails field', () => {
|
||||
const filterValue = 'test';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [emailsFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
or: [
|
||||
{
|
||||
[emailsFieldMetadataItem.name]: {
|
||||
primaryEmail: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[emailsFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PHONES field type', () => {
|
||||
it('should generate correct filter for phones field', () => {
|
||||
const filterValue = '123';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [phonesFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
or: [
|
||||
{
|
||||
[phonesFieldMetadataItem.name]: {
|
||||
primaryPhoneNumber: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[phonesFieldMetadataItem.name]: {
|
||||
primaryPhoneCallingCode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[phonesFieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NUMBER field type', () => {
|
||||
it('should generate correct filter for number field with numeric value', () => {
|
||||
const filterValue = '123.1';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [numberFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[numberFieldMetadataItem.name]: {
|
||||
eq: 123.1,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not generate filter for number field with non-numeric value', () => {
|
||||
const filterValue = 'not a number';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [numberFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('CURRENCY field type', () => {
|
||||
it('should generate correct filter for currency field with numeric value', () => {
|
||||
const filterValue = '123';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [currencyFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[currencyFieldMetadataItem.name]: {
|
||||
amountMicros: {
|
||||
eq: 123000000,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate correct filter for currency field with currency code', () => {
|
||||
const filterValue = 'USD';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [currencyFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
(result.recordGqlOperationFilter.or as any)?.[0][
|
||||
currencyFieldMetadataItem.name
|
||||
].currencyCode.in.includes(filterValue),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SELECT field type', () => {
|
||||
it('should generate correct filter for select field with matching option', () => {
|
||||
const filterValue = 'r';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [selectFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
const { foundCorrespondingSelectOptions: expectedOptions } =
|
||||
filterSelectOptionsOfFieldMetadataItem({
|
||||
fieldMetadataItem: selectFieldMetadataItem,
|
||||
filterValue,
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[selectFieldMetadataItem.name]: {
|
||||
in: expectedOptions?.map((option) => option.value),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not generate filter for select field with non-matching value', () => {
|
||||
const filterValue = 'not-found';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [selectFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MULTI_SELECT field type', () => {
|
||||
it('should generate correct filter for multi-select field with matching option', () => {
|
||||
const filterValue = 'r';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [multiSelectFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
const { foundCorrespondingSelectOptions: expectedOptions } =
|
||||
filterSelectOptionsOfFieldMetadataItem({
|
||||
fieldMetadataItem: multiSelectFieldMetadataItem,
|
||||
filterValue,
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter.or).toContainEqual({
|
||||
[multiSelectFieldMetadataItem.name]: {
|
||||
containsAny: expectedOptions?.map((option) => option.value),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not generate filter for multi-select field with non-matching value', () => {
|
||||
const filterValue = 'not-found';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [multiSelectFieldMetadataItem],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('combined field filters', () => {
|
||||
it('should generate OR filter combining all matching field types', () => {
|
||||
const filterValue = 'a';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: mockObjectMetadataItemWithAllFields,
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toHaveProperty('or');
|
||||
expect(Array.isArray(result.recordGqlOperationFilter.or)).toBe(true);
|
||||
expect(
|
||||
(result.recordGqlOperationFilter.or as any[])?.length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle empty filter value', () => {
|
||||
const filterValue = '';
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue,
|
||||
objectMetadataItem: mockObjectMetadataItemWithAllFields,
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toEqual({});
|
||||
});
|
||||
|
||||
it('should handle object with no fields', () => {
|
||||
const emptyObjectMetadata = {
|
||||
...mockObjectMetadataItem,
|
||||
fields: [],
|
||||
};
|
||||
|
||||
const result = turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
filterValue: 'test',
|
||||
objectMetadataItem: emptyObjectMetadata,
|
||||
});
|
||||
|
||||
expect(result.recordGqlOperationFilter).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -303,6 +303,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
lte: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
};
|
||||
case RecordFilterOperand.Is:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
eq: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${filterType} filter`,
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const createAnyFieldRecordFilterBaseProperties = ({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
filterValue: string;
|
||||
fieldMetadataItem: FieldMetadataItem;
|
||||
}): Pick<
|
||||
RecordFilter,
|
||||
'id' | 'value' | 'displayValue' | 'label' | 'fieldMetadataId'
|
||||
> => {
|
||||
return {
|
||||
id: v4(),
|
||||
value: filterValue,
|
||||
displayValue: '',
|
||||
label: '',
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,25 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
|
||||
export const filterSelectOptionsOfFieldMetadataItem = ({
|
||||
fieldMetadataItem,
|
||||
filterValue,
|
||||
}: {
|
||||
fieldMetadataItem: FieldMetadataItem;
|
||||
filterValue: string;
|
||||
}) => {
|
||||
const selectOptions = fieldMetadataItem.options;
|
||||
|
||||
const foundCorrespondingSelectOptions = selectOptions?.filter(
|
||||
(selectOption) =>
|
||||
selectOption.value
|
||||
.toLocaleLowerCase()
|
||||
.includes(filterValue.toLocaleLowerCase()) ||
|
||||
selectOption.label
|
||||
.toLocaleLowerCase()
|
||||
.includes(filterValue.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
return {
|
||||
foundCorrespondingSelectOptions,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,236 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { turnRecordFilterIntoRecordGqlOperationFilter } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter';
|
||||
import { createAnyFieldRecordFilterBaseProperties } from '@/object-record/record-filter/utils/createAnyFieldRecordFilterBaseProperties';
|
||||
import { filterSelectOptionsOfFieldMetadataItem } from '@/object-record/record-filter/utils/filterSelectOptionsOfFieldMetadataItem';
|
||||
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { z } from 'zod';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
|
||||
|
||||
export const turnAnyFieldFilterIntoRecordGqlFilter = ({
|
||||
filterValue,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
filterValue: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const fieldMetadataItems = objectMetadataItem.fields;
|
||||
|
||||
const anyFieldRecordFilters: RecordFilter[] = [];
|
||||
|
||||
const isFilterValueANumber = z.coerce.number().safeParse(filterValue).success;
|
||||
|
||||
for (const fieldMetadataItem of fieldMetadataItems) {
|
||||
switch (fieldMetadataItem.type) {
|
||||
case FieldMetadataType.TEXT: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'TEXT',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.ADDRESS: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'ADDRESS',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.LINKS: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'LINKS',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.FULL_NAME: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'FULL_NAME',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.ARRAY: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'ARRAY',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.EMAILS: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'EMAILS',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.PHONES: {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'PHONES',
|
||||
} satisfies RecordFilter);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.NUMBER: {
|
||||
if (isFilterValueANumber) {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Is,
|
||||
type: 'NUMBER',
|
||||
} satisfies RecordFilter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.CURRENCY: {
|
||||
if (isFilterValueANumber) {
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Is,
|
||||
type: 'CURRENCY',
|
||||
subFieldName: 'amountMicros',
|
||||
} satisfies RecordFilter);
|
||||
}
|
||||
|
||||
if (isNonEmptyString(filterValue)) {
|
||||
const foundCorrespondingCurrencies = CURRENCIES.filter(
|
||||
(currency) =>
|
||||
currency.label.includes(filterValue) ||
|
||||
currency.value.includes(filterValue),
|
||||
);
|
||||
|
||||
if (isNonEmptyArray(foundCorrespondingCurrencies)) {
|
||||
const arrayOfCurrenciesStringified = JSON.stringify(
|
||||
foundCorrespondingCurrencies.map((currency) => currency.value),
|
||||
);
|
||||
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
filterValue: arrayOfCurrenciesStringified,
|
||||
fieldMetadataItem,
|
||||
}),
|
||||
operand: RecordFilterOperand.Is,
|
||||
type: 'CURRENCY',
|
||||
subFieldName: 'currencyCode',
|
||||
} satisfies RecordFilter);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.SELECT: {
|
||||
if (isNonEmptyString(filterValue)) {
|
||||
const { foundCorrespondingSelectOptions } =
|
||||
filterSelectOptionsOfFieldMetadataItem({
|
||||
fieldMetadataItem,
|
||||
filterValue,
|
||||
});
|
||||
|
||||
if (isNonEmptyArray(foundCorrespondingSelectOptions)) {
|
||||
const arrayOfSelectValues = JSON.stringify(
|
||||
foundCorrespondingSelectOptions.map(
|
||||
(selectOption) => selectOption.value,
|
||||
),
|
||||
);
|
||||
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
fieldMetadataItem,
|
||||
filterValue: arrayOfSelectValues,
|
||||
}),
|
||||
operand: RecordFilterOperand.Is,
|
||||
type: 'SELECT',
|
||||
} satisfies RecordFilter);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.MULTI_SELECT: {
|
||||
if (isNonEmptyString(filterValue)) {
|
||||
const { foundCorrespondingSelectOptions } =
|
||||
filterSelectOptionsOfFieldMetadataItem({
|
||||
fieldMetadataItem,
|
||||
filterValue,
|
||||
});
|
||||
|
||||
if (isNonEmptyArray(foundCorrespondingSelectOptions)) {
|
||||
const arrayOfSelectValues = JSON.stringify(
|
||||
foundCorrespondingSelectOptions.map(
|
||||
(selectOption) => selectOption.value,
|
||||
),
|
||||
);
|
||||
|
||||
anyFieldRecordFilters.push({
|
||||
...createAnyFieldRecordFilterBaseProperties({
|
||||
fieldMetadataItem,
|
||||
filterValue: arrayOfSelectValues,
|
||||
}),
|
||||
operand: RecordFilterOperand.Contains,
|
||||
type: 'MULTI_SELECT',
|
||||
} satisfies RecordFilter);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const baseRecordGqlOperationFilters = anyFieldRecordFilters
|
||||
.map((recordFilter) =>
|
||||
turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies: {},
|
||||
fieldMetadataItems: objectMetadataItem.fields,
|
||||
recordFilter,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const recordGqlOperationFilter: RecordGqlOperationFilter = {
|
||||
or: baseRecordGqlOperationFilters,
|
||||
};
|
||||
|
||||
if (baseRecordGqlOperationFilters.length === 0) {
|
||||
return { recordGqlOperationFilter: {} };
|
||||
}
|
||||
|
||||
return {
|
||||
recordGqlOperationFilter,
|
||||
};
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
@ -40,6 +41,10 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const { totalCount } = useFindManyRecords({
|
||||
@ -52,6 +57,7 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
),
|
||||
limit: 1,
|
||||
skip: contextStoreTargetedRecordsRule.mode === 'selection',
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
|
||||
@ -74,5 +76,22 @@ export const RecordIndexFiltersToContextStoreEffect = () => {
|
||||
};
|
||||
}, [recordIndexFilters, setContextStoreFilters]);
|
||||
|
||||
const setContextStoreAnyFieldFilterValue = useSetRecoilComponentStateV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValueV2(
|
||||
anyFieldFilterValueComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setContextStoreAnyFieldFilterValue(anyFieldFilterValue);
|
||||
|
||||
return () => {
|
||||
setContextStoreAnyFieldFilterValue('');
|
||||
};
|
||||
}, [anyFieldFilterValue, setContextStoreAnyFieldFilterValue]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
@ -72,6 +73,10 @@ export const useRecordIndexLazyFetchRecords = ({
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||
@ -83,6 +88,7 @@ export const useRecordIndexLazyFetchRecords = ({
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
);
|
||||
|
||||
const finalColumns = [
|
||||
|
||||
@ -2,9 +2,11 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { combineFilters } from '@/object-record/record-filter/utils/combineFilters';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
@ -37,18 +39,28 @@ export const useFindManyRecordIndexTableParams = (
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const stateFilter = computeRecordGqlOperationFilter({
|
||||
const currentFilters = computeRecordGqlOperationFilter({
|
||||
fields: objectMetadataItem?.fields ?? [],
|
||||
filterValueDependencies,
|
||||
recordFilterGroups: currentRecordFilterGroups,
|
||||
recordFilters: currentRecordFilters,
|
||||
});
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValueV2(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
objectMetadataItem,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||
|
||||
return {
|
||||
objectNameSingular,
|
||||
filter: combineFilters([stateFilter, recordGroupFilter]),
|
||||
filter: combineFilters([currentFilters, recordGroupFilter, anyFieldFilter]),
|
||||
orderBy,
|
||||
// If we have a current record group definition, we only want to fetch 8 records by page
|
||||
...(currentRecordGroupDefinition ? { limit: 8 } : {}),
|
||||
|
||||
@ -15,7 +15,9 @@ import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hook
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { combineFilters } from '@/object-record/record-filter/utils/combineFilters';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -64,6 +66,16 @@ export const useLoadRecordIndexBoardColumn = ({
|
||||
fields: objectMetadataItem.fields,
|
||||
});
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValueV2(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
objectMetadataItem,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||
|
||||
const recordGqlFields = useRecordBoardRecordGqlFields({
|
||||
@ -78,6 +90,7 @@ export const useLoadRecordIndexBoardColumn = ({
|
||||
: { is: 'NULL' };
|
||||
|
||||
const combinedFilters = combineFilters([
|
||||
anyFieldFilter,
|
||||
requestFilters,
|
||||
{
|
||||
[kanbanFieldMetadataItem.name]: recordIndexKanbanFieldMetadataFilterValue,
|
||||
|
||||
@ -4,8 +4,10 @@ import { buildRecordGqlFieldsAggregateForView } from '@/object-record/record-boa
|
||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { UserContext } from '@/users/contexts/UserContext';
|
||||
@ -54,10 +56,20 @@ export const useAggregateRecordsForHeader = ({
|
||||
recordIndexKanbanAggregateOperation,
|
||||
});
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValueV2(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
objectMetadataItem,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const { data } = useAggregateRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
recordGqlFieldsAggregate,
|
||||
filter: { ...requestFilters, ...additionalFilters },
|
||||
filter: { ...requestFilters, ...additionalFilters, ...anyFieldFilter },
|
||||
});
|
||||
|
||||
const { value, labelWithFieldName } = computeAggregateValueAndLabel({
|
||||
|
||||
@ -2,8 +2,10 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
@ -85,10 +87,20 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
||||
}
|
||||
: {};
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValueV2(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
objectMetadataItem,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const { data, loading } = useAggregateRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
recordGqlFieldsAggregate,
|
||||
filter: { ...requestFilters, ...recordGroupFilter },
|
||||
filter: { ...requestFilters, ...recordGroupFilter, ...anyFieldFilter },
|
||||
skip: !isDefined(aggregateOperationForViewField),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user