Move open-record-in to object metadata and member preference (#23614)
Replaces the per-view "Open in" setting with a two-level model, following up on #23422 / #23424 and superseding the closed #23446 and #23457: - `objectMetadata.openRecordIn`: `SIDE_PANEL` | `RECORD_PAGE` | `USER_CHOICE` (default `USER_CHOICE`) - `workspaceMember.openRecordIn`: `SIDE_PANEL` | `RECORD_PAGE` (default `SIDE_PANEL`), editable in Settings > Experience The rule: records open where the member prefers, unless the object pins them, and never in a panel there is no room for (mobile always resolves to the record page). ## Why Having the setting on views, objects and members at once was heavy, and view-level resolution was fragile: a chip rendered outside a view (notes, front components, kanban cards pointing at another object) had no view to read from, which is the class of bug behind #23422. Resolution is now context-free: it needs only the object, the current member and the viewport, so chips behave identically everywhere by construction. ## Changes **Object level** - New `openRecordIn` enum column on `objectMetadata`, editable through `updateOneObject` and surfaced in Settings > Data model > Object > Layout ("Open records in": Member preference / Side Panel / Record Page) - Standard definitions pin `workflow`, `workflowVersion`, `dashboard` and `messageCampaign` to the record page (matching the previously hardcoded list) and `calendarEvent` to the side panel (it has no curated record page); everything else, including `workflowRun`, follows the member preference - Apps can set it in `defineObject()` via the object manifest **Member level** - New `openRecordIn` standard field on `workspaceMember`, persisted through the existing settings path (same as `colorScheme`) and exposed in Settings > Experience **View level (deprecated)** - `view.openRecordIn` is no longer read or written by the frontend; the "Open in" entry is gone from the view options dropdown - The column, DTO field and inputs are kept for one release for API compatibility: the output field carries a `deprecationReason`, the inputs keep accepting the value with a `Deprecated:` description (NestJS silently drops input fields that have a `deprecationReason`, which would have been a breaking change) **Upgrade (2.27)** - Fast instance command adds the `objectMetadata.openRecordIn` column defaulting to `USER_CHOICE` - Workspace command adds the `workspaceMember.openRecordIn` field - Workspace command seeds the object column from the standard definitions (any non-`USER_CHOICE` value), then lifts deliberate per-view record page choices onto objects the definitions don't pin **Debt removed** - `canOpenObjectInSidePanel` hardcoded object list and its test - `ObjectOptionsDropdownLayoutOpenInContent` and the `layoutOpenIn` dropdown wiring - `DefaultViewOpenRecordIn` - Context-store/view-based resolution in `useResolveOpenRecordIn` (now reads object metadata + member + viewport) - Front components no longer guess from the current view: an explicit side-panel call honours a pinned object and the viewport, nothing else ## Verification - Ran the three upgrade commands against a live database: column created, the pinned standard objects seeded per workspace (record page pins plus calendarEvent to side panel), member field backfilled to `SIDE_PANEL`; seed rerun is a no-op - Seed command verified on a simulated pre-upgrade workspace (index view set to record page on company): pins the standard objects plus company, idempotent on rerun - Both packages typecheck and lint clean; affected unit suites and the application sync, view creation and metadata cache integration specs pass --------- Co-authored-by: Thomas des Francs <tdesfrancs@gmail.com>
This commit is contained in:
@@ -343,6 +343,7 @@ type Object {
|
||||
isUICreatable: Boolean!
|
||||
isUIReadOnly: Boolean! @deprecated(reason: "Use isUIEditable")
|
||||
isSearchable: Boolean!
|
||||
openRecordIn: ObjectOpenRecordIn!
|
||||
applicationId: UUID!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
@@ -369,6 +370,12 @@ type Object {
|
||||
): ObjectIndexMetadatasConnection!
|
||||
}
|
||||
|
||||
enum ObjectOpenRecordIn {
|
||||
SIDE_PANEL
|
||||
RECORD_PAGE
|
||||
USER_CHOICE
|
||||
}
|
||||
|
||||
input CursorPaging {
|
||||
"""Paginate before opaque cursor"""
|
||||
before: ConnectionCursor
|
||||
@@ -436,6 +443,7 @@ type WorkspaceMember {
|
||||
name: FullName!
|
||||
userEmail: String!
|
||||
colorScheme: String!
|
||||
openRecordIn: OpenRecordIn!
|
||||
avatarUrl: String
|
||||
locale: String
|
||||
calendarStartDay: Int
|
||||
@@ -447,6 +455,11 @@ type WorkspaceMember {
|
||||
numberFormat: WorkspaceMemberNumberFormatEnum
|
||||
}
|
||||
|
||||
enum OpenRecordIn {
|
||||
SIDE_PANEL
|
||||
RECORD_PAGE
|
||||
}
|
||||
|
||||
"""Date format as Month first, Day first, Year first or system as default"""
|
||||
enum WorkspaceMemberDateFormatEnum {
|
||||
SYSTEM
|
||||
@@ -793,7 +806,7 @@ type View {
|
||||
position: Float!
|
||||
isCompact: Boolean!
|
||||
isCustom: Boolean!
|
||||
openRecordIn: ViewOpenRecordIn!
|
||||
openRecordIn: ViewOpenRecordIn! @deprecated(reason: "Superseded by objectMetadata.openRecordIn and the workspace member preference; kept one release for API compatibility, no longer read by the frontend.")
|
||||
kanbanAggregateOperation: AggregateOperations
|
||||
kanbanAggregateOperationFieldMetadataId: UUID
|
||||
mainGroupByFieldMetadataId: UUID
|
||||
@@ -3749,6 +3762,10 @@ input CreateViewInput {
|
||||
isCompact: Boolean = false
|
||||
shouldHideEmptyGroups: Boolean = false
|
||||
kanbanColumnWidth: Int
|
||||
|
||||
"""
|
||||
Deprecated: Superseded by objectMetadata.openRecordIn and the workspace member preference; kept one release for API compatibility, no longer read by the frontend.
|
||||
"""
|
||||
openRecordIn: ViewOpenRecordIn = SIDE_PANEL
|
||||
kanbanAggregateOperation: AggregateOperations
|
||||
kanbanAggregateOperationFieldMetadataId: UUID
|
||||
@@ -3767,6 +3784,10 @@ input UpdateViewInput {
|
||||
icon: String
|
||||
position: Float
|
||||
isCompact: Boolean
|
||||
|
||||
"""
|
||||
Deprecated: Superseded by objectMetadata.openRecordIn and the workspace member preference; kept one release for API compatibility, no longer read by the frontend.
|
||||
"""
|
||||
openRecordIn: ViewOpenRecordIn
|
||||
kanbanAggregateOperation: AggregateOperations
|
||||
kanbanAggregateOperationFieldMetadataId: UUID
|
||||
@@ -3809,6 +3830,10 @@ input UpsertViewWidgetViewSettingsInput {
|
||||
type: ViewType
|
||||
mainGroupByFieldMetadataId: UUID
|
||||
shouldHideEmptyGroups: Boolean
|
||||
|
||||
"""
|
||||
Deprecated: Superseded by objectMetadata.openRecordIn and the workspace member preference; kept one release for API compatibility, no longer read by the frontend.
|
||||
"""
|
||||
openRecordIn: ViewOpenRecordIn
|
||||
kanbanAggregateOperation: AggregateOperations
|
||||
kanbanAggregateOperationFieldMetadataId: UUID
|
||||
@@ -4182,6 +4207,7 @@ input UpdateObjectPayload {
|
||||
imageIdentifierFieldMetadataId: UUID
|
||||
isLabelSyncedWithName: Boolean
|
||||
isSearchable: Boolean
|
||||
openRecordIn: ObjectOpenRecordIn
|
||||
}
|
||||
|
||||
input CreateOneIndexInput {
|
||||
|
||||
Reference in New Issue
Block a user