Support more standard objects in Record Page Layout (#15288)

Used the `PageLayoutRenderer` for people, opportunities, tasks and
notes.

## Demo


https://github.com/user-attachments/assets/35e61702-f338-47b3-91b7-fd3951a0c967
This commit is contained in:
Baptiste Devessier
2025-10-24 17:38:49 +02:00
committed by GitHub
parent 93ab1c4118
commit 9b2a73d50a
25 changed files with 1161 additions and 65 deletions
@@ -4550,6 +4550,7 @@ export enum WidgetType {
GRAPH = 'GRAPH',
IFRAME = 'IFRAME',
NOTES = 'NOTES',
RICH_TEXT = 'RICH_TEXT',
TASKS = 'TASKS',
TIMELINE = 'TIMELINE',
VIEW = 'VIEW'
@@ -4378,6 +4378,7 @@ export enum WidgetType {
GRAPH = 'GRAPH',
IFRAME = 'IFRAME',
NOTES = 'NOTES',
RICH_TEXT = 'RICH_TEXT',
TASKS = 'TASKS',
TIMELINE = 'TIMELINE',
VIEW = 'VIEW'
@@ -3,7 +3,7 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { useRecoilValue } from 'recoil';
@@ -15,11 +15,9 @@ export const CancelRecordPageLayoutSingleRecordAction = () => {
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
// For COMPANY objects, use DEFAULT_PAGE_LAYOUT_ID
// For other objects, use the record's pageLayoutId (when backend is ready)
const pageLayoutId =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
? DEFAULT_PAGE_LAYOUT_ID
? DEFAULT_RECORD_PAGE_LAYOUT_ID
: selectedRecord?.pageLayoutId;
const { setIsPageLayoutInEditMode } =
@@ -3,7 +3,7 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { useRecoilValue } from 'recoil';
@@ -14,11 +14,9 @@ export const EditRecordPageLayoutSingleRecordAction = () => {
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
// For COMPANY objects, use DEFAULT_PAGE_LAYOUT_ID
// For other objects, use the record's pageLayoutId (when backend is ready)
const pageLayoutId =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
? DEFAULT_PAGE_LAYOUT_ID
? DEFAULT_RECORD_PAGE_LAYOUT_ID
: selectedRecord?.pageLayoutId;
const { setIsPageLayoutInEditMode } =
@@ -3,7 +3,7 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { useSavePageLayout } from '@/page-layout/hooks/useSavePageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { useRecoilValue } from 'recoil';
@@ -15,11 +15,9 @@ export const SaveRecordPageLayoutSingleRecordAction = () => {
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
// For COMPANY objects, use DEFAULT_PAGE_LAYOUT_ID
// For other objects, use the record's pageLayoutId (when backend is ready)
const pageLayoutId =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
? DEFAULT_PAGE_LAYOUT_ID
? DEFAULT_RECORD_PAGE_LAYOUT_ID
: selectedRecord?.pageLayoutId;
const { savePageLayout } = useSavePageLayout(pageLayoutId);
@@ -18,12 +18,22 @@ export const PageLayoutDispatcher = ({
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_ENABLED,
);
const isStandardObjectWithDefaultLayout =
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Company ||
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Person ||
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Opportunity ||
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Task;
if (
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Dashboard ||
(targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Company &&
isRecordPageEnabled)
(isRecordPageEnabled && isStandardObjectWithDefaultLayout)
) {
return (
<PageLayoutRecordPageRenderer
@@ -1,6 +1,16 @@
import { FIND_ONE_PAGE_LAYOUT } from '@/dashboards/graphql/queries/findOnePageLayout';
import { DEFAULT_PAGE_LAYOUT } from '@/page-layout/constants/DefaultPageLayout';
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_COMPANY_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultCompanyRecordPageLayout';
import { DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultCompanyRecordPageLayoutId';
import { DEFAULT_NOTE_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultNoteRecordPageLayout';
import { DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultNoteRecordPageLayoutId';
import { DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultOpportunityRecordPageLayout';
import { DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultOpportunityRecordPageLayoutId';
import { DEFAULT_PERSON_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultPersonRecordPageLayout';
import { DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPersonRecordPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultRecordPageLayout';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { DEFAULT_TASK_RECORD_PAGE_LAYOUT } from '@/page-layout/constants/DefaultTaskRecordPageLayout';
import { DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultTaskRecordPageLayoutId';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
@@ -15,6 +25,24 @@ import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
const getDefaultLayoutById = (layoutId: string): PageLayout => {
switch (layoutId) {
case DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID:
return DEFAULT_COMPANY_RECORD_PAGE_LAYOUT;
case DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID:
return DEFAULT_PERSON_RECORD_PAGE_LAYOUT;
case DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID:
return DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT;
case DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID:
return DEFAULT_NOTE_RECORD_PAGE_LAYOUT;
case DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID:
return DEFAULT_TASK_RECORD_PAGE_LAYOUT;
case DEFAULT_RECORD_PAGE_LAYOUT_ID:
default:
return DEFAULT_RECORD_PAGE_LAYOUT;
}
};
type PageLayoutInitializationQueryEffectProps = {
pageLayoutId: string;
onInitialized?: (pageLayout: PageLayout) => void;
@@ -26,7 +54,13 @@ export const PageLayoutInitializationQueryEffect = ({
}: PageLayoutInitializationQueryEffectProps) => {
const [isInitialized, setIsInitialized] = useState(false);
const isDefaultLayout = pageLayoutId === DEFAULT_PAGE_LAYOUT_ID;
const isDefaultLayout =
pageLayoutId === DEFAULT_RECORD_PAGE_LAYOUT_ID ||
pageLayoutId === DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID ||
pageLayoutId === DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID ||
pageLayoutId === DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID ||
pageLayoutId === DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID ||
pageLayoutId === DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID;
const { data } = useQuery(FIND_ONE_PAGE_LAYOUT, {
variables: {
@@ -36,7 +70,7 @@ export const PageLayoutInitializationQueryEffect = ({
});
const pageLayout: PageLayout | undefined = isDefaultLayout
? DEFAULT_PAGE_LAYOUT
? getDefaultLayoutById(pageLayoutId)
: data?.getPageLayout
? transformPageLayout(data.getPageLayout)
: undefined;
@@ -0,0 +1,241 @@
import { DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultCompanyRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
name: 'Default Company Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
id: 'company-tab-fields',
title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-fields',
pageLayoutTabId: 'company-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-timeline',
pageLayoutTabId: 'company-tab-timeline',
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-tasks',
title: 'Tasks',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-tasks',
pageLayoutTabId: 'company-tab-tasks',
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-notes',
title: 'Notes',
position: 400,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-notes',
pageLayoutTabId: 'company-tab-notes',
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-files',
title: 'Files',
position: 500,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-files',
pageLayoutTabId: 'company-tab-files',
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-emails',
title: 'Emails',
position: 600,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-emails',
pageLayoutTabId: 'company-tab-emails',
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'company-tab-calendar',
title: 'Calendar',
position: 700,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'company-widget-calendar',
pageLayoutTabId: 'company-tab-calendar',
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1,2 @@
export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID =
'default-company-page-layout';
@@ -0,0 +1,132 @@
import { DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultNoteRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
name: 'Default Note Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
id: 'note-tab-fields',
title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'note-widget-fields',
pageLayoutTabId: 'note-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'note-widget-note',
pageLayoutTabId: 'note-tab-fields',
title: 'Note',
type: WidgetType.RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'note-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'note-widget-timeline',
pageLayoutTabId: 'note-tab-timeline',
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'note-tab-files',
title: 'Files',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'note-widget-files',
pageLayoutTabId: 'note-tab-files',
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1 @@
export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID = 'default-note-page-layout';
@@ -0,0 +1,241 @@
import { DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultOpportunityRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
name: 'Default Opportunity Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-fields',
title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-fields',
pageLayoutTabId: 'opportunity-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-timeline',
pageLayoutTabId: 'opportunity-tab-timeline',
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-tasks',
title: 'Tasks',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-tasks',
pageLayoutTabId: 'opportunity-tab-tasks',
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-notes',
title: 'Notes',
position: 400,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-notes',
pageLayoutTabId: 'opportunity-tab-notes',
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-files',
title: 'Files',
position: 500,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-files',
pageLayoutTabId: 'opportunity-tab-files',
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-emails',
title: 'Emails',
position: 600,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-emails',
pageLayoutTabId: 'opportunity-tab-emails',
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'opportunity-tab-calendar',
title: 'Calendar',
position: 700,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'opportunity-widget-calendar',
pageLayoutTabId: 'opportunity-tab-calendar',
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1,2 @@
export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID =
'default-opportunity-page-layout';
@@ -1 +0,0 @@
export const DEFAULT_PAGE_LAYOUT_ID = 'DEFAULT_LAYOUT_ID';
@@ -0,0 +1,241 @@
import { DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPersonRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
name: 'Default Person Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
id: 'person-tab-fields',
title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-fields',
pageLayoutTabId: 'person-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-timeline',
pageLayoutTabId: 'person-tab-timeline',
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-tasks',
title: 'Tasks',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-tasks',
pageLayoutTabId: 'person-tab-tasks',
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-notes',
title: 'Notes',
position: 400,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-notes',
pageLayoutTabId: 'person-tab-notes',
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-files',
title: 'Files',
position: 500,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-files',
pageLayoutTabId: 'person-tab-files',
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-emails',
title: 'Emails',
position: 600,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-emails',
pageLayoutTabId: 'person-tab-emails',
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'person-tab-calendar',
title: 'Calendar',
position: 700,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'person-widget-calendar',
pageLayoutTabId: 'person-tab-calendar',
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1,2 @@
export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID =
'default-person-page-layout';
@@ -1,15 +1,10 @@
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
/**
* Default mock PageLayout used when pageLayoutId equals DEFAULT_PAGE_LAYOUT_ID.
* This combines base record layout tabs (fields, timeline, tasks, notes, files)
* with company-specific tabs (emails, calendar).
*/
export const DEFAULT_PAGE_LAYOUT: PageLayout = {
export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_PAGE_LAYOUT_ID,
id: DEFAULT_RECORD_PAGE_LAYOUT_ID,
name: 'Default Record Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
@@ -17,7 +12,6 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
// Fields tab (position 100)
{
__typename: 'PageLayoutTab',
id: 'default-tab-fields',
@@ -25,7 +19,7 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -51,14 +45,13 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
},
],
},
// Timeline tab (position 200)
{
__typename: 'PageLayoutTab',
id: 'default-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -84,14 +77,13 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
},
],
},
// Tasks tab (position 300)
{
__typename: 'PageLayoutTab',
id: 'default-tab-tasks',
title: 'Tasks',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -115,35 +107,15 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'default-widget-tasks-2',
pageLayoutTabId: 'default-tab-tasks',
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
// Notes tab (position 400)
{
__typename: 'PageLayoutTab',
id: 'default-tab-notes',
title: 'Notes',
position: 400,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -169,14 +141,13 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
},
],
},
// Files tab (position 500)
{
__typename: 'PageLayoutTab',
id: 'default-tab-files',
title: 'Files',
position: 500,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -202,14 +173,13 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
},
],
},
// Emails tab (position 600) - Company specific
{
__typename: 'PageLayoutTab',
id: 'default-tab-emails',
title: 'Emails',
position: 600,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -235,14 +205,13 @@ export const DEFAULT_PAGE_LAYOUT: PageLayout = {
},
],
},
// Calendar tab (position 700) - Company specific
{
__typename: 'PageLayoutTab',
id: 'default-tab-calendar',
title: 'Calendar',
position: 700,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -0,0 +1 @@
export const DEFAULT_RECORD_PAGE_LAYOUT_ID = 'default-page-layout';
@@ -0,0 +1,132 @@
import { DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultTaskRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { PageLayoutType, WidgetType } from '~/generated/graphql';
export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
name: 'Default Task Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
id: 'task-tab-fields',
title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'task-widget-fields',
pageLayoutTabId: 'task-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'task-widget-note',
pageLayoutTabId: 'task-tab-fields',
title: 'Note',
type: WidgetType.RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'task-tab-timeline',
title: 'Timeline',
position: 200,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'task-widget-timeline',
pageLayoutTabId: 'task-tab-timeline',
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
{
__typename: 'PageLayoutTab',
id: 'task-tab-files',
title: 'Files',
position: 300,
layoutMode: 'vertical-list',
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'task-widget-files',
pageLayoutTabId: 'task-tab-files',
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1 @@
export const DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID = 'default-task-page-layout';
@@ -1,9 +1,24 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { DEFAULT_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPageLayoutId';
import { DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultCompanyRecordPageLayoutId';
import { DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultNoteRecordPageLayoutId';
import { DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultOpportunityRecordPageLayoutId';
import { DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultPersonRecordPageLayoutId';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultTaskRecordPageLayoutId';
import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier';
import { isDefined } from 'twenty-shared/utils';
const OBJECT_NAME_TO_DEFAULT_LAYOUT_ID: Record<string, string> = {
[CoreObjectNameSingular.Company]: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
[CoreObjectNameSingular.Person]: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
[CoreObjectNameSingular.Opportunity]:
DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
[CoreObjectNameSingular.Note]: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
[CoreObjectNameSingular.Task]: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
};
export const useRecordPageLayoutId = ({
id,
targetObjectNameSingular,
@@ -27,8 +42,11 @@ export const useRecordPageLayoutId = ({
};
}
// TODO: implement a default layout
const defaultLayoutId =
OBJECT_NAME_TO_DEFAULT_LAYOUT_ID[targetObjectNameSingular] ??
DEFAULT_RECORD_PAGE_LAYOUT_ID;
return {
pageLayoutId: DEFAULT_PAGE_LAYOUT_ID,
pageLayoutId: defaultLayoutId,
};
};
@@ -5,9 +5,13 @@ import { FileWidget } from '@/page-layout/widgets/files/components/FileWidget';
import { GraphWidgetRenderer } from '@/page-layout/widgets/graph/components/GraphWidgetRenderer';
import { IframeWidget } from '@/page-layout/widgets/iframe/components/IframeWidget';
import { NoteWidget } from '@/page-layout/widgets/notes/components/NoteWidget';
import { RichTextWidget } from '@/page-layout/widgets/rich-text/components/RichTextWidget';
import { TaskWidget } from '@/page-layout/widgets/tasks/components/TaskWidget';
import { TimelineWidget } from '@/page-layout/widgets/timeline/components/TimelineWidget';
import { type PageLayoutWidget, WidgetType } from '~/generated/graphql';
import {
type PageLayoutWidget,
WidgetType,
} from '~/generated-metadata/graphql';
type WidgetContentRendererProps = {
widget: PageLayoutWidget;
@@ -35,6 +39,9 @@ export const WidgetContentRenderer = ({
case WidgetType.NOTES:
return <NoteWidget widget={widget} />;
case WidgetType.RICH_TEXT:
return <RichTextWidget widget={widget} />;
case WidgetType.FILES:
return <FileWidget widget={widget} />;
@@ -0,0 +1,21 @@
import { RichTextCard } from '@/ui/layout/show-page/components/RichTextCard';
import styled from '@emotion/styled';
import { type PageLayoutWidget } from '~/generated/graphql';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;
type RichTextWidgetProps = {
widget: PageLayoutWidget;
};
export const RichTextWidget = ({ widget: _widget }: RichTextWidgetProps) => {
return (
<StyledContainer>
<RichTextCard />
</StyledContainer>
);
};
@@ -0,0 +1,45 @@
import { type MigrationInterface, type QueryRunner } from 'typeorm';
export class AddRichTextWidgetType1761215000000 implements MigrationInterface {
name = 'AddRichTextWidgetType1761215000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "core"."pageLayoutWidget_type_enum" RENAME TO "pageLayoutWidget_type_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "core"."pageLayoutWidget_type_enum" AS ENUM('VIEW', 'IFRAME', 'FIELDS', 'GRAPH', 'TIMELINE', 'TASKS', 'NOTES', 'FILES', 'EMAILS', 'CALENDAR', 'RICH_TEXT')`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" TYPE "core"."pageLayoutWidget_type_enum" USING "type"::"text"::"core"."pageLayoutWidget_type_enum"`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" SET DEFAULT 'VIEW'`,
);
await queryRunner.query(
`DROP TYPE "core"."pageLayoutWidget_type_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "core"."pageLayoutWidget_type_enum_old" AS ENUM('VIEW', 'IFRAME', 'FIELDS', 'GRAPH', 'TIMELINE', 'TASKS', 'NOTES', 'FILES', 'EMAILS', 'CALENDAR')`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" TYPE "core"."pageLayoutWidget_type_enum_old" USING "type"::"text"::"core"."pageLayoutWidget_type_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "type" SET DEFAULT 'VIEW'`,
);
await queryRunner.query(`DROP TYPE "core"."pageLayoutWidget_type_enum"`);
await queryRunner.query(
`ALTER TYPE "core"."pageLayoutWidget_type_enum_old" RENAME TO "pageLayoutWidget_type_enum"`,
);
}
}
@@ -9,4 +9,5 @@ export enum WidgetType {
FILES = 'FILES',
EMAILS = 'EMAILS',
CALENDAR = 'CALENDAR',
RICH_TEXT = 'RICH_TEXT',
}