diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuRecordFilterOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuRecordFilterOperandSelect.tsx
index 9f27de297c..82b57581ac 100644
--- a/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuRecordFilterOperandSelect.tsx
+++ b/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuRecordFilterOperandSelect.tsx
@@ -2,7 +2,6 @@ import { AdvancedFilterRecordFilterOperandSelectContent } from '@/object-record/
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
-import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
@@ -33,7 +32,7 @@ export const AdvancedFilterCommandMenuRecordFilterOperandSelect = ({
? getRecordFilterOperands({
filterType,
subFieldName: filter?.subFieldName,
- }).filter((operand) => operand !== RecordFilterOperand.IS_RELATIVE)
+ })
: [];
if (isDisabled === true) {
diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuValueFormInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuValueFormInput.tsx
index ec97e88574..2816f58731 100644
--- a/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuValueFormInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/advanced-filter/command-menu/components/AdvancedFilterCommandMenuValueFormInput.tsx
@@ -9,6 +9,7 @@ import { configurableViewFilterOperands } from '@/object-record/object-filter-dr
import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput';
import { FormBooleanFieldInput } from '@/object-record/record-field/ui/form-types/components/FormBooleanFieldInput';
import { FormMultiSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormMultiSelectFieldInput';
+import { FormRelativeDatePicker } from '@/object-record/record-field/ui/form-types/components/FormRelativeDatePicker';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import {
type FieldMetadata,
@@ -16,11 +17,13 @@ import {
type FieldSelectMetadata,
} from '@/object-record/record-field/ui/types/FieldMetadata';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
+import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
+import { stringifyRelativeDateFilter } from '@/views/view-filter-value/utils/stringifyRelativeDateFilter';
import { isObject, isString } from '@sniptt/guards';
import { useContext } from 'react';
import { FieldMetadataType } from 'twenty-shared/types';
-import { isDefined } from 'twenty-shared/utils';
+import { isDefined, type RelativeDateFilter } from 'twenty-shared/utils';
import { parseBooleanFromStringValue } from 'twenty-shared/workflow';
import { type JsonValue } from 'type-fest';
@@ -65,6 +68,10 @@ export const AdvancedFilterCommandMenuValueFormInput = ({
}
};
+ const handleRelativeDateFilterChange = (newValue: RelativeDateFilter) => {
+ applyObjectFilterDropdownFilterValue(stringifyRelativeDateFilter(newValue));
+ };
+
const fieldMetadataItemUsedInDropdown = useRecoilComponentValue(
fieldMetadataItemUsedInDropdownComponentSelector,
);
@@ -93,10 +100,24 @@ export const AdvancedFilterCommandMenuValueFormInput = ({
recordFilter.type === FieldMetadataType.DATE ||
recordFilter.type === FieldMetadataType.DATE_TIME;
+ const isRelativeDateFilter =
+ isFilterableByDateValue &&
+ recordFilter.operand === RecordFilterOperand.IS_RELATIVE;
+
if (isDisabled || operandHasNoInput) {
return null;
}
+ if (isRelativeDateFilter) {
+ return (
+
+ );
+ }
+
if (isFilterableByTextValue) {
return (
{
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValue(
@@ -42,7 +42,8 @@ export const useApplyObjectFilterDropdownOperand = () => {
const { getInitialFilterValue } = useGetInitialFilterValue();
- const { userTimezone } = useUserTimezone();
+ const { getRelativeDateFilterWithUserTimezone } =
+ useGetRelativeDateFilterWithUserTimezone();
const applyObjectFilterDropdownOperand = (
newOperand: RecordFilterOperand,
@@ -103,16 +104,17 @@ export const useApplyObjectFilterDropdownOperand = () => {
recordFilterToUpsert.displayValue = displayValue;
} else if (newOperand === RecordFilterOperand.IS_RELATIVE) {
- const defaultRelativeDate: RelativeDateFilter = {
- ...DEFAULT_RELATIVE_DATE_FILTER_VALUE,
- timezone: userTimezone,
- };
+ const newRelativeDateFilter = getRelativeDateFilterWithUserTimezone(
+ DEFAULT_RELATIVE_DATE_FILTER_VALUE,
+ );
- recordFilterToUpsert.value =
- stringifyRelativeDateFilter(defaultRelativeDate);
+ recordFilterToUpsert.value = stringifyRelativeDateFilter(
+ newRelativeDateFilter,
+ );
- recordFilterToUpsert.displayValue =
- getRelativeDateDisplayValue(defaultRelativeDate);
+ recordFilterToUpsert.displayValue = getRelativeDateDisplayValue(
+ newRelativeDateFilter,
+ );
} else {
recordFilterToUpsert.value = '';
recordFilterToUpsert.displayValue = '';
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormRelativeDatePicker.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormRelativeDatePicker.tsx
index d054cc4c9f..903979c341 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormRelativeDatePicker.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormRelativeDatePicker.tsx
@@ -1,19 +1,19 @@
+import { useGetRelativeDateFilterWithUserTimezone } from '@/object-record/record-filter/hooks/useGetRelativeDateFilterWithUserTimezone';
import { RelativeDatePickerHeader } from '@/ui/input/components/internal/date/components/RelativeDatePickerHeader';
-import { isNonEmptyString, isString } from '@sniptt/guards';
+import { isNonEmptyString } from '@sniptt/guards';
import { useId } from 'react';
import { DEFAULT_RELATIVE_DATE_FILTER_VALUE } from 'twenty-shared/constants';
import {
type RelativeDateFilter,
- safeParseRelativeDateFilterJSONStringified,
+ resolveRelativeDateFilterStringified,
} from 'twenty-shared/utils';
-import { type JsonValue } from 'type-fest';
export type FormRelativeDatePickerProps = {
label?: string;
defaultValue?: string;
- onChange: (value: JsonValue) => void;
+ onChange: (value: RelativeDateFilter) => void;
readonly?: boolean;
};
@@ -24,22 +24,27 @@ export const FormRelativeDatePicker = ({
}: FormRelativeDatePickerProps) => {
const instanceId = useId();
- const value =
- isString(defaultValue) && isNonEmptyString(defaultValue)
- ? safeParseRelativeDateFilterJSONStringified(defaultValue)
- : DEFAULT_RELATIVE_DATE_FILTER_VALUE;
+ const { getRelativeDateFilterWithUserTimezone } =
+ useGetRelativeDateFilterWithUserTimezone();
+
+ const valueParsed = isNonEmptyString(defaultValue)
+ ? resolveRelativeDateFilterStringified(defaultValue)
+ : DEFAULT_RELATIVE_DATE_FILTER_VALUE;
const handleValueChange = (newValue: RelativeDateFilter) => {
- onChange(JSON.stringify(newValue));
+ const newValueWithTimezone =
+ getRelativeDateFilterWithUserTimezone(newValue);
+
+ onChange(newValueWithTimezone);
};
return (
{
+ const { userTimezone } = useUserTimezone();
+ const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
+
+ const getRelativeDateFilterWithUserTimezone = (
+ relativeDateFilter: RelativeDateFilter,
+ ): RelativeDateFilter => {
+ const userDefinedCalendarStartDay =
+ CalendarStartDay[
+ currentWorkspaceMember?.calendarStartDay ?? CalendarStartDay.SYSTEM
+ ];
+ const defaultSystemCalendarStartDay = detectCalendarStartDay();
+
+ const resolvedCalendarStartDay = (
+ userDefinedCalendarStartDay === CalendarStartDay[CalendarStartDay.SYSTEM]
+ ? defaultSystemCalendarStartDay
+ : userDefinedCalendarStartDay
+ ) as FirstDayOfTheWeek;
+
+ return {
+ ...relativeDateFilter,
+ timezone: userTimezone,
+ firstDayOfTheWeek: resolvedCalendarStartDay,
+ };
+ };
+
+ return {
+ getRelativeDateFilterWithUserTimezone,
+ };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts
index 2ea165036e..ae9da12e4e 100644
--- a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts
+++ b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts
@@ -9,7 +9,7 @@ export type RecordFilter = {
id: string;
fieldMetadataId: string;
value: string;
- /** @deprecated We shouldn't implement new features with this field and instead try to create utils to obtain the displayValue of a filter type at runtime */
+ /** @deprecated We shouldn't implement new features with this field and instead try to create utils to obtain the displayValue at runtime */
displayValue: string;
type: FilterableAndTSVectorFieldType;
recordFilterGroupId?: string;
diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx
index d52648a660..d4330a41d5 100644
--- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx
+++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx
@@ -4,6 +4,7 @@ import { RELATIVE_DATE_DIRECTION_SELECT_OPTIONS } from '@/ui/input/components/in
import { RELATIVE_DATE_UNITS_SELECT_OPTIONS } from '@/ui/input/components/internal/date/constants/RelativeDateUnitSelectOptions';
import styled from '@emotion/styled';
+import { useState } from 'react';
import { type Nullable } from 'twenty-shared/types';
import {
relativeDateFilterSchema,
@@ -43,8 +44,10 @@ export const RelativeDatePickerHeader = ({
}: RelativeDatePickerHeaderProps) => {
const amountString = amount?.toString() ?? '';
- const textInputValue = direction === 'THIS' ? '' : amountString;
- const textInputPlaceholder = direction === 'THIS' ? '-' : 'Number';
+ const amountTextValue = direction === 'THIS' ? '' : amountString;
+ const amountInputPlaceholder = direction === 'THIS' ? '-' : 'Number';
+
+ const [draftAmountValue, setDraftAmountValue] = useState(amountTextValue);
const isUnitPlural = amount && amount > 1 && direction !== 'THIS';
const unitSelectOptions = RELATIVE_DATE_UNITS_SELECT_OPTIONS.map((unit) => ({
@@ -62,6 +65,14 @@ export const RelativeDatePickerHeader = ({
return;
}
+ if (draftAmountValue === '') {
+ setDraftAmountValue('1');
+ }
+
+ if (newDirection === 'THIS') {
+ setDraftAmountValue('');
+ }
+
onChange?.({
direction: newDirection,
amount: amount,
@@ -75,9 +86,11 @@ export const RelativeDatePickerHeader = ({
{
const amountString = text.replace(/[^0-9]|^0+/g, '');
+ setDraftAmountValue(amountString);
+
const amount = parseInt(amountString);
const valueParts = {
@@ -90,7 +103,7 @@ export const RelativeDatePickerHeader = ({
onChange?.(valueParts);
}
}}
- placeholder={textInputPlaceholder}
+ placeholder={amountInputPlaceholder}
disabled={direction === 'THIS' || readonly}
/>