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:
@@ -1,5 +1,6 @@
|
||||
import { type ObjectFieldManifest } from '@/application/objectFieldManifest.type';
|
||||
import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsType';
|
||||
import { type ObjectOpenRecordIn } from '@/types/ObjectOpenRecordIn';
|
||||
|
||||
export type ObjectManifest = SyncableEntityOptions & {
|
||||
nameSingular: string;
|
||||
@@ -13,6 +14,7 @@ export type ObjectManifest = SyncableEntityOptions & {
|
||||
isUICreatable?: boolean;
|
||||
// When false, records of this object are not editable through the generic UI
|
||||
isUIEditable?: boolean;
|
||||
openRecordIn?: ObjectOpenRecordIn;
|
||||
fields: ObjectFieldManifest[];
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
+3
@@ -3223,6 +3223,9 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e
|
||||
"numberFormat": {
|
||||
"universalIdentifier": "20202020-7f40-4e7f-b126-11c0eda6b141",
|
||||
},
|
||||
"openRecordIn": {
|
||||
"universalIdentifier": "20202020-1b16-419d-9323-6f3ea850e5d9",
|
||||
},
|
||||
"ownedOpportunities": {
|
||||
"universalIdentifier": "20202020-9e4d-4b3a-8c1f-6d7e8f9a0b1c",
|
||||
},
|
||||
|
||||
@@ -1033,6 +1033,9 @@ export const STANDARD_OBJECT_FIELDS = {
|
||||
colorScheme: {
|
||||
universalIdentifier: '20202020-66bc-47f2-adac-f2ef7c598b63',
|
||||
},
|
||||
openRecordIn: {
|
||||
universalIdentifier: '20202020-1b16-419d-9323-6f3ea850e5d9',
|
||||
},
|
||||
locale: {
|
||||
universalIdentifier: '20202020-402e-4695-b169-794fa015afbe',
|
||||
},
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum ObjectOpenRecordIn {
|
||||
SIDE_PANEL = 'SIDE_PANEL',
|
||||
RECORD_PAGE = 'RECORD_PAGE',
|
||||
USER_CHOICE = 'USER_CHOICE',
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum OpenRecordIn {
|
||||
SIDE_PANEL = 'SIDE_PANEL',
|
||||
RECORD_PAGE = 'RECORD_PAGE',
|
||||
}
|
||||
@@ -163,6 +163,7 @@ export { NavigationMenuItemType } from './NavigationMenuItemType';
|
||||
export type { NonNullableRequired } from './NonNullableRequired';
|
||||
export type { Nullable } from './Nullable';
|
||||
export type { NullablePartial } from './NullablePartial';
|
||||
export { ObjectOpenRecordIn } from './ObjectOpenRecordIn';
|
||||
export type { ObjectPermissions } from './ObjectPermissions';
|
||||
export type { ObjectRecord } from './ObjectRecord';
|
||||
export type {
|
||||
@@ -177,6 +178,7 @@ export { OrderByDirection } from './ObjectRecordGroupBy';
|
||||
export { ObjectRecordGroupByDateGranularity } from './ObjectRecordGroupByDateGranularity';
|
||||
export type { ObjectsPermissions } from './ObjectsPermissions';
|
||||
export type { ObjectsPermissionsByRoleId } from './ObjectsPermissionsByRoleId';
|
||||
export { OpenRecordIn } from './OpenRecordIn';
|
||||
export type { OrderBy } from './OrderBy';
|
||||
export type {
|
||||
ChartRecordFilter,
|
||||
|
||||
Reference in New Issue
Block a user