feat: record batch deleteMany (#3096)
feat: support record batch deleteMany Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -10,6 +10,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
|||||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
import { useGenerateCreateManyRecordMutation } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
import { useGenerateCreateManyRecordMutation } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
||||||
import { useGenerateCreateOneRecordMutation } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
import { useGenerateCreateOneRecordMutation } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
||||||
|
import { useGenerateDeleteManyRecordMutation } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
|
||||||
import { useGenerateExecuteQuickActionOnOneRecordMutation } from '@/object-record/hooks/useGenerateExecuteQuickActionOnOneRecordMutation';
|
import { useGenerateExecuteQuickActionOnOneRecordMutation } from '@/object-record/hooks/useGenerateExecuteQuickActionOnOneRecordMutation';
|
||||||
import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenerateFindManyRecordsQuery';
|
import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenerateFindManyRecordsQuery';
|
||||||
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
|
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
|
||||||
@ -107,6 +108,10 @@ export const useObjectMetadataItem = (
|
|||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deleteManyRecordsMutation = useGenerateDeleteManyRecordMutation({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
const executeQuickActionOnOneRecordMutation =
|
const executeQuickActionOnOneRecordMutation =
|
||||||
useGenerateExecuteQuickActionOnOneRecordMutation({
|
useGenerateExecuteQuickActionOnOneRecordMutation({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
@ -131,6 +136,7 @@ export const useObjectMetadataItem = (
|
|||||||
deleteOneRecordMutation,
|
deleteOneRecordMutation,
|
||||||
executeQuickActionOnOneRecordMutation,
|
executeQuickActionOnOneRecordMutation,
|
||||||
createManyRecordsMutation,
|
createManyRecordsMutation,
|
||||||
|
deleteManyRecordsMutation,
|
||||||
mapToObjectRecordIdentifier,
|
mapToObjectRecordIdentifier,
|
||||||
getObjectOrderByField,
|
getObjectOrderByField,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
|
|
||||||
|
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||||
|
import { useOptimisticEvict } from '@/apollo/optimistic-effect/hooks/useOptimisticEvict';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
type useDeleteOneRecordProps = {
|
||||||
|
objectNameSingular: string;
|
||||||
|
refetchFindManyQuery?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteManyRecords = <T>({
|
||||||
|
objectNameSingular,
|
||||||
|
refetchFindManyQuery = false,
|
||||||
|
}: useDeleteOneRecordProps) => {
|
||||||
|
const { performOptimisticEvict } = useOptimisticEvict();
|
||||||
|
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
objectMetadataItem,
|
||||||
|
deleteManyRecordsMutation,
|
||||||
|
findManyRecordsQuery,
|
||||||
|
} = useObjectMetadataItem({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const deleteManyRecords = async (idsToDelete: string[]) => {
|
||||||
|
triggerOptimisticEffects({
|
||||||
|
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||||
|
deletedRecordIds: idsToDelete,
|
||||||
|
});
|
||||||
|
|
||||||
|
idsToDelete.forEach((idToDelete) => {
|
||||||
|
performOptimisticEvict(
|
||||||
|
capitalize(objectMetadataItem.nameSingular),
|
||||||
|
'id',
|
||||||
|
idToDelete,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteRecordFilter: ObjectRecordQueryFilter = {
|
||||||
|
id: {
|
||||||
|
in: idsToDelete,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const deletedRecords = await apolloClient.mutate({
|
||||||
|
mutation: deleteManyRecordsMutation,
|
||||||
|
variables: {
|
||||||
|
filter: deleteRecordFilter,
|
||||||
|
// atMost: idsToDelete.length,
|
||||||
|
},
|
||||||
|
refetchQueries: refetchFindManyQuery
|
||||||
|
? [getOperationName(findManyRecordsQuery) ?? '']
|
||||||
|
: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return deletedRecords.data[
|
||||||
|
`delete${capitalize(objectMetadataItem.namePlural)}`
|
||||||
|
] as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { deleteManyRecords };
|
||||||
|
};
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const useGenerateDeleteManyRecordMutation = ({
|
||||||
|
objectMetadataItem,
|
||||||
|
}: {
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
}) => {
|
||||||
|
if (!objectMetadataItem) {
|
||||||
|
return EMPTY_MUTATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const capitalizedObjectName = capitalize(objectMetadataItem.namePlural);
|
||||||
|
|
||||||
|
return gql`
|
||||||
|
mutation DeleteMany${capitalizedObjectName}($filter: ${capitalize(
|
||||||
|
objectMetadataItem.nameSingular,
|
||||||
|
)}FilterInput!) {
|
||||||
|
delete${capitalizedObjectName}(filter: $filter) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
||||||
@ -4,7 +4,7 @@ import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
import { useExecuteQuickActionOnOneRecord } from '@/object-record/hooks/useExecuteQuickActionOnOneRecord';
|
import { useExecuteQuickActionOnOneRecord } from '@/object-record/hooks/useExecuteQuickActionOnOneRecord';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
||||||
@ -71,7 +71,7 @@ export const useRecordTableContextMenuEntries = (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { deleteOneRecord } = useDeleteOneRecord({
|
const { deleteManyRecords } = useDeleteManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,13 +87,9 @@ export const useRecordTableContextMenuEntries = (
|
|||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
resetTableRowSelection();
|
resetTableRowSelection();
|
||||||
await Promise.all(
|
await deleteManyRecords(rowIdsToDelete);
|
||||||
rowIdsToDelete.map(async (rowId) => {
|
|
||||||
await deleteOneRecord(rowId);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[deleteOneRecord, resetTableRowSelection],
|
[deleteManyRecords, resetTableRowSelection],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleExecuteQuickActionOnClick = useRecoilCallback(
|
const handleExecuteQuickActionOnClick = useRecoilCallback(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
|
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
|
|
||||||
@ -11,8 +11,8 @@ export const useDeleteSelectedRecordBoardCardsInternal = () => {
|
|||||||
const removeCardIds = useRemoveRecordBoardCardIdsInternal();
|
const removeCardIds = useRemoveRecordBoardCardIdsInternal();
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
const { deleteOneRecord: deleteOneOpportunity } =
|
const { deleteManyRecords: deleteManyOpportunities } =
|
||||||
useDeleteOneRecord<Opportunity>({
|
useDeleteManyRecords<Opportunity>({
|
||||||
objectNameSingular: 'opportunity',
|
objectNameSingular: 'opportunity',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -25,11 +25,7 @@ export const useDeleteSelectedRecordBoardCardsInternal = () => {
|
|||||||
.getLoadable(selectedCardIdsSelector)
|
.getLoadable(selectedCardIdsSelector)
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
await Promise.all(
|
await deleteManyOpportunities?.(selectedCardIds);
|
||||||
selectedCardIds.map(async (id) => {
|
|
||||||
await deleteOneOpportunity?.(id);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
removeCardIds(selectedCardIds);
|
removeCardIds(selectedCardIds);
|
||||||
selectedCardIds.forEach((id) => {
|
selectedCardIds.forEach((id) => {
|
||||||
apolloClient.cache.evict({ id: `Opportunity:${id}` });
|
apolloClient.cache.evict({ id: `Opportunity:${id}` });
|
||||||
@ -38,7 +34,7 @@ export const useDeleteSelectedRecordBoardCardsInternal = () => {
|
|||||||
[
|
[
|
||||||
selectedCardIdsSelector,
|
selectedCardIdsSelector,
|
||||||
removeCardIds,
|
removeCardIds,
|
||||||
deleteOneOpportunity,
|
deleteManyOpportunities,
|
||||||
apolloClient.cache,
|
apolloClient.cache,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export class DeleteManyQueryFactory {
|
|||||||
mutation {
|
mutation {
|
||||||
deleteFrom${
|
deleteFrom${
|
||||||
options.targetTableName
|
options.targetTableName
|
||||||
}Collection(filter: ${stringifyWithoutKeyQuote(args.filter)}) {
|
}Collection(filter: ${stringifyWithoutKeyQuote(args.filter)}, atMost: 30) {
|
||||||
affectedCount
|
affectedCount
|
||||||
records {
|
records {
|
||||||
${fieldsString}
|
${fieldsString}
|
||||||
|
|||||||
Reference in New Issue
Block a user