Closed JarWarren closed 1 day ago
Create Confirmation Modal Component
DeleteConfirmationModal.tsx
in a shared components directory.
import React from 'react';
import { Modal, Button } from 'twenty-ui';
type DeleteConfirmationModalProps = { isOpen: boolean; onConfirm: () => void; onCancel: () => void; };
export const DeleteConfirmationModal = ({ isOpen, onConfirm, onCancel }: DeleteConfirmationModalProps) => (
Are you sure you want to delete this item? This action cannot be undone.
);
Integrate Modal in Task Deletion
ActivityTitle.tsx
to include the modal and handle its state.
import { useState } from 'react';
import { DeleteConfirmationModal } from '@/components/DeleteConfirmationModal';
export const ActivityTitle = ({ activityId }: ActivityTitleProps) => { const [isModalOpen, setModalOpen] = useState(false); const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular: 'Task' });
const handleDelete = () => setModalOpen(true); const confirmDelete = () => { deleteOneRecord(activityId); setModalOpen(false); };
return ( <>
<DeleteConfirmationModal isOpen={isModalOpen} onConfirm={confirmDelete} onCancel={() => setModalOpen(false)} />
</> ); };
Integrate Modal in Note Deletion
ActivityTitle.tsx
.Integrate Modal in Single Object Deletion
useDeleteOneRecord.ts
to include modal logic.
import { useState } from 'react';
import { DeleteConfirmationModal } from '@/components/DeleteConfirmationModal';
export const useDeleteOneRecord = ({ objectNameSingular }: useDeleteOneRecordProps) => { const [isModalOpen, setModalOpen] = useState(false); const apolloClient = useApolloClient();
const deleteOneRecord = useCallback(async (idToDelete: string) => { setModalOpen(true); const confirmDelete = async () => { const deletedRecord = await apolloClient.mutate({ mutation: deleteOneRecordMutation, variables: { idToDelete }, optimisticResponse: { [mutationResponseField]: { __typename: capitalize(objectNameSingular), id: idToDelete } }, update: (cache, { data }) => { const record = data?.[mutationResponseField]; if (!record) return; const cachedRecord = getRecordFromCache(record.id, cache); if (!cachedRecord) return; triggerDeleteRecordsOptimisticEffect({ cache, objectMetadataItem, recordsToDelete: [cachedRecord], objectMetadataItems }); }, }); setModalOpen(false); return deletedRecord.data?.[mutationResponseField] ?? null; }; return ( <DeleteConfirmationModal isOpen={isModalOpen} onConfirm={confirmDelete} onCancel={() => setModalOpen(false)} /> ); }, [apolloClient, deleteOneRecordMutation, getRecordFromCache, mutationResponseField, objectMetadataItem, objectNameSingular, objectMetadataItems]);
return { deleteOneRecord }; };
Update the components and hooks as shown to include the deletion confirmation modal.
/packages/twenty-front/src/modules/object-record/hooks/useDeleteOneRecord.ts /packages/twenty-front/src/modules/activities/comment/Comment.tsx /packages/twenty-front/src/modules/activities/components/ActivityTitle.tsx /packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx /packages/twenty-front/src/modules/activities /packages/twenty-front/src/modules/object-record
Hi @JarWarren, thanks for the feedback! We will address your issue soon by implementing the Soft Delete feature (https://github.com/twentyhq/twenty/issues/6075), which will allow you to retrieve a deleted issue.
In this context, resolving this issue
Scope & Context
In many cases, deletion is an irreversible single-click procedure.
Current behavior
Tasks and notes can be deleted by (mis)clicking a trash button. No confirmation is needed.
Objects (Person, Company, etc) can be deleted through the menu option, which requires a second click, but still no confirmation is needed. This risks substantial data loss for large objects.
Expected behavior
A confirmation modal, similar to batch deletion or API key deletion.
Example: See #4016 for an example modal.