Improved date filter input behavior (#12596)
Opening a date picker when creating a filter now directly applies today's date to avoid any in-between state issues. This allows the date picker and the operand selection to behave nicely when creating a date filter. Fixes https://github.com/twentyhq/core-team-issues/issues/1049
This commit is contained in:
@ -3,21 +3,20 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
|
|||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
import { useCreateRecordFilterFromObjectFilterDropdownCurrentStates } from '@/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates';
|
import { useCreateRecordFilterFromObjectFilterDropdownCurrentStates } from '@/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
export const useApplyObjectFilterDropdownFilterValue = () => {
|
export const useApplyObjectFilterDropdownFilterValue = () => {
|
||||||
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
|
const objectFilterDropdownCurrentRecordFilterCallbackState =
|
||||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
useRecoilComponentCallbackStateV2(
|
||||||
);
|
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const objectFilterDropdownFilterNotYetCreated = !isDefined(
|
const fieldMetadataItemUsedInDropdownCallbackState =
|
||||||
objectFilterDropdownCurrentRecordFilter,
|
useRecoilComponentCallbackStateV2(
|
||||||
);
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
|
||||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { createRecordFilterFromObjectFilterDropdownCurrentStates } =
|
const { createRecordFilterFromObjectFilterDropdownCurrentStates } =
|
||||||
useCreateRecordFilterFromObjectFilterDropdownCurrentStates();
|
useCreateRecordFilterFromObjectFilterDropdownCurrentStates();
|
||||||
@ -25,39 +24,55 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
|
|||||||
const { upsertObjectFilterDropdownCurrentFilter } =
|
const { upsertObjectFilterDropdownCurrentFilter } =
|
||||||
useUpsertObjectFilterDropdownCurrentFilter();
|
useUpsertObjectFilterDropdownCurrentFilter();
|
||||||
|
|
||||||
const applyObjectFilterDropdownFilterValue = (
|
const applyObjectFilterDropdownFilterValue = useRecoilCallback(
|
||||||
newFilterValue: string,
|
({ snapshot }) =>
|
||||||
newDisplayValue?: string,
|
(newFilterValue: string, newDisplayValue?: string) => {
|
||||||
) => {
|
const objectFilterDropdownCurrentRecordFilter = snapshot
|
||||||
if (objectFilterDropdownFilterNotYetCreated) {
|
.getLoadable(objectFilterDropdownCurrentRecordFilterCallbackState)
|
||||||
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
.getValue();
|
||||||
throw new Error(
|
|
||||||
`Field metadata item is not defined in object filter dropdown when setting a filter value to create it, this should not happen.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { newRecordFilterFromObjectFilterDropdownStates } =
|
const fieldMetadataItemUsedInDropdown = snapshot
|
||||||
createRecordFilterFromObjectFilterDropdownCurrentStates(
|
.getLoadable(fieldMetadataItemUsedInDropdownCallbackState)
|
||||||
fieldMetadataItemUsedInDropdown,
|
.getValue();
|
||||||
|
|
||||||
|
const objectFilterDropdownFilterNotYetCreated = !isDefined(
|
||||||
|
objectFilterDropdownCurrentRecordFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
const newCurrentRecordFilter = {
|
if (objectFilterDropdownFilterNotYetCreated) {
|
||||||
...newRecordFilterFromObjectFilterDropdownStates,
|
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||||
value: newFilterValue,
|
throw new Error(
|
||||||
displayValue: newDisplayValue ?? newFilterValue,
|
`Field metadata item is not defined in object filter dropdown when setting a filter value to create it, this should not happen.`,
|
||||||
} satisfies RecordFilter;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
|
const { newRecordFilterFromObjectFilterDropdownStates } =
|
||||||
} else {
|
createRecordFilterFromObjectFilterDropdownCurrentStates();
|
||||||
const newCurrentRecordFilter = {
|
|
||||||
...objectFilterDropdownCurrentRecordFilter,
|
|
||||||
value: newFilterValue,
|
|
||||||
displayValue: newDisplayValue ?? newFilterValue,
|
|
||||||
} satisfies RecordFilter;
|
|
||||||
|
|
||||||
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
|
const newCurrentRecordFilter = {
|
||||||
}
|
...newRecordFilterFromObjectFilterDropdownStates,
|
||||||
};
|
value: newFilterValue,
|
||||||
|
displayValue: newDisplayValue ?? newFilterValue,
|
||||||
|
} satisfies RecordFilter;
|
||||||
|
|
||||||
|
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
|
||||||
|
} else {
|
||||||
|
const newCurrentRecordFilter = {
|
||||||
|
...objectFilterDropdownCurrentRecordFilter,
|
||||||
|
value: newFilterValue,
|
||||||
|
displayValue: newDisplayValue ?? newFilterValue,
|
||||||
|
} satisfies RecordFilter;
|
||||||
|
|
||||||
|
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
objectFilterDropdownCurrentRecordFilterCallbackState,
|
||||||
|
fieldMetadataItemUsedInDropdownCallbackState,
|
||||||
|
createRecordFilterFromObjectFilterDropdownCurrentStates,
|
||||||
|
upsertObjectFilterDropdownCurrentFilter,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
applyObjectFilterDropdownFilterValue,
|
applyObjectFilterDropdownFilterValue,
|
||||||
|
|||||||
@ -1,23 +1,29 @@
|
|||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
export const useUpsertObjectFilterDropdownCurrentFilter = () => {
|
export const useUpsertObjectFilterDropdownCurrentFilter = () => {
|
||||||
const setObjectFilterDropdownCurrentRecordFilter =
|
const objectFilterDropdownCurrentRecordFilterCallbackState =
|
||||||
useSetRecoilComponentStateV2(
|
useRecoilComponentCallbackStateV2(
|
||||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
const upsertObjectFilterDropdownCurrentFilter = (
|
const upsertObjectFilterDropdownCurrentFilter = useRecoilCallback(
|
||||||
recordFilterToUpsert: RecordFilter,
|
({ set }) =>
|
||||||
) => {
|
(recordFilterToUpsert: RecordFilter) => {
|
||||||
upsertRecordFilter(recordFilterToUpsert);
|
upsertRecordFilter(recordFilterToUpsert);
|
||||||
|
|
||||||
setObjectFilterDropdownCurrentRecordFilter(recordFilterToUpsert);
|
set(
|
||||||
};
|
objectFilterDropdownCurrentRecordFilterCallbackState,
|
||||||
|
recordFilterToUpsert,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[objectFilterDropdownCurrentRecordFilterCallbackState, upsertRecordFilter],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
upsertObjectFilterDropdownCurrentFilter,
|
upsertObjectFilterDropdownCurrentFilter,
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
import { FilterableAndTSVectorFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
import { FilterableAndTSVectorFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||||
import { z } from 'zod';
|
import { getDateFilterDisplayValue } from '@/object-record/record-filter/utils/getDateFilterDisplayValue';
|
||||||
|
|
||||||
export const getInitialFilterValue = (
|
export const getInitialFilterValue = (
|
||||||
newType: FilterableAndTSVectorFieldType,
|
newType: FilterableAndTSVectorFieldType,
|
||||||
newOperand: RecordFilterOperand,
|
newOperand: RecordFilterOperand,
|
||||||
oldValue?: string,
|
|
||||||
oldDisplayValue?: string,
|
|
||||||
): Pick<RecordFilter, 'value' | 'displayValue'> | Record<string, never> => {
|
): Pick<RecordFilter, 'value' | 'displayValue'> | Record<string, never> => {
|
||||||
switch (newType) {
|
switch (newType) {
|
||||||
case 'DATE':
|
case 'DATE':
|
||||||
@ -19,12 +17,10 @@ export const getInitialFilterValue = (
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (activeDatePickerOperands.includes(newOperand)) {
|
if (activeDatePickerOperands.includes(newOperand)) {
|
||||||
const date = z.coerce.date().safeParse(oldValue).data ?? new Date();
|
const date = new Date();
|
||||||
const value = date.toISOString();
|
const value = date.toISOString();
|
||||||
const displayValue =
|
|
||||||
newType === 'DATE'
|
const { displayValue } = getDateFilterDisplayValue(date, newType);
|
||||||
? date.toLocaleString()
|
|
||||||
: date.toLocaleDateString();
|
|
||||||
|
|
||||||
return { value, displayValue };
|
return { value, displayValue };
|
||||||
}
|
}
|
||||||
@ -32,12 +28,13 @@ export const getInitialFilterValue = (
|
|||||||
if (newOperand === RecordFilterOperand.IsRelative) {
|
if (newOperand === RecordFilterOperand.IsRelative) {
|
||||||
return { value: '', displayValue: '' };
|
return { value: '', displayValue: '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: oldValue ?? '',
|
value: '',
|
||||||
displayValue: oldDisplayValue ?? '',
|
displayValue: '',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
@ -20,14 +21,19 @@ export const useCreateEmptyRecordFilterFromFieldMetadataItem = () => {
|
|||||||
const defaultSubFieldName =
|
const defaultSubFieldName =
|
||||||
getDefaultSubFieldNameForCompositeFilterableFieldType(filterType);
|
getDefaultSubFieldNameForCompositeFilterableFieldType(filterType);
|
||||||
|
|
||||||
|
const { displayValue, value } = getInitialFilterValue(
|
||||||
|
filterType,
|
||||||
|
defaultOperand,
|
||||||
|
);
|
||||||
|
|
||||||
const newRecordFilter: RecordFilter = {
|
const newRecordFilter: RecordFilter = {
|
||||||
id: v4(),
|
id: v4(),
|
||||||
fieldMetadataId: fieldMetadataItem.id,
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
operand: defaultOperand,
|
operand: defaultOperand,
|
||||||
displayValue: '',
|
displayValue,
|
||||||
label: fieldMetadataItem.label,
|
label: fieldMetadataItem.label,
|
||||||
type: filterType,
|
type: filterType,
|
||||||
value: '',
|
value,
|
||||||
subFieldName: defaultSubFieldName,
|
subFieldName: defaultSubFieldName,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,59 +1,82 @@
|
|||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
export const useCreateRecordFilterFromObjectFilterDropdownCurrentStates =
|
export const useCreateRecordFilterFromObjectFilterDropdownCurrentStates =
|
||||||
() => {
|
() => {
|
||||||
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
const fieldMetadataItemUsedInDropdownCallbackState =
|
||||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
useRecoilComponentCallbackStateV2(
|
||||||
);
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
|
||||||
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
|
||||||
selectedOperandInDropdownComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
|
|
||||||
subFieldNameUsedInDropdownComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const createRecordFilterFromObjectFilterDropdownCurrentStates = (
|
|
||||||
fieldMetadataItem: FieldMetadataItem,
|
|
||||||
) => {
|
|
||||||
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
|
||||||
throw new Error(
|
|
||||||
`Field metadata item used in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filterType = getFilterTypeFromFieldType(
|
|
||||||
fieldMetadataItemUsedInDropdown.type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isDefined(selectedOperandInDropdown)) {
|
const selectedOperandInDropdownCallbackState =
|
||||||
throw new Error(
|
useRecoilComponentCallbackStateV2(
|
||||||
`Selected operand in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`,
|
selectedOperandInDropdownComponentState,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const newRecordFilterFromObjectFilterDropdownStates: RecordFilter = {
|
const subFieldNameUsedInDropdownCallbackState =
|
||||||
id: v4(),
|
useRecoilComponentCallbackStateV2(
|
||||||
fieldMetadataId: fieldMetadataItemUsedInDropdown?.id,
|
subFieldNameUsedInDropdownComponentState,
|
||||||
operand: selectedOperandInDropdown,
|
);
|
||||||
displayValue: '',
|
|
||||||
label: fieldMetadataItem.label,
|
|
||||||
type: filterType,
|
|
||||||
value: '',
|
|
||||||
subFieldName: subFieldNameUsedInDropdown,
|
|
||||||
};
|
|
||||||
|
|
||||||
return { newRecordFilterFromObjectFilterDropdownStates };
|
const createRecordFilterFromObjectFilterDropdownCurrentStates =
|
||||||
};
|
useRecoilCallback(
|
||||||
|
({ snapshot }) =>
|
||||||
|
() => {
|
||||||
|
const fieldMetadataItemUsedInDropdown = snapshot
|
||||||
|
.getLoadable(fieldMetadataItemUsedInDropdownCallbackState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const selectedOperandInDropdown = snapshot
|
||||||
|
.getLoadable(selectedOperandInDropdownCallbackState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const subFieldNameUsedInDropdown = snapshot
|
||||||
|
.getLoadable(subFieldNameUsedInDropdownCallbackState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||||
|
throw new Error(
|
||||||
|
`Field metadata item used in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterType = getFilterTypeFromFieldType(
|
||||||
|
fieldMetadataItemUsedInDropdown.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isDefined(selectedOperandInDropdown)) {
|
||||||
|
throw new Error(
|
||||||
|
`Selected operand in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newRecordFilterFromObjectFilterDropdownStates: RecordFilter =
|
||||||
|
{
|
||||||
|
id: v4(),
|
||||||
|
fieldMetadataId: fieldMetadataItemUsedInDropdown.id,
|
||||||
|
operand: selectedOperandInDropdown,
|
||||||
|
displayValue: '',
|
||||||
|
label: fieldMetadataItemUsedInDropdown.label,
|
||||||
|
type: filterType,
|
||||||
|
value: '',
|
||||||
|
subFieldName: subFieldNameUsedInDropdown,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { newRecordFilterFromObjectFilterDropdownStates };
|
||||||
|
},
|
||||||
|
[
|
||||||
|
fieldMetadataItemUsedInDropdownCallbackState,
|
||||||
|
selectedOperandInDropdownCallbackState,
|
||||||
|
subFieldNameUsedInDropdownCallbackState,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createRecordFilterFromObjectFilterDropdownCurrentStates,
|
createRecordFilterFromObjectFilterDropdownCurrentStates,
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { getDateFilterDisplayValue } from '../getDateFilterDisplayValue';
|
||||||
|
|
||||||
|
describe('getDateFilterDisplayValue', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
const mockDate = new Date('2025-06-13T14:30:00Z');
|
||||||
|
|
||||||
|
// Mocking responses for date methods to avoid timezone issues
|
||||||
|
jest
|
||||||
|
.spyOn(mockDate, 'toLocaleString')
|
||||||
|
.mockReturnValue('6/13/2025, 2:30:00 PM');
|
||||||
|
jest.spyOn(mockDate, 'toLocaleDateString').mockReturnValue('6/13/2025');
|
||||||
|
|
||||||
|
global.Date = jest.fn(() => mockDate) as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return date and time for DATE_TIME field type', () => {
|
||||||
|
const date = new Date('2025-06-13T14:30:00Z');
|
||||||
|
const result = getDateFilterDisplayValue(date, 'DATE_TIME');
|
||||||
|
expect(result).toEqual({ displayValue: '6/13/2025, 2:30:00 PM' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return only date for DATE field type', () => {
|
||||||
|
const date = new Date('2025-06-13T14:30:00Z');
|
||||||
|
const result = getDateFilterDisplayValue(date, 'DATE');
|
||||||
|
expect(result).toEqual({ displayValue: '6/13/2025' });
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { FilterableAndTSVectorFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||||
|
|
||||||
|
export const getDateFilterDisplayValue = (
|
||||||
|
value: Date,
|
||||||
|
fieldType: FilterableAndTSVectorFieldType,
|
||||||
|
) => {
|
||||||
|
const displayValue =
|
||||||
|
fieldType === 'DATE_TIME'
|
||||||
|
? value.toLocaleString()
|
||||||
|
: value.toLocaleDateString();
|
||||||
|
|
||||||
|
return { displayValue };
|
||||||
|
};
|
||||||
@ -1,25 +1,10 @@
|
|||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
|
||||||
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
|
||||||
|
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
|
||||||
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
|
||||||
import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId';
|
import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId';
|
||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
|
||||||
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
|
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
|
||||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown } from '@/views/hooks/useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
import { useIcons } from 'twenty-ui/display';
|
import { useIcons } from 'twenty-ui/display';
|
||||||
import { MenuItem } from 'twenty-ui/navigation';
|
import { MenuItem } from 'twenty-ui/navigation';
|
||||||
|
|
||||||
@ -30,14 +15,6 @@ export type ViewBarFilterDropdownFieldSelectMenuItemProps = {
|
|||||||
export const ViewBarFilterDropdownFieldSelectMenuItem = ({
|
export const ViewBarFilterDropdownFieldSelectMenuItem = ({
|
||||||
fieldMetadataItemToSelect,
|
fieldMetadataItemToSelect,
|
||||||
}: ViewBarFilterDropdownFieldSelectMenuItemProps) => {
|
}: ViewBarFilterDropdownFieldSelectMenuItemProps) => {
|
||||||
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
|
|
||||||
fieldMetadataItemIdUsedInDropdownComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [, setObjectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2(
|
|
||||||
objectFilterDropdownFilterIsSelectedComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { resetSelectedItem } = useSelectableList(FILTER_FIELD_LIST_ID);
|
const { resetSelectedItem } = useSelectableList(FILTER_FIELD_LIST_ID);
|
||||||
|
|
||||||
const isSelectedItem = useRecoilComponentFamilyValueV2(
|
const isSelectedItem = useRecoilComponentFamilyValueV2(
|
||||||
@ -45,58 +22,8 @@ export const ViewBarFilterDropdownFieldSelectMenuItem = ({
|
|||||||
fieldMetadataItemToSelect.id,
|
fieldMetadataItemToSelect.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
|
const { initializeFilterOnFieldMetataItemFromViewBarFilterDropdown } =
|
||||||
selectedOperandInDropdownComponentState,
|
useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown();
|
||||||
);
|
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
|
||||||
|
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
|
||||||
currentRecordFiltersComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setObjectFilterDropdownCurrentRecordFilter =
|
|
||||||
useSetRecoilComponentStateV2(
|
|
||||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => {
|
|
||||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
|
|
||||||
|
|
||||||
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
|
||||||
|
|
||||||
if (filterType === 'RELATION' || filterType === 'SELECT') {
|
|
||||||
setHotkeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker);
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOperand = getRecordFilterOperands({
|
|
||||||
filterType,
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
setObjectFilterDropdownFilterIsSelected(true);
|
|
||||||
|
|
||||||
const duplicateFilterInCurrentRecordFilters =
|
|
||||||
findDuplicateRecordFilterInNonAdvancedRecordFilters({
|
|
||||||
recordFilters: currentRecordFilters,
|
|
||||||
fieldMetadataItemId: fieldMetadataItem.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterIsAlreadyInCurrentRecordFilters = isDefined(
|
|
||||||
duplicateFilterInCurrentRecordFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filterIsAlreadyInCurrentRecordFilters) {
|
|
||||||
setObjectFilterDropdownCurrentRecordFilter(
|
|
||||||
duplicateFilterInCurrentRecordFilters,
|
|
||||||
);
|
|
||||||
|
|
||||||
setSelectedOperandInDropdown(
|
|
||||||
duplicateFilterInCurrentRecordFilters.operand,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setSelectedOperandInDropdown(defaultOperand);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
@ -105,7 +32,9 @@ export const ViewBarFilterDropdownFieldSelectMenuItem = ({
|
|||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
|
|
||||||
handleSelectFilter(fieldMetadataItemToSelect);
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown(
|
||||||
|
fieldMetadataItemToSelect,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -0,0 +1,318 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
|
import { useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown } from '../useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown';
|
||||||
|
|
||||||
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||||
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
|
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getMockPersonObjectMetadataItem } from '~/testing/mock-data/people';
|
||||||
|
|
||||||
|
const mockSetHotkeyScope = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/ui/utilities/hotkey/hooks/useSetHotkeyScope', () => ({
|
||||||
|
useSetHotkeyScope: () => mockSetHotkeyScope,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const peopleObjectMetadataItemMock = getMockPersonObjectMetadataItem();
|
||||||
|
const personCityFieldMetadataItemMock =
|
||||||
|
peopleObjectMetadataItemMock.fields.find((field) => field.name === 'city');
|
||||||
|
const personCompanyFieldMetadataItemMock =
|
||||||
|
peopleObjectMetadataItemMock.fields.find((field) => field.name === 'company');
|
||||||
|
const personCreatedAtFieldMetadataItemMock =
|
||||||
|
peopleObjectMetadataItemMock.fields.find(
|
||||||
|
(field) => field.name === 'createdAt',
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<RecoilRoot
|
||||||
|
initializeState={(snapshot) => {
|
||||||
|
snapshot.set(objectMetadataItemsState, generatedMockObjectMetadataItems);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||||
|
value={{ instanceId: 'test' }}
|
||||||
|
>
|
||||||
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
|
value={{ instanceId: 'test' }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
|
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||||
|
</RecoilRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize filter for a basic text field with no existing filter', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const { initializeFilterOnFieldMetataItemFromViewBarFilterDropdown } =
|
||||||
|
useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown();
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
|
const objectFilterDropdownFilterIsSelected = useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown,
|
||||||
|
fieldMetadataItemUsedInDropdown,
|
||||||
|
objectFilterDropdownFilterIsSelected,
|
||||||
|
selectedOperandInDropdown,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!personCityFieldMetadataItemMock) {
|
||||||
|
throw new Error('personCityFieldMetadataItemMock is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOperand = getRecordFilterOperands({
|
||||||
|
filterType: getFilterTypeFromFieldType(
|
||||||
|
personCityFieldMetadataItemMock.type,
|
||||||
|
),
|
||||||
|
})?.[0];
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.initializeFilterOnFieldMetataItemFromViewBarFilterDropdown(
|
||||||
|
personCityFieldMetadataItemMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.fieldMetadataItemUsedInDropdown?.id).toBe(
|
||||||
|
personCityFieldMetadataItemMock.id,
|
||||||
|
);
|
||||||
|
expect(result.current.objectFilterDropdownFilterIsSelected).toBe(true);
|
||||||
|
expect(result.current.selectedOperandInDropdown).toBe(defaultOperand);
|
||||||
|
expect(mockSetHotkeyScope).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize filter with a relation field', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const { initializeFilterOnFieldMetataItemFromViewBarFilterDropdown } =
|
||||||
|
useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown();
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
|
const objectFilterDropdownFilterIsSelected = useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown,
|
||||||
|
fieldMetadataItemUsedInDropdown,
|
||||||
|
objectFilterDropdownFilterIsSelected,
|
||||||
|
selectedOperandInDropdown,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!personCompanyFieldMetadataItemMock) {
|
||||||
|
throw new Error('personCompanyFieldMetadataItemMock is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOperand = getRecordFilterOperands({
|
||||||
|
filterType: getFilterTypeFromFieldType(
|
||||||
|
personCompanyFieldMetadataItemMock.type,
|
||||||
|
),
|
||||||
|
})?.[0];
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.initializeFilterOnFieldMetataItemFromViewBarFilterDropdown(
|
||||||
|
personCompanyFieldMetadataItemMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.fieldMetadataItemUsedInDropdown?.id).toBe(
|
||||||
|
personCompanyFieldMetadataItemMock.id,
|
||||||
|
);
|
||||||
|
expect(result.current.objectFilterDropdownFilterIsSelected).toBe(true);
|
||||||
|
expect(result.current.selectedOperandInDropdown).toBe(defaultOperand);
|
||||||
|
expect(mockSetHotkeyScope).toHaveBeenCalledWith(
|
||||||
|
SingleRecordPickerHotkeyScope.SingleRecordPicker,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize filter with a duplicate field on city', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const { initializeFilterOnFieldMetataItemFromViewBarFilterDropdown } =
|
||||||
|
useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown();
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
|
const objectFilterDropdownFilterIsSelected = useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectFilterDropdownCurrentRecordFilter =
|
||||||
|
useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown,
|
||||||
|
fieldMetadataItemUsedInDropdown,
|
||||||
|
objectFilterDropdownFilterIsSelected,
|
||||||
|
selectedOperandInDropdown,
|
||||||
|
setCurrentRecordFilters,
|
||||||
|
currentRecordFilters,
|
||||||
|
objectFilterDropdownCurrentRecordFilter,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!personCityFieldMetadataItemMock) {
|
||||||
|
throw new Error('personCityFieldMetadataItemMock is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOperand = getRecordFilterOperands({
|
||||||
|
filterType: getFilterTypeFromFieldType(
|
||||||
|
personCityFieldMetadataItemMock.type,
|
||||||
|
),
|
||||||
|
})?.[0];
|
||||||
|
|
||||||
|
const mockExistingFilterOnCity: RecordFilter = {
|
||||||
|
id: 'existing-filter-id',
|
||||||
|
fieldMetadataId: personCityFieldMetadataItemMock.id,
|
||||||
|
operand: defaultOperand,
|
||||||
|
displayValue: 'Test City',
|
||||||
|
label: personCityFieldMetadataItemMock.label,
|
||||||
|
type: getFilterTypeFromFieldType(personCityFieldMetadataItemMock.type),
|
||||||
|
value: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.setCurrentRecordFilters([mockExistingFilterOnCity]);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters.length).toBe(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.initializeFilterOnFieldMetataItemFromViewBarFilterDropdown(
|
||||||
|
personCityFieldMetadataItemMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.objectFilterDropdownCurrentRecordFilter).toBe(
|
||||||
|
mockExistingFilterOnCity,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize filter on a date field correctly', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const { initializeFilterOnFieldMetataItemFromViewBarFilterDropdown } =
|
||||||
|
useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown();
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
|
);
|
||||||
|
const objectFilterDropdownFilterIsSelected = useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectFilterDropdownCurrentRecordFilter =
|
||||||
|
useRecoilComponentValueV2(
|
||||||
|
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown,
|
||||||
|
fieldMetadataItemUsedInDropdown,
|
||||||
|
objectFilterDropdownFilterIsSelected,
|
||||||
|
selectedOperandInDropdown,
|
||||||
|
setCurrentRecordFilters,
|
||||||
|
currentRecordFilters,
|
||||||
|
objectFilterDropdownCurrentRecordFilter,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!personCreatedAtFieldMetadataItemMock) {
|
||||||
|
throw new Error('personCreatedAtFieldMetadataItemMock is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters.length).toBe(0);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.initializeFilterOnFieldMetataItemFromViewBarFilterDropdown(
|
||||||
|
personCreatedAtFieldMetadataItemMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.fieldMetadataItemUsedInDropdown?.id).toBe(
|
||||||
|
personCreatedAtFieldMetadataItemMock.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
result.current.objectFilterDropdownCurrentRecordFilter?.fieldMetadataId,
|
||||||
|
).toBe(personCreatedAtFieldMetadataItemMock.id);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
result.current.objectFilterDropdownCurrentRecordFilter?.value,
|
||||||
|
).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { useUpsertObjectFilterDropdownCurrentFilter } from '@/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter';
|
||||||
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters';
|
||||||
|
import { getDateFilterDisplayValue } from '@/object-record/record-filter/utils/getDateFilterDisplayValue';
|
||||||
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
|
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
export const useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown =
|
||||||
|
() => {
|
||||||
|
const selectedOperandInDropdownCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(
|
||||||
|
selectedOperandInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
|
const currentRecordFiltersCallbackState = useRecoilComponentCallbackStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectFilterDropdownCurrentRecordFilterCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(
|
||||||
|
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldMetadataItemUsedInDropdownCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(
|
||||||
|
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectFilterDropdownFilterIsSelectedCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(
|
||||||
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertObjectFilterDropdownCurrentFilter } =
|
||||||
|
useUpsertObjectFilterDropdownCurrentFilter();
|
||||||
|
|
||||||
|
const initializeFilterOnFieldMetataItemFromViewBarFilterDropdown =
|
||||||
|
useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(fieldMetadataItem: FieldMetadataItem) => {
|
||||||
|
set(
|
||||||
|
fieldMetadataItemUsedInDropdownCallbackState,
|
||||||
|
fieldMetadataItem.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = snapshot
|
||||||
|
.getLoadable(currentRecordFiltersCallbackState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const filterType = getFilterTypeFromFieldType(
|
||||||
|
fieldMetadataItem.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filterType === 'RELATION' || filterType === 'SELECT') {
|
||||||
|
setHotkeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(objectFilterDropdownFilterIsSelectedCallbackState, true);
|
||||||
|
|
||||||
|
const defaultOperand = getRecordFilterOperands({
|
||||||
|
filterType,
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
const duplicateFilterInCurrentRecordFilters =
|
||||||
|
findDuplicateRecordFilterInNonAdvancedRecordFilters({
|
||||||
|
recordFilters: currentRecordFilters,
|
||||||
|
fieldMetadataItemId: fieldMetadataItem.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterIsAlreadyInCurrentRecordFilters = isDefined(
|
||||||
|
duplicateFilterInCurrentRecordFilters,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filterIsAlreadyInCurrentRecordFilters) {
|
||||||
|
set(
|
||||||
|
objectFilterDropdownCurrentRecordFilterCallbackState,
|
||||||
|
duplicateFilterInCurrentRecordFilters,
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
selectedOperandInDropdownCallbackState,
|
||||||
|
duplicateFilterInCurrentRecordFilters.operand,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
set(selectedOperandInDropdownCallbackState, defaultOperand);
|
||||||
|
|
||||||
|
if (filterType === 'DATE' || filterType === 'DATE_TIME') {
|
||||||
|
const date = new Date();
|
||||||
|
const value = date.toISOString();
|
||||||
|
|
||||||
|
const { displayValue } = getDateFilterDisplayValue(
|
||||||
|
date,
|
||||||
|
filterType,
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialDateRecordFilter: RecordFilter = {
|
||||||
|
id: v4(),
|
||||||
|
fieldMetadataId: fieldMetadataItem.id,
|
||||||
|
operand: defaultOperand,
|
||||||
|
displayValue,
|
||||||
|
label: fieldMetadataItem.label,
|
||||||
|
type: filterType,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
|
||||||
|
upsertObjectFilterDropdownCurrentFilter(
|
||||||
|
initialDateRecordFilter,
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
objectFilterDropdownCurrentRecordFilterCallbackState,
|
||||||
|
initialDateRecordFilter,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
fieldMetadataItemUsedInDropdownCallbackState,
|
||||||
|
objectFilterDropdownCurrentRecordFilterCallbackState,
|
||||||
|
selectedOperandInDropdownCallbackState,
|
||||||
|
setHotkeyScope,
|
||||||
|
objectFilterDropdownFilterIsSelectedCallbackState,
|
||||||
|
currentRecordFiltersCallbackState,
|
||||||
|
upsertObjectFilterDropdownCurrentFilter,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initializeFilterOnFieldMetataItemFromViewBarFilterDropdown,
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user