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

@ -161,13 +161,9 @@ export const SettingsObjectNewFieldStep2 = () => {
const excludedFieldTypes: SettingsSupportedFieldType[] = (
[
// FieldMetadataType.Email,
// FieldMetadataType.FullName,
FieldMetadataType.Link,
FieldMetadataType.Numeric,
// FieldMetadataType.Probability,
// FieldMetadataType.Uuid,
// FieldMetadataType.Phone,
FieldMetadataType.RichText,
] as const
).filter(isDefined);

View File

@ -1,86 +0,0 @@
import styled from '@emotion/styled';
import { IconArchive, IconCheck, IconCheckbox } from 'twenty-ui';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton';
import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
import { TASKS_TAB_LIST_COMPONENT_ID } from '@/activities/tasks/constants/TasksTabListComponentId';
import { ObjectFilterDropdownButton } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
import { PageHeader } from '@/ui/layout/page/PageHeader';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { TopBar } from '@/ui/layout/top-bar/TopBar';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { TasksEffect } from './TasksEffect';
const StyledTasksContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
overflow: auto;
`;
const StyledTabListContainer = styled.div`
align-items: end;
display: flex;
height: 40px;
`;
export const Tasks = () => {
const TASK_TABS = [
{
id: 'to-do',
title: 'To do',
Icon: IconCheck,
},
{
id: 'done',
title: 'Done',
Icon: IconArchive,
},
];
const filterDropdownId = 'tasks-assignee-filter';
return (
<PageContainer>
<RecordFieldValueSelectorContextProvider>
<RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}>
<TasksEffect filterDropdownId={filterDropdownId} />
<PageHeader title="Tasks" Icon={IconCheckbox}>
<PageAddTaskButton />
</PageHeader>
<PageBody>
<StyledTasksContainer>
<TopBar
leftComponent={
<StyledTabListContainer>
<TabList
tabListId={TASKS_TAB_LIST_COMPONENT_ID}
tabs={TASK_TABS}
/>
</StyledTabListContainer>
}
rightComponent={
<ObjectFilterDropdownButton
filterDropdownId={filterDropdownId}
key="tasks-filter-dropdown-button"
hotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
}
/>
<TaskGroups filterDropdownId={filterDropdownId} />
</StyledTasksContainer>
</PageBody>
</RecoilScope>
</RecordFieldValueSelectorContextProvider>
</PageContainer>
);
};

View File

@ -1,53 +0,0 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { v4 } from 'uuid';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { isDefined } from '~/utils/isDefined';
import { tasksFilterDefinitions } from './tasks-filter-definitions';
type TasksEffectProps = {
filterDropdownId: string;
};
export const TasksEffect = ({ filterDropdownId }: TasksEffectProps) => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const {
setSelectedFilter,
setAvailableFilterDefinitions,
setObjectFilterDropdownSelectedRecordIds,
} = useFilterDropdown({
filterDropdownId: filterDropdownId,
});
useEffect(() => {
setAvailableFilterDefinitions(tasksFilterDefinitions);
}, [setAvailableFilterDefinitions]);
useEffect(() => {
if (isDefined(currentWorkspaceMember)) {
setSelectedFilter({
id: v4(),
fieldMetadataId: 'assigneeId',
value: JSON.stringify(currentWorkspaceMember.id),
operand: ViewFilterOperand.Is,
displayValue:
currentWorkspaceMember.name?.firstName +
' ' +
currentWorkspaceMember.name?.lastName,
displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined,
definition: tasksFilterDefinitions[0],
});
setObjectFilterDropdownSelectedRecordIds([currentWorkspaceMember.id]);
}
}, [
currentWorkspaceMember,
setSelectedFilter,
setObjectFilterDropdownSelectedRecordIds,
]);
return <></>;
};

View File

@ -1,44 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { graphql, HttpResponse } from 'msw';
import { AppPath } from '@/types/AppPath';
import {
PageDecorator,
PageDecoratorArgs,
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedWorkspaceMemberData } from '~/testing/mock-data/users';
import { sleep } from '~/utils/sleep';
import { Tasks } from '../Tasks';
const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Tasks/Default',
component: Tasks,
decorators: [PageDecorator],
args: { routePath: AppPath.TasksPage },
parameters: {
msw: {
handlers: [
graphql.query('FindOneWorkspaceMember', () => {
return HttpResponse.json({
data: {
workspaceMember: mockedWorkspaceMemberData,
},
});
}),
graphqlMocks.handlers,
],
},
},
};
export default meta;
export type Story = StoryObj<typeof Tasks>;
export const Default: Story = {
play: async () => {
await sleep(100);
},
};

View File

@ -1,17 +0,0 @@
import { IconUserCircle } from 'twenty-ui';
import { Activity } from '@/activities/types/Activity';
import { FilterDefinitionByEntity } from '@/object-record/object-filter-dropdown/types/FilterDefinitionByEntity';
export const tasksFilterDefinitions: FilterDefinitionByEntity<Activity>[] = [
{
fieldMetadataId: 'assigneeId',
label: 'Assignee',
iconName: 'IconUser',
type: 'RELATION',
relationObjectMetadataNamePlural: 'workspaceMembers',
relationObjectMetadataNameSingular: 'workspaceMember',
selectAllLabel: 'All assignees',
SelectAllIcon: IconUserCircle,
},
];