Activity as standard object (#6219)

In this PR I layout the first steps to migrate Activity to a traditional
Standard objects

Since this is a big transition, I'd rather split it into several
deployments / PRs

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/012e2bbf-9d1b-4723-aaf6-269ef588b050">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: bosiraphael <71827178+bosiraphael@users.noreply.github.com>
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Faisal-imtiyaz123 <142205282+Faisal-imtiyaz123@users.noreply.github.com>
Co-authored-by: Prateek Jain <prateekj1171998@gmail.com>
This commit is contained in:
Félix Malfait
2024-07-31 15:36:11 +02:00
committed by GitHub
parent defcee2a02
commit 80c0fc7ff1
239 changed files with 18418 additions and 8671 deletions

View File

@ -0,0 +1,77 @@
import { PartialBlock } from '@blocknote/core';
import { getFirstNonEmptyLineOfRichText } from '../getFirstNonEmptyLineOfRichText';
describe('getFirstNonEmptyLineOfRichText', () => {
it('should return an empty string if the input is null', () => {
const result = getFirstNonEmptyLineOfRichText(null);
expect(result).toBe('');
});
it('should return an empty string if the input is an empty array', () => {
const result = getFirstNonEmptyLineOfRichText([]);
expect(result).toBe('');
});
it('should return the first non-empty line of text', () => {
const input: PartialBlock[] = [
{ content: [{ text: '', type: 'text', styles: {} }] },
{ content: [{ text: ' ', type: 'text', styles: {} }] },
{ content: [{ text: 'First non-empty line', type: 'text', styles: {} }] },
{ content: [{ text: 'Second line', type: 'text', styles: {} }] },
];
const result = getFirstNonEmptyLineOfRichText(input);
expect(result).toBe('First non-empty line');
});
it('should return an empty string if all lines are empty', () => {
const input: PartialBlock[] = [
{ content: [{ text: '', type: 'text', styles: {} }] },
{ content: [{ text: ' ', type: 'text', styles: {} }] },
{ content: [{ text: '\n', type: 'text', styles: {} }] },
];
const result = getFirstNonEmptyLineOfRichText(input);
expect(result).toBe('');
});
it('should handle mixed content correctly', () => {
const input: PartialBlock[] = [
{ content: [{ text: '', type: 'text', styles: {} }] },
{ content: [{ text: ' ', type: 'text', styles: {} }] },
{ content: [{ text: 'First non-empty line', type: 'text', styles: {} }] },
{ content: [{ text: '', type: 'text', styles: {} }] },
{
content: [{ text: 'Second non-empty line', type: 'text', styles: {} }],
},
];
const result = getFirstNonEmptyLineOfRichText(input);
expect(result).toBe('First non-empty line');
});
it('should handle content with multiple text objects correctly', () => {
const input: PartialBlock[] = [
{
content: [
{ text: '', type: 'text', styles: {} },
{ text: ' ', type: 'text', styles: {} },
],
},
{
content: [
{ text: 'First non-empty line', type: 'text', styles: {} },
{ text: 'Second line', type: 'text', styles: {} },
],
},
];
const result = getFirstNonEmptyLineOfRichText(input);
expect(result).toBe('First non-empty line');
});
it('should handle content with undefined or null content', () => {
const input: PartialBlock[] = [
{ content: undefined },
{ content: [{ text: 'First non-empty line', type: 'text', styles: {} }] },
];
const result = getFirstNonEmptyLineOfRichText(input);
expect(result).toBe('First non-empty line');
});
});

View File

@ -0,0 +1,23 @@
import { PartialBlock } from '@blocknote/core';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const getFirstNonEmptyLineOfRichText = (
fieldValue: PartialBlock[] | null,
): string => {
if (fieldValue === null) {
return '';
}
for (const node of fieldValue) {
if (!isUndefinedOrNull(node.content)) {
const contentArray = node.content as Array<{ text: string }>;
if (contentArray.length > 0) {
for (const content of contentArray) {
if (content.text.trim() !== '') {
return content.text;
}
}
}
}
}
return '';
};

View File

@ -2,7 +2,12 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useRef } from 'react';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import {
useRecoilCallback,
useRecoilState,
useRecoilValue,
useSetRecoilState,
} from 'recoil';
import { Key } from 'ts-key-enum';
import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
@ -16,7 +21,6 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { isDefined } from '~/utils/isDefined';
import { useRightDrawer } from '../hooks/useRightDrawer';
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerHotkeyScope } from '../types/RightDrawerHotkeyScope';
@ -49,8 +53,7 @@ export const RightDrawer = () => {
const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState);
const isRightDrawerExpanded = useRecoilValue(isRightDrawerExpandedState);
const [, setIsRightDrawerAnimationCompleted] = useRecoilState(
const setIsRightDrawerAnimationCompleted = useSetRecoilState(
isRightDrawerAnimationCompletedState,
);
@ -101,7 +104,7 @@ export const RightDrawer = () => {
const isMobile = useIsMobile();
const rightDrawerWidth = isRightDrawerOpen
? isMobile || isRightDrawerExpanded
? isMobile
? '100%'
: theme.rightDrawerWidth
: '0';

View File

@ -4,8 +4,6 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { RightDrawerCalendarEvent } from '@/activities/calendar/right-drawer/components/RightDrawerCalendarEvent';
import { RightDrawerAIChat } from '@/activities/copilot/right-drawer/components/RightDrawerAIChat';
import { RightDrawerEmailThread } from '@/activities/emails/right-drawer/components/RightDrawerEmailThread';
import { RightDrawerCreateActivity } from '@/activities/right-drawer/components/create/RightDrawerCreateActivity';
import { RightDrawerEditActivity } from '@/activities/right-drawer/components/edit/RightDrawerEditActivity';
import { RightDrawerRecord } from '@/object-record/record-right-drawer/components/RightDrawerRecord';
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
@ -31,14 +29,6 @@ const StyledRightDrawerBody = styled.div`
`;
const RIGHT_DRAWER_PAGES_CONFIG = {
[RightDrawerPages.CreateActivity]: {
page: <RightDrawerCreateActivity />,
topBar: <RightDrawerTopBar page={RightDrawerPages.CreateActivity} />,
},
[RightDrawerPages.EditActivity]: {
page: <RightDrawerEditActivity />,
topBar: <RightDrawerTopBar page={RightDrawerPages.EditActivity} />,
},
[RightDrawerPages.ViewEmailThread]: {
page: <RightDrawerEmailThread />,
topBar: <RightDrawerTopBar page={RightDrawerPages.ViewEmailThread} />,

View File

@ -3,8 +3,9 @@ import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue } from 'recoil';
import { Chip, ChipAccent, ChipSize, useIcons } from 'twenty-ui';
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
import { RightDrawerTopBarCloseButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
import { RightDrawerTopBarExpandButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
@ -62,6 +63,8 @@ export const RightDrawerTopBar = ({ page }: { page: RightDrawerPages }) => {
viewableRecordNameSingularState,
);
const viewableRecordId = useRecoilValue(viewableRecordIdState);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: viewableRecordNameSingular ?? 'company',
});
@ -80,20 +83,15 @@ export const RightDrawerTopBar = ({ page }: { page: RightDrawerPages }) => {
onClick={handleOnclick}
isRightDrawerMinimized={isRightDrawerMinimized}
>
{!isRightDrawerMinimized &&
(page === RightDrawerPages.EditActivity ||
page === RightDrawerPages.CreateActivity) && <ActivityActionBar />}
{!isRightDrawerMinimized &&
page !== RightDrawerPages.EditActivity &&
page !== RightDrawerPages.CreateActivity && (
<Chip
label={label}
leftComponent={<Icon size={theme.icon.size.md} />}
size={ChipSize.Large}
accent={ChipAccent.TextSecondary}
clickable={false}
/>
)}
{!isRightDrawerMinimized && (
<Chip
label={label}
leftComponent={<Icon size={theme.icon.size.md} />}
size={ChipSize.Large}
accent={ChipAccent.TextSecondary}
clickable={false}
/>
)}
{isRightDrawerMinimized && (
<StyledMinimizeTopBarTitleContainer>
<StyledMinimizeTopBarIcon>
@ -106,8 +104,15 @@ export const RightDrawerTopBar = ({ page }: { page: RightDrawerPages }) => {
{!isMobile && !isRightDrawerMinimized && (
<RightDrawerTopBarMinimizeButton />
)}
{!isMobile && !isRightDrawerMinimized && (
<RightDrawerTopBarExpandButton />
<RightDrawerTopBarExpandButton
to={
getBasePathToShowPage({
objectNameSingular: viewableRecordNameSingular ?? '',
}) + viewableRecordId
}
/>
)}
<RightDrawerTopBarCloseButton />
</StyledTopBarWrapper>

View File

@ -1,33 +1,19 @@
import {
IconLayoutSidebarRightCollapse,
IconLayoutSidebarRightExpand,
} from 'twenty-ui';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
import { IconExternalLink } from 'twenty-ui';
export const RightDrawerTopBarExpandButton = () => {
const { isRightDrawerExpanded, downsizeRightDrawer, expandRightDrawer } =
useRightDrawer();
const handleButtonClick = () => {
if (isRightDrawerExpanded === true) {
downsizeRightDrawer();
return;
}
expandRightDrawer();
};
export const RightDrawerTopBarExpandButton = ({ to }: { to: string }) => {
const { closeRightDrawer } = useRightDrawer();
return (
<LightIconButton
size="medium"
accent="tertiary"
Icon={
isRightDrawerExpanded
? IconLayoutSidebarRightCollapse
: IconLayoutSidebarRightExpand
}
onClick={handleButtonClick}
/>
<UndecoratedLink to={to}>
<LightIconButton
size="medium"
accent="tertiary"
Icon={IconExternalLink}
onClick={closeRightDrawer}
/>
</UndecoratedLink>
);
};

View File

@ -1,8 +1,6 @@
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
export const RIGHT_DRAWER_PAGE_ICONS = {
[RightDrawerPages.CreateActivity]: 'IconNote',
[RightDrawerPages.EditActivity]: 'IconNote',
[RightDrawerPages.ViewEmailThread]: 'IconMail',
[RightDrawerPages.ViewCalendarEvent]: 'IconCalendarEvent',
[RightDrawerPages.ViewRecord]: 'Icon123',

View File

@ -1,8 +1,6 @@
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
export const RIGHT_DRAWER_PAGE_TITLES = {
[RightDrawerPages.CreateActivity]: 'Create Activity',
[RightDrawerPages.EditActivity]: 'Edit Activity',
[RightDrawerPages.ViewEmailThread]: 'Email Thread',
[RightDrawerPages.ViewCalendarEvent]: 'Calendar Event',
[RightDrawerPages.ViewRecord]: 'Record Editor',

View File

@ -1,8 +1,7 @@
import { act } from 'react-dom/test-utils';
import { renderHook } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { isRightDrawerExpandedState } from '../../states/isRightDrawerExpandedState';
import { isRightDrawerOpenState } from '../../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../../states/rightDrawerPageState';
import { RightDrawerPages } from '../../types/RightDrawerPages';
@ -13,7 +12,6 @@ describe('useRightDrawer', () => {
const useCombinedHooks = () => {
const { openRightDrawer, closeRightDrawer } = useRightDrawer();
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
const isRightDrawerExpanded = useRecoilValue(isRightDrawerExpandedState);
const rightDrawerPage = useRecoilValue(rightDrawerPageState);
@ -21,7 +19,6 @@ describe('useRightDrawer', () => {
openRightDrawer,
closeRightDrawer,
isRightDrawerOpen,
isRightDrawerExpanded,
rightDrawerPage,
};
};
@ -31,26 +28,21 @@ describe('useRightDrawer', () => {
});
expect(result.current.rightDrawerPage).toBeNull();
expect(result.current.isRightDrawerExpanded).toBeFalsy();
expect(result.current.isRightDrawerOpen).toBeFalsy();
expect(result.current.openRightDrawer).toBeInstanceOf(Function);
expect(result.current.closeRightDrawer).toBeInstanceOf(Function);
await act(async () => {
result.current.openRightDrawer(RightDrawerPages.CreateActivity);
result.current.openRightDrawer(RightDrawerPages.ViewRecord);
});
expect(result.current.rightDrawerPage).toEqual(
RightDrawerPages.CreateActivity,
);
expect(result.current.isRightDrawerExpanded).toBeFalsy();
expect(result.current.rightDrawerPage).toEqual(RightDrawerPages.ViewRecord);
expect(result.current.isRightDrawerOpen).toBeTruthy();
await act(async () => {
result.current.closeRightDrawer();
});
expect(result.current.isRightDrawerExpanded).toBeFalsy();
expect(result.current.isRightDrawerOpen).toBeFalsy();
});
});

View File

@ -1,25 +1,22 @@
import { useRecoilCallback, useRecoilState } from 'recoil';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
import { rightDrawerCloseEventState } from '@/ui/layout/right-drawer/states/rightDrawerCloseEventsState';
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerPages } from '../types/RightDrawerPages';
export const useRightDrawer = () => {
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const [isRightDrawerExpanded] = useRecoilState(isRightDrawerExpandedState);
const [isRightDrawerMinimized] = useRecoilState(isRightDrawerMinimizedState);
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState);
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
const rightDrawerPage = useRecoilValue(rightDrawerPageState);
const openRightDrawer = useRecoilCallback(
({ set }) =>
(rightDrawerPage: RightDrawerPages) => {
set(rightDrawerPageState, rightDrawerPage);
set(isRightDrawerExpandedState, false);
set(isRightDrawerOpenState, true);
set(isRightDrawerMinimizedState, false);
},
@ -29,7 +26,6 @@ export const useRightDrawer = () => {
const closeRightDrawer = useRecoilCallback(
({ set }) =>
() => {
set(isRightDrawerExpandedState, false);
set(isRightDrawerOpenState, false);
set(isRightDrawerMinimizedState, false);
},
@ -39,7 +35,6 @@ export const useRightDrawer = () => {
const minimizeRightDrawer = useRecoilCallback(
({ set }) =>
() => {
set(isRightDrawerExpandedState, false);
set(isRightDrawerOpenState, true);
set(isRightDrawerMinimizedState, true);
},
@ -50,32 +45,11 @@ export const useRightDrawer = () => {
({ set }) =>
() => {
set(isRightDrawerMinimizedState, false);
set(isRightDrawerExpandedState, false);
set(isRightDrawerOpenState, true);
},
[],
);
const expandRightDrawer = useRecoilCallback(
({ set }) =>
() => {
set(isRightDrawerExpandedState, true);
set(isRightDrawerOpenState, true);
set(isRightDrawerMinimizedState, false);
},
[],
);
const downsizeRightDrawer = useRecoilCallback(
({ set }) =>
() => {
set(isRightDrawerExpandedState, false);
set(isRightDrawerOpenState, true);
set(isRightDrawerMinimizedState, false);
},
[],
);
const isSameEventThanRightDrawerClose = useRecoilCallback(
({ snapshot }) =>
(event: MouseEvent | TouchEvent) => {
@ -95,14 +69,11 @@ export const useRightDrawer = () => {
return {
rightDrawerPage,
isRightDrawerOpen,
isRightDrawerExpanded,
isRightDrawerMinimized,
openRightDrawer,
closeRightDrawer,
minimizeRightDrawer,
maximizeRightDrawer,
expandRightDrawer,
downsizeRightDrawer,
isSameEventThanRightDrawerClose,
};
};

View File

@ -1,6 +0,0 @@
import { createState } from 'twenty-ui';
export const isRightDrawerExpandedState = createState<boolean>({
key: 'isRightDrawerExpandedState',
defaultValue: false,
});

View File

@ -1,6 +1,4 @@
export enum RightDrawerPages {
CreateActivity = 'create-activity',
EditActivity = 'edit-activity',
ViewEmailThread = 'view-email-thread',
ViewCalendarEvent = 'view-calendar-event',
ViewRecord = 'view-record',

View File

@ -0,0 +1,31 @@
import { RichTextEditor } from '@/activities/components/RichTextEditor';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import styled from '@emotion/styled';
const StyledShowPageActivityContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
export const ShowPageActivityContainer = ({
targetableObject,
}: {
targetableObject: Pick<
ActivityTargetableObject,
'targetObjectNameSingular' | 'id'
>;
}) => {
return (
<StyledShowPageActivityContainer>
<RichTextEditor
activityId={targetableObject.id}
fillTitleFromBody={false}
activityObjectNameSingular={
targetableObject.targetObjectNameSingular as
| CoreObjectNameSingular.Note
| CoreObjectNameSingular.Task
}
/>
</StyledShowPageActivityContainer>
);
};

View File

@ -2,7 +2,6 @@ import styled from '@emotion/styled';
import { IconCheckbox, IconNotes, IconPlus } from 'twenty-ui';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityType } from '@/activities/types/Activity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { IconButton } from '@/ui/input/button/components/IconButton';
@ -11,6 +10,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SHOW_PAGE_ADD_BUTTON_DROPDOWN_ID } from '@/ui/layout/show-page/constants/ShowPageAddButtonDropdownId';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { Dropdown } from '../../dropdown/components/Dropdown';
import { DropdownMenu } from '../../dropdown/components/DropdownMenu';
@ -24,17 +24,37 @@ export const ShowPageAddButton = ({
activityTargetObject: ActivityTargetableObject;
}) => {
const { closeDropdown, toggleDropdown } = useDropdown('add-show-page');
const openCreateActivity = useOpenCreateActivityDrawer();
const openNote = useOpenCreateActivityDrawer({
activityObjectNameSingular: CoreObjectNameSingular.Note,
});
const openTask = useOpenCreateActivityDrawer({
activityObjectNameSingular: CoreObjectNameSingular.Task,
});
const handleSelect = (type: ActivityType) => {
openCreateActivity({
type,
targetableObjects: [activityTargetObject],
});
const handleSelect = (objectNameSingular: CoreObjectNameSingular) => {
if (objectNameSingular === CoreObjectNameSingular.Note) {
openNote({
targetableObjects: [activityTargetObject],
});
}
if (objectNameSingular === CoreObjectNameSingular.Task) {
openTask({
targetableObjects: [activityTargetObject],
});
}
closeDropdown();
};
if (
activityTargetObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task ||
activityTargetObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note
) {
return;
}
return (
<StyledContainer>
<Dropdown
@ -53,13 +73,13 @@ export const ShowPageAddButton = ({
<DropdownMenu>
<DropdownMenuItemsContainer>
<MenuItem
onClick={() => handleSelect('Note')}
onClick={() => handleSelect(CoreObjectNameSingular.Note)}
accent="default"
LeftIcon={IconNotes}
text="Note"
/>
<MenuItem
onClick={() => handleSelect('Task')}
onClick={() => handleSelect(CoreObjectNameSingular.Task)}
accent="default"
LeftIcon={IconCheckbox}
text="Task"

View File

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
import {
IconCalendarEvent,
IconCheckbox,
IconHome,
IconList,
IconMail,
IconNotes,
IconPaperclip,
@ -16,9 +16,9 @@ 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/timelineActivities/components/TimelineActivities';
import { TimelineActivitiesQueryEffect } from '@/activities/timelineActivities/components/TimelineActivitiesQueryEffect';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
@ -51,8 +51,9 @@ type ShowPageRightContainerProps = {
tasks?: boolean;
notes?: boolean;
emails?: boolean;
summary?: JSX.Element;
isRightDrawer?: boolean;
fieldsBox?: JSX.Element;
summaryCard?: JSX.Element;
isInRightDrawer?: boolean;
loading: boolean;
};
@ -63,10 +64,13 @@ export const ShowPageRightContainer = ({
notes,
emails,
loading,
summary,
isRightDrawer = false,
fieldsBox,
summaryCard,
isInRightDrawer = false,
}: ShowPageRightContainerProps) => {
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
const { activeTabIdState } = useTabList(
`${TAB_LIST_COMPONENT_ID}-${isInRightDrawer}`,
);
const activeTabId = useRecoilValue(activeTabIdState);
const targetObjectNameSingular =
@ -80,24 +84,60 @@ export const ShowPageRightContainer = ({
const shouldDisplayCalendarTab = isCompanyOrPerson;
const shouldDisplayEmailsTab = emails && isCompanyOrPerson;
const isMobile = useIsMobile() || isRightDrawer;
const isMobile = useIsMobile() || isInRightDrawer;
const tabs = [
{
id: 'summary',
title: 'Summary',
Icon: IconHome,
id: 'richText',
title: 'Note',
Icon: IconNotes,
hide:
loading ||
(targetableObject.targetObjectNameSingular !==
CoreObjectNameSingular.Note &&
targetableObject.targetObjectNameSingular !==
CoreObjectNameSingular.Task),
},
{
id: 'fields',
title: 'Fields',
Icon: IconList,
hide: !isMobile,
},
{
id: 'timeline',
title: 'Timeline',
Icon: IconTimelineEvent,
hide: !timeline || isRightDrawer,
hide: !timeline || isInRightDrawer,
},
{
id: 'tasks',
title: 'Tasks',
Icon: IconCheckbox,
hide:
!tasks ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task,
},
{
id: 'notes',
title: 'Notes',
Icon: IconNotes,
hide:
!notes ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task,
},
{
id: 'files',
title: 'Files',
Icon: IconPaperclip,
hide: !notes,
},
{ id: 'tasks', title: 'Tasks', Icon: IconCheckbox, hide: !tasks },
{ id: 'notes', title: 'Notes', Icon: IconNotes, hide: !notes },
{ id: 'files', title: 'Files', Icon: IconPaperclip, hide: !notes },
{
id: 'emails',
title: 'Emails',
@ -117,14 +157,23 @@ export const ShowPageRightContainer = ({
case 'timeline':
return (
<>
<TimelineActivitiesQueryEffect
<TimelineActivities
targetableObject={targetableObject}
isInRightDrawer={isInRightDrawer}
/>
<TimelineActivities targetableObject={targetableObject} />
</>
);
case 'summary':
return summary;
case 'richText':
return (
(targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Note ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Task) && (
<ShowPageActivityContainer targetableObject={targetableObject} />
)
);
case 'fields':
return fieldsBox;
case 'tasks':
return <ObjectTasks targetableObject={targetableObject} />;
case 'notes':
@ -142,10 +191,11 @@ export const ShowPageRightContainer = ({
return (
<StyledShowPageRightContainer isMobile={isMobile}>
{summaryCard}
<StyledTabListContainer>
<TabList
loading={loading}
tabListId={TAB_LIST_COMPONENT_ID}
tabListId={`${TAB_LIST_COMPONENT_ID}-${isInRightDrawer}`}
tabs={tabs}
/>
</StyledTabListContainer>

View File

@ -1,8 +1,8 @@
import { Link, useNavigate } from 'react-router-dom';
import isPropValid from '@emotion/is-prop-valid';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import { Link, useNavigate } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { IconComponent, MOBILE_VIEWPORT, Pill } from 'twenty-ui';

View File

@ -0,0 +1,43 @@
import {
NavigationDrawerItem,
NavigationDrawerItemProps,
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import styled from '@emotion/styled';
const StyledItem = styled.div`
margin-left: ${({ theme }) => theme.spacing(4)};
`;
type NavigationDrawerSubItemProps = NavigationDrawerItemProps;
export const NavigationDrawerSubItem = ({
className,
label,
level = 1,
Icon,
to,
onClick,
active,
danger,
soon,
count,
keyboard,
}: NavigationDrawerSubItemProps) => {
return (
<StyledItem>
<NavigationDrawerItem
className={className}
label={label}
level={level}
Icon={Icon}
to={to}
onClick={onClick}
active={active}
danger={danger}
soon={soon}
count={count}
keyboard={keyboard}
/>
</StyledItem>
);
};