diff --git a/packages/twenty-front/src/modules/activities/calendar/right-drawer/components/RightDrawerCalendarEvent.tsx b/packages/twenty-front/src/modules/activities/calendar/right-drawer/components/RightDrawerCalendarEvent.tsx index 4f760bf95..06aa66012 100644 --- a/packages/twenty-front/src/modules/activities/calendar/right-drawer/components/RightDrawerCalendarEvent.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/right-drawer/components/RightDrawerCalendarEvent.tsx @@ -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({ objectNameSingular: FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.objectNameSingular, - objectRecordId: viewableCalendarEventId ?? '', + objectRecordId: viewableRecordId ?? '', recordGqlFields: FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.fields, onCompleted: (record) => setRecords([record]), }); diff --git a/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/__tests__/useOpenCalendarEventRightDrawer.test.tsx b/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/__tests__/useOpenCalendarEventRightDrawer.test.tsx index 027f7f875..980160850 100644 --- a/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/__tests__/useOpenCalendarEventRightDrawer.test.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/__tests__/useOpenCalendarEventRightDrawer.test.tsx @@ -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'); }); }); diff --git a/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer.ts b/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer.ts index 9cd27d017..b10743f35 100644 --- a/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer.ts +++ b/packages/twenty-front/src/modules/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer.ts @@ -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 }; diff --git a/packages/twenty-front/src/modules/activities/calendar/states/viewableCalendarEventIdState.ts b/packages/twenty-front/src/modules/activities/calendar/states/viewableCalendarEventIdState.ts deleted file mode 100644 index 32d1951bc..000000000 --- a/packages/twenty-front/src/modules/activities/calendar/states/viewableCalendarEventIdState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from 'twenty-ui'; - -export const viewableCalendarEventIdState = createState({ - key: 'viewableCalendarEventIdState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/activities/comment/__stories__/Comment.stories.tsx b/packages/twenty-front/src/modules/activities/comment/__stories__/Comment.stories.tsx index a75afeb82..8df2da809 100644 --- a/packages/twenty-front/src/modules/activities/comment/__stories__/Comment.stories.tsx +++ b/packages/twenty-front/src/modules/activities/comment/__stories__/Comment.stories.tsx @@ -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; }; diff --git a/packages/twenty-front/src/modules/activities/comment/__stories__/CommentHeader.stories.tsx b/packages/twenty-front/src/modules/activities/comment/__stories__/CommentHeader.stories.tsx index d61ebc25b..25c01d745 100644 --- a/packages/twenty-front/src/modules/activities/comment/__stories__/CommentHeader.stories.tsx +++ b/packages/twenty-front/src/modules/activities/comment/__stories__/CommentHeader.stories.tsx @@ -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; }; diff --git a/packages/twenty-front/src/modules/activities/components/ActivityEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityEditor.tsx index b560a76f3..e06b79c9c 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityEditor.tsx @@ -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 = ({ - diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadHeader.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadHeader.tsx index 7063110ac..29cd280ed 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadHeader.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadHeader.tsx @@ -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 ( - {}} /> {subject} diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.tsx b/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.tsx index 4dae422ca..d7d3efe03 100644 --- a/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.tsx +++ b/packages/twenty-front/src/modules/activities/emails/hooks/__tests__/useEmailThread.test.tsx @@ -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(); }); }); diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/useEmailThread.ts b/packages/twenty-front/src/modules/activities/emails/hooks/useEmailThread.ts index a0757a18a..c2fa722ed 100644 --- a/packages/twenty-front/src/modules/activities/emails/hooks/useEmailThread.ts +++ b/packages/twenty-front/src/modules/activities/emails/hooks/useEmailThread.ts @@ -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 }; diff --git a/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/useRightDrawerEmailThread.ts b/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/useRightDrawerEmailThread.ts index dce19bb82..bc415e406 100644 --- a/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/useRightDrawerEmailThread.ts +++ b/packages/twenty-front/src/modules/activities/emails/right-drawer/hooks/useRightDrawerEmailThread.ts @@ -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(() => { diff --git a/packages/twenty-front/src/modules/activities/emails/states/viewableEmailThreadIdState.ts b/packages/twenty-front/src/modules/activities/emails/states/viewableEmailThreadIdState.ts deleted file mode 100644 index 494ec3d9c..000000000 --- a/packages/twenty-front/src/modules/activities/emails/states/viewableEmailThreadIdState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from 'twenty-ui'; - -export const viewableEmailThreadIdState = createState({ - key: 'viewableEmailThreadIdState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenActivityRightDrawer.test.tsx b/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenActivityRightDrawer.test.tsx index 19a027551..1294d6ca2 100644 --- a/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenActivityRightDrawer.test.tsx +++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenActivityRightDrawer.test.tsx @@ -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 }) => ( @@ -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'); }); }); diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenCreateActivityDrawer.test.tsx b/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenCreateActivityDrawer.test.tsx index eb60717a4..b545e455c 100644 --- a/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenCreateActivityDrawer.test.tsx +++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useOpenCreateActivityDrawer.test.tsx @@ -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); }); }); diff --git a/packages/twenty-front/src/modules/activities/hooks/useOpenActivityRightDrawer.ts b/packages/twenty-front/src/modules/activities/hooks/useOpenActivityRightDrawer.ts index b0279e17e..185661f2a 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useOpenActivityRightDrawer.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useOpenActivityRightDrawer.ts @@ -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); }; diff --git a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts index 521473627..d85646096 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts @@ -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); diff --git a/packages/twenty-front/src/modules/activities/right-drawer/components/ActivityActionBar.tsx b/packages/twenty-front/src/modules/activities/right-drawer/components/ActivityActionBar.tsx index 27cfbf2d3..60cce744e 100644 --- a/packages/twenty-front/src/modules/activities/right-drawer/components/ActivityActionBar.tsx +++ b/packages/twenty-front/src/modules/activities/right-drawer/components/ActivityActionBar.tsx @@ -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 ( - { - const isMobile = useIsMobile(); - - return ( - - - - {!isMobile && } - - {showActionBar && } - - ); -}; diff --git a/packages/twenty-front/src/modules/activities/right-drawer/components/create/RightDrawerCreateActivity.tsx b/packages/twenty-front/src/modules/activities/right-drawer/components/create/RightDrawerCreateActivity.tsx index fe6ac8eca..d4b64f7f5 100644 --- a/packages/twenty-front/src/modules/activities/right-drawer/components/create/RightDrawerCreateActivity.tsx +++ b/packages/twenty-front/src/modules/activities/right-drawer/components/create/RightDrawerCreateActivity.tsx @@ -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 && ( diff --git a/packages/twenty-front/src/modules/activities/right-drawer/components/edit/RightDrawerEditActivity.tsx b/packages/twenty-front/src/modules/activities/right-drawer/components/edit/RightDrawerEditActivity.tsx index 288d7aca8..84d016869 100644 --- a/packages/twenty-front/src/modules/activities/right-drawer/components/edit/RightDrawerEditActivity.tsx +++ b/packages/twenty-front/src/modules/activities/right-drawer/components/edit/RightDrawerEditActivity.tsx @@ -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 && ( - + {viewableRecordId && ( + )} ); diff --git a/packages/twenty-front/src/modules/activities/states/viewableActivityIdState.ts b/packages/twenty-front/src/modules/activities/states/viewableActivityIdState.ts deleted file mode 100644 index 393a2f96a..000000000 --- a/packages/twenty-front/src/modules/activities/states/viewableActivityIdState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from 'twenty-ui'; - -export const viewableActivityIdState = createState({ - key: 'activities/viewable-activity-id', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts index 47ab23f5b..5cb2d9a43 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useApolloClient } from '@apollo/client'; import { v4 } from 'uuid'; @@ -28,6 +29,7 @@ export const useCreateOneRecord = < skipPostOptmisticEffect = false, }: useCreateOneRecordProps) => { const apolloClient = useApolloClient(); + const [loading, setLoading] = useState(false); const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, @@ -50,6 +52,8 @@ export const useCreateOneRecord = < const { objectMetadataItems } = useObjectMetadataItems(); const createOneRecord = async (input: Partial) => { + setLoading(true); + const idForCreation = input.id ?? v4(); const sanitizedInput = { @@ -94,6 +98,7 @@ export const useCreateOneRecord = < recordsToCreate: [record], objectMetadataItems, }); + setLoading(false); }, }); @@ -102,5 +107,6 @@ export const useCreateOneRecord = < return { createOneRecord, + loading, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx index a6fca0e4d..9874373e8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx @@ -74,6 +74,7 @@ const RelationFieldInputWithContext = ({ relationObjectMetadataNameSingular: CoreObjectNameSingular.WorkspaceMember, objectMetadataNameSingular: 'person', + relationFieldMetadataId: '20202020-8c37-4163-ba06-1dada334ce3e', }, }} entityId={entityId} diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx b/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx new file mode 100644 index 000000000..49b27cdfe --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx @@ -0,0 +1,40 @@ +import { useRecoilValue } from 'recoil'; + +import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; +import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; +import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer'; +import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; +import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect'; +import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; + +export const RightDrawerRecord = () => { + const viewableRecordNameSingular = useRecoilValue( + viewableRecordNameSingularState, + ); + const viewableRecordId = useRecoilValue(viewableRecordIdState); + + if (!viewableRecordNameSingular) { + throw new Error(`Object name is not defined`); + } + + if (!viewableRecordId) { + throw new Error(`Record id is not defined`); + } + + const { objectNameSingular, objectRecordId } = useRecordShowPage( + viewableRecordNameSingular ?? '', + viewableRecordId ?? '', + ); + + return ( + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts new file mode 100644 index 000000000..c75e6cf8a --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui'; + +export const viewableRecordIdState = createState({ + key: 'activities/viewable-record-id', + defaultValue: null, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts new file mode 100644 index 000000000..3116430e1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui'; + +export const viewableRecordNameSingularState = createState({ + key: 'activities/viewable-record-name-singular', + defaultValue: null, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx index 87667d22f..828fa028a 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx @@ -27,6 +27,7 @@ import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPag import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard'; import { ShowPageRecoilScopeContext } from '@/ui/layout/states/ShowPageRecoilScopeContext'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { FieldMetadataType, FileFolder, @@ -39,12 +40,14 @@ type RecordShowContainerProps = { objectNameSingular: string; objectRecordId: string; loading: boolean; + isInRightDrawer?: boolean; }; export const RecordShowContainer = ({ objectNameSingular, objectRecordId, loading, + isInRightDrawer = false, }: RecordShowContainerProps) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, @@ -129,114 +132,118 @@ export const RecordShowContainer = ({ ); const isReadOnly = objectMetadataItem.isRemote; + const isMobile = useIsMobile() || isInRightDrawer; const isPrefetchLoading = useIsPrefetchLoading(); - return ( - - - - {isDefined(recordFromStore) && ( - <> - - - - } - avatarType={recordIdentifier?.avatarType ?? 'rounded'} - onUploadPicture={ - objectNameSingular === 'person' ? onUploadPicture : undefined - } - /> - - {isPrefetchLoading ? ( - - ) : ( - inlineFieldMetadataItems.map((fieldMetadataItem, index) => ( - - - - )) - )} - - - {relationFieldMetadataItems?.map((fieldMetadataItem, index) => ( + const summary = ( + <> + {isDefined(recordFromStore) && ( + <> + + + + } + avatarType={recordIdentifier?.avatarType ?? 'rounded'} + onUploadPicture={ + objectNameSingular === 'person' ? onUploadPicture : undefined + } + /> + + {isPrefetchLoading ? ( + + ) : ( + inlineFieldMetadataItems.map((fieldMetadataItem, index) => ( - - ))} - - )} + )) + )} + + + {relationFieldMetadataItems?.map((fieldMetadataItem, index) => ( + + + + ))} + + )} + + ); + + return ( + + + + {!isMobile && summary} {recordFromStore ? ( ) : ( diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainerEffect.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainerEffect.tsx deleted file mode 100644 index e3b835c9b..000000000 --- a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainerEffect.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useEffect } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; - -import { Activity } from '@/activities/types/Activity'; -import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; -import { recordLoadingFamilyState } from '@/object-record/record-store/states/recordLoadingFamilyState'; -import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; -import { isDefined } from '~/utils/isDefined'; - -export const RecordShowContainer = ({ - objectRecordId, - objectNameSingular, -}: { - objectRecordId: string; - objectNameSingular: string; -}) => { - const { record: activity, loading } = useFindOneRecord({ - objectRecordId, - objectNameSingular, - }); - - const setRecordStore = useSetRecoilState( - recordStoreFamilyState(objectRecordId), - ); - - const [recordLoading, setRecordLoading] = useRecoilState( - recordLoadingFamilyState(objectRecordId), - ); - - useEffect(() => { - if (loading !== recordLoading) { - setRecordLoading(loading); - } - }, [loading, recordLoading, setRecordLoading]); - - useEffect(() => { - if (!loading && isDefined(activity)) { - setRecordStore(activity); - } - }, [loading, setRecordStore, activity]); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts new file mode 100644 index 000000000..f584c4cab --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts @@ -0,0 +1,98 @@ +import { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { useSetRecoilState } from 'recoil'; +import { useIcons } from 'twenty-ui'; + +import { useFavorites } from '@/favorites/hooks/useFavorites'; +import { useLabelIdentifierFieldMetadataItem } from '@/object-metadata/hooks/useLabelIdentifierFieldMetadataItem'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; +import { findOneRecordForShowPageOperationSignatureFactory } from '@/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory'; +import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { isDefined } from '~/utils/isDefined'; +import { capitalize } from '~/utils/string/capitalize'; + +export const useRecordShowPage = ( + propsObjectNameSingular: string, + propsObjectRecordId: string, +) => { + const { + objectNameSingular: paramObjectNameSingular, + objectRecordId: paramObjectRecordId, + } = useParams(); + + const objectNameSingular = propsObjectNameSingular || paramObjectNameSingular; + const objectRecordId = propsObjectRecordId || paramObjectRecordId; + + if (!objectNameSingular || !objectRecordId) { + throw new Error('Object name or Record id is not defined'); + } + + const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); + const { labelIdentifierFieldMetadataItem } = + useLabelIdentifierFieldMetadataItem({ objectNameSingular }); + const { favorites, createFavorite, deleteFavorite } = useFavorites(); + const setEntityFields = useSetRecoilState( + recordStoreFamilyState(objectRecordId), + ); + const { getIcon } = useIcons(); + const headerIcon = getIcon(objectMetadataItem?.icon); + const FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE = + findOneRecordForShowPageOperationSignatureFactory({ objectMetadataItem }); + const { record, loading } = useFindOneRecord({ + objectRecordId, + objectNameSingular, + recordGqlFields: FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE.fields, + }); + + useEffect(() => { + if (isDefined(record)) { + setEntityFields(record); + } + }, [record, setEntityFields]); + + const correspondingFavorite = favorites.find( + (favorite) => favorite.recordId === objectRecordId, + ); + const isFavorite = isDefined(correspondingFavorite); + + const handleFavoriteButtonClick = async () => { + if (!objectNameSingular || !record) return; + + if (isFavorite) { + deleteFavorite(correspondingFavorite.id); + } else { + createFavorite(record, objectNameSingular); + } + }; + + const labelIdentifierFieldValue = + record?.[labelIdentifierFieldMetadataItem?.name ?? '']; + const pageName = + labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName + ? [ + labelIdentifierFieldValue?.firstName, + labelIdentifierFieldValue?.lastName, + ].join(' ') + : isDefined(labelIdentifierFieldValue) + ? `${labelIdentifierFieldValue}` + : ''; + + const pageTitle = pageName.trim() + ? `${pageName} - ${capitalize(objectNameSingular)}` + : capitalize(objectNameSingular); + + return { + objectNameSingular, + objectRecordId, + headerIcon, + loading, + pageTitle, + pageName, + isFavorite, + handleFavoriteButtonClick, + record, + objectMetadataItem, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx index a4a1b6b2e..0ce3ef74b 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx @@ -17,6 +17,7 @@ import { RecordDetailSectionHeader } from '@/object-record/record-show/record-de import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch'; +import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; @@ -72,7 +73,7 @@ export const RecordDetailRelationSection = ({ const relationRecordIds = relationRecords.map(({ id }) => id); - const dropdownId = `record-field-card-relation-picker-${fieldDefinition.label}`; + const dropdownId = `record-field-card-relation-picker-${fieldDefinition.label}-${entityId}`; const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId); @@ -138,6 +139,14 @@ export const RecordDetailRelationSection = ({ ); }; + const { createNewRecordAndOpenRightDrawer } = + useAddNewRecordAndOpenRightDrawer({ + relationObjectMetadataNameSingular, + relationObjectMetadataItem, + relationFieldMetadataItem, + entityId, + }); + return ( } diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRow.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRow.tsx index 1441c109a..dd1ce2cd7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRow.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableRow.tsx @@ -54,6 +54,7 @@ export const RecordTableRow = ({ getBasePathToShowPage({ objectNameSingular: objectMetadataItem.nameSingular, }) + recordId, + objectNameSingular: objectMetadataItem.nameSingular, isSelected: currentRowSelected, isReadOnly: objectMetadataItem.isRemote ?? false, isPendingRow, diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx index 73518d419..9c0bf8b7b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/__stories__/perf/RecordTableCell.perf.stories.tsx @@ -79,6 +79,8 @@ const meta: Meta = { > { + handleClick(); + /* + Disabling sidepanel access for now, TODO: launch + if (!isFieldInputOnly) { + openTableCell(undefined, true); + } + */ + }; + const isFirstColumn = columnIndex === 0; const customButtonIcon = useGetButtonIcon(); - const buttonIcon = isFirstColumn ? IconArrowUpRight : customButtonIcon; + const buttonIcon = isFirstColumn + ? IconArrowUpRight // IconLayoutSidebarRightExpand - Disabling sidepanel access for now + : customButtonIcon; const showButton = isDefined(buttonIcon) && @@ -136,7 +148,7 @@ export const RecordTableCellSoftFocusMode = ({ {editModeContentOnly ? editModeContent : nonEditModeContent} {showButton && ( - + )} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts index e4d84e248..792b33adc 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts @@ -8,6 +8,7 @@ export const recordTableRow: RecordTableRowContextProps = { isSelected: false, recordId: 'recordId', pathToShowPage: '/', + objectNameSingular: 'objectNameSingular', isReadOnly: false, }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts index cf7846adf..af1630986 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts @@ -31,9 +31,14 @@ export const useOpenRecordTableCellFromCell = () => { const cellPosition = useCurrentTableCellPosition(); const customCellHotkeyScope = useContext(CellHotkeyScopeContext); const { entityId, fieldDefinition } = useContext(FieldContext); - const { isReadOnly, pathToShowPage } = useContext(RecordTableRowContext); + const { isReadOnly, pathToShowPage, objectNameSingular } = useContext( + RecordTableRowContext, + ); - const openTableCell = (initialValue?: string) => { + const openTableCell = ( + initialValue?: string, + isActionButtonClick = false, + ) => { onOpenTableCell({ cellPosition, customCellHotkeyScope, @@ -41,7 +46,9 @@ export const useOpenRecordTableCellFromCell = () => { fieldDefinition, isReadOnly, pathToShowPage, + objectNameSingular, initialValue, + isActionButtonClick, }); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts index e9508f536..b9e279b0b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2.ts @@ -1,15 +1,19 @@ import { useNavigate } from 'react-router-dom'; -import { useRecoilCallback } from 'recoil'; +import { useRecoilCallback, useSetRecoilState } from 'recoil'; import { useInitDraftValueV2 } from '@/object-record/record-field/hooks/useInitDraftValueV2'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty'; +import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; +import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { SOFT_FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/SoftFocusClickOutsideListenerId'; import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; import { useMoveEditModeToTableCellPosition } from '@/object-record/record-table/hooks/internal/useMoveEditModeToCellPosition'; import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition'; +import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; +import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -28,9 +32,11 @@ export type OpenTableCellArgs = { cellPosition: TableCellPosition; isReadOnly: boolean; pathToShowPage: string; + objectNameSingular: string; customCellHotkeyScope: HotkeyScope | null; fieldDefinition: FieldDefinition; entityId: string; + isActionButtonClick: boolean; }; export const useOpenRecordTableCellV2 = (tableScopeId: string) => { @@ -48,6 +54,12 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { const initDraftValue = useInitDraftValueV2(); + const { openRightDrawer } = useRightDrawer(); + const setViewableRecordId = useSetRecoilState(viewableRecordIdState); + const setViewableRecordNameSingular = useSetRecoilState( + viewableRecordNameSingularState, + ); + const openTableCell = useRecoilCallback( ({ snapshot }) => ({ @@ -55,9 +67,11 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { cellPosition, isReadOnly, pathToShowPage, + objectNameSingular, customCellHotkeyScope, fieldDefinition, entityId, + isActionButtonClick, }: OpenTableCellArgs) => { if (isReadOnly) { return; @@ -78,9 +92,19 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { fieldValue, }); - if (isFirstColumnCell && !isEmpty) { + if (isFirstColumnCell && !isEmpty && !isActionButtonClick) { leaveTableFocus(); navigate(pathToShowPage); + + return; + } + + if (isFirstColumnCell && !isEmpty && isActionButtonClick) { + leaveTableFocus(); + setViewableRecordId(entityId); + setViewableRecordNameSingular(objectNameSingular); + openRightDrawer(RightDrawerPages.ViewRecord); + return; } @@ -112,10 +136,13 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => { setDragSelectionStartEnabled, toggleClickOutsideListener, leaveTableFocus, - navigate, setHotkeyScope, initDraftValue, moveEditModeToTableCellPosition, + openRightDrawer, + setViewableRecordId, + setViewableRecordNameSingular, + navigate, ], ); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx index d5e4ef29c..18da2c62b 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx @@ -1,9 +1,12 @@ -import { useEffect } from 'react'; +import { useContext, useEffect } from 'react'; import { IconForbid } from 'twenty-ui'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; +import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; @@ -39,11 +42,33 @@ export const RelationPicker = ({ selectedEntity: EntityForSelect | null | undefined, ) => onSubmit(selectedEntity ?? null); + const { objectMetadataItem: relationObjectMetadataItem } = + useObjectMetadataItem({ + objectNameSingular: + fieldDefinition.metadata.relationObjectMetadataNameSingular, + }); + + const relationFieldMetadataItem = relationObjectMetadataItem.fields.find( + ({ id }) => id === fieldDefinition.metadata.relationFieldMetadataId, + ); + + const { entityId } = useContext(FieldContext); + + const { createNewRecordAndOpenRightDrawer } = + useAddNewRecordAndOpenRightDrawer({ + relationObjectMetadataNameSingular: + fieldDefinition.metadata.relationObjectMetadataNameSingular, + relationObjectMetadataItem, + relationFieldMetadataItem, + entityId, + }); + return ( ))} - {showCreateButton && !loading && ( + {showCreateButton && ( <> {entitiesToSelect.length > 0 && } void; + onCreate?: ((searchInput?: string) => void) | (() => void); relationObjectNameSingular: string; relationPickerScopeId?: string; selectedRelationRecordIds: string[]; @@ -54,8 +54,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ relationPickerSearchFilterState, ); - const showCreateButton = - isDefined(onCreate) && relationPickerSearchFilter !== ''; + const showCreateButton = isDefined(onCreate); const entities = useFilteredSearchEntityQuery({ filters: [ @@ -71,6 +70,20 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ objectNameSingular: relationObjectNameSingular, }); + let onCreateWithInput = undefined; + + if (isDefined(onCreate)) { + onCreateWithInput = () => { + if (onCreate.length > 0) { + (onCreate as (searchInput?: string) => void)( + relationPickerSearchFilter, + ); + } else { + (onCreate as () => void)(); + } + }; + } + return ( <> { + const setViewableRecordId = useSetRecoilState(viewableRecordIdState); + const setViewableRecordNameSingular = useSetRecoilState( + viewableRecordNameSingularState, + ); + + const { createOneRecord } = useCreateOneRecord({ + objectNameSingular: relationObjectMetadataNameSingular, + }); + + const { updateOneRecord } = useUpdateOneRecord({ + objectNameSingular: + relationFieldMetadataItem?.relationDefinition?.targetObjectMetadata + .nameSingular ?? 'workspaceMember', + }); + + const { openRightDrawer } = useRightDrawer(); + + if ( + relationObjectMetadataNameSingular === 'workspaceMember' || + !isDefined( + relationFieldMetadataItem?.relationDefinition?.targetObjectMetadata + .nameSingular, + ) + ) { + return { + createNewRecordAndOpenRightDrawer: undefined, + }; + } + + return { + createNewRecordAndOpenRightDrawer: async (searchInput?: string) => { + const newRecordId = v4(); + const labelIdentifierType = getLabelIdentifierFieldMetadataItem( + relationObjectMetadataItem, + )?.type; + const createRecordPayload: { + id: string; + name: + | string + | { firstName: string | undefined; lastName: string | undefined }; + [key: string]: any; + } = + labelIdentifierType === FieldMetadataType.FullName + ? { + id: newRecordId, + name: + searchInput && searchInput.split(' ').length > 1 + ? { + firstName: searchInput.split(' ')[0], + lastName: searchInput.split(' ').slice(1).join(' '), + } + : { firstName: searchInput, lastName: '' }, + } + : { id: newRecordId, name: searchInput ?? '' }; + + if ( + relationFieldMetadataItem?.relationDefinition?.direction === + RelationDefinitionType.ManyToOne + ) { + createRecordPayload[ + `${relationFieldMetadataItem?.relationDefinition?.targetFieldMetadata.name}Id` + ] = entityId; + } + + await createOneRecord(createRecordPayload); + + if ( + relationFieldMetadataItem?.relationDefinition?.direction === + RelationDefinitionType.OneToMany + ) { + await updateOneRecord({ + idToUpdate: entityId, + updateOneRecordInput: { + [`${relationFieldMetadataItem?.relationDefinition?.targetFieldMetadata.name}Id`]: + newRecordId, + }, + }); + } + + setViewableRecordId(newRecordId); + setViewableRecordNameSingular(relationObjectMetadataNameSingular); + openRightDrawer(RightDrawerPages.ViewRecord); + }, + }; +}; diff --git a/packages/twenty-front/src/modules/ui/layout/page/PageBody.tsx b/packages/twenty-front/src/modules/ui/layout/page/PageBody.tsx index 4e1269b3e..5b97f13dd 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/PageBody.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/PageBody.tsx @@ -1 +1,50 @@ -export { RightDrawerContainer as PageBody } from './RightDrawerContainer'; +import { ReactNode } from 'react'; +import styled from '@emotion/styled'; +import { MOBILE_VIEWPORT } from 'twenty-ui'; + +import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer'; + +import { PagePanel } from './PagePanel'; + +type PageBodyProps = { + children: ReactNode; +}; + +const StyledMainContainer = styled.div` + background: ${({ theme }) => theme.background.noisy}; + box-sizing: border-box; + display: flex; + flex: 1 1 auto; + flex-direction: row; + gap: ${({ theme }) => theme.spacing(2)}; + min-height: 0; + padding-bottom: ${({ theme }) => theme.spacing(3)}; + padding-right: ${({ theme }) => theme.spacing(3)}; + padding-left: 0; + width: 100%; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-left: ${({ theme }) => theme.spacing(3)}; + padding-bottom: 0; + } +`; + +type LeftContainerProps = { + isRightDrawerOpen?: boolean; +}; + +const StyledLeftContainer = styled.div` + display: flex; + flex-direction: column; + position: relative; + width: 100%; +`; + +export const PageBody = ({ children }: PageBodyProps) => ( + + + {children} + + + +); diff --git a/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx b/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx index 711400532..becc29aae 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx @@ -4,8 +4,8 @@ import { IconComponent } from 'twenty-ui'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import { PageBody } from './PageBody'; import { PageHeader } from './PageHeader'; -import { RightDrawerContainer } from './RightDrawerContainer'; type SubMenuTopBarContainerProps = { children: JSX.Element | JSX.Element[]; @@ -32,7 +32,7 @@ export const SubMenuTopBarContainer = ({ return ( {isMobile && } - {children} + {children} ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx index 907d16dda..0fc54fbda 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx @@ -7,6 +7,7 @@ import { Key } from 'ts-key-enum'; import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener'; import { isRightDrawerAnimationCompletedState } from '@/ui/layout/right-drawer/states/isRightDrawerAnimationCompleted'; +import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState'; import { rightDrawerCloseEventState } from '@/ui/layout/right-drawer/states/rightDrawerCloseEventsState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; @@ -46,6 +47,8 @@ export const RightDrawer = () => { isRightDrawerOpenState, ); + const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState); + const isRightDrawerExpanded = useRecoilValue(isRightDrawerExpandedState); const [, setIsRightDrawerAnimationCompleted] = useRecoilState( isRightDrawerAnimationCompletedState, @@ -69,8 +72,11 @@ export const RightDrawer = () => { const isRightDrawerOpen = snapshot .getLoadable(isRightDrawerOpenState) .getValue(); + const isRightDrawerMinimized = snapshot + .getLoadable(isRightDrawerMinimizedState) + .getValue(); - if (isRightDrawerOpen) { + if (isRightDrawerOpen && !isRightDrawerMinimized) { set(rightDrawerCloseEventState, event); closeRightDrawer(); } @@ -115,6 +121,13 @@ export const RightDrawer = () => { closed: { x: '100%', }, + minimized: { + x: '0%', + width: 'auto', + height: 'auto', + bottom: '0', + top: 'auto', + }, }; const handleAnimationComplete = () => { setIsRightDrawerAnimationCompleted(isRightDrawerOpen); @@ -122,8 +135,20 @@ export const RightDrawer = () => { return ( , - topBar: , + topBar: , }, [RightDrawerPages.EditActivity]: { page: , - topBar: , + topBar: , }, [RightDrawerPages.ViewEmailThread]: { page: , - topBar: , + topBar: , }, [RightDrawerPages.ViewCalendarEvent]: { page: , - topBar: , + topBar: , + }, + [RightDrawerPages.ViewRecord]: { + page: , + topBar: , }, }; @@ -53,10 +59,14 @@ export const RightDrawerRouter = () => { ? RIGHT_DRAWER_PAGES_CONFIG[rightDrawerPage] : {}; + const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState); + return ( {topBar} - {page} + {!isRightDrawerMinimized && ( + {page} + )} ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx new file mode 100644 index 000000000..1e371c0eb --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx @@ -0,0 +1,116 @@ +import { useTheme } from '@emotion/react'; +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 { 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'; +import { RightDrawerTopBarMinimizeButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarMinimizeButton'; +import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar'; +import { RIGHT_DRAWER_PAGE_ICONS } from '@/ui/layout/right-drawer/constants/RightDrawerPageIcons'; +import { RIGHT_DRAWER_PAGE_TITLES } from '@/ui/layout/right-drawer/constants/RightDrawerPageTitles'; +import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState'; +import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; + +const StyledTopBarWrapper = styled.div` + display: flex; +`; + +const StyledMinimizeTopBarTitleContainer = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + height: 24px; + width: 168px; +`; + +const StyledMinimizeTopBarTitle = styled.div` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +const StyledMinimizeTopBarIcon = styled.div` + align-items: center; + display: flex; +`; + +export const RightDrawerTopBar = ({ page }: { page: RightDrawerPages }) => { + const isMobile = useIsMobile(); + + const [isRightDrawerMinimized, setIsRightDrawerMinimized] = useRecoilState( + isRightDrawerMinimizedState, + ); + + const theme = useTheme(); + + const handleOnclick = () => { + if (isRightDrawerMinimized) { + setIsRightDrawerMinimized(false); + } + }; + + const { getIcon } = useIcons(); + + const PageIcon = getIcon(RIGHT_DRAWER_PAGE_ICONS[page]); + + const viewableRecordNameSingular = useRecoilValue( + viewableRecordNameSingularState, + ); + + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: viewableRecordNameSingular ?? 'company', + }); + + const ObjectIcon = getIcon(objectMetadataItem.icon); + + const label = + page === RightDrawerPages.ViewRecord + ? objectMetadataItem.labelSingular + : RIGHT_DRAWER_PAGE_TITLES[page]; + + const Icon = page === RightDrawerPages.ViewRecord ? ObjectIcon : PageIcon; + + return ( + + {!isRightDrawerMinimized && + (page === RightDrawerPages.EditActivity || + page === RightDrawerPages.CreateActivity) && } + {!isRightDrawerMinimized && + page !== RightDrawerPages.EditActivity && + page !== RightDrawerPages.CreateActivity && ( + } + size={ChipSize.Large} + accent={ChipAccent.TextSecondary} + clickable={false} + /> + )} + {isRightDrawerMinimized && ( + + + + + {label} + + )} + + {!isMobile && !isRightDrawerMinimized && ( + + )} + {!isMobile && !isRightDrawerMinimized && ( + + )} + + + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton.tsx index b7e0f0102..9a225f457 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton.tsx @@ -1,4 +1,4 @@ -import { IconChevronsRight } from 'twenty-ui'; +import { IconX } from 'twenty-ui'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; @@ -13,7 +13,7 @@ export const RightDrawerTopBarCloseButton = () => { return ( { - const [isRightDrawerExpanded, setIsRightDrawerExpanded] = useRecoilState( - isRightDrawerExpandedState, - ); + const { isRightDrawerExpanded, downsizeRightDrawer, expandRightDrawer } = + useRightDrawer(); const handleButtonClick = () => { - setIsRightDrawerExpanded(!isRightDrawerExpanded); + if (isRightDrawerExpanded === true) { + downsizeRightDrawer(); + return; + } + expandRightDrawer(); }; return ( diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarMinimizeButton.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarMinimizeButton.tsx new file mode 100644 index 000000000..b95be3578 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBarMinimizeButton.tsx @@ -0,0 +1,22 @@ +import { IconMinus } from 'twenty-ui'; + +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; +import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; + +export const RightDrawerTopBarMinimizeButton = () => { + const { isRightDrawerMinimized, minimizeRightDrawer, maximizeRightDrawer } = + useRightDrawer(); + + const handleButtonClick = () => { + isRightDrawerMinimized ? maximizeRightDrawer() : minimizeRightDrawer(); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/StyledRightDrawerTopBar.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/StyledRightDrawerTopBar.tsx index fe3cae76e..3378e36dd 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/StyledRightDrawerTopBar.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/StyledRightDrawerTopBar.tsx @@ -1,6 +1,8 @@ import styled from '@emotion/styled'; -export const StyledRightDrawerTopBar = styled.div` +export const StyledRightDrawerTopBar = styled.div<{ + isRightDrawerMinimized: boolean; +}>` align-items: center; background: ${({ theme }) => theme.background.secondary}; border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; @@ -9,9 +11,12 @@ export const StyledRightDrawerTopBar = styled.div` flex-direction: row; font-size: ${({ theme }) => theme.font.size.md}; gap: ${({ theme }) => theme.spacing(1)}; - height: 56px; + height: ${({ isRightDrawerMinimized }) => + isRightDrawerMinimized ? '40px' : '56px'}; justify-content: space-between; padding-left: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(2)}; + cursor: ${({ isRightDrawerMinimized }) => + isRightDrawerMinimized ? 'pointer' : 'default'}; `; diff --git a/packages/twenty-front/src/modules/activities/right-drawer/components/__stories__/RightDrawerActivityTopBar.stories.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/__stories__/RightDrawerTopBar.stories.tsx similarity index 70% rename from packages/twenty-front/src/modules/activities/right-drawer/components/__stories__/RightDrawerActivityTopBar.stories.tsx rename to packages/twenty-front/src/modules/ui/layout/right-drawer/components/__stories__/RightDrawerTopBar.stories.tsx index fa7616383..3f69b0dc3 100644 --- a/packages/twenty-front/src/modules/activities/right-drawer/components/__stories__/RightDrawerActivityTopBar.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/__stories__/RightDrawerTopBar.stories.tsx @@ -3,11 +3,11 @@ import { Meta, StoryObj } from '@storybook/react'; import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { RightDrawerActivityTopBar } from '../RightDrawerActivityTopBar'; +import { RightDrawerTopBar } from '../RightDrawerTopBar'; -const meta: Meta = { +const meta: Meta = { title: 'Modules/Activities/RightDrawer/RightDrawerActivityTopBar', - component: RightDrawerActivityTopBar, + component: RightDrawerTopBar, decorators: [ (Story) => (
@@ -22,6 +22,6 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = {}; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts new file mode 100644 index 000000000..f1c46ba19 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageIcons.ts @@ -0,0 +1,9 @@ +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', +}; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts new file mode 100644 index 000000000..6edb8fec2 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/constants/RightDrawerPageTitles.ts @@ -0,0 +1,9 @@ +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', +}; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/hooks/useRightDrawer.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/hooks/useRightDrawer.ts index 5cf5cc621..86cc303c8 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/hooks/useRightDrawer.ts +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/hooks/useRightDrawer.ts @@ -1,5 +1,6 @@ import { useRecoilCallback, useRecoilState } 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'; @@ -9,6 +10,8 @@ import { RightDrawerPages } from '../types/RightDrawerPages'; export const useRightDrawer = () => { const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState); + const [isRightDrawerExpanded] = useRecoilState(isRightDrawerExpandedState); + const [isRightDrawerMinimized] = useRecoilState(isRightDrawerMinimizedState); const [rightDrawerPage] = useRecoilState(rightDrawerPageState); @@ -18,6 +21,7 @@ export const useRightDrawer = () => { set(rightDrawerPageState, rightDrawerPage); set(isRightDrawerExpandedState, false); set(isRightDrawerOpenState, true); + set(isRightDrawerMinimizedState, false); }, [], ); @@ -27,6 +31,47 @@ export const useRightDrawer = () => { () => { set(isRightDrawerExpandedState, false); set(isRightDrawerOpenState, false); + set(isRightDrawerMinimizedState, false); + }, + [], + ); + + const minimizeRightDrawer = useRecoilCallback( + ({ set }) => + () => { + set(isRightDrawerExpandedState, false); + set(isRightDrawerOpenState, true); + set(isRightDrawerMinimizedState, true); + }, + [], + ); + + const maximizeRightDrawer = useRecoilCallback( + ({ 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); }, [], ); @@ -50,8 +95,14 @@ export const useRightDrawer = () => { return { rightDrawerPage, isRightDrawerOpen, + isRightDrawerExpanded, + isRightDrawerMinimized, openRightDrawer, closeRightDrawer, + minimizeRightDrawer, + maximizeRightDrawer, + expandRightDrawer, + downsizeRightDrawer, isSameEventThanRightDrawerClose, }; }; diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts new file mode 100644 index 000000000..9b2124030 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui'; + +export const isRightDrawerMinimizedState = createState({ + key: 'ui/layout/is-right-drawer-minimized', + defaultValue: false, +}); diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts index df0cd28ca..487b1a16f 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/types/RightDrawerPages.ts @@ -3,4 +3,5 @@ export enum RightDrawerPages { EditActivity = 'edit-activity', ViewEmailThread = 'view-email-thread', ViewCalendarEvent = 'view-calendar-event', + ViewRecord = 'view-record', } diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx index fa5d5c1e8..402bc97dc 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageLeftContainer.tsx @@ -4,22 +4,23 @@ import styled from '@emotion/styled'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; -const StyledOuterContainer = styled.div` +const StyledOuterContainer = styled.div<{ isMobile: boolean }>` background: ${({ theme }) => theme.background.secondary}; border-bottom-left-radius: 8px; - border-right: ${({ theme }) => - useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`}; + border-right: ${({ theme, isMobile }) => + isMobile ? 'none' : `1px solid ${theme.border.color.medium}`}; border-top-left-radius: 8px; display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(3)}; z-index: 10; + width: 'auto'; `; -const StyledInnerContainer = styled.div` +const StyledInnerContainer = styled.div<{ isMobile: boolean }>` display: flex; flex-direction: column; - width: ${() => (useIsMobile() ? `100%` : '348px')}; + width: ${({ isMobile }) => (isMobile ? `100%` : '348px')}; `; const StyledIntermediateContainer = styled.div` @@ -29,24 +30,30 @@ const StyledIntermediateContainer = styled.div` `; export type ShowPageLeftContainerProps = { + forceMobile: boolean; children: ReactNode; }; export const ShowPageLeftContainer = ({ + forceMobile = false, children, }: ShowPageLeftContainerProps) => { - const isMobile = useIsMobile(); - return isMobile ? ( - - {children} - - ) : ( - - - - {children} - - + const isMobile = useIsMobile() || forceMobile; + return ( + + {isMobile ? ( + + {children} + + ) : ( + + + + {children} + + + + )} ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx index 7c3ebf2ec..1a4c51e44 100644 --- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx @@ -24,12 +24,12 @@ import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -const StyledShowPageRightContainer = styled.div` +const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>` display: flex; flex: 1 0 0; flex-direction: column; justify-content: start; - overflow: ${() => (useIsMobile() ? 'none' : 'hidden')}; + overflow: ${(isMobile) => (isMobile ? 'none' : 'hidden')}; width: calc(100% + 4px); `; @@ -53,6 +53,8 @@ type ShowPageRightContainerProps = { tasks?: boolean; notes?: boolean; emails?: boolean; + summary?: JSX.Element; + isRightDrawer?: boolean; loading: boolean; }; @@ -63,8 +65,12 @@ export const ShowPageRightContainer = ({ notes, emails, loading, + summary, + isRightDrawer = false, }: ShowPageRightContainerProps) => { - const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID); + const { activeTabIdState } = useTabList( + TAB_LIST_COMPONENT_ID + isRightDrawer, + ); const activeTabId = useRecoilValue(activeTabIdState); const shouldDisplayCalendarTab = @@ -80,12 +86,20 @@ export const ShowPageRightContainer = ({ CoreObjectNameSingular.Company) || targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Person; + const isMobile = useIsMobile() || isRightDrawer; + const TASK_TABS = [ + { + id: 'summary', + title: 'Summary', + Icon: IconCheckbox, + hide: !isMobile, + }, { id: 'timeline', title: 'Timeline', Icon: IconTimelineEvent, - hide: !timeline, + hide: !timeline || isRightDrawer, }, { id: 'tasks', @@ -127,14 +141,15 @@ export const ShowPageRightContainer = ({ ]; return ( - + + {activeTabId === 'summary' && summary} {activeTabId === 'timeline' && ( <> @@ -157,6 +172,7 @@ export const ShowPageRightContainer = ({ {activeTabId === 'logs' && ( )} + {} ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx b/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx index 1d9f4baa5..ba93d808f 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx +++ b/packages/twenty-front/src/modules/ui/layout/tab/components/TabList.tsx @@ -35,7 +35,7 @@ const StyledContainer = styled.div` `; export const TabList = ({ tabs, tabListId, loading }: TabListProps) => { - const initialActiveTabId = tabs[0].id; + const initialActiveTabId = tabs.find((tab) => !tab.hide)?.id || ''; const { activeTabIdState, setActiveTabId } = useTabList(tabListId); diff --git a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx index 0e3e21018..bcb3508dc 100644 --- a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx @@ -35,7 +35,6 @@ export const RecordIndexPage = () => { const handleAddButtonClick = async () => { setPendingRecordId(v4()); - setSelectedTableCellEditMode(-1, 0); setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes); }; diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx index 736f16210..215d51973 100644 --- a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx @@ -1,17 +1,9 @@ -import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import { useSetRecoilState } from 'recoil'; -import { useIcons } from 'twenty-ui'; -import { useFavorites } from '@/favorites/hooks/useFavorites'; -import { useLabelIdentifierFieldMetadataItem } from '@/object-metadata/hooks/useLabelIdentifierFieldMetadataItem'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer'; -import { findOneRecordForShowPageOperationSignatureFactory } from '@/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory'; +import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect'; import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; -import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { PageBody } from '@/ui/layout/page/PageBody'; import { PageContainer } from '@/ui/layout/page/PageContainer'; import { PageFavoriteButton } from '@/ui/layout/page/PageFavoriteButton'; @@ -19,93 +11,29 @@ import { PageHeader } from '@/ui/layout/page/PageHeader'; import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddButton'; import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMoreButton'; import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; -import { isDefined } from '~/utils/isDefined'; -import { capitalize } from '~/utils/string/capitalize'; export const RecordShowPage = () => { - const { objectNameSingular, objectRecordId } = useParams<{ + const parameters = useParams<{ objectNameSingular: string; objectRecordId: string; }>(); - if (!objectNameSingular) { - throw new Error(`Object name is not defined`); - } - - if (!objectRecordId) { - throw new Error(`Record id is not defined`); - } - - const { objectMetadataItem } = useObjectMetadataItem({ + const { objectNameSingular, - }); - - const { labelIdentifierFieldMetadataItem } = - useLabelIdentifierFieldMetadataItem({ - objectNameSingular, - }); - - const { favorites, createFavorite, deleteFavorite } = useFavorites(); - - const setEntityFields = useSetRecoilState( - recordStoreFamilyState(objectRecordId), - ); - - const { getIcon } = useIcons(); - - const headerIcon = getIcon(objectMetadataItem?.icon); - - const FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE = - findOneRecordForShowPageOperationSignatureFactory({ objectMetadataItem }); - - const { record, loading } = useFindOneRecord({ objectRecordId, - objectNameSingular, - recordGqlFields: FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE.fields, - }); - - useEffect(() => { - if (!record) { - return; - } - - setEntityFields(record); - }, [record, setEntityFields]); - - const correspondingFavorite = favorites.find( - (favorite) => favorite.recordId === objectRecordId, + headerIcon, + loading, + pageTitle, + pageName, + isFavorite, + handleFavoriteButtonClick, + record, + objectMetadataItem, + } = useRecordShowPage( + parameters.objectNameSingular ?? '', + parameters.objectRecordId ?? '', ); - const isFavorite = isDefined(correspondingFavorite); - - const handleFavoriteButtonClick = async () => { - if (!objectNameSingular || !record) return; - - if (isFavorite && isDefined(record)) { - deleteFavorite(correspondingFavorite.id); - } else { - createFavorite(record, objectNameSingular); - } - }; - - const labelIdentifierFieldValue = - record?.[labelIdentifierFieldMetadataItem?.name ?? '']; - - const pageName = - labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName - ? [ - labelIdentifierFieldValue?.firstName, - labelIdentifierFieldValue?.lastName, - ].join(' ') - : isDefined(labelIdentifierFieldValue) - ? `${labelIdentifierFieldValue}` - : ''; - - const pageTitle = pageName.trim() - ? `${pageName} - ${capitalize(objectNameSingular)}` - : capitalize(objectNameSingular); - return (