Generate Field widgets for relations (#17047)
To release version 1, we must ensure feature parity with the current production version. In the future, all widgets will be stored in the backend. For now, let's compute Field widgets for relations at runtime. https://github.com/user-attachments/assets/acffe2f9-0f9b-4eff-bb89-c41f7caf5441
This commit is contained in:
committed by
GitHub
parent
b1c821b0e3
commit
dec2b44808
+4
-1
@@ -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);
|
||||
|
||||
|
||||
+4
-267
@@ -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(),
|
||||
|
||||
+103
@@ -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,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user