@FelixMalfait WDYT?
We can refactor shouldDisplay Files/Tasks/Notes Tab etc into a hook.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
nitin
2024-11-11 13:56:27 +05:30
committed by GitHub
parent 6d62dd9fd1
commit c19e54f24b
11 changed files with 495 additions and 198 deletions

View File

@@ -0,0 +1,106 @@
import { Calendar } from '@/activities/calendar/components/Calendar';
import { EmailThreads } from '@/activities/emails/components/EmailThreads';
import { Attachments } from '@/activities/files/components/Attachments';
import { Notes } from '@/activities/notes/components/Notes';
import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
import { TimelineActivities } from '@/activities/timeline-activities/components/TimelineActivities';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { FieldsCard } from '@/object-record/record-show/components/FieldsCard';
import { CardType } from '@/object-record/record-show/types/CardType';
import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer';
import { WorkflowRunOutputVisualizer } from '@/workflow/components/WorkflowRunOutputVisualizer';
import { WorkflowRunVersionVisualizer } from '@/workflow/components/WorkflowRunVersionVisualizer';
import { WorkflowVersionVisualizer } from '@/workflow/components/WorkflowVersionVisualizer';
import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowVersionVisualizerEffect';
import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer';
import { WorkflowVisualizerEffect } from '@/workflow/components/WorkflowVisualizerEffect';
import styled from '@emotion/styled';
const StyledGreyBox = styled.div<{ isInRightDrawer?: boolean }>`
background: ${({ theme, isInRightDrawer }) =>
isInRightDrawer ? theme.background.secondary : ''};
border: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? `1px solid ${theme.border.color.medium}` : ''};
border-radius: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? theme.border.radius.md : ''};
height: ${({ isInRightDrawer }) => (isInRightDrawer ? 'auto' : '100%')};
margin: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? theme.spacing(4) : ''};
`;
type CardComponentProps = {
targetableObject: Pick<
ActivityTargetableObject,
'targetObjectNameSingular' | 'id'
>;
isInRightDrawer?: boolean;
};
type CardComponentType = (props: CardComponentProps) => JSX.Element | null;
export const CardComponents: Record<CardType, CardComponentType> = {
[CardType.TimelineCard]: ({ targetableObject, isInRightDrawer }) => (
<TimelineActivities
targetableObject={targetableObject}
isInRightDrawer={isInRightDrawer}
/>
),
[CardType.FieldCard]: ({ targetableObject, isInRightDrawer }) => (
<StyledGreyBox isInRightDrawer={isInRightDrawer}>
<FieldsCard
objectNameSingular={targetableObject.targetObjectNameSingular}
objectRecordId={targetableObject.id}
/>
</StyledGreyBox>
),
[CardType.RichTextCard]: ({ targetableObject }) => (
<ShowPageActivityContainer targetableObject={targetableObject} />
),
[CardType.TaskCard]: ({ targetableObject }) => (
<ObjectTasks targetableObject={targetableObject} />
),
[CardType.NoteCard]: ({ targetableObject }) => (
<Notes targetableObject={targetableObject} />
),
[CardType.FileCard]: ({ targetableObject }) => (
<Attachments targetableObject={targetableObject} />
),
[CardType.EmailCard]: ({ targetableObject }) => (
<EmailThreads targetableObject={targetableObject} />
),
[CardType.CalendarCard]: ({ targetableObject }) => (
<Calendar targetableObject={targetableObject} />
),
[CardType.WorkflowCard]: ({ targetableObject }) => (
<>
<WorkflowVisualizerEffect workflowId={targetableObject.id} />
<WorkflowVisualizer targetableObject={targetableObject} />
</>
),
[CardType.WorkflowVersionCard]: ({ targetableObject }) => (
<>
<WorkflowVersionVisualizerEffect
workflowVersionId={targetableObject.id}
/>
<WorkflowVersionVisualizer workflowVersionId={targetableObject.id} />
</>
),
[CardType.WorkflowRunCard]: ({ targetableObject }) => (
<WorkflowRunVersionVisualizer workflowRunId={targetableObject.id} />
),
[CardType.WorkflowRunOutputCard]: ({ targetableObject }) => (
<WorkflowRunOutputVisualizer workflowRunId={targetableObject.id} />
),
};

View File

@@ -39,6 +39,7 @@ export const RecordShowContainer = ({
loading,
objectNameSingular as CoreObjectNameSingular,
isInRightDrawer,
objectMetadataItem,
);
return (

View File

@@ -0,0 +1,83 @@
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 {
IconCheckbox,
IconList,
IconNotes,
IconPaperclip,
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: [],
},
},
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'],
},
},
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'],
},
},
};

View File

@@ -1,125 +1,265 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { BASE_RECORD_LAYOUT } from '@/object-record/record-show/constants/BaseRecordLayout';
import { CardType } from '@/object-record/record-show/types/CardType';
import { RecordLayout } from '@/object-record/record-show/types/RecordLayout';
import { SingleTabProps } from '@/ui/layout/tab/components/TabList';
import { RecordLayoutTab } from '@/ui/layout/tab/types/RecordLayoutTab';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import {
IconCalendarEvent,
IconCheckbox,
IconList,
IconMail,
IconNotes,
IconPaperclip,
IconPrinter,
IconSettings,
IconTimelineEvent,
} from 'twenty-ui';
import { FeatureFlag, FieldMetadataType } from '~/generated-metadata/graphql';
export const useRecordShowContainerTabs = (
loading: boolean,
targetObjectNameSingular: CoreObjectNameSingular,
isInRightDrawer: boolean,
) => {
objectMetadataItem: ObjectMetadataItem,
): SingleTabProps[] => {
const isMobile = useIsMobile();
const isWorkflowEnabled = useIsFeatureEnabled('IS_WORKFLOW_ENABLED');
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const isWorkflow =
isWorkflowEnabled &&
targetObjectNameSingular === CoreObjectNameSingular.Workflow;
const isWorkflowVersion =
isWorkflowEnabled &&
targetObjectNameSingular === CoreObjectNameSingular.WorkflowVersion;
const isWorkflowRun =
isWorkflowEnabled &&
targetObjectNameSingular === CoreObjectNameSingular.WorkflowRun;
const isWorkflowRelated = isWorkflow || isWorkflowVersion || isWorkflowRun;
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const isCompanyOrPerson = [
CoreObjectNameSingular.Company,
CoreObjectNameSingular.Person,
].includes(targetObjectNameSingular);
const shouldDisplayCalendarTab = isCompanyOrPerson;
const shouldDisplayEmailsTab = isCompanyOrPerson;
// Object-specific layouts that override or extend the base layout
const OBJECT_SPECIFIC_LAYOUTS: Partial<
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: [],
},
},
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: [],
},
},
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: [],
},
},
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: [],
},
},
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: 'Workflow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
[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: [],
},
},
},
[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: [],
},
},
workflowRunFlow: {
title: 'Flow',
position: 0,
Icon: IconSettings,
cards: [{ type: CardType.WorkflowRunCard }],
hide: {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: ['IS_WORKFLOW_ENABLED'],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
},
},
},
};
return [
{
id: 'richText',
title: 'Note',
Icon: IconNotes,
hide:
loading ||
(targetObjectNameSingular !== CoreObjectNameSingular.Note &&
targetObjectNameSingular !== CoreObjectNameSingular.Task),
},
{
id: 'fields',
title: 'Fields',
Icon: IconList,
hide: !(isMobile || isInRightDrawer),
},
{
id: 'timeline',
title: 'Timeline',
Icon: IconTimelineEvent,
hide: isInRightDrawer || isWorkflowRelated,
},
{
id: 'tasks',
title: 'Tasks',
Icon: IconCheckbox,
hide:
targetObjectNameSingular === CoreObjectNameSingular.Note ||
targetObjectNameSingular === CoreObjectNameSingular.Task ||
isWorkflowRelated,
},
{
id: 'notes',
title: 'Notes',
Icon: IconNotes,
hide:
targetObjectNameSingular === CoreObjectNameSingular.Note ||
targetObjectNameSingular === CoreObjectNameSingular.Task ||
isWorkflowRelated,
},
{
id: 'files',
title: 'Files',
Icon: IconPaperclip,
hide: isWorkflowRelated,
},
{
id: 'emails',
title: 'Emails',
Icon: IconMail,
hide: !shouldDisplayEmailsTab,
},
{
id: 'calendar',
title: 'Calendar',
Icon: IconCalendarEvent,
hide: !shouldDisplayCalendarTab,
},
{
id: 'workflow',
title: 'Workflow',
Icon: IconSettings,
hide: !isWorkflow,
},
{
id: 'workflowVersion',
title: 'Flow',
Icon: IconSettings,
hide: !isWorkflowVersion,
},
{
id: 'workflowRunOutput',
title: 'Output',
Icon: IconPrinter,
hide: !isWorkflowRun,
},
{
id: 'workflowRunFlow',
title: 'Flow',
Icon: IconSettings,
hide: !isWorkflowRun,
},
];
// Merge base layout with object-specific layout
const tabDefinitions: RecordLayout = {
...BASE_RECORD_LAYOUT,
...(OBJECT_SPECIFIC_LAYOUTS[targetObjectNameSingular] || {}),
};
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 {
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:
loading ||
baseHide ||
featureNotEnabled ||
requiredObjectsInactive ||
relationsDontExist,
};
});
};

View File

@@ -0,0 +1,14 @@
export enum CardType {
FieldCard = 'FieldCard',
TimelineCard = 'TimelineCard',
TaskCard = 'TaskCard',
NoteCard = 'NoteCard',
FileCard = 'FileCard',
EmailCard = 'EmailCard',
CalendarCard = 'CalendarCard',
WorkflowCard = 'WorkflowCard',
WorkflowVersionCard = 'WorkflowVersionCard',
WorkflowRunCard = 'WorkflowRunCard',
WorkflowRunOutputCard = 'WorkflowRunOutputCard',
RichTextCard = 'RichTextCard',
}

View File

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

View File

@@ -1,28 +1,15 @@
import { RecordShowRightDrawerActionMenu } from '@/action-menu/components/RecordShowRightDrawerActionMenu';
import { Calendar } from '@/activities/calendar/components/Calendar';
import { EmailThreads } from '@/activities/emails/components/EmailThreads';
import { Attachments } from '@/activities/files/components/Attachments';
import { Notes } from '@/activities/notes/components/Notes';
import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
import { TimelineActivities } from '@/activities/timeline-activities/components/TimelineActivities';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading';
import { CardComponents } from '@/object-record/record-show/components/CardComponents';
import { FieldsCard } from '@/object-record/record-show/components/FieldsCard';
import { SummaryCard } from '@/object-record/record-show/components/SummaryCard';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer';
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { WorkflowRunOutputVisualizer } from '@/workflow/components/WorkflowRunOutputVisualizer';
import { WorkflowRunVersionVisualizer } from '@/workflow/components/WorkflowRunVersionVisualizer';
import { WorkflowVersionVisualizer } from '@/workflow/components/WorkflowVersionVisualizer';
import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowVersionVisualizerEffect';
import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer';
import { WorkflowVisualizerEffect } from '@/workflow/components/WorkflowVisualizerEffect';
import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue } from 'recoil';
@@ -46,19 +33,6 @@ const StyledTabListContainer = styled.div`
height: 40px;
`;
const StyledGreyBox = styled.div<{ isInRightDrawer: boolean }>`
background: ${({ theme, isInRightDrawer }) =>
isInRightDrawer ? theme.background.secondary : ''};
border: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? `1px solid ${theme.border.color.medium}` : ''};
border-radius: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? theme.border.radius.md : ''};
height: ${({ isInRightDrawer }) => (isInRightDrawer ? 'auto' : '100%')};
margin: ${({ isInRightDrawer, theme }) =>
isInRightDrawer ? theme.spacing(4) : ''};
`;
const StyledButtonContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.secondary};
@@ -127,72 +101,19 @@ export const ShowPageSubContainer = ({
);
const renderActiveTabContent = () => {
switch (activeTabId) {
case 'timeline':
return (
<>
<TimelineActivities
targetableObject={targetableObject}
isInRightDrawer={isInRightDrawer}
/>
</>
);
case 'richText':
return (
(targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task) && (
<ShowPageActivityContainer targetableObject={targetableObject} />
)
);
case 'fields':
return (
<StyledGreyBox isInRightDrawer={isInRightDrawer}>
{fieldsCard}
</StyledGreyBox>
);
case 'tasks':
return <ObjectTasks targetableObject={targetableObject} />;
case 'notes':
return <Notes targetableObject={targetableObject} />;
case 'files':
return <Attachments targetableObject={targetableObject} />;
case 'emails':
return <EmailThreads targetableObject={targetableObject} />;
case 'calendar':
return <Calendar targetableObject={targetableObject} />;
case 'workflow':
return (
<>
<WorkflowVisualizerEffect workflowId={targetableObject.id} />
const activeTab = tabs.find((tab) => tab.id === activeTabId);
if (!activeTab?.cards?.length) return null;
<WorkflowVisualizer targetableObject={targetableObject} />
</>
);
case 'workflowVersion':
return (
<>
<WorkflowVersionVisualizerEffect
workflowVersionId={targetableObject.id}
/>
<WorkflowVersionVisualizer
workflowVersionId={targetableObject.id}
/>
</>
);
case 'workflowRunFlow':
return (
<WorkflowRunVersionVisualizer workflowRunId={targetableObject.id} />
);
case 'workflowRunOutput':
return (
<WorkflowRunOutputVisualizer workflowRunId={targetableObject.id} />
);
default:
return <></>;
}
return activeTab.cards.map((card, index) => {
const CardComponent = CardComponents[card.type];
return CardComponent ? (
<CardComponent
key={`${activeTab.id}-card-${index}`}
targetableObject={targetableObject}
isInRightDrawer={isInRightDrawer}
/>
) : null;
});
};
const [recordFromStore] = useRecoilState<ObjectRecord | null>(

View File

@@ -7,6 +7,7 @@ import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { TabListScope } from '@/ui/layout/tab/scopes/TabListScope';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { LayoutCard } from '@/ui/layout/tab/types/LayoutCard';
import { Tab } from './Tab';
export type SingleTabProps = {
@@ -16,6 +17,7 @@ export type SingleTabProps = {
hide?: boolean;
disabled?: boolean;
pill?: string | React.ReactElement;
cards?: LayoutCard[];
};
type TabListProps = {

View File

@@ -0,0 +1,5 @@
import { CardType } from '@/object-record/record-show/types/CardType';
export type LayoutCard = {
type: CardType;
};

View File

@@ -0,0 +1,11 @@
import { LayoutCard } from '@/ui/layout/tab/types/LayoutCard';
import { TabVisibilityConfig } from '@/ui/layout/tab/types/TabVisibilityConfig';
import { IconComponent } from 'twenty-ui';
export type RecordLayoutTab = {
title: string;
position: number;
Icon: IconComponent;
hide: TabVisibilityConfig;
cards: LayoutCard[];
};

View File

@@ -0,0 +1,11 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { FeatureFlagKey } from '@/workspace/types/FeatureFlagKey';
export type TabVisibilityConfig = {
ifMobile: boolean;
ifDesktop: boolean;
ifInRightDrawer: boolean;
ifFeaturesDisabled: FeatureFlagKey[];
ifRequiredObjectsInactive: CoreObjectNameSingular[];
ifRelationsMissing: string[];
};