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:
@ -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);
|
||||
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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 <></>;
|
||||
};
|
||||
@ -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);
|
||||
},
|
||||
};
|
||||
@ -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,
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user