({ workspaceId, tableId }: RowMutationContext)
| 1042 | * Returns both deleted ids and failure details for partial-failure UI. |
| 1043 | */ |
| 1044 | export function useDeleteTableRows({ workspaceId, tableId }: RowMutationContext) { |
| 1045 | const queryClient = useQueryClient() |
| 1046 | |
| 1047 | return useMutation({ |
| 1048 | mutationFn: async (rowIds: string[]): Promise<TableRowsDeleteResult> => { |
| 1049 | const uniqueRowIds = Array.from(new Set(rowIds)) |
| 1050 | |
| 1051 | // The delete contract caps `rowIds` at MAX_BULK_OPERATION_SIZE, so large |
| 1052 | // selections (e.g. "select all") are sent as sequential chunks. |
| 1053 | const chunkSize = TABLE_LIMITS.MAX_BULK_OPERATION_SIZE |
| 1054 | const deletedRowIds: string[] = [] |
| 1055 | const missingRowIds: string[] = [] |
| 1056 | for (let i = 0; i < uniqueRowIds.length; i += chunkSize) { |
| 1057 | const chunk = uniqueRowIds.slice(i, i + chunkSize) |
| 1058 | const response = await requestJson(deleteTableRowsContract, { |
| 1059 | params: { tableId }, |
| 1060 | body: { workspaceId, rowIds: chunk }, |
| 1061 | }) |
| 1062 | deletedRowIds.push(...(response.data.deletedRowIds || [])) |
| 1063 | missingRowIds.push(...(response.data.missingRowIds || [])) |
| 1064 | } |
| 1065 | |
| 1066 | if (missingRowIds.length > 0) { |
| 1067 | const failureCount = missingRowIds.length |
| 1068 | const totalCount = uniqueRowIds.length |
| 1069 | const successCount = deletedRowIds.length |
| 1070 | const firstMissing = missingRowIds[0] |
| 1071 | throw new Error( |
| 1072 | `Failed to delete ${failureCount} of ${totalCount} row(s)${successCount > 0 ? ` (${successCount} deleted successfully)` : ''}. Row not found: ${firstMissing}` |
| 1073 | ) |
| 1074 | } |
| 1075 | |
| 1076 | return { deletedRowIds } |
| 1077 | }, |
| 1078 | onError: (error) => { |
| 1079 | if (isValidationError(error)) return |
| 1080 | toast.error(error.message, { duration: 5000 }) |
| 1081 | }, |
| 1082 | onSettled: () => { |
| 1083 | invalidateRowCount(queryClient, tableId) |
| 1084 | }, |
| 1085 | }) |
| 1086 | } |
| 1087 | |
| 1088 | interface DeleteTableRowsAsyncVariables { |
| 1089 | /** Active filter; omit for a whole-table "select all". */ |
no test coverage detected