From c19e54f24bf5dcb156a92ab81c8d2b39d5f3dd1a Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:56:27 +0530 Subject: [PATCH] Hide tabs (#7841) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @FelixMalfait WDYT? We can refactor shouldDisplay Files/Tasks/Notes Tab etc into a hook. --------- Co-authored-by: FĂ©lix Malfait --- .../record-show/components/CardComponents.tsx | 106 ++++++ .../components/RecordShowContainer.tsx | 1 + .../record-show/constants/BaseRecordLayout.ts | 83 +++++ .../hooks/useRecordShowContainerTabs.ts | 340 ++++++++++++------ .../record-show/types/CardType.ts | 14 + .../record-show/types/RecordLayout.ts | 3 + .../components/ShowPageSubContainer.tsx | 107 +----- .../ui/layout/tab/components/TabList.tsx | 2 + .../modules/ui/layout/tab/types/LayoutCard.ts | 5 + .../ui/layout/tab/types/RecordLayoutTab.ts | 11 + .../layout/tab/types/TabVisibilityConfig.ts | 11 + 11 files changed, 490 insertions(+), 193 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-show/constants/BaseRecordLayout.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-show/types/RecordLayout.ts create mode 100644 packages/twenty-front/src/modules/ui/layout/tab/types/LayoutCard.ts create mode 100644 packages/twenty-front/src/modules/ui/layout/tab/types/RecordLayoutTab.ts create mode 100644 packages/twenty-front/src/modules/ui/layout/tab/types/TabVisibilityConfig.ts diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx new file mode 100644 index 000000000000..d1e3e68ba379 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx @@ -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.TimelineCard]: ({ targetableObject, isInRightDrawer }) => ( + + ), + + [CardType.FieldCard]: ({ targetableObject, isInRightDrawer }) => ( + + + + ), + + [CardType.RichTextCard]: ({ targetableObject }) => ( + + ), + + [CardType.TaskCard]: ({ targetableObject }) => ( + + ), + + [CardType.NoteCard]: ({ targetableObject }) => ( + + ), + + [CardType.FileCard]: ({ targetableObject }) => ( + + ), + + [CardType.EmailCard]: ({ targetableObject }) => ( + + ), + + [CardType.CalendarCard]: ({ targetableObject }) => ( + + ), + + [CardType.WorkflowCard]: ({ targetableObject }) => ( + <> + + + + ), + + [CardType.WorkflowVersionCard]: ({ targetableObject }) => ( + <> + + + + ), + + [CardType.WorkflowRunCard]: ({ targetableObject }) => ( + + ), + + [CardType.WorkflowRunOutputCard]: ({ targetableObject }) => ( + + ), +}; diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx index 79b194d2a8f2..09e4ff633684 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx @@ -39,6 +39,7 @@ export const RecordShowContainer = ({ loading, objectNameSingular as CoreObjectNameSingular, isInRightDrawer, + objectMetadataItem, ); return ( diff --git a/packages/twenty-front/src/modules/object-record/record-show/constants/BaseRecordLayout.ts b/packages/twenty-front/src/modules/object-record/record-show/constants/BaseRecordLayout.ts new file mode 100644 index 000000000000..a55848a1dd70 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-show/constants/BaseRecordLayout.ts @@ -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 = { + 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'], + }, + }, +}; diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts index a1d6a81d2e2f..0e2cdf95e0de 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts @@ -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; - - 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, + // Object-specific layouts that override or extend the base layout + const OBJECT_SPECIFIC_LAYOUTS: Partial< + Record + > = { + [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, }, - { - id: 'notes', - title: 'Notes', - Icon: IconNotes, - hide: - targetObjectNameSingular === CoreObjectNameSingular.Note || - targetObjectNameSingular === CoreObjectNameSingular.Task || - isWorkflowRelated, + [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, }, - { - id: 'files', - title: 'Files', - Icon: IconPaperclip, - hide: isWorkflowRelated, + [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: [], + }, + }, }, - { - id: 'emails', - title: 'Emails', - Icon: IconMail, - hide: !shouldDisplayEmailsTab, + [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: [], + }, + }, }, - { - id: 'calendar', - title: 'Calendar', - Icon: IconCalendarEvent, - hide: !shouldDisplayCalendarTab, + [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: [], + }, + }, }, - { - id: 'workflow', - title: 'Workflow', - Icon: IconSettings, - hide: !isWorkflow, + [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: [], + }, + }, }, - { - id: 'workflowVersion', - title: 'Flow', - Icon: IconSettings, - hide: !isWorkflowVersion, + [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: [], + }, + }, }, - { - 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] => + 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, + }; + }); }; diff --git a/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts b/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts new file mode 100644 index 000000000000..6a805d0af131 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts @@ -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', +} diff --git a/packages/twenty-front/src/modules/object-record/record-show/types/RecordLayout.ts b/packages/twenty-front/src/modules/object-record/record-show/types/RecordLayout.ts new file mode 100644 index 000000000000..e38825b76960 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-show/types/RecordLayout.ts @@ -0,0 +1,3 @@ +import { RecordLayoutTab } from '@/ui/layout/tab/types/RecordLayoutTab'; + +export type RecordLayout = Record; diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx index 639c92926043..b9ab15777d10 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx @@ -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 ( - <> - - - ); - case 'richText': - return ( - (targetableObject.targetObjectNameSingular === - CoreObjectNameSingular.Note || - targetableObject.targetObjectNameSingular === - CoreObjectNameSingular.Task) && ( - - ) - ); - case 'fields': - return ( - - {fieldsCard} - - ); - case 'tasks': - return ; - case 'notes': - return ; - case 'files': - return ; - case 'emails': - return ; - case 'calendar': - return ; - case 'workflow': - return ( - <> - - - - - ); - case 'workflowVersion': - return ( - <> - - - - - ); - case 'workflowRunFlow': - return ( - - ); - case 'workflowRunOutput': - return ( - - ); - default: - return <>; - } + const activeTab = tabs.find((tab) => tab.id === activeTabId); + if (!activeTab?.cards?.length) return null; + + return activeTab.cards.map((card, index) => { + const CardComponent = CardComponents[card.type]; + return CardComponent ? ( + + ) : null; + }); }; const [recordFromStore] = useRecoilState( diff --git a/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx b/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx index d0936c28fed5..9fcb497c62a4 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx +++ b/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx @@ -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 = { diff --git a/packages/twenty-front/src/modules/ui/layout/tab/types/LayoutCard.ts b/packages/twenty-front/src/modules/ui/layout/tab/types/LayoutCard.ts new file mode 100644 index 000000000000..d37120cefb60 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/tab/types/LayoutCard.ts @@ -0,0 +1,5 @@ +import { CardType } from '@/object-record/record-show/types/CardType'; + +export type LayoutCard = { + type: CardType; +}; diff --git a/packages/twenty-front/src/modules/ui/layout/tab/types/RecordLayoutTab.ts b/packages/twenty-front/src/modules/ui/layout/tab/types/RecordLayoutTab.ts new file mode 100644 index 000000000000..efdf02ae2121 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/tab/types/RecordLayoutTab.ts @@ -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[]; +}; diff --git a/packages/twenty-front/src/modules/ui/layout/tab/types/TabVisibilityConfig.ts b/packages/twenty-front/src/modules/ui/layout/tab/types/TabVisibilityConfig.ts new file mode 100644 index 000000000000..ddca84235551 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/tab/types/TabVisibilityConfig.ts @@ -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[]; +};