Files
twenty/packages/twenty-front/src/modules/activities/right-drawer/components/ActivityActionBar.tsx
Félix Malfait 09bfb617b2 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">
2024-06-03 17:15:05 +02:00

134 lines
4.9 KiB
TypeScript

import styled from '@emotion/styled';
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { IconTrash } from 'twenty-ui';
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
import { temporaryActivityForEditorState } from '@/activities/states/temporaryActivityForEditorState';
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';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
import { isDefined } from '~/utils/isDefined';
const StyledButtonContainer = styled.div`
display: inline-flex;
gap: ${({ theme }) => theme.spacing(2)};
`;
export const ActivityActionBar = () => {
const viewableRecordId = useRecoilValue(viewableRecordIdState);
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const { deleteOneRecord: deleteOneActivity } = useDeleteOneRecord({
objectNameSingular: CoreObjectNameSingular.Activity,
});
const { deleteManyRecords: deleteManyActivityTargets } = useDeleteManyRecords(
{
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
},
);
const [temporaryActivityForEditor, setTemporaryActivityForEditor] =
useRecoilState(temporaryActivityForEditorState);
const deleteActivityFromCache = useDeleteRecordFromCache({
objectNameSingular: CoreObjectNameSingular.Activity,
});
const deleteActivityTargetFromCache = useDeleteRecordFromCache({
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
});
const [isActivityInCreateMode] = useRecoilState(isActivityInCreateModeState);
const [isUpsertingActivityInDB] = useRecoilState(
isUpsertingActivityInDBState,
);
const { refreshShowPageFindManyActivitiesQueries } =
useRefreshShowPageFindManyActivitiesQueries();
const deleteActivity = useRecoilCallback(
({ snapshot }) =>
async () => {
if (!activityIdInDrawer) {
throw new Error(
'activityIdInDrawer is not defined, this should not happen',
);
}
const activity = snapshot
.getLoadable(recordStoreFamilyState(activityIdInDrawer))
.getValue() as Activity;
setIsRightDrawerOpen(false);
if (!isNonEmptyString(viewableRecordId)) {
return;
}
if (isActivityInCreateMode && isDefined(temporaryActivityForEditor)) {
deleteActivityFromCache(temporaryActivityForEditor);
setTemporaryActivityForEditor(null);
return;
}
if (isNonEmptyString(activityIdInDrawer)) {
const activityTargetIdsToDelete: string[] =
activity.activityTargets.map(mapToRecordId) ?? [];
deleteActivityFromCache(activity);
activity.activityTargets.forEach((activityTarget: ActivityTarget) => {
deleteActivityTargetFromCache(activityTarget);
});
refreshShowPageFindManyActivitiesQueries();
if (isNonEmptyArray(activityTargetIdsToDelete)) {
await deleteManyActivityTargets(activityTargetIdsToDelete);
}
await deleteOneActivity?.(viewableRecordId);
}
},
[
activityIdInDrawer,
setIsRightDrawerOpen,
viewableRecordId,
isActivityInCreateMode,
temporaryActivityForEditor,
deleteActivityFromCache,
setTemporaryActivityForEditor,
refreshShowPageFindManyActivitiesQueries,
deleteOneActivity,
deleteActivityTargetFromCache,
deleteManyActivityTargets,
],
);
const actionsAreDisabled = isUpsertingActivityInDB;
return (
<StyledButtonContainer>
<IconButton
Icon={IconTrash}
onClick={deleteActivity}
size="medium"
variant="secondary"
disabled={actionsAreDisabled}
/>
</StyledButtonContainer>
);
};