Don't show summary/fields on workflow record pages (#8804)

Hiding the left column on workflow pages


This raises additional questions: how do we edit the title then?
Probably need a significant refacto of PageHeader


<img width="1094" alt="Screenshot 2024-11-29 at 10 01 08"
src="https://github.com/user-attachments/assets/b98d55cb-5097-4e46-bac5-5570d9ca0ad8">
This commit is contained in:
Félix Malfait
2024-11-29 18:11:44 +01:00
committed by GitHub
parent ebc381f009
commit 29eb9fe77b
7 changed files with 323 additions and 282 deletions

View File

@ -35,7 +35,7 @@ export const RecordShowContainer = ({
objectRecordId,
});
const tabs = useRecordShowContainerTabs(
const { layout, tabs } = useRecordShowContainerTabs(
loading,
objectNameSingular as CoreObjectNameSingular,
isInRightDrawer,
@ -61,6 +61,7 @@ export const RecordShowContainer = ({
<ShowPageContainer>
<ShowPageSubContainer
tabs={tabs}
layout={layout}
targetableObject={{
id: objectRecordId,
targetObjectNameSingular: objectMetadataItem?.nameSingular,

View File

@ -1,6 +1,6 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { CardType } from '@/object-record/record-show/types/CardType';
import { RecordLayoutTab } from '@/ui/layout/tab/types/RecordLayoutTab';
import { RecordLayout } from '@/object-record/record-show/types/RecordLayout';
import {
IconCheckbox,
IconList,
@ -9,75 +9,77 @@ import {
IconTimelineEvent,
} from 'twenty-ui';
export const BASE_RECORD_LAYOUT: Record<string, RecordLayoutTab> = {
fields: {
title: 'Fields',
Icon: IconList,
position: 100,
cards: [{ type: CardType.FieldCard }],
hide: {
ifMobile: false,
ifDesktop: true,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
export const BASE_RECORD_LAYOUT: RecordLayout = {
tabs: {
fields: {
title: 'Fields',
Icon: IconList,
position: 100,
cards: [{ type: CardType.FieldCard }],
hide: {
ifMobile: false,
ifDesktop: true,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
timeline: {
title: 'Timeline',
Icon: IconTimelineEvent,
position: 200,
cards: [{ type: CardType.TimelineCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: true,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
timeline: {
title: 'Timeline',
Icon: IconTimelineEvent,
position: 200,
cards: [{ type: CardType.TimelineCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: true,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
tasks: {
title: 'Tasks',
Icon: IconCheckbox,
position: 300,
cards: [{ type: CardType.TaskCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Task],
ifRelationsMissing: ['taskTargets'],
tasks: {
title: 'Tasks',
Icon: IconCheckbox,
position: 300,
cards: [{ type: CardType.TaskCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Task],
ifRelationsMissing: ['taskTargets'],
},
},
},
notes: {
title: 'Notes',
Icon: IconNotes,
position: 400,
cards: [{ type: CardType.NoteCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Note],
ifRelationsMissing: ['noteTargets'],
notes: {
title: 'Notes',
Icon: IconNotes,
position: 400,
cards: [{ type: CardType.NoteCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Note],
ifRelationsMissing: ['noteTargets'],
},
},
},
files: {
title: 'Files',
Icon: IconPaperclip,
position: 500,
cards: [{ type: CardType.FileCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Attachment],
ifRelationsMissing: ['attachments'],
files: {
title: 'Files',
Icon: IconPaperclip,
position: 500,
cards: [{ type: CardType.FileCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [CoreObjectNameSingular.Attachment],
ifRelationsMissing: ['attachments'],
},
},
},
};

View File

@ -23,7 +23,7 @@ export const useRecordShowContainerTabs = (
targetObjectNameSingular: CoreObjectNameSingular,
isInRightDrawer: boolean,
objectMetadataItem: ObjectMetadataItem,
): SingleTabProps[] => {
): { layout: RecordLayout; tabs: SingleTabProps[] } => {
const isMobile = useIsMobile();
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
@ -34,235 +34,258 @@ export const useRecordShowContainerTabs = (
Record<CoreObjectNameSingular, RecordLayout>
> = {
[CoreObjectNameSingular.Note]: {
richText: {
title: 'Note',
position: 0,
Icon: IconNotes,
cards: [{ type: CardType.RichTextCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
richText: {
title: 'Note',
position: 0,
Icon: IconNotes,
cards: [{ type: CardType.RichTextCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
tasks: null,
notes: null,
},
tasks: null,
notes: null,
},
[CoreObjectNameSingular.Task]: {
richText: {
title: 'Note',
position: 0,
Icon: IconNotes,
cards: [{ type: CardType.RichTextCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
richText: {
title: 'Note',
position: 0,
Icon: IconNotes,
cards: [{ type: CardType.RichTextCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
tasks: null,
notes: null,
},
tasks: null,
notes: null,
},
[CoreObjectNameSingular.Company]: {
emails: {
title: 'Emails',
position: 600,
Icon: IconMail,
cards: [{ type: CardType.EmailCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
emails: {
title: 'Emails',
position: 600,
Icon: IconMail,
cards: [{ type: CardType.EmailCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
calendar: {
title: 'Calendar',
position: 700,
Icon: IconCalendarEvent,
cards: [{ type: CardType.CalendarCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
calendar: {
title: 'Calendar',
position: 700,
Icon: IconCalendarEvent,
cards: [{ type: CardType.CalendarCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
},
[CoreObjectNameSingular.Person]: {
emails: {
title: 'Emails',
position: 600,
Icon: IconMail,
cards: [{ type: CardType.EmailCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
emails: {
title: 'Emails',
position: 600,
Icon: IconMail,
cards: [{ type: CardType.EmailCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
calendar: {
title: 'Calendar',
position: 700,
Icon: IconCalendarEvent,
cards: [{ type: CardType.CalendarCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
calendar: {
title: 'Calendar',
position: 700,
Icon: IconCalendarEvent,
cards: [{ type: CardType.CalendarCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
},
[CoreObjectNameSingular.Workflow]: {
workflow: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
hideSummaryAndFields: true,
tabs: {
workflow: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
timeline: null,
fields: null,
},
timeline: null,
},
[CoreObjectNameSingular.WorkflowVersion]: {
workflowVersion: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowVersionCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
workflowVersion: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowVersionCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
timeline: null,
},
timeline: null,
},
[CoreObjectNameSingular.WorkflowRun]: {
workflowRunOutput: {
title: 'Output',
position: 0,
Icon: IconPrinter,
cards: [{ type: CardType.WorkflowRunOutputCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
tabs: {
workflowRunOutput: {
title: 'Output',
position: 0,
Icon: IconPrinter,
cards: [{ type: CardType.WorkflowRunOutputCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
workflowRunFlow: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowRunCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
workflowRunFlow: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowRunCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
timeline: null,
},
timeline: null,
},
};
// Merge base layout with object-specific layout
const tabDefinitions: RecordLayout = {
const recordLayout: RecordLayout = {
...BASE_RECORD_LAYOUT,
...(OBJECT_SPECIFIC_LAYOUTS[targetObjectNameSingular] || {}),
tabs: {
...BASE_RECORD_LAYOUT.tabs,
...(OBJECT_SPECIFIC_LAYOUTS[targetObjectNameSingular]?.tabs || {}),
},
};
return Object.entries(tabDefinitions)
.filter(
(entry): entry is [string, NonNullable<RecordLayoutTab>] =>
entry[1] !== null && entry[1] !== undefined,
)
.sort(([, a], [, b]) => a.position - b.position)
.map(([key, { title, Icon, hide, cards }]) => {
// Special handling for fields tab
if (key === 'fields') {
return {
layout: recordLayout,
tabs: Object.entries(recordLayout.tabs)
.filter(
(entry): entry is [string, NonNullable<RecordLayoutTab>] =>
entry[1] !== null && entry[1] !== undefined,
)
.sort(([, a], [, b]) => a.position - b.position)
.map(([key, { title, Icon, hide, cards }]) => {
// Special handling for fields tab
if (key === 'fields') {
return {
id: key,
title,
Icon,
cards,
hide: !(isMobile || isInRightDrawer),
};
}
const baseHide =
(hide.ifMobile && isMobile) ||
(hide.ifDesktop && !isMobile) ||
(hide.ifInRightDrawer && isInRightDrawer);
const featureNotEnabled =
hide.ifFeaturesDisabled.length > 0 &&
!hide.ifFeaturesDisabled.every((flagKey) => {
return !!currentWorkspace?.featureFlags?.find(
(flag: FeatureFlag) => flag.key === flagKey && flag.value,
);
});
const requiredObjectsInactive =
hide.ifRequiredObjectsInactive.length > 0 &&
!hide.ifRequiredObjectsInactive.every((obj) =>
objectMetadataItems.some(
(item) => item.nameSingular === obj && item.isActive,
),
);
const relationsDontExist =
hide.ifRelationsMissing.length > 0 &&
!hide.ifRelationsMissing.every((rel) =>
objectMetadataItem.fields.some(
(field) =>
field.type === FieldMetadataType.Relation &&
field.name === rel &&
field.isActive,
),
);
return {
id: key,
title,
Icon,
cards,
hide: !(isMobile || isInRightDrawer),
hide:
loading ||
baseHide ||
featureNotEnabled ||
requiredObjectsInactive ||
relationsDontExist,
};
}
const baseHide =
(hide.ifMobile && isMobile) ||
(hide.ifDesktop && !isMobile) ||
(hide.ifInRightDrawer && isInRightDrawer);
const featureNotEnabled =
hide.ifFeaturesDisabled.length > 0 &&
!hide.ifFeaturesDisabled.every((flagKey) => {
return !!currentWorkspace?.featureFlags?.find(
(flag: FeatureFlag) => flag.key === flagKey && flag.value,
);
});
const requiredObjectsInactive =
hide.ifRequiredObjectsInactive.length > 0 &&
!hide.ifRequiredObjectsInactive.every((obj) =>
objectMetadataItems.some(
(item) => item.nameSingular === obj && item.isActive,
),
);
const relationsDontExist =
hide.ifRelationsMissing.length > 0 &&
!hide.ifRelationsMissing.every((rel) =>
objectMetadataItem.fields.some(
(field) =>
field.type === FieldMetadataType.Relation &&
field.name === rel &&
field.isActive,
),
);
return {
id: key,
title,
Icon,
cards,
hide:
loading ||
baseHide ||
featureNotEnabled ||
requiredObjectsInactive ||
relationsDontExist,
};
});
}),
};
};

View File

@ -1,3 +1,6 @@
import { RecordLayoutTab } from '@/ui/layout/tab/types/RecordLayoutTab';
export type RecordLayout = Record<string, RecordLayoutTab | null>;
export type RecordLayout = {
hideSummaryAndFields?: boolean;
tabs: Record<string, RecordLayoutTab | null>;
};

View File

@ -0,0 +1,3 @@
import { RecordLayout } from '@/object-record/record-show/types/RecordLayout';
export type RecordLayoutMap = Record<string, RecordLayout | null>;