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
@@ -37,6 +37,7 @@ const PARTIAL_ID_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -67,6 +68,7 @@ const PARTIAL_CREATED_AT_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -97,6 +99,7 @@ const PARTIAL_UPDATED_AT_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -127,6 +130,7 @@ const PARTIAL_DELETED_AT_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -157,6 +161,7 @@ const PARTIAL_CREATED_BY_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -187,6 +192,7 @@ const PARTIAL_UPDATED_BY_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -217,6 +223,7 @@ const PARTIAL_POSITION_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -249,6 +256,7 @@ const PARTIAL_SEARCH_VECTOR_FIELD = {
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
viewSortUniversalIdentifiers: [],
@@ -717,6 +717,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
isCustom: true,
anyFieldFilterValue: null,
calendarFieldMetadataUniversalIdentifier: null,
calendarEndFieldMetadataUniversalIdentifier: null,
calendarLayout: null,
isCompact: false,
shouldHideEmptyGroups: false,
@@ -51,6 +51,7 @@ export const buildNameFlatFieldMetadataForCustomObject = ({
viewFieldUniversalIdentifiers: [],
kanbanAggregateOperationViewUniversalIdentifiers: [],
calendarViewUniversalIdentifiers: [],
calendarEndViewUniversalIdentifiers: [],
mainGroupByFieldMetadataViewUniversalIdentifiers: [],
fieldPermissionUniversalIdentifiers: [],
universalSettings: null,
@@ -31,6 +31,7 @@ export const computeFlatRecordPageFieldsViewToCreate = ({
isCustom: true,
anyFieldFilterValue: null,
calendarFieldMetadataUniversalIdentifier: null,
calendarEndFieldMetadataUniversalIdentifier: null,
calendarLayout: null,
isCompact: false,
shouldHideEmptyGroups: false,