mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +00:00 
			
		
		
		
	Hide tabs (#7841)
@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:
		| @@ -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} /> | ||||
|   ), | ||||
| }; | ||||
| @@ -39,6 +39,7 @@ export const RecordShowContainer = ({ | ||||
|     loading, | ||||
|     objectNameSingular as CoreObjectNameSingular, | ||||
|     isInRightDrawer, | ||||
|     objectMetadataItem, | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|   | ||||
| @@ -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'], | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| @@ -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, | ||||
|       }; | ||||
|     }); | ||||
| }; | ||||
|   | ||||
| @@ -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', | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| import { RecordLayoutTab } from '@/ui/layout/tab/types/RecordLayoutTab'; | ||||
|  | ||||
| export type RecordLayout = Record<string, RecordLayoutTab | null>; | ||||
| @@ -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>( | ||||
|   | ||||
| @@ -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 = { | ||||
|   | ||||
| @@ -0,0 +1,5 @@ | ||||
| import { CardType } from '@/object-record/record-show/types/CardType'; | ||||
|  | ||||
| export type LayoutCard = { | ||||
|   type: CardType; | ||||
| }; | ||||
| @@ -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[]; | ||||
| }; | ||||
| @@ -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[]; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user
	 nitin
					nitin