diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutInitializationQueryEffect.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutInitializationQueryEffect.tsx index d9bf2738e3..7fcabedac1 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutInitializationQueryEffect.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutInitializationQueryEffect.tsx @@ -17,6 +17,7 @@ import { DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT } from '@/page-layout/constants/Defaul import { DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultWorkflowRunPageLayoutId'; import { DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT } from '@/page-layout/constants/DefaultWorkflowVersionPageLayout'; import { DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultWorkflowVersionPageLayoutId'; +import { usePageLayoutWithRelationWidgets } from '@/page-layout/hooks/usePageLayoutWithRelationWidgets'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState'; @@ -84,12 +85,14 @@ export const PageLayoutInitializationQueryEffect = ({ skip: isDefaultLayout, }); - const pageLayout: PageLayout | undefined = isDefaultLayout + const basePageLayout: PageLayout | undefined = isDefaultLayout ? getDefaultLayoutById(pageLayoutId) : data?.getPageLayout ? transformPageLayout(data.getPageLayout) : undefined; + const pageLayout = usePageLayoutWithRelationWidgets(basePageLayout); + const pageLayoutPersistedComponentCallbackState = useRecoilComponentCallbackState(pageLayoutPersistedComponentState); diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts index e81eb340e2..163508face 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts @@ -35,280 +35,17 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { title: 'Fields', type: WidgetType.FIELDS, objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 3, - column: 0, - rowSpan: 12, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldsConfiguration', - configurationType: WidgetConfigurationType.FIELDS, - sections: [], - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-fields-2', - pageLayoutTabId: 'company-tab-fields', - title: 'Fields', - type: WidgetType.FIELDS, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 3, - column: 0, - rowSpan: 12, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldsConfiguration', - configurationType: WidgetConfigurationType.FIELDS, - sections: [], - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-account-owner', - pageLayoutTabId: 'company-tab-fields', - title: 'Account Owner', - type: WidgetType.FIELD, - objectMetadataId: null, gridPosition: { __typename: 'GridPosition', row: 0, column: 0, - rowSpan: 1, + rowSpan: 12, columnSpan: 12, }, configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'accountOwner', - layout: 'CARD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-opportunities', - pageLayoutTabId: 'company-tab-fields', - title: 'Opportunities', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 1, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'opportunities', - layout: 'CARD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-people', - pageLayoutTabId: 'company-tab-fields', - title: 'People', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'people', - layout: 'CARD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-people-2', - pageLayoutTabId: 'company-tab-fields', - title: 'People', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'people', - layout: 'CARD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-people-3', - pageLayoutTabId: 'company-tab-fields', - title: 'People (FIELD)', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'people', - layout: 'FIELD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-name', - pageLayoutTabId: 'company-tab-fields', - title: 'Name', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'name', - layout: 'FIELD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-name-2', - pageLayoutTabId: 'company-tab-fields', - title: 'Name', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'name', - layout: 'FIELD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-employees', - pageLayoutTabId: 'company-tab-fields', - title: 'Employees', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'employees', - layout: 'FIELD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-linkedin', - pageLayoutTabId: 'company-tab-fields', - title: 'LinkedIn', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'linkedinLink', - layout: 'FIELD', - }, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - deletedAt: null, - }, - { - __typename: 'PageLayoutWidget', - id: 'company-widget-visa', - pageLayoutTabId: 'company-tab-fields', - title: 'Visa Sponsorship', - type: WidgetType.FIELD, - objectMetadataId: null, - gridPosition: { - __typename: 'GridPosition', - row: 2, - column: 0, - rowSpan: 1, - columnSpan: 12, - }, - configuration: { - __typename: 'FieldConfiguration', - configurationType: WidgetConfigurationType.FIELD, - fieldMetadataId: 'visaSponsorship', - layout: 'FIELD', + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts b/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts new file mode 100644 index 0000000000..158a3b5854 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts @@ -0,0 +1,103 @@ +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems'; +import { type PageLayout } from '@/page-layout/types/PageLayout'; +import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; +import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; +import { isDefined } from 'twenty-shared/utils'; +import { + PageLayoutType, + WidgetConfigurationType, + WidgetType, +} from '~/generated/graphql'; + +const createRelationFieldWidget = ( + field: FieldMetadataItem, + tabId: string, +): PageLayoutWidget => ({ + __typename: 'PageLayoutWidget' as const, + id: `dynamic-relation-widget-${field.id}`, + pageLayoutTabId: tabId, + title: field.label, + type: WidgetType.FIELD, + objectMetadataId: null, + gridPosition: { + __typename: 'GridPosition' as const, + row: 0, + column: 0, + rowSpan: 1, + columnSpan: 12, + }, + configuration: { + __typename: 'FieldConfiguration' as const, + configurationType: WidgetConfigurationType.FIELD, + fieldMetadataId: field.id, + layout: 'CARD' as const, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + deletedAt: null, +}); + +const createRelationFieldWidgets = ( + relationFields: FieldMetadataItem[], + tabId: string, +): PageLayoutWidget[] => { + return relationFields.map((field) => createRelationFieldWidget(field, tabId)); +}; + +const injectRelationWidgetsIntoLayout = ( + layout: PageLayout, + boxedRelationFieldMetadataItems: FieldMetadataItem[], +): PageLayout => { + if (boxedRelationFieldMetadataItems.length === 0) { + return layout; + } + + const firstTab = layout.tabs[0]; + if (!isDefined(firstTab)) { + return layout; + } + + const relationWidgets = createRelationFieldWidgets( + boxedRelationFieldMetadataItems, + firstTab.id, + ); + + return { + ...layout, + tabs: layout.tabs.map((tab) => { + if (tab.id === firstTab.id) { + return { + ...tab, + widgets: [...tab.widgets, ...relationWidgets], + }; + } + return tab; + }), + }; +}; + +export const usePageLayoutWithRelationWidgets = ( + basePageLayout: PageLayout | undefined, +): PageLayout | undefined => { + const { targetRecordIdentifier, layoutType } = useLayoutRenderingContext(); + + const { boxedRelationFieldMetadataItems } = useFieldListFieldMetadataItems({ + objectNameSingular: targetRecordIdentifier?.targetObjectNameSingular ?? '', + }); + + if (!isDefined(basePageLayout)) { + return undefined; + } + + const isRecordPage = layoutType === PageLayoutType.RECORD_PAGE; + + if (!isRecordPage) { + return basePageLayout; + } + + return injectRelationWidgetsIntoLayout( + basePageLayout, + boxedRelationFieldMetadataItems, + ); +};