Feat/activities custom objects (#3213)

* WIP

* WIP - MultiObjectSearch

* WIP

* WIP

* Finished working version

* Fix

* Fixed and cleaned

* Fix

* Disabled files and emails for custom objects

* Cleaned console.log

* Fixed attachment

* Fixed

* fix lint

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2024-01-05 09:08:33 +01:00
committed by GitHub
parent c15e138d72
commit b112b74022
72 changed files with 1611 additions and 551 deletions

View File

@ -5,6 +5,7 @@ import { isNonEmptyString } from '@sniptt/guards';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { Avatar, AvatarType } from '@/users/components/Avatar';
import { Nullable } from '~/types/Nullable';
import { Chip, ChipVariant } from './Chip';
@ -13,7 +14,7 @@ export type EntityChipProps = {
entityId: string;
name: string;
avatarUrl?: string;
avatarType?: AvatarType;
avatarType?: Nullable<AvatarType>;
variant?: EntityChipVariant;
LeftIcon?: IconComponent;
className?: string;

View File

@ -124,13 +124,13 @@ export const Checkbox = ({
React.useState<boolean>(false);
React.useEffect(() => {
setIsInternalChecked(checked);
setIsInternalChecked(checked ?? false);
}, [checked]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(event);
onCheckedChange?.(event.target.checked);
setIsInternalChecked(event.target.checked);
setIsInternalChecked(event.target.checked ?? false);
};
const checkboxId = 'checkbox' + v4();

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityType } from '@/activities/types/Activity';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { IconCheckbox, IconNotes, IconPlus } from '@/ui/display/icon/index';
import { IconButton } from '@/ui/input/button/components/IconButton';
@ -21,13 +21,13 @@ const StyledContainer = styled.div`
export const ShowPageAddButton = ({
entity,
}: {
entity: ActivityTargetableEntity;
entity: ActivityTargetableObject;
}) => {
const { closeDropdown, toggleDropdown } = useDropdown('add-show-page');
const openCreateActivity = useOpenCreateActivityDrawer();
const handleSelect = (type: ActivityType) => {
openCreateActivity({ type, targetableEntities: [entity] });
openCreateActivity({ type, targetableObjects: [entity] });
closeDropdown();
};

View File

@ -3,9 +3,10 @@ import styled from '@emotion/styled';
import { Threads } from '@/activities/emails/components/Threads';
import { Attachments } from '@/activities/files/components/Attachments';
import { Notes } from '@/activities/notes/components/Notes';
import { EntityTasks } from '@/activities/tasks/components/EntityTasks';
import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
import { Timeline } from '@/activities/timeline/components/Timeline';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { isStandardObject } from '@/object-metadata/utils/isStandardObject';
import {
IconCheckbox,
IconMail,
@ -40,7 +41,7 @@ const StyledTabListContainer = styled.div`
`;
type ShowPageRightContainerProps = {
entity?: ActivityTargetableEntity;
targetableObject?: ActivityTargetableObject;
timeline?: boolean;
tasks?: boolean;
notes?: boolean;
@ -48,7 +49,7 @@ type ShowPageRightContainerProps = {
};
export const ShowPageRightContainer = ({
entity,
targetableObject,
timeline,
tasks,
notes,
@ -60,42 +61,44 @@ export const ShowPageRightContainer = ({
ShowPageRecoilScopeContext,
);
if (!entity) return <></>;
if (!targetableObject) return <></>;
const targetableObjectIsStandardObject = isStandardObject(
targetableObject.targetObjectNameSingular,
);
const TASK_TABS = [
{
id: 'timeline',
title: 'Timeline',
Icon: IconTimelineEvent,
hide: !timeline,
disabled: entity.type === 'Custom',
},
{
id: 'tasks',
title: 'Tasks',
Icon: IconCheckbox,
hide: !tasks,
disabled: entity.type === 'Custom',
},
{
id: 'notes',
title: 'Notes',
Icon: IconNotes,
hide: !notes,
disabled: entity.type === 'Custom',
},
{
id: 'files',
title: 'Files',
Icon: IconPaperclip,
hide: !notes,
disabled: entity.type === 'Custom',
disabled: !targetableObjectIsStandardObject,
},
{
id: 'emails',
title: 'Emails',
Icon: IconMail,
hide: !emails,
disabled: !isMessagingEnabled || entity.type === 'Custom',
disabled: !isMessagingEnabled || !targetableObjectIsStandardObject,
},
];
@ -104,11 +107,17 @@ export const ShowPageRightContainer = ({
<StyledTabListContainer>
<TabList context={ShowPageRecoilScopeContext} tabs={TASK_TABS} />
</StyledTabListContainer>
{activeTabId === 'timeline' && <Timeline entity={entity} />}
{activeTabId === 'tasks' && <EntityTasks entity={entity} />}
{activeTabId === 'notes' && <Notes entity={entity} />}
{activeTabId === 'files' && <Attachments targetableEntity={entity} />}
{activeTabId === 'emails' && <Threads entity={entity} />}
{activeTabId === 'timeline' && (
<Timeline targetableObject={targetableObject} />
)}
{activeTabId === 'tasks' && (
<ObjectTasks targetableObject={targetableObject} />
)}
{activeTabId === 'notes' && <Notes targetableObject={targetableObject} />}
{activeTabId === 'files' && (
<Attachments targetableObject={targetableObject} />
)}
{activeTabId === 'emails' && <Threads entity={targetableObject} />}
</StyledShowPageRightContainer>
);
};

View File

@ -1,6 +1,7 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
import { Checkbox } from '@/ui/input/components/Checkbox';
import {
@ -14,6 +15,7 @@ const StyledLeftContentWithCheckboxContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
type MenuItemMultiSelectAvatarProps = {
@ -48,7 +50,7 @@ export const MenuItemMultiSelectAvatar = ({
<StyledMenuItemLeftContent>
{avatar}
<StyledMenuItemLabel hasLeftIcon={!!avatar}>
{text}
<OverflowingTextWithTooltip text={text} />
</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
</StyledLeftContentWithCheckboxContainer>

View File

@ -71,10 +71,11 @@ export const StyledMenuItemBase = styled.li<MenuItemBaseProps>`
export const StyledMenuItemLabel = styled.div<{ hasLeftIcon: boolean }>`
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.regular};
overflow: hidden;
padding-left: ${({ theme, hasLeftIcon }) =>
hasLeftIcon ? '' : theme.spacing(1)};
text-overflow: ellipsis;
white-space: nowrap;
`;