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:
@ -13,6 +13,7 @@ import { AddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-a
|
|||||||
import { DeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction';
|
import { DeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction';
|
||||||
import { DestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DestroySingleRecordAction';
|
import { DestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DestroySingleRecordAction';
|
||||||
import { ExportNoteActionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportNoteActionSingleRecordAction';
|
import { ExportNoteActionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportNoteActionSingleRecordAction';
|
||||||
|
import { ExportSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportSingleRecordAction';
|
||||||
import { NavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToNextRecordSingleRecordAction';
|
import { NavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToNextRecordSingleRecordAction';
|
||||||
import { NavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToPreviousRecordSingleRecordAction';
|
import { NavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToPreviousRecordSingleRecordAction';
|
||||||
import { RemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RemoveFromFavoritesSingleRecordAction';
|
import { RemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RemoveFromFavoritesSingleRecordAction';
|
||||||
@ -137,10 +138,10 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
],
|
],
|
||||||
component: <RemoveFromFavoritesSingleRecordAction />,
|
component: <RemoveFromFavoritesSingleRecordAction />,
|
||||||
},
|
},
|
||||||
[SingleRecordActionKeys.EXPORT]: {
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||||
type: ActionType.Standard,
|
type: ActionType.Standard,
|
||||||
scope: ActionScope.RecordSelection,
|
scope: ActionScope.RecordSelection,
|
||||||
key: SingleRecordActionKeys.EXPORT,
|
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||||
label: msg`Export`,
|
label: msg`Export`,
|
||||||
shortLabel: msg`Export`,
|
shortLabel: msg`Export`,
|
||||||
position: 4,
|
position: 4,
|
||||||
@ -149,12 +150,24 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
isPinned: false,
|
isPinned: false,
|
||||||
shouldBeRegistered: ({ selectedRecord }) =>
|
shouldBeRegistered: ({ selectedRecord }) =>
|
||||||
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||||
availableOn: [
|
availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||||
ActionViewType.SHOW_PAGE,
|
|
||||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
|
||||||
],
|
|
||||||
component: <ExportMultipleRecordsAction />,
|
component: <ExportMultipleRecordsAction />,
|
||||||
},
|
},
|
||||||
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||||
|
type: ActionType.Standard,
|
||||||
|
scope: ActionScope.RecordSelection,
|
||||||
|
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||||
|
label: msg`Export`,
|
||||||
|
shortLabel: msg`Export`,
|
||||||
|
position: 4,
|
||||||
|
Icon: IconFileExport,
|
||||||
|
accent: 'default',
|
||||||
|
isPinned: false,
|
||||||
|
shouldBeRegistered: ({ selectedRecord }) =>
|
||||||
|
isDefined(selectedRecord) && !selectedRecord.isRemote,
|
||||||
|
availableOn: [ActionViewType.SHOW_PAGE],
|
||||||
|
component: <ExportSingleRecordAction />,
|
||||||
|
},
|
||||||
[MultipleRecordsActionKeys.EXPORT]: {
|
[MultipleRecordsActionKeys.EXPORT]: {
|
||||||
type: ActionType.Standard,
|
type: ActionType.Standard,
|
||||||
scope: ActionScope.RecordSelection,
|
scope: ActionScope.RecordSelection,
|
||||||
|
|||||||
@ -222,7 +222,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
|||||||
SingleRecordActionKeys.DELETE,
|
SingleRecordActionKeys.DELETE,
|
||||||
SingleRecordActionKeys.DESTROY,
|
SingleRecordActionKeys.DESTROY,
|
||||||
SingleRecordActionKeys.RESTORE,
|
SingleRecordActionKeys.RESTORE,
|
||||||
SingleRecordActionKeys.EXPORT,
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||||
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||||
MultipleRecordsActionKeys.DELETE,
|
MultipleRecordsActionKeys.DELETE,
|
||||||
MultipleRecordsActionKeys.DESTROY,
|
MultipleRecordsActionKeys.DESTROY,
|
||||||
MultipleRecordsActionKeys.RESTORE,
|
MultipleRecordsActionKeys.RESTORE,
|
||||||
@ -259,14 +260,18 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
|||||||
position: 12,
|
position: 12,
|
||||||
label: msg`Permanently destroy workflow`,
|
label: msg`Permanently destroy workflow`,
|
||||||
},
|
},
|
||||||
[SingleRecordActionKeys.EXPORT]: {
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||||
position: 13,
|
position: 13,
|
||||||
label: msg`Export workflow`,
|
label: msg`Export workflow`,
|
||||||
shouldBeRegistered: ({ selectedRecord }) =>
|
shouldBeRegistered: ({ selectedRecord }) =>
|
||||||
!isDefined(selectedRecord?.deletedAt),
|
!isDefined(selectedRecord?.deletedAt),
|
||||||
},
|
},
|
||||||
[MultipleRecordsActionKeys.EXPORT]: {
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||||
position: 14,
|
position: 14,
|
||||||
|
label: msg`Export workflow`,
|
||||||
|
},
|
||||||
|
[MultipleRecordsActionKeys.EXPORT]: {
|
||||||
|
position: 15,
|
||||||
label: msg`Export workflows`,
|
label: msg`Export workflows`,
|
||||||
},
|
},
|
||||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||||
|
|||||||
@ -51,7 +51,8 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
|||||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||||
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||||
SingleRecordActionKeys.EXPORT,
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||||
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||||
MultipleRecordsActionKeys.EXPORT,
|
MultipleRecordsActionKeys.EXPORT,
|
||||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||||
@ -73,7 +74,11 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
|||||||
isPinned: false,
|
isPinned: false,
|
||||||
position: 3,
|
position: 3,
|
||||||
},
|
},
|
||||||
[SingleRecordActionKeys.EXPORT]: {
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||||
|
position: 4,
|
||||||
|
label: msg`Export run`,
|
||||||
|
},
|
||||||
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||||
position: 4,
|
position: 4,
|
||||||
label: msg`Export run`,
|
label: msg`Export run`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -118,7 +118,8 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
|||||||
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||||
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||||
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||||
SingleRecordActionKeys.EXPORT,
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||||
|
SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
|
||||||
MultipleRecordsActionKeys.EXPORT,
|
MultipleRecordsActionKeys.EXPORT,
|
||||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||||
@ -140,7 +141,11 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
|||||||
position: 6,
|
position: 6,
|
||||||
isPinned: false,
|
isPinned: false,
|
||||||
},
|
},
|
||||||
[SingleRecordActionKeys.EXPORT]: {
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
|
||||||
|
position: 7,
|
||||||
|
label: msg`Export version`,
|
||||||
|
},
|
||||||
|
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
|
||||||
position: 7,
|
position: 7,
|
||||||
label: msg`Export version`,
|
label: msg`Export version`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Action } from '@/action-menu/actions/components/Action';
|
import { Action } from '@/action-menu/actions/components/Action';
|
||||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||||
import { useExportRecords } from '@/object-record/record-index/export/hooks/useExportRecords';
|
import { useRecordIndexExportRecords } from '@/object-record/record-index/export/hooks/useRecordIndexExportRecords';
|
||||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export const ExportMultipleRecordsAction = () => {
|
|||||||
throw new Error('Current view ID is not defined');
|
throw new Error('Current view ID is not defined');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { download } = useExportRecords({
|
const { download } = useRecordIndexExportRecords({
|
||||||
delayMs: 100,
|
delayMs: 100,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordIndexId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
recordIndexId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||||
|
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||||
|
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||||
|
import { useExportSingleRecord } from '@/object-record/record-show/hooks/useExportSingleRecord';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
|
||||||
|
import { Action } from '@/action-menu/actions/components/Action';
|
||||||
|
|
||||||
|
export const ExportSingleRecordAction = () => {
|
||||||
|
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||||
|
|
||||||
|
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||||
|
contextStoreCurrentViewIdComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!contextStoreCurrentViewId) {
|
||||||
|
throw new Error('Current view ID is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordId = useSelectedRecordIdOrThrow();
|
||||||
|
|
||||||
|
const filename = `${objectMetadataItem.nameSingular}.csv`;
|
||||||
|
const { download } = useExportSingleRecord({
|
||||||
|
filename,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Action onClick={download} />;
|
||||||
|
};
|
||||||
@ -6,6 +6,7 @@ export enum SingleRecordActionKeys {
|
|||||||
NAVIGATE_TO_NEXT_RECORD = 'navigate-to-next-record-single-record',
|
NAVIGATE_TO_NEXT_RECORD = 'navigate-to-next-record-single-record',
|
||||||
NAVIGATE_TO_PREVIOUS_RECORD = 'navigate-to-previous-record-single-record',
|
NAVIGATE_TO_PREVIOUS_RECORD = 'navigate-to-previous-record-single-record',
|
||||||
EXPORT_NOTE_TO_PDF = 'export-note-to-pdf-single-record',
|
EXPORT_NOTE_TO_PDF = 'export-note-to-pdf-single-record',
|
||||||
EXPORT = 'export-single-record',
|
EXPORT_FROM_RECORD_INDEX = 'export-from-record-index-single-record',
|
||||||
|
EXPORT_FROM_RECORD_SHOW = 'export-from-record-show-single-record',
|
||||||
RESTORE = 'restore-single-record',
|
RESTORE = 'restore-single-record',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export const createMockActionMenuActions = ({
|
|||||||
{
|
{
|
||||||
type: ActionType.Standard,
|
type: ActionType.Standard,
|
||||||
scope: ActionScope.RecordSelection,
|
scope: ActionScope.RecordSelection,
|
||||||
key: SingleRecordActionKeys.EXPORT,
|
key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
|
||||||
label: msg`Export`,
|
label: msg`Export`,
|
||||||
shortLabel: msg`Export`,
|
shortLabel: msg`Export`,
|
||||||
position: 4,
|
position: 4,
|
||||||
@ -53,10 +53,7 @@ export const createMockActionMenuActions = ({
|
|||||||
accent: 'default',
|
accent: 'default',
|
||||||
isPinned: false,
|
isPinned: false,
|
||||||
shouldBeRegistered: () => true,
|
shouldBeRegistered: () => true,
|
||||||
availableOn: [
|
availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
|
||||||
ActionViewType.SHOW_PAGE,
|
|
||||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
|
||||||
],
|
|
||||||
component: <Action onClick={exportMock} />,
|
component: <Action onClick={exportMock} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -29,9 +29,14 @@ describe('computeContextStoreFilters', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(filters).toEqual({
|
expect(filters).toEqual({
|
||||||
id: {
|
and: [
|
||||||
in: ['1', '2', '3'],
|
{
|
||||||
},
|
id: {
|
||||||
|
in: ['1', '2', '3'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -34,19 +34,21 @@ export const computeContextStoreFilters = (
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (contextStoreTargetedRecordsRule.mode === 'selection') {
|
if (contextStoreTargetedRecordsRule.mode === 'selection') {
|
||||||
queryFilter =
|
queryFilter = makeAndFilterVariables([
|
||||||
contextStoreTargetedRecordsRule.selectedRecordIds.length > 0
|
contextStoreTargetedRecordsRule.selectedRecordIds.length > 0
|
||||||
? {
|
? {
|
||||||
id: {
|
id: {
|
||||||
in: contextStoreTargetedRecordsRule.selectedRecordIds,
|
in: contextStoreTargetedRecordsRule.selectedRecordIds,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: computeRecordGqlOperationFilter({
|
: undefined,
|
||||||
filterValueDependencies,
|
computeRecordGqlOperationFilter({
|
||||||
fields: objectMetadataItem?.fields ?? [],
|
filterValueDependencies,
|
||||||
recordFilters: contextStoreFilters,
|
fields: objectMetadataItem?.fields ?? [],
|
||||||
recordFilterGroups: [],
|
recordFilters: contextStoreFilters,
|
||||||
});
|
recordFilterGroups: [],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryFilter;
|
return queryFilter;
|
||||||
|
|||||||
@ -2,7 +2,10 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
|||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
|
|
||||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||||
import { displayedExportProgress, generateCsv } from '../useExportRecords';
|
import {
|
||||||
|
displayedExportProgress,
|
||||||
|
generateCsv,
|
||||||
|
} from '../useRecordIndexExportRecords';
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
@ -3,8 +3,8 @@ import { act } from 'react';
|
|||||||
import {
|
import {
|
||||||
percentage,
|
percentage,
|
||||||
sleep,
|
sleep,
|
||||||
useExportFetchRecords,
|
useRecordIndexLazyFetchRecords,
|
||||||
} from '../useExportFetchRecords';
|
} from '../useRecordIndexLazyFetchRecords';
|
||||||
|
|
||||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||||
@ -107,7 +107,7 @@ describe('useRecordData', () => {
|
|||||||
|
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useExportFetchRecords({
|
useRecordIndexLazyFetchRecords({
|
||||||
recordIndexId,
|
recordIndexId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
pageSize: 30,
|
pageSize: 30,
|
||||||
@ -134,7 +134,7 @@ describe('useRecordData', () => {
|
|||||||
mockFetchAllRecords.mockReturnValue([mockPerson]);
|
mockFetchAllRecords.mockReturnValue([mockPerson]);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
useExportFetchRecords({
|
useRecordIndexLazyFetchRecords({
|
||||||
recordIndexId,
|
recordIndexId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
callback,
|
callback,
|
||||||
@ -167,7 +167,7 @@ describe('useRecordData', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableData: useExportFetchRecords({
|
tableData: useRecordIndexLazyFetchRecords({
|
||||||
recordIndexId,
|
recordIndexId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
callback,
|
callback,
|
||||||
@ -260,7 +260,7 @@ describe('useRecordData', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableData: useExportFetchRecords({
|
tableData: useRecordIndexLazyFetchRecords({
|
||||||
recordIndexId,
|
recordIndexId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
callback,
|
callback,
|
||||||
@ -7,8 +7,8 @@ import { useExportProcessRecordsForCSV } from '@/object-record/object-options-dr
|
|||||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import {
|
import {
|
||||||
UseRecordDataOptions,
|
UseRecordDataOptions,
|
||||||
useExportFetchRecords,
|
useRecordIndexLazyFetchRecords,
|
||||||
} from '@/object-record/record-index/export/hooks/useExportFetchRecords';
|
} from '@/object-record/record-index/export/hooks/useRecordIndexLazyFetchRecords';
|
||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { COMPOSITE_FIELD_SUB_FIELD_LABELS } from '@/settings/data-model/constants/CompositeFieldSubFieldLabel';
|
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';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
type GenerateExportOptions = {
|
type GenerateExportOptions = {
|
||||||
columns: ColumnDefinition<FieldMetadata>[];
|
columns: Pick<
|
||||||
|
ColumnDefinition<FieldMetadata>,
|
||||||
|
'size' | 'label' | 'type' | 'metadata'
|
||||||
|
>[];
|
||||||
rows: Record<string, any>[];
|
rows: Record<string, any>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,7 +124,7 @@ type UseExportTableDataOptions = Omit<UseRecordDataOptions, 'callback'> & {
|
|||||||
filename: string;
|
filename: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useExportRecords = ({
|
export const useRecordIndexExportRecords = ({
|
||||||
delayMs,
|
delayMs,
|
||||||
filename,
|
filename,
|
||||||
maximumRequests = 100,
|
maximumRequests = 100,
|
||||||
@ -144,7 +147,7 @@ export const useExportRecords = ({
|
|||||||
[filename, processRecordsForCSVExport],
|
[filename, processRecordsForCSVExport],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { getTableData: download, progress } = useExportFetchRecords({
|
const { getTableData: download, progress } = useRecordIndexLazyFetchRecords({
|
||||||
delayMs,
|
delayMs,
|
||||||
maximumRequests,
|
maximumRequests,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
@ -36,7 +36,7 @@ export type UseRecordDataOptions = {
|
|||||||
viewType?: ViewType;
|
viewType?: ViewType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useExportFetchRecords = ({
|
export const useRecordIndexLazyFetchRecords = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
delayMs,
|
delayMs,
|
||||||
maximumRequests = 100,
|
maximumRequests = 100,
|
||||||
@ -74,6 +74,10 @@ export const useExportFetchRecords = ({
|
|||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
|
|
||||||
|
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||||
|
objectMetadataItem.nameSingular,
|
||||||
|
);
|
||||||
|
|
||||||
const queryFilter = computeContextStoreFilters(
|
const queryFilter = computeContextStoreFilters(
|
||||||
contextStoreTargetedRecordsRule,
|
contextStoreTargetedRecordsRule,
|
||||||
contextStoreFilters,
|
contextStoreFilters,
|
||||||
@ -81,10 +85,6 @@ export const useExportFetchRecords = ({
|
|||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
);
|
);
|
||||||
|
|
||||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
|
||||||
objectMetadataItem.nameSingular,
|
|
||||||
);
|
|
||||||
|
|
||||||
const finalColumns = [
|
const finalColumns = [
|
||||||
...columns,
|
...columns,
|
||||||
...(hiddenKanbanFieldColumn && viewType === ViewType.Kanban
|
...(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