Add weekly layout to record calendar (#22819)

## Summary

- Add a week layout to record calendar views and persist the selected
layout.
- Render `DATE` calendars as an all-day week and `DATE_TIME` calendars
as an hourly week.
- Add an optional end date field across calendar configuration,
metadata, persistence, and complete-view upserts.
- Use configured end values for ranged and multi-day events, with a
one-hour fallback when a `DATE_TIME` end is absent or invalid.
- Keep calendar cards consistent with the existing compact view,
including checkbox selection and whole-card record opening.
- Gate the weekly layout and end-date behavior behind the public Labs
`IS_CALENDAR_WEEK_VIEW_ENABLED` workspace feature flag.

## Week interactions

- Show overlapping timed events side by side and cap the visible records
at two per day.
- Display start and end times on timed cards, enforce a readable
30-minute minimum height, and keep today’s text contrast stronger.
- Drag timed events between days and times with 30-minute snapping while
preserving their duration, including zero-duration events.
- Show a create button when hovering a 30-minute slot; keyboard users
can focus a day, move the slot with the arrow keys, and reach the same
contextual action.
- Initialize new records with the selected slot time and a compatible
writable end value one hour later.
- Show the workspace time zone and current-time indicator in timed
weeks; date-only weeks keep the all-day section without an hourly grid.

## Configuration and data loading

- Only allow end fields that match the start field type, and prevent
selecting the same field for both boundaries.
- Load records whose ranges overlap the visible period so month and week
layouts display the same relevant records.
- Resolve and persist calendar end fields when updating existing views
through `upsert_complete_view`.
- Fall back to Month and ignore the configured end field while the flag
is disabled, without overwriting either persisted setting, so
re-enabling restores the previous configuration.
- Expose the flag in Labs and keep it default-off for workspaces without
a stored value; enable it in the development seeder.

<img width="1285" height="808" alt="Screenshot 2026-07-15 at 15 50 17"
src="https://github.com/user-attachments/assets/b7e3f7f1-ca77-492f-8cce-cca186ebca0b"
/>
This commit is contained in:
Weiko
2026-07-15 16:30:18 +02:00
committed by GitHub
parent 0dbae2eda3
commit 25bd2897a3
158 changed files with 5446 additions and 214 deletions
@@ -0,0 +1,109 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { recordIndexCalendarEndFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarEndFieldMetadataIdState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
import { getAvailableCalendarEndFieldMetadataItems } from '@/views/view-picker/utils/getAvailableCalendarEndFieldMetadataItems';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronLeft, useIcons } from 'twenty-ui/icon';
import { MenuItemSelect } from 'twenty-ui/navigation';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const ObjectOptionsDropdownCalendarEndFieldsContent = () => {
const { t } = useLingui();
const { getIcon } = useIcons();
const [searchInput, setSearchInput] = useState('');
const isCalendarWeekViewEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
);
const { resetContent, closeDropdown } = useObjectOptionsDropdown();
const { currentView } = useGetCurrentViewOnly();
const { updateCurrentView } = useUpdateCurrentView();
const { availableFieldsForCalendar } = useGetAvailableFieldsForCalendar();
const setRecordIndexCalendarEndFieldMetadataId = useSetAtomState(
recordIndexCalendarEndFieldMetadataIdState,
);
const availableCalendarEndFieldMetadataItems =
getAvailableCalendarEndFieldMetadataItems({
availableFieldsForCalendar,
calendarFieldMetadataId: currentView?.calendarFieldMetadataId,
});
const filteredCalendarEndFields =
availableCalendarEndFieldMetadataItems.filter((field) =>
field.label.toLowerCase().includes(searchInput.toLowerCase()),
);
const handleCalendarEndFieldChange = async (
fieldMetadataItem: FieldMetadataItem | null,
) => {
if (!isCalendarWeekViewEnabled) {
return;
}
const calendarEndFieldMetadataId = fieldMetadataItem?.id ?? null;
setRecordIndexCalendarEndFieldMetadataId(calendarEndFieldMetadataId);
await updateCurrentView({ calendarEndFieldMetadataId });
closeDropdown();
};
if (!isCalendarWeekViewEnabled) {
return null;
}
return (
<DropdownContent>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={() => resetContent()}
Icon={IconChevronLeft}
/>
}
>
{t`End date field`}
</DropdownMenuHeader>
<DropdownMenuSearchInput
autoFocus
value={searchInput}
placeholder={t`Search fields`}
onChange={(event) => setSearchInput(event.target.value)}
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<MenuItemSelect
selected={!isDefined(currentView?.calendarEndFieldMetadataId)}
onClick={() => handleCalendarEndFieldChange(null)}
text={t`None`}
/>
{filteredCalendarEndFields.map((fieldMetadataItem) => (
<MenuItemSelect
key={fieldMetadataItem.id}
selected={
fieldMetadataItem.id === currentView?.calendarEndFieldMetadataId
}
onClick={() => handleCalendarEndFieldChange(fieldMetadataItem)}
LeftIcon={getIcon(fieldMetadataItem.icon)}
text={fieldMetadataItem.label}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
);
};
@@ -1,5 +1,6 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { recordIndexCalendarEndFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarEndFieldMetadataIdState';
import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
@@ -13,6 +14,7 @@ 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 { isDefined } from 'twenty-shared/utils';
import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/icon';
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
@@ -32,6 +34,9 @@ export const ObjectOptionsDropdownCalendarFieldsContent = () => {
const setRecordIndexCalendarFieldMetadataId = useSetAtomState(
recordIndexCalendarFieldMetadataIdState,
);
const setRecordIndexCalendarEndFieldMetadataId = useSetAtomState(
recordIndexCalendarEndFieldMetadataIdState,
);
const calendarFieldMetadata = currentView?.calendarFieldMetadataId
? objectMetadataItem.fields.find(
@@ -39,6 +44,12 @@ export const ObjectOptionsDropdownCalendarFieldsContent = () => {
)
: undefined;
const calendarEndFieldMetadata = currentView?.calendarEndFieldMetadataId
? objectMetadataItem.fields.find(
(field) => field.id === currentView.calendarEndFieldMetadataId,
)
: undefined;
const filteredCalendarFields = availableFieldsForCalendar.filter((field) =>
field.label.toLowerCase().includes(searchInput.toLowerCase()),
);
@@ -46,9 +57,22 @@ export const ObjectOptionsDropdownCalendarFieldsContent = () => {
const handleCalendarFieldChange = async (
fieldMetadataItem: FieldMetadataItem,
) => {
const shouldClearCalendarEndField =
isDefined(currentView?.calendarEndFieldMetadataId) &&
(!isDefined(calendarEndFieldMetadata) ||
calendarEndFieldMetadata.id === fieldMetadataItem.id ||
calendarEndFieldMetadata.type !== fieldMetadataItem.type);
setRecordIndexCalendarFieldMetadataId(fieldMetadataItem.id);
if (shouldClearCalendarEndField) {
setRecordIndexCalendarEndFieldMetadataId(null);
}
await updateCurrentView({
calendarFieldMetadataId: fieldMetadataItem.id,
...(shouldClearCalendarEndField
? { calendarEndFieldMetadataId: null }
: {}),
});
closeDropdown();
};
@@ -1,5 +1,6 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { getSupportedRecordCalendarLayout } from '@/object-record/record-calendar/utils/getSupportedRecordCalendarLayout';
import { recordIndexCalendarLayoutState } from '@/object-record/record-index/states/recordIndexCalendarLayoutState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
@@ -12,6 +13,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { t } from '@lingui/core/macro';
import { Pill } from 'twenty-ui/data-display';
import {
@@ -21,13 +23,23 @@ import {
IconTimelineEvent,
} from 'twenty-ui/icon';
import { MenuItemSelect } from 'twenty-ui/navigation';
import { ViewCalendarLayout } from '~/generated-metadata/graphql';
import {
FeatureFlagKey,
ViewCalendarLayout,
} from '~/generated-metadata/graphql';
export const ObjectOptionsDropdownCalendarViewContent = () => {
const { resetContent } = useObjectOptionsDropdown();
const recordIndexCalendarLayout = useAtomStateValue(
recordIndexCalendarLayoutState,
);
const isCalendarWeekViewEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
);
const supportedCalendarLayout = getSupportedRecordCalendarLayout({
calendarLayout: recordIndexCalendarLayout,
isCalendarWeekViewEnabled,
});
const setRecordIndexCalendarLayout = useSetAtomState(
recordIndexCalendarLayoutState,
);
@@ -47,6 +59,18 @@ export const ObjectOptionsDropdownCalendarViewContent = () => {
];
const handleCalendarViewChange = async (calendarView: ViewCalendarLayout) => {
if (
calendarView === ViewCalendarLayout.WEEK &&
!isCalendarWeekViewEnabled
) {
return;
}
if (calendarView === supportedCalendarLayout) {
closeDropdown();
return;
}
setRecordIndexCalendarLayout(calendarView);
await updateCurrentView({
calendarLayout: calendarView,
@@ -75,17 +99,26 @@ export const ObjectOptionsDropdownCalendarViewContent = () => {
<SelectableListItem
itemId={ViewCalendarLayout.WEEK}
onEnter={() => {
handleCalendarViewChange(ViewCalendarLayout.WEEK);
if (isCalendarWeekViewEnabled) {
handleCalendarViewChange(ViewCalendarLayout.WEEK);
}
}}
>
<MenuItemSelect
LeftIcon={IconCalendarWeek}
text={t`Week`}
selected={recordIndexCalendarLayout === ViewCalendarLayout.WEEK}
selected={supportedCalendarLayout === ViewCalendarLayout.WEEK}
onClick={
isCalendarWeekViewEnabled
? () => handleCalendarViewChange(ViewCalendarLayout.WEEK)
: undefined
}
focused={selectedItemId === ViewCalendarLayout.WEEK}
contextualText={<Pill label={t`Soon`} />}
contextualText={
isCalendarWeekViewEnabled ? undefined : <Pill label={t`Soon`} />
}
contextualTextPosition="right"
disabled
disabled={!isCalendarWeekViewEnabled}
/>
</SelectableListItem>
<SelectableListItem
@@ -95,7 +128,7 @@ export const ObjectOptionsDropdownCalendarViewContent = () => {
<MenuItemSelect
LeftIcon={IconCalendarMonth}
text={t`Month`}
selected={recordIndexCalendarLayout === ViewCalendarLayout.MONTH}
selected={supportedCalendarLayout === ViewCalendarLayout.MONTH}
onClick={() => handleCalendarViewChange(ViewCalendarLayout.MONTH)}
focused={selectedItemId === ViewCalendarLayout.MONTH}
/>
@@ -1,5 +1,6 @@
import { ObjectOptionsDropdownAddRecordGroupContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownAddRecordGroupContent';
import { ObjectOptionsDropdownCalendarFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarFieldsContent';
import { ObjectOptionsDropdownCalendarEndFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarEndFieldsContent';
import { ObjectOptionsDropdownCalendarViewContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarViewContent';
import { ObjectOptionsDropdownFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownFieldsContent';
import { ObjectOptionsDropdownHiddenFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenFieldsContent';
@@ -12,9 +13,14 @@ import { ObjectOptionsDropdownRecordGroupsContent } from '@/object-record/object
import { ObjectOptionsDropdownRecordGroupSortContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent';
import { ObjectOptionsDropdownVisibilityContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const ObjectOptionsDropdownContent = () => {
const { currentContentId } = useObjectOptionsDropdown();
const isCalendarWeekViewEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
);
switch (currentContentId) {
case 'layout':
@@ -39,6 +45,12 @@ export const ObjectOptionsDropdownContent = () => {
return <ObjectOptionsDropdownCalendarViewContent />;
case 'calendarFields':
return <ObjectOptionsDropdownCalendarFieldsContent />;
case 'calendarEndFields':
return isCalendarWeekViewEnabled ? (
<ObjectOptionsDropdownCalendarEndFieldsContent />
) : (
<ObjectOptionsDropdownMenuContent />
);
case 'visibility':
return <ObjectOptionsDropdownVisibilityContent />;
default:
@@ -2,6 +2,7 @@ import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-option
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { getSupportedRecordCalendarLayout } from '@/object-record/record-calendar/utils/getSupportedRecordCalendarLayout';
import { recordIndexCalendarLayoutState } from '@/object-record/record-index/states/recordIndexCalendarLayoutState';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
@@ -22,6 +23,7 @@ import {
viewTypeIconMapping,
} from '@/views/types/ViewType';
import { useDestroyViewFromCurrentState } from '@/views/view-picker/hooks/useDestroyViewFromCurrentState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
@@ -37,7 +39,10 @@ import {
} from 'twenty-ui/icon';
import { AppTooltip } from 'twenty-ui/surfaces';
import { MenuItem } from 'twenty-ui/navigation';
import { ViewCalendarLayout } from '~/generated-metadata/graphql';
import {
FeatureFlagKey,
ViewCalendarLayout,
} from '~/generated-metadata/graphql';
interface ObjectOptionsDropdownCustomViewProps {
onBackToDefault?: () => void;
@@ -70,6 +75,12 @@ export const ObjectOptionsDropdownCustomView = ({
)
: undefined;
const calendarEndFieldMetadata = currentView?.calendarEndFieldMetadataId
? objectMetadataItem.fields.find(
(field) => field.id === currentView.calendarEndFieldMetadataId,
)
: undefined;
const viewsOnCurrentObject = useAtomFamilySelectorValue(
viewsFromObjectMetadataItemFamilySelector,
{ objectMetadataItemId: objectMetadataItem.id },
@@ -81,6 +92,13 @@ export const ObjectOptionsDropdownCustomView = ({
const recordIndexCalendarLayout = useAtomStateValue(
recordIndexCalendarLayoutState,
);
const isCalendarWeekViewEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
);
const supportedCalendarLayout = getSupportedRecordCalendarLayout({
calendarLayout: recordIndexCalendarLayout,
isCalendarWeekViewEnabled,
});
const { visibleBoardFields } = useObjectOptionsForBoard({
objectNameSingular: objectMetadataItem.nameSingular,
@@ -111,7 +129,11 @@ export const ObjectOptionsDropdownCustomView = ({
'Visibility',
'Fields',
...(customViewData?.type === ViewType.CALENDAR
? ['CalendarDateField', 'CalendarView']
? [
'CalendarDateField',
...(isCalendarWeekViewEnabled ? ['CalendarEndDateField'] : []),
'CalendarView',
]
: []),
...(customViewData?.type !== ViewType.CALENDAR ? ['Group'] : []),
'Delete view',
@@ -196,6 +218,29 @@ export const ObjectOptionsDropdownCustomView = ({
/>
</SelectableListItem>
</div>
{isCalendarWeekViewEnabled && (
<div id="calendar-end-date-field-picker-menu-item">
<SelectableListItem
itemId="CalendarEndDateField"
onEnter={() => onContentChange('calendarEndFields')}
>
<MenuItem
focused={selectedItemId === 'CalendarEndDateField'}
onClick={() => onContentChange('calendarEndFields')}
LeftIcon={IconCalendar}
text={t`End date field`}
contextualText={
isDefaultView
? t`Not available on Default View`
: (calendarEndFieldMetadata?.label ?? t`None`)
}
contextualTextPosition="right"
hasSubMenu
disabled={isDefaultView}
/>
</SelectableListItem>
</div>
)}
<SelectableListItem
itemId="CalendarView"
onEnter={() => onContentChange('calendarView')}
@@ -206,9 +251,9 @@ export const ObjectOptionsDropdownCustomView = ({
LeftIcon={IconCalendarWeek}
text={t`Calendar view`}
contextualText={
recordIndexCalendarLayout === ViewCalendarLayout.MONTH
supportedCalendarLayout === ViewCalendarLayout.MONTH
? t`Month`
: recordIndexCalendarLayout === ViewCalendarLayout.WEEK
: supportedCalendarLayout === ViewCalendarLayout.WEEK
? t`Week`
: t`Day`
}
@@ -1,6 +1,7 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu';
import { getSupportedRecordCalendarLayout } from '@/object-record/record-calendar/utils/getSupportedRecordCalendarLayout';
import { recordIndexCalendarLayoutState } from '@/object-record/record-index/states/recordIndexCalendarLayoutState';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
@@ -24,6 +25,7 @@ import {
} from '@/views/types/ViewType';
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useLingui } from '@lingui/react/macro';
import { useCallback } from 'react';
@@ -41,6 +43,7 @@ import {
import { OverflowingTextWithTooltip } from 'twenty-ui/surfaces';
import { MenuItem, MenuItemSelect, MenuItemToggle } from 'twenty-ui/navigation';
import {
FeatureFlagKey,
ViewCalendarLayout,
ViewOpenRecordIn,
} from '~/generated-metadata/graphql';
@@ -72,6 +75,13 @@ export const ObjectOptionsDropdownLayoutContent = () => {
const recordIndexCalendarLayout = useAtomStateValue(
recordIndexCalendarLayoutState,
);
const isCalendarWeekViewEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
);
const supportedCalendarLayout = getSupportedRecordCalendarLayout({
calendarLayout: recordIndexCalendarLayout,
isCalendarWeekViewEnabled,
});
const recordIndexGroupFieldMetadataItem = useAtomComponentStateValue(
recordIndexGroupFieldMetadataItemComponentState,
);
@@ -82,6 +92,12 @@ export const ObjectOptionsDropdownLayoutContent = () => {
)
: undefined;
const calendarEndFieldMetadata = currentView?.calendarEndFieldMetadataId
? objectMetadataItem.fields.find(
(field) => field.id === currentView.calendarEndFieldMetadataId,
)
: undefined;
const { setAndPersistViewType } = useSetViewTypeFromLayoutOptionsMenu();
const { availableFieldsForGrouping, navigateToSelectSettings } =
useGetAvailableFieldsToGroupRecordsBy();
@@ -127,7 +143,11 @@ export const ObjectOptionsDropdownLayoutContent = () => {
ViewOpenRecordIn.SIDE_PANEL,
...(currentView?.type === ViewType.KANBAN ? ['Group'] : []),
...(currentView?.type === ViewType.CALENDAR
? ['CalendarView', 'CalendarDateField']
? [
'CalendarView',
'CalendarDateField',
...(isCalendarWeekViewEnabled ? ['CalendarEndDateField'] : []),
]
: []),
...(currentView?.type !== ViewType.TABLE ? ['Compact view'] : []),
];
@@ -236,6 +256,24 @@ export const ObjectOptionsDropdownLayoutContent = () => {
hasSubMenu
/>
</SelectableListItem>
{isCalendarWeekViewEnabled && (
<SelectableListItem
itemId="CalendarEndDateField"
onEnter={() => onContentChange('calendarEndFields')}
>
<MenuItem
focused={selectedItemId === 'CalendarEndDateField'}
onClick={() => onContentChange('calendarEndFields')}
LeftIcon={IconCalendar}
text={t`End date field`}
contextualText={
calendarEndFieldMetadata?.label ?? t`None`
}
contextualTextPosition="right"
hasSubMenu
/>
</SelectableListItem>
)}
<SelectableListItem
itemId="CalendarView"
onEnter={() => onContentChange('calendarView')}
@@ -246,9 +284,9 @@ export const ObjectOptionsDropdownLayoutContent = () => {
LeftIcon={IconCalendarWeek}
text={t`Calendar view`}
contextualText={
recordIndexCalendarLayout === ViewCalendarLayout.MONTH
supportedCalendarLayout === ViewCalendarLayout.MONTH
? t`Month`
: recordIndexCalendarLayout === ViewCalendarLayout.WEEK
: supportedCalendarLayout === ViewCalendarLayout.WEEK
? t`Week`
: t`Day`
}
@@ -97,6 +97,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
...currentView,
type: viewType,
calendarFieldMetadataId,
calendarEndFieldMetadataId: null,
calendarLayout: ViewCalendarLayout.MONTH,
},
objectMetadataItem,
@@ -109,6 +110,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
updateCurrentViewParams.calendarLayout = ViewCalendarLayout.MONTH;
updateCurrentViewParams.calendarFieldMetadataId =
calendarFieldMetadataId;
updateCurrentViewParams.calendarEndFieldMetadataId = null;
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
return await updateCurrentView(updateCurrentViewParams);
}
@@ -9,5 +9,6 @@ export type ObjectOptionsContentId =
| 'recordGroupSort'
| 'addRecordGroup'
| 'calendarFields'
| 'calendarEndFields'
| 'calendarView'
| 'visibility';