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:
+2
-5
@@ -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(
|
||||
|
||||
+48
@@ -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 = [
|
||||
{
|
||||
|
||||
+13
-3
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user