feat: enable export of deleted records (#12776)
resolve #12662 This PR enables exporting deleted records by detecting when deleted view mode is active and adding deletedAt: { is: 'NOT_NULL' } to graphqlFilter. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -2,7 +2,10 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { displayedExportProgress, generateCsv } from '../useExportRecords';
|
||||
import {
|
||||
displayedExportProgress,
|
||||
generateCsv,
|
||||
} from '../useRecordIndexExportRecords';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@ -3,8 +3,8 @@ import { act } from 'react';
|
||||
import {
|
||||
percentage,
|
||||
sleep,
|
||||
useExportFetchRecords,
|
||||
} from '../useExportFetchRecords';
|
||||
useRecordIndexLazyFetchRecords,
|
||||
} from '../useRecordIndexLazyFetchRecords';
|
||||
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||
@ -107,7 +107,7 @@ describe('useRecordData', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useExportFetchRecords({
|
||||
useRecordIndexLazyFetchRecords({
|
||||
recordIndexId,
|
||||
objectMetadataItem,
|
||||
pageSize: 30,
|
||||
@ -134,7 +134,7 @@ describe('useRecordData', () => {
|
||||
mockFetchAllRecords.mockReturnValue([mockPerson]);
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useExportFetchRecords({
|
||||
useRecordIndexLazyFetchRecords({
|
||||
recordIndexId,
|
||||
objectMetadataItem,
|
||||
callback,
|
||||
@ -167,7 +167,7 @@ describe('useRecordData', () => {
|
||||
);
|
||||
|
||||
return {
|
||||
tableData: useExportFetchRecords({
|
||||
tableData: useRecordIndexLazyFetchRecords({
|
||||
recordIndexId,
|
||||
objectMetadataItem,
|
||||
callback,
|
||||
@ -260,7 +260,7 @@ describe('useRecordData', () => {
|
||||
);
|
||||
|
||||
return {
|
||||
tableData: useExportFetchRecords({
|
||||
tableData: useRecordIndexLazyFetchRecords({
|
||||
recordIndexId,
|
||||
objectMetadataItem,
|
||||
callback,
|
||||
@ -7,8 +7,8 @@ import { useExportProcessRecordsForCSV } from '@/object-record/object-options-dr
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import {
|
||||
UseRecordDataOptions,
|
||||
useExportFetchRecords,
|
||||
} from '@/object-record/record-index/export/hooks/useExportFetchRecords';
|
||||
useRecordIndexLazyFetchRecords,
|
||||
} from '@/object-record/record-index/export/hooks/useRecordIndexLazyFetchRecords';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { COMPOSITE_FIELD_SUB_FIELD_LABELS } from '@/settings/data-model/constants/CompositeFieldSubFieldLabel';
|
||||
@ -20,7 +20,10 @@ import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
type GenerateExportOptions = {
|
||||
columns: ColumnDefinition<FieldMetadata>[];
|
||||
columns: Pick<
|
||||
ColumnDefinition<FieldMetadata>,
|
||||
'size' | 'label' | 'type' | 'metadata'
|
||||
>[];
|
||||
rows: Record<string, any>[];
|
||||
};
|
||||
|
||||
@ -121,7 +124,7 @@ type UseExportTableDataOptions = Omit<UseRecordDataOptions, 'callback'> & {
|
||||
filename: string;
|
||||
};
|
||||
|
||||
export const useExportRecords = ({
|
||||
export const useRecordIndexExportRecords = ({
|
||||
delayMs,
|
||||
filename,
|
||||
maximumRequests = 100,
|
||||
@ -144,7 +147,7 @@ export const useExportRecords = ({
|
||||
[filename, processRecordsForCSVExport],
|
||||
);
|
||||
|
||||
const { getTableData: download, progress } = useExportFetchRecords({
|
||||
const { getTableData: download, progress } = useRecordIndexLazyFetchRecords({
|
||||
delayMs,
|
||||
maximumRequests,
|
||||
objectMetadataItem,
|
||||
@ -36,7 +36,7 @@ export type UseRecordDataOptions = {
|
||||
viewType?: ViewType;
|
||||
};
|
||||
|
||||
export const useExportFetchRecords = ({
|
||||
export const useRecordIndexLazyFetchRecords = ({
|
||||
objectMetadataItem,
|
||||
delayMs,
|
||||
maximumRequests = 100,
|
||||
@ -74,6 +74,10 @@ export const useExportFetchRecords = ({
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
const queryFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
@ -81,10 +85,6 @@ export const useExportFetchRecords = ({
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
const finalColumns = [
|
||||
...columns,
|
||||
...(hiddenKanbanFieldColumn && viewType === ViewType.Kanban
|
||||
@ -0,0 +1,69 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useExportProcessRecordsForCSV } from '@/object-record/object-options-dropdown/hooks/useExportProcessRecordsForCSV';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { csvDownloader } from '@/object-record/record-index/export/hooks/useRecordIndexExportRecords';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type UseSingleExportTableDataOptions = {
|
||||
filename: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
recordId: string;
|
||||
};
|
||||
export const useExportSingleRecord = ({
|
||||
filename,
|
||||
objectMetadataItem,
|
||||
recordId,
|
||||
}: UseSingleExportTableDataOptions) => {
|
||||
const { processRecordsForCSVExport } = useExportProcessRecordsForCSV(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
const downloadCsv = useMemo(
|
||||
() =>
|
||||
(
|
||||
record: ObjectRecord,
|
||||
columns: Pick<
|
||||
ColumnDefinition<FieldMetadata>,
|
||||
'size' | 'label' | 'type' | 'metadata'
|
||||
>[],
|
||||
) => {
|
||||
const recordToArray = [record];
|
||||
const recordsProcessedForExport =
|
||||
processRecordsForCSVExport(recordToArray);
|
||||
|
||||
csvDownloader(filename, { rows: recordsProcessedForExport, columns });
|
||||
},
|
||||
[filename, processRecordsForCSVExport],
|
||||
);
|
||||
|
||||
const columns: Pick<
|
||||
ColumnDefinition<FieldMetadata>,
|
||||
'size' | 'label' | 'type' | 'metadata'
|
||||
>[] = objectMetadataItem.fields
|
||||
.filter((field) => field.isActive)
|
||||
.map((field, index) =>
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
field,
|
||||
objectMetadataItem,
|
||||
position: index,
|
||||
}),
|
||||
);
|
||||
const { record, error } = useFindOneRecord({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectRecordId: recordId,
|
||||
withSoftDeleted: true,
|
||||
});
|
||||
const download = () => {
|
||||
if (isDefined(error) || !isDefined(record)) {
|
||||
return;
|
||||
}
|
||||
downloadCsv(record, columns);
|
||||
};
|
||||
return { download };
|
||||
};
|
||||
Reference in New Issue
Block a user