Right drawer to edit records (#5551)
This PR introduces a new side panel to edit records and the ability to minimize the side panel. The goal is leverage this sidepanel to be able to create records while being in another show page. I'm opening the PR for feedback since it involved refactoring and therefore already touches a lot of files, even though it was quick to implement. <img width="1503" alt="Screenshot 2024-05-23 at 17 41 37" src="https://github.com/twentyhq/twenty/assets/6399865/6f17e7a8-f4e9-4eb4-b392-c756db7198ac">
This commit is contained in:
@ -2,18 +2,18 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CalendarEventDetails } from '@/activities/calendar/components/CalendarEventDetails';
|
||||
import { FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE } from '@/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature';
|
||||
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
|
||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore';
|
||||
|
||||
export const RightDrawerCalendarEvent = () => {
|
||||
const { setRecords } = useSetRecordInStore();
|
||||
const viewableCalendarEventId = useRecoilValue(viewableCalendarEventIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const { record: calendarEvent } = useFindOneRecord<CalendarEvent>({
|
||||
objectNameSingular:
|
||||
FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.objectNameSingular,
|
||||
objectRecordId: viewableCalendarEventId ?? '',
|
||||
objectRecordId: viewableRecordId ?? '',
|
||||
recordGqlFields: FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.fields,
|
||||
onCompleted: (record) => setRecords([record]),
|
||||
});
|
||||
|
||||
@ -2,7 +2,7 @@ import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenCalendarEventRightDrawer } from '@/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer';
|
||||
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
|
||||
|
||||
describe('useOpenCalendarEventRightDrawer', () => {
|
||||
@ -10,26 +10,24 @@ describe('useOpenCalendarEventRightDrawer', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
|
||||
const viewableCalendarEventId = useRecoilValue(
|
||||
viewableCalendarEventIdState,
|
||||
);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
return {
|
||||
...useOpenCalendarEventRightDrawer(),
|
||||
isRightDrawerOpen,
|
||||
viewableCalendarEventId,
|
||||
viewableRecordId,
|
||||
};
|
||||
},
|
||||
{ wrapper: RecoilRoot },
|
||||
);
|
||||
|
||||
expect(result.current.isRightDrawerOpen).toBe(false);
|
||||
expect(result.current.viewableCalendarEventId).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
|
||||
act(() => {
|
||||
result.current.openCalendarEventRightDrawer('1234');
|
||||
});
|
||||
|
||||
expect(result.current.isRightDrawerOpen).toBe(true);
|
||||
expect(result.current.viewableCalendarEventId).toBe('1234');
|
||||
expect(result.current.viewableRecordId).toBe('1234');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
@ -9,14 +9,12 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
|
||||
export const useOpenCalendarEventRightDrawer = () => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const setViewableCalendarEventId = useSetRecoilState(
|
||||
viewableCalendarEventIdState,
|
||||
);
|
||||
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);
|
||||
|
||||
const openCalendarEventRightDrawer = (calendarEventId: string) => {
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
openRightDrawer(RightDrawerPages.ViewCalendarEvent);
|
||||
setViewableCalendarEventId(calendarEventId);
|
||||
setViewableRecordId(calendarEventId);
|
||||
};
|
||||
|
||||
return { openCalendarEventRightDrawer };
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const viewableCalendarEventIdState = createState<string | null>({
|
||||
key: 'viewableCalendarEventIdState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
import { ActivityActionBar } from '../../right-drawer/components/ActivityActionBar';
|
||||
import { Comment } from '../Comment';
|
||||
@ -11,11 +11,11 @@ import { Comment } from '../Comment';
|
||||
import { mockComment, mockCommentWithLongValues } from './mock-comment';
|
||||
|
||||
const CommentSetterEffect = () => {
|
||||
const setViewableActivity = useSetRecoilState(viewableActivityIdState);
|
||||
const setViewableRecord = useSetRecoilState(viewableRecordIdState);
|
||||
|
||||
useEffect(() => {
|
||||
setViewableActivity('test-id');
|
||||
}, [setViewableActivity]);
|
||||
setViewableRecord('test-id');
|
||||
}, [setViewableRecord]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import { DateTime } from 'luxon';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { avatarUrl } from '~/testing/mock-data/users';
|
||||
|
||||
@ -13,11 +13,11 @@ import { CommentHeader } from '../CommentHeader';
|
||||
import { mockComment, mockCommentWithLongValues } from './mock-comment';
|
||||
|
||||
const CommentHeaderSetterEffect = () => {
|
||||
const setViewableActivity = useSetRecoilState(viewableActivityIdState);
|
||||
const setViewableRecord = useSetRecoilState(viewableRecordIdState);
|
||||
|
||||
useEffect(() => {
|
||||
setViewableActivity('test-id');
|
||||
}, [setViewableActivity]);
|
||||
setViewableRecord('test-id');
|
||||
}, [setViewableRecord]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@ -7,7 +7,6 @@ import { ActivityComments } from '@/activities/components/ActivityComments';
|
||||
import { ActivityCreationDate } from '@/activities/components/ActivityCreationDate';
|
||||
import { ActivityEditorFields } from '@/activities/components/ActivityEditorFields';
|
||||
import { ActivityTitleEffect } from '@/activities/components/ActivityTitleEffect';
|
||||
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { ActivityTitle } from './ActivityTitle';
|
||||
@ -68,7 +67,6 @@ export const ActivityEditor = ({
|
||||
<StyledContainer ref={containerRef}>
|
||||
<StyledUpperPartContainer>
|
||||
<StyledTopContainer>
|
||||
<ActivityTypeDropdown activityId={activityId} />
|
||||
<ActivityTitleEffect activityId={activityId} />
|
||||
<StyledTitleContainer>
|
||||
<ActivityTitle activityId={activityId} />
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { IconMail, Tag } from 'twenty-ui';
|
||||
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||
|
||||
@ -43,7 +42,6 @@ export const EmailThreadHeader = ({
|
||||
}: EmailThreadHeaderProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<Tag Icon={IconMail} color="gray" text="Email" onClick={() => {}} />
|
||||
<StyledHead>
|
||||
<StyledHeading>{subject}</StyledHeading>
|
||||
<StyledContent>
|
||||
|
||||
@ -2,7 +2,7 @@ import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useEmailThread } from '@/activities/emails/hooks/useEmailThread';
|
||||
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
|
||||
|
||||
const viewableEmailThreadId = '1234';
|
||||
@ -13,24 +13,22 @@ describe('useEmailThread', () => {
|
||||
() => {
|
||||
const emailThread = useEmailThread();
|
||||
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
|
||||
const viewableEmailThreadId = useRecoilValue(
|
||||
viewableEmailThreadIdState,
|
||||
);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
|
||||
return { ...emailThread, isRightDrawerOpen, viewableEmailThreadId };
|
||||
return { ...emailThread, isRightDrawerOpen, viewableRecordId };
|
||||
},
|
||||
{ wrapper: RecoilRoot },
|
||||
);
|
||||
|
||||
expect(result.current.isRightDrawerOpen).toBe(false);
|
||||
expect(result.current.viewableEmailThreadId).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
|
||||
act(() => {
|
||||
result.current.openEmailThread(viewableEmailThreadId);
|
||||
});
|
||||
|
||||
expect(result.current.isRightDrawerOpen).toBe(true);
|
||||
expect(result.current.viewableEmailThreadId).toBe(viewableEmailThreadId);
|
||||
expect(result.current.viewableRecordId).toBe(viewableEmailThreadId);
|
||||
});
|
||||
|
||||
it('should close email thread if trying to open the same thread id', () => {
|
||||
@ -40,15 +38,16 @@ describe('useEmailThread', () => {
|
||||
const [isRightDrawerOpen, setIsRightDrawerOpen] = useRecoilState(
|
||||
isRightDrawerOpenState,
|
||||
);
|
||||
const [viewableEmailThreadId, setViewableEmailThreadId] =
|
||||
useRecoilState(viewableEmailThreadIdState);
|
||||
const [viewableRecordId, setViewableRecordId] = useRecoilState(
|
||||
viewableRecordIdState,
|
||||
);
|
||||
|
||||
return {
|
||||
...emailThread,
|
||||
isRightDrawerOpen,
|
||||
viewableEmailThreadId,
|
||||
viewableRecordId,
|
||||
setIsRightDrawerOpen,
|
||||
setViewableEmailThreadId,
|
||||
setViewableRecordId,
|
||||
};
|
||||
},
|
||||
{ wrapper: RecoilRoot },
|
||||
@ -56,7 +55,7 @@ describe('useEmailThread', () => {
|
||||
|
||||
act(() => {
|
||||
result.current.setIsRightDrawerOpen(true);
|
||||
result.current.setViewableEmailThreadId(viewableEmailThreadId);
|
||||
result.current.setViewableRecordId(viewableEmailThreadId);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@ -64,6 +63,6 @@ describe('useEmailThread', () => {
|
||||
});
|
||||
|
||||
expect(result.current.isRightDrawerOpen).toBe(false);
|
||||
expect(result.current.viewableEmailThreadId).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useOpenEmailThreadRightDrawer } from '@/activities/emails/right-drawer/hooks/useOpenEmailThreadRightDrawer';
|
||||
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
|
||||
|
||||
export const useEmailThread = () => {
|
||||
const { closeRightDrawer } = useRightDrawer();
|
||||
const openEmailThredRightDrawer = useOpenEmailThreadRightDrawer();
|
||||
const openEmailThreadRightDrawer = useOpenEmailThreadRightDrawer();
|
||||
|
||||
const openEmailThread = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
@ -17,19 +17,19 @@ export const useEmailThread = () => {
|
||||
.getValue();
|
||||
|
||||
const viewableEmailThreadId = snapshot
|
||||
.getLoadable(viewableEmailThreadIdState)
|
||||
.getLoadable(viewableRecordIdState)
|
||||
.getValue();
|
||||
|
||||
if (isRightDrawerOpen && viewableEmailThreadId === threadId) {
|
||||
set(viewableEmailThreadIdState, null);
|
||||
set(viewableRecordIdState, null);
|
||||
closeRightDrawer();
|
||||
return;
|
||||
}
|
||||
|
||||
openEmailThredRightDrawer();
|
||||
set(viewableEmailThreadIdState, threadId);
|
||||
openEmailThreadRightDrawer();
|
||||
set(viewableRecordIdState, threadId);
|
||||
},
|
||||
[closeRightDrawer, openEmailThredRightDrawer],
|
||||
[closeRightDrawer, openEmailThreadRightDrawer],
|
||||
);
|
||||
|
||||
return { openEmailThread };
|
||||
|
||||
@ -4,16 +4,16 @@ import gql from 'graphql-tag';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { fetchAllThreadMessagesOperationSignatureFactory } from '@/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory';
|
||||
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
|
||||
import { EmailThreadMessage as EmailThreadMessageType } from '@/activities/emails/types/EmailThreadMessage';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
export const useRightDrawerEmailThread = () => {
|
||||
const viewableEmailThreadId = useRecoilValue(viewableEmailThreadIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
const thread = apolloClient.readFragment({
|
||||
id: `TimelineThread:${viewableEmailThreadId}`,
|
||||
id: `TimelineThread:${viewableRecordId}`,
|
||||
fragment: gql`
|
||||
fragment timelineThread on TimelineThread {
|
||||
id
|
||||
@ -25,7 +25,7 @@ export const useRightDrawerEmailThread = () => {
|
||||
|
||||
const FETCH_ALL_MESSAGES_OPERATION_SIGNATURE =
|
||||
fetchAllThreadMessagesOperationSignatureFactory({
|
||||
messageThreadId: viewableEmailThreadId,
|
||||
messageThreadId: viewableRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
@ -39,7 +39,7 @@ export const useRightDrawerEmailThread = () => {
|
||||
FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.objectNameSingular,
|
||||
orderBy: FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.variables.orderBy,
|
||||
recordGqlFields: FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.fields,
|
||||
skip: !viewableEmailThreadId,
|
||||
skip: !viewableRecordId,
|
||||
});
|
||||
|
||||
const fetchMoreMessages = useCallback(() => {
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const viewableEmailThreadIdState = createState<string | null>({
|
||||
key: 'viewableEmailThreadIdState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -5,7 +5,7 @@ import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
@ -18,12 +18,12 @@ describe('useOpenActivityRightDrawer', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const viewableActivityId = useRecoilValue(viewableActivityIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
activityIdInDrawer,
|
||||
viewableActivityId,
|
||||
viewableRecordId,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -32,11 +32,11 @@ describe('useOpenActivityRightDrawer', () => {
|
||||
);
|
||||
|
||||
expect(result.current.activityIdInDrawer).toBeNull();
|
||||
expect(result.current.viewableActivityId).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
act(() => {
|
||||
result.current.openActivityRightDrawer('123');
|
||||
});
|
||||
expect(result.current.activityIdInDrawer).toBe('123');
|
||||
expect(result.current.viewableActivityId).toBe('123');
|
||||
expect(result.current.viewableRecordId).toBe('123');
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,9 +5,9 @@ import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
const mockUUID = '37873e04-2f83-4468-9ab7-3f87da6cafad';
|
||||
|
||||
@ -28,7 +28,7 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const openActivityRightDrawer = useOpenCreateActivityDrawer();
|
||||
const viewableActivityId = useRecoilValue(viewableActivityIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
@ -36,7 +36,7 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
activityIdInDrawer,
|
||||
viewableActivityId,
|
||||
viewableRecordId,
|
||||
setObjectMetadataItems,
|
||||
};
|
||||
},
|
||||
@ -50,7 +50,7 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
});
|
||||
|
||||
expect(result.current.activityIdInDrawer).toBeNull();
|
||||
expect(result.current.viewableActivityId).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
await act(async () => {
|
||||
result.current.openActivityRightDrawer({
|
||||
type: 'Note',
|
||||
@ -58,6 +58,6 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
});
|
||||
});
|
||||
expect(result.current.activityIdInDrawer).toBe(mockUUID);
|
||||
expect(result.current.viewableActivityId).toBe(mockUUID);
|
||||
expect(result.current.viewableRecordId).toBe(mockUUID);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||
|
||||
export const useOpenActivityRightDrawer = () => {
|
||||
const { openRightDrawer, isRightDrawerOpen, rightDrawerPage } =
|
||||
useRightDrawer();
|
||||
const [viewableActivityId, setViewableActivityId] = useRecoilState(
|
||||
viewableActivityIdState,
|
||||
const [viewableRecordId, setViewableRecordId] = useRecoilState(
|
||||
viewableRecordIdState,
|
||||
);
|
||||
const setActivityIdInDrawer = useSetRecoilState(activityIdInDrawerState);
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
@ -21,13 +20,13 @@ export const useOpenActivityRightDrawer = () => {
|
||||
if (
|
||||
isRightDrawerOpen &&
|
||||
rightDrawerPage === RightDrawerPages.EditActivity &&
|
||||
viewableActivityId === activityId
|
||||
viewableRecordId === activityId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableActivityId(activityId);
|
||||
setViewableRecordId(activityId);
|
||||
setActivityIdInDrawer(activityId);
|
||||
openRightDrawer(RightDrawerPages.EditActivity);
|
||||
};
|
||||
|
||||
@ -6,8 +6,8 @@ import { activityTargetableEntityArrayState } from '@/activities/states/activity
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { temporaryActivityForEditorState } from '@/activities/states/temporaryActivityForEditorState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { ActivityType } from '@/activities/types/Activity';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
@ -26,7 +26,7 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
const setActivityTargetableEntityArray = useSetRecoilState(
|
||||
activityTargetableEntityArrayState,
|
||||
);
|
||||
const setViewableActivityId = useSetRecoilState(viewableActivityIdState);
|
||||
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);
|
||||
|
||||
const setIsCreatingActivity = useSetRecoilState(isActivityInCreateModeState);
|
||||
|
||||
@ -59,7 +59,7 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
setTemporaryActivityForEditor(createdActivityInCache);
|
||||
setIsCreatingActivity(true);
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableActivityId(createdActivityInCache.id);
|
||||
setViewableRecordId(createdActivityInCache.id);
|
||||
setActivityTargetableEntityArray(targetableObjects ?? []);
|
||||
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||
setIsUpsertingActivityInDB(false);
|
||||
|
||||
@ -1,23 +1,20 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { IconPlus, IconTrash } from 'twenty-ui';
|
||||
import { IconTrash } from 'twenty-ui';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { activityTargetableEntityArrayState } from '@/activities/states/activityTargetableEntityArrayState';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { temporaryActivityForEditorState } from '@/activities/states/temporaryActivityForEditorState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline/states/objectShowPageTargetableObjectIdState';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useDeleteRecordFromCache } from '@/object-record/cache/hooks/useDeleteRecordFromCache';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
@ -30,12 +27,9 @@ const StyledButtonContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const ActivityActionBar = () => {
|
||||
const viewableActivityId = useRecoilValue(viewableActivityIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
|
||||
|
||||
const activityTargetableEntityArray = useRecoilValue(
|
||||
activityTargetableEntityArrayState,
|
||||
);
|
||||
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
const { deleteOneRecord: deleteOneActivity } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
@ -62,15 +56,9 @@ export const ActivityActionBar = () => {
|
||||
isUpsertingActivityInDBState,
|
||||
);
|
||||
|
||||
const objectShowPageTargetableObject = useRecoilValue(
|
||||
objectShowPageTargetableObjectState,
|
||||
);
|
||||
|
||||
const { refreshShowPageFindManyActivitiesQueries } =
|
||||
useRefreshShowPageFindManyActivitiesQueries();
|
||||
|
||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||
|
||||
const deleteActivity = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
@ -86,7 +74,7 @@ export const ActivityActionBar = () => {
|
||||
|
||||
setIsRightDrawerOpen(false);
|
||||
|
||||
if (!isNonEmptyString(viewableActivityId)) {
|
||||
if (!isNonEmptyString(viewableRecordId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -111,13 +99,13 @@ export const ActivityActionBar = () => {
|
||||
await deleteManyActivityTargets(activityTargetIdsToDelete);
|
||||
}
|
||||
|
||||
await deleteOneActivity?.(viewableActivityId);
|
||||
await deleteOneActivity?.(viewableRecordId);
|
||||
}
|
||||
},
|
||||
[
|
||||
activityIdInDrawer,
|
||||
setIsRightDrawerOpen,
|
||||
viewableActivityId,
|
||||
viewableRecordId,
|
||||
isActivityInCreateMode,
|
||||
temporaryActivityForEditor,
|
||||
deleteActivityFromCache,
|
||||
@ -129,34 +117,10 @@ export const ActivityActionBar = () => {
|
||||
],
|
||||
);
|
||||
|
||||
const record = useRecoilValue(
|
||||
recordStoreFamilyState(viewableActivityId ?? ''),
|
||||
);
|
||||
|
||||
const addActivity = () => {
|
||||
setIsRightDrawerOpen(false);
|
||||
if (isDefined(record) && isDefined(objectShowPageTargetableObject)) {
|
||||
openCreateActivity({
|
||||
type: record?.type,
|
||||
customAssignee: record?.assignee,
|
||||
targetableObjects: activityTargetableEntityArray,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const actionsAreDisabled = isUpsertingActivityInDB;
|
||||
|
||||
const isCreateActionDisabled = isActivityInCreateMode;
|
||||
|
||||
return (
|
||||
<StyledButtonContainer>
|
||||
<IconButton
|
||||
Icon={IconPlus}
|
||||
onClick={addActivity}
|
||||
size="medium"
|
||||
variant="secondary"
|
||||
disabled={actionsAreDisabled || isCreateActionDisabled}
|
||||
/>
|
||||
<IconButton
|
||||
Icon={IconTrash}
|
||||
onClick={deleteActivity}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||
import { RightDrawerTopBarCloseButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
|
||||
import { RightDrawerTopBarExpandButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
|
||||
import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
type RightDrawerActivityTopBarProps = { showActionBar?: boolean };
|
||||
|
||||
const StyledTopBarWrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const RightDrawerActivityTopBar = ({
|
||||
showActionBar = true,
|
||||
}: RightDrawerActivityTopBarProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledRightDrawerTopBar>
|
||||
<StyledTopBarWrapper>
|
||||
<RightDrawerTopBarCloseButton />
|
||||
{!isMobile && <RightDrawerTopBarExpandButton />}
|
||||
</StyledTopBarWrapper>
|
||||
{showActionBar && <ActivityActionBar />}
|
||||
</StyledRightDrawerTopBar>
|
||||
);
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
|
||||
import { RightDrawerActivityTopBar } from '../RightDrawerActivityTopBar';
|
||||
|
||||
const meta: Meta<typeof RightDrawerActivityTopBar> = {
|
||||
title: 'Modules/Activities/RightDrawer/RightDrawerActivityTopBar',
|
||||
component: RightDrawerActivityTopBar,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ width: '500px' }}>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
ComponentWithRouterDecorator,
|
||||
],
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RightDrawerActivityTopBar>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -1,17 +1,17 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
import { RightDrawerActivity } from '../RightDrawerActivity';
|
||||
|
||||
export const RightDrawerCreateActivity = () => {
|
||||
const viewableActivityId = useRecoilValue(viewableActivityIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
|
||||
return (
|
||||
<>
|
||||
{viewableActivityId && (
|
||||
{viewableRecordId && (
|
||||
<RightDrawerActivity
|
||||
activityId={viewableActivityId}
|
||||
activityId={viewableRecordId}
|
||||
showComment={false}
|
||||
fillTitleFromBody={true}
|
||||
/>
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
import { RightDrawerActivity } from '../RightDrawerActivity';
|
||||
|
||||
export const RightDrawerEditActivity = () => {
|
||||
const viewableActivityId = useRecoilValue(viewableActivityIdState);
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
|
||||
return (
|
||||
<>
|
||||
{viewableActivityId && (
|
||||
<RightDrawerActivity activityId={viewableActivityId} />
|
||||
{viewableRecordId && (
|
||||
<RightDrawerActivity activityId={viewableRecordId} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const viewableActivityIdState = createState<string | null>({
|
||||
key: 'activities/viewable-activity-id',
|
||||
defaultValue: null,
|
||||
});
|
||||
Reference in New Issue
Block a user