mirror of
https://github.com/lingble/twenty.git
synced 2025-10-29 20:02:29 +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