fix: exclude non-groupBy date fields (deletedAt) from calendar field selection (#21764)

Closes #21608

The Tasks Calendar renders an empty grid because `GroupByTasks` fails
with `Field "deletedAt" is not supported in groupBy` while the header
count (`AggregateTasks`) still succeeds.

The calendar renders by grouping records on the selected date field.
Calendar-field eligibility only checked `isFieldMetadataDateKind`, so
`deletedAt` (a system DATE_TIME field) could be picked or auto-defaulted
as the calendar field — and the groupBy engine correctly rejects it
(only `createdAt`/`updatedAt` are groupable system date fields).

Fix: gate calendar-field eligibility on
`isFieldMetadataSupportedInGroupBy` (the same authority the backend
groupBy validator uses), so non-groupable date fields can no longer be
selected.

- `useGetAvailableFieldsForCalendar` — add the groupBy-support check
alongside the date-kind filter
- `ObjectOptionsDropdownCalendarFieldsContent` — reuse the hook's list
instead of re-filtering raw fields
This commit is contained in:
nitin
2026-06-18 18:18:08 +05:30
committed by GitHub
parent 148d312cc7
commit 9bc0db5666
3 changed files with 63 additions and 8 deletions
@@ -13,7 +13,6 @@ import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useG
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { isFieldMetadataDateKind } from 'twenty-shared/utils';
import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/icon';
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
@@ -27,14 +26,12 @@ export const ObjectOptionsDropdownCalendarFieldsContent = () => {
const { currentView } = useGetCurrentViewOnly();
const { updateCurrentView } = useUpdateCurrentView();
const { navigateToDateFieldSettings } = useGetAvailableFieldsForCalendar();
const { availableFieldsForCalendar, navigateToDateFieldSettings } =
useGetAvailableFieldsForCalendar();
const setRecordIndexCalendarFieldMetadataId = useSetAtomState(
recordIndexCalendarFieldMetadataIdState,
);
const availableFieldsForCalendar = objectMetadataItem.fields.filter((field) =>
isFieldMetadataDateKind(field.type),
);
const calendarFieldMetadata = currentView?.calendarFieldMetadataId
? objectMetadataItem.fields.find(
@@ -96,6 +96,54 @@ describe('useGetAvailableFieldsForCalendar', () => {
]);
});
it('should exclude date fields not supported in groupBy while keeping createdAt and updatedAt', () => {
const fields = [
{
id: '1',
name: 'dueAt',
type: FieldMetadataType.DATE_TIME,
label: 'Due Date',
isActive: true,
isSystem: false,
},
{
id: '2',
name: 'createdAt',
type: FieldMetadataType.DATE_TIME,
label: 'Created At',
isActive: true,
isSystem: true,
},
{
id: '3',
name: 'updatedAt',
type: FieldMetadataType.DATE_TIME,
label: 'Updated At',
isActive: true,
isSystem: true,
},
{
id: '4',
name: 'deletedAt',
type: FieldMetadataType.DATE_TIME,
label: 'Deleted At',
isActive: true,
isSystem: true,
},
];
const objectMetadataItems = [createMockObjectMetadataItem(fields)];
const wrapper = createWrapper(objectMetadataItems);
const { result } = renderHook(() => useGetAvailableFieldsForCalendar(), {
wrapper,
});
expect(
result.current.availableFieldsForCalendar.map((field) => field.name),
).toEqual(['dueAt', 'createdAt', 'updatedAt']);
});
it('should return the navigateToDateFieldSettings function', () => {
const fields = [
{
@@ -8,7 +8,11 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
import { FieldMetadataType, SettingsPath } from 'twenty-shared/types';
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
import {
isDefined,
isFieldMetadataDateKind,
isFieldMetadataSupportedInGroupBy,
} from 'twenty-shared/utils';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
export const useGetAvailableFieldsForCalendar = () => {
@@ -26,8 +30,14 @@ export const useGetAvailableFieldsForCalendar = () => {
);
const availableFieldsForCalendar =
objectMetadataItem?.readableFields.filter((field) =>
isFieldMetadataDateKind(field.type),
objectMetadataItem?.readableFields.filter(
(field) =>
isFieldMetadataDateKind(field.type) &&
isFieldMetadataSupportedInGroupBy({
type: field.type,
name: field.name,
isSystem: field.isSystem ?? false,
}),
) ?? [];
const navigate = useNavigateSettings();