Fix record board export not taking filters into account (#8505)

Fix Export CSV action not taking into account the filters applied on the
Kanban index view
This commit is contained in:
Charles Bochet
2024-11-14 23:28:53 +01:00
committed by Charles Bochet
parent f023d55784
commit 43dce43ab8
11 changed files with 110 additions and 94 deletions

View File

@@ -1,13 +1,13 @@
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import {
displayedExportProgress,
useExportRecordData,
} from '@/action-menu/hooks/useExportRecordData';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { IconDatabaseExport } from 'twenty-ui'; import { IconDatabaseExport } from 'twenty-ui';
import {
displayedExportProgress,
useExportRecords,
} from '@/object-record/record-index/export/hooks/useExportRecords';
import { useEffect } from 'react'; import { useEffect } from 'react';
export const ExportRecordsActionEffect = ({ export const ExportRecordsActionEffect = ({
@@ -22,7 +22,7 @@ export const ExportRecordsActionEffect = ({
contextStoreNumberOfSelectedRecordsComponentState, contextStoreNumberOfSelectedRecordsComponentState,
); );
const { progress, download } = useExportRecordData({ const { progress, download } = useExportRecords({
delayMs: 100, delayMs: 100,
objectMetadataItem, objectMetadataItem,
recordIndexId: objectMetadataItem.namePlural, recordIndexId: objectMetadataItem.namePlural,

View File

@@ -26,6 +26,7 @@ import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActio
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState';
import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect';
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@@ -207,7 +208,7 @@ export const RecordIndexContainer = () => {
viewBarId={recordIndexId} viewBarId={recordIndexId}
/> />
</SpreadsheetImportProvider> </SpreadsheetImportProvider>
<RecordIndexFiltersToContextStoreEffect />
{recordIndexViewType === ViewType.Table && ( {recordIndexViewType === ViewType.Table && (
<> <>
<RecordIndexTableContainer <RecordIndexTableContainer

View File

@@ -0,0 +1,76 @@
import { useContext, useEffect } from 'react';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector';
import { unselectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useRecoilValue } from 'recoil';
export const RecordIndexFiltersToContextStoreEffect = () => {
const { recordIndexId } = useContext(RecordIndexRootPropsContext);
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
const setContextStoreTargetedRecords = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
const hasUserSelectedAllRows = useRecoilComponentValueV2(
hasUserSelectedAllRowsComponentState,
recordIndexId,
);
const selectedRowIds = useRecoilComponentValueV2(
selectedRowIdsComponentSelector,
recordIndexId,
);
const unselectedRowIds = useRecoilComponentValueV2(
unselectedRowIdsComponentSelector,
recordIndexId,
);
useEffect(() => {
if (hasUserSelectedAllRows) {
setContextStoreTargetedRecords({
mode: 'exclusion',
excludedRecordIds: unselectedRowIds,
});
} else {
setContextStoreTargetedRecords({
mode: 'selection',
selectedRecordIds: selectedRowIds,
});
}
return () => {
setContextStoreTargetedRecords({
mode: 'selection',
selectedRecordIds: [],
});
};
}, [
hasUserSelectedAllRows,
selectedRowIds,
setContextStoreTargetedRecords,
unselectedRowIds,
]);
const setContextStoreFilters = useSetRecoilComponentStateV2(
contextStoreFiltersComponentState,
);
useEffect(() => {
setContextStoreFilters(recordIndexFilters);
return () => {
setContextStoreFilters([]);
};
}, [recordIndexFilters, setContextStoreFilters]);
return <></>;
};

View File

@@ -1,21 +1,12 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useHandleToggleColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleColumnFilter'; import { useHandleToggleColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleColumnFilter';
import { useHandleToggleColumnSort } from '@/object-record/record-index/hooks/useHandleToggleColumnSort'; import { useHandleToggleColumnSort } from '@/object-record/record-index/hooks/useHandleToggleColumnSort';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector';
import { unselectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView';
import { useRecoilValue } from 'recoil';
export const RecordIndexTableContainerEffect = () => { export const RecordIndexTableContainerEffect = () => {
const { recordIndexId, objectNameSingular } = useContext( const { recordIndexId, objectNameSingular } = useContext(
@@ -77,61 +68,5 @@ export const RecordIndexTableContainerEffect = () => {
); );
}, [setRecordCountInCurrentView, setOnEntityCountChange]); }, [setRecordCountInCurrentView, setOnEntityCountChange]);
const setContextStoreTargetedRecords = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
const hasUserSelectedAllRows = useRecoilComponentValueV2(
hasUserSelectedAllRowsComponentState,
recordIndexId,
);
const selectedRowIds = useRecoilComponentValueV2(
selectedRowIdsComponentSelector,
recordIndexId,
);
const unselectedRowIds = useRecoilComponentValueV2(
unselectedRowIdsComponentSelector,
recordIndexId,
);
const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
useEffect(() => {
if (hasUserSelectedAllRows) {
setContextStoreTargetedRecords({
mode: 'exclusion',
excludedRecordIds: unselectedRowIds,
});
} else {
setContextStoreTargetedRecords({
mode: 'selection',
selectedRecordIds: selectedRowIds,
});
}
return () => {
setContextStoreTargetedRecords({
mode: 'selection',
selectedRecordIds: [],
});
};
}, [
hasUserSelectedAllRows,
selectedRowIds,
setContextStoreTargetedRecords,
unselectedRowIds,
]);
const setContextStoreFilters = useSetRecoilComponentStateV2(
contextStoreFiltersComponentState,
);
useEffect(() => {
setContextStoreFilters(recordIndexFilters);
return () => {
setContextStoreFilters([]);
};
}, [recordIndexFilters, setContextStoreFilters]);
return <></>; return <></>;
}; };

View File

@@ -1,6 +1,10 @@
import { renderHook, waitFor } from '@testing-library/react'; import { renderHook, waitFor } from '@testing-library/react';
import { act } from 'react'; import { act } from 'react';
import { percentage, sleep, useRecordData } from '../useRecordData'; import {
percentage,
sleep,
useExportFetchRecords,
} from '../useExportFetchRecords';
import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments'; import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
@@ -9,6 +13,7 @@ import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/opti
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { MockedResponse } from '@apollo/client/testing'; import { MockedResponse } from '@apollo/client/testing';
import { expect } from '@storybook/test';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper'; import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
@@ -178,7 +183,7 @@ describe('useRecordData', () => {
const { result } = renderHook( const { result } = renderHook(
() => () =>
useRecordData({ useExportFetchRecords({
recordIndexId, recordIndexId,
objectMetadataItem, objectMetadataItem,
pageSize: 30, pageSize: 30,
@@ -204,7 +209,7 @@ describe('useRecordData', () => {
const callback = jest.fn(); const callback = jest.fn();
const { result } = renderHook( const { result } = renderHook(
() => () =>
useRecordData({ useExportFetchRecords({
recordIndexId, recordIndexId,
objectMetadataItem, objectMetadataItem,
callback, callback,
@@ -232,7 +237,7 @@ describe('useRecordData', () => {
recordIndexId, recordIndexId,
); );
return { return {
tableData: useRecordData({ tableData: useExportFetchRecords({
recordIndexId, recordIndexId,
objectMetadataItem, objectMetadataItem,
callback, callback,
@@ -325,7 +330,7 @@ describe('useRecordData', () => {
recordIndexId, recordIndexId,
); );
return { return {
tableData: useRecordData({ tableData: useExportFetchRecords({
recordIndexId, recordIndexId,
objectMetadataItem, objectMetadataItem,
callback, callback,

View File

@@ -7,7 +7,7 @@ import {
displayedExportProgress, displayedExportProgress,
download, download,
generateCsv, generateCsv,
} from '../useExportRecordData'; } from '../useExportRecords';
jest.useFakeTimers(); jest.useFakeTimers();

View File

@@ -45,7 +45,7 @@ type ExportProgress = {
displayType: 'percentage' | 'number'; displayType: 'percentage' | 'number';
}; };
export const useRecordData = ({ export const useExportFetchRecords = ({
objectMetadataItem, objectMetadataItem,
delayMs, delayMs,
maximumRequests = 100, maximumRequests = 100,

View File

@@ -5,7 +5,7 @@ import { isDefined } from 'twenty-ui';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { convertCurrencyMicrosToCurrencyAmount } from '~/utils/convertCurrencyToCurrencyMicros'; import { convertCurrencyMicrosToCurrencyAmount } from '~/utils/convertCurrencyToCurrencyMicros';
export const useProcessRecordsForCSVExport = (objectNameSingular: string) => { export const useExportProcessRecordsForCSV = (objectNameSingular: string) => {
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,
}); });

View File

@@ -2,13 +2,12 @@ import { json2csv } from 'json-2-csv';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/record-index/options/constants/ExportTableDataDefaultPageSize';
import { useProcessRecordsForCSVExport } from '@/object-record/record-index/options/hooks/useProcessRecordsForCSVExport';
import { import {
UseRecordDataOptions, UseRecordDataOptions,
useRecordData, useExportFetchRecords,
} from '@/object-record/record-index/options/hooks/useRecordData'; } from '@/object-record/record-index/export/hooks/useExportFetchRecords';
import { useExportProcessRecordsForCSV } from '@/object-record/record-index/export/hooks/useExportProcessRecordsForCSV';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/record-index/options/constants/ExportTableDataDefaultPageSize';
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 { RelationDefinitionType } from '~/generated-metadata/graphql'; import { RelationDefinitionType } from '~/generated-metadata/graphql';
@@ -142,7 +141,7 @@ type UseExportTableDataOptions = Omit<UseRecordDataOptions, 'callback'> & {
filename: string; filename: string;
}; };
export const useExportRecordData = ({ export const useExportRecords = ({
delayMs, delayMs,
filename, filename,
maximumRequests = 100, maximumRequests = 100,
@@ -151,7 +150,7 @@ export const useExportRecordData = ({
recordIndexId, recordIndexId,
viewType, viewType,
}: UseExportTableDataOptions) => { }: UseExportTableDataOptions) => {
const { processRecordsForCSVExport } = useProcessRecordsForCSVExport( const { processRecordsForCSVExport } = useExportProcessRecordsForCSV(
objectMetadataItem.nameSingular, objectMetadataItem.nameSingular,
); );
@@ -165,7 +164,7 @@ export const useExportRecordData = ({
[filename, processRecordsForCSVExport], [filename, processRecordsForCSVExport],
); );
const { getTableData: download, progress } = useRecordData({ const { getTableData: download, progress } = useExportFetchRecords({
delayMs, delayMs,
maximumRequests, maximumRequests,
objectMetadataItem, objectMetadataItem,

View File

@@ -20,15 +20,15 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter'; import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId'; import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
import {
displayedExportProgress,
useExportRecordData,
} from '@/action-menu/hooks/useExportRecordData';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder'; import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import {
displayedExportProgress,
useExportRecords,
} from '@/object-record/record-index/export/hooks/useExportRecords';
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard'; import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable'; import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope'; import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
@@ -167,7 +167,7 @@ export const RecordIndexOptionsDropdownContent = ({
const { openObjectRecordsSpreasheetImportDialog } = const { openObjectRecordsSpreasheetImportDialog } =
useOpenObjectRecordsSpreasheetImportDialog(objectMetadataItem.nameSingular); useOpenObjectRecordsSpreasheetImportDialog(objectMetadataItem.nameSingular);
const { progress, download } = useExportRecordData({ const { progress, download } = useExportRecords({
delayMs: 100, delayMs: 100,
filename: `${objectMetadataItem.nameSingular}.csv`, filename: `${objectMetadataItem.nameSingular}.csv`,
objectMetadataItem, objectMetadataItem,

View File

@@ -40,8 +40,8 @@ export * from './icon/components/IllustrationIconToggle';
export * from './icon/components/IllustrationIconUid'; export * from './icon/components/IllustrationIconUid';
export * from './icon/components/IllustrationIconUser'; export * from './icon/components/IllustrationIconUser';
export * from './icon/components/IllustrationIconWrapper'; export * from './icon/components/IllustrationIconWrapper';
export * from './icon/components/llustrationIconLink';
export * from './icon/components/TablerIcons'; export * from './icon/components/TablerIcons';
export * from './icon/components/llustrationIconLink';
export * from './icon/hooks/useIcons'; export * from './icon/hooks/useIcons';
export * from './icon/providers/IconsProvider'; export * from './icon/providers/IconsProvider';
export * from './icon/states/iconsState'; export * from './icon/states/iconsState';