Fix export view missing column and viewGroups not persisted bug (#10584)

Fixes https://github.com/twentyhq/twenty/issues/10535
Also fixes missing columns in export view
This commit is contained in:
Charles Bochet
2025-02-28 15:59:13 +01:00
committed by GitHub
parent 122a6a7801
commit 00b650a121
20 changed files with 70 additions and 38 deletions

View File

@ -41,6 +41,7 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
componentInstanceId: '1',
contextStoreCurrentObjectMetadataNameSingular:
personMockObjectMetadataItem.nameSingular,
contextStoreCurrentViewId: 'my-view-id',
contextStoreTargetedRecordsRule: {
mode: 'selection',
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],

View File

@ -56,6 +56,7 @@ const getWrapper = (
componentInstanceId: '1',
contextStoreCurrentObjectMetadataNameSingular:
personMockObjectMetadataItem.nameSingular,
contextStoreCurrentViewId: 'my-view-id',
contextStoreTargetedRecordsRule: {
mode: 'selection',
selectedRecordIds: [firstPeopleMock.id, secondPeopleMock.id],

View File

@ -23,6 +23,7 @@ jest.mock('@/object-record/record-index/export/hooks/useExportRecords', () => ({
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
apolloMocks: [],
componentInstanceId: '1',
contextStoreCurrentViewId: 'my-view-id',
contextStoreCurrentObjectMetadataNameSingular:
personMockObjectMetadataItem.nameSingular,
contextStoreTargetedRecordsRule: {

View File

@ -1,4 +1,5 @@
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
@ -11,6 +12,7 @@ import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRec
import { useCheckIsSoftDeleteFilter } from '@/object-record/record-filter/hooks/useCheckIsSoftDeleteFilter';
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -22,10 +24,21 @@ export const useDeleteMultipleRecordsAction: ActionHookWithObjectMetadataItem =
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
useState(false);
const contextStoreCurrentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
);
if (!contextStoreCurrentViewId) {
throw new Error('Current view ID is not defined');
}
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();
const { resetTableRowSelection } = useRecordTable({
recordTableId: objectMetadataItem.namePlural,
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
objectMetadataItem.namePlural,
contextStoreCurrentViewId,
),
});
const { deleteManyRecords } = useDeleteManyRecords({

View File

@ -1,4 +1,5 @@
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
@ -12,6 +13,7 @@ import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRec
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -23,8 +25,19 @@ export const useDestroyMultipleRecordsAction: ActionHookWithObjectMetadataItem =
const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
useState(false);
const contextStoreCurrentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
);
if (!contextStoreCurrentViewId) {
throw new Error('Current view ID is not defined');
}
const { resetTableRowSelection } = useRecordTable({
recordTableId: objectMetadataItem.namePlural,
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
objectMetadataItem.namePlural,
contextStoreCurrentViewId,
),
});
const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission();

View File

@ -1,16 +1,30 @@
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useExportRecords } from '@/object-record/record-index/export/hooks/useExportRecords';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const useExportMultipleRecordsAction = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
const contextStoreCurrentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
);
if (!contextStoreCurrentViewId) {
throw new Error('Current view ID is not defined');
}
const { download } = useExportRecords({
delayMs: 100,
objectMetadataItem,
recordIndexId: objectMetadataItem.namePlural,
recordIndexId: getRecordIndexIdFromObjectNamePluralAndViewId(
objectMetadataItem.namePlural,
contextStoreCurrentViewId,
),
filename: `${objectMetadataItem.nameSingular}.csv`,
});

View File

@ -19,17 +19,16 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLingui } from '@lingui/react/macro';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { useLingui } from '@lingui/react/macro';
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const { t } = useLingui();
const {
viewType,
currentContentId,
recordIndexId,
objectMetadataItem,
onContentChange,
closeDropdown,
@ -49,7 +48,6 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({
viewBarId: recordIndexId,
viewType,
});

View File

@ -67,7 +67,6 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
handleVisibilityChange: handleRecordGroupVisibilityChange,
handleHideEmptyRecordGroupChange,
} = useRecordGroupVisibility({
viewBarId: recordIndexId,
viewType,
});

View File

@ -27,7 +27,7 @@ export const useRecordGroupActions = ({
const navigate = useNavigateSettings();
const location = useLocation();
const { objectNameSingular, recordIndexId } = useRecordIndexContextOrThrow();
const { objectNameSingular } = useRecordIndexContextOrThrow();
const { columnDefinition: recordGroupDefinition } = useContext(
RecordBoardColumnContext,
@ -43,7 +43,6 @@ export const useRecordGroupActions = ({
const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({
viewBarId: recordIndexId,
viewType,
});

View File

@ -29,7 +29,7 @@ export const useRecordGroupReorder = ({
visibleRecordGroupIdsComponentFamilySelector,
);
const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId);
const { saveViewGroups } = useSaveCurrentViewGroups();
const handleOrderChange: OnDragEndResponder = useRecoilCallback(
({ snapshot }) =>

View File

@ -8,12 +8,10 @@ import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefin
import { useRecoilCallback } from 'recoil';
type UseRecordGroupVisibilityParams = {
viewBarId: string;
viewType: ViewType;
};
export const useRecordGroupVisibility = ({
viewBarId,
viewType,
}: UseRecordGroupVisibilityParams) => {
const objectOptionsDropdownRecordGroupHideFamilyState =
@ -21,7 +19,7 @@ export const useRecordGroupVisibility = ({
recordIndexRecordGroupHideComponentFamilyState,
);
const { saveViewGroup } = useSaveCurrentViewGroups(viewBarId);
const { saveViewGroup } = useSaveCurrentViewGroups();
const handleVisibilityChange = useRecoilCallback(
({ set }) =>

View File

@ -6,6 +6,7 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
@ -14,7 +15,6 @@ import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewType } from '@/views/types/ViewType';
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));

View File

@ -10,12 +10,12 @@ import {
} from '@/object-record/record-index/export/hooks/useExportFetchRecords';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { t } from '@lingui/core/macro';
import { saveAs } from 'file-saver';
import { isDefined } from 'twenty-shared';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
import { FieldMetadataType } from '~/generated/graphql';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { t } from '@lingui/core/macro';
type GenerateExportOptions = {
columns: ColumnDefinition<FieldMetadata>[];

View File

@ -80,15 +80,10 @@ describe('useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups', () => {
wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({
apolloMocks: [],
componentInstanceId: 'instanceId',
contextStoreCurrentViewId: mockView.id,
contextStoreCurrentObjectMetadataNameSingular:
mockObjectMetadataItemNameSingular,
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: 'instanceId',
}),
mockView.id,
);
snapshot.set(prefetchViewsState, [mockView]);
},
}),

View File

@ -85,13 +85,8 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
componentInstanceId: 'instanceId',
contextStoreCurrentObjectMetadataNameSingular:
mockObjectMetadataItemNameSingular,
contextStoreCurrentViewId: mockView.id,
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: 'instanceId',
}),
mockView.id,
);
snapshot.set(prefetchViewsState, [mockView]);
},
}),

View File

@ -73,14 +73,8 @@ describe('useApplyCurrentViewSortsToCurrentRecordSorts', () => {
componentInstanceId: 'instanceId',
contextStoreCurrentObjectMetadataNameSingular:
mockObjectMetadataItemNameSingular,
contextStoreCurrentViewId: mockView.id,
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: 'instanceId',
}),
mockView.id,
);
snapshot.set(prefetchViewsState, [mockView]);
},
}),

View File

@ -9,7 +9,7 @@ import { isDefined } from 'twenty-shared';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => {
export const useSaveCurrentViewGroups = () => {
const { createViewGroupRecords, updateViewGroupRecords } =
usePersistViewGroupRecords();
@ -17,7 +17,6 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => {
const currentViewIdCallbackState = useRecoilComponentCallbackStateV2(
contextStoreCurrentViewIdComponentState,
viewBarComponentId,
);
const saveViewGroup = useRecoilCallback(
@ -31,7 +30,7 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => {
return;
}
const view = await getViewFromPrefetchState(currentViewId);
const view = getViewFromPrefetchState(currentViewId);
if (isUndefinedOrNull(view)) {
return;

View File

@ -1,6 +1,7 @@
import { PropsWithChildren, useEffect, useState } from 'react';
import { contextStoreCurrentObjectMetadataItemComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import {
@ -16,11 +17,13 @@ export type JestContextStoreSetterMocks = {
contextStoreNumberOfSelectedRecords?: number;
contextStoreFilters?: RecordFilter[];
contextStoreCurrentObjectMetadataNameSingular?: string;
contextStoreCurrentViewId?: string;
};
type JestContextStoreSetterProps =
PropsWithChildren<JestContextStoreSetterMocks>;
export const JestContextStoreSetter = ({
contextStoreCurrentViewId,
contextStoreTargetedRecordsRule = {
mode: 'selection',
selectedRecordIds: [],
@ -46,6 +49,10 @@ export const JestContextStoreSetter = ({
contextStoreFiltersComponentState,
);
const setContextStoreCurrentViewId = useSetRecoilComponentStateV2(
contextStoreCurrentViewIdComponentState,
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: contextStoreCurrentObjectMetadataNameSingular,
});
@ -54,6 +61,7 @@ export const JestContextStoreSetter = ({
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
setContextStoreCurrentViewId(contextStoreCurrentViewId);
setContextStoreTargetedRecordsRule(contextStoreTargetedRecordsRule);
setContextStoreCurrentObjectMetadataItem(objectMetadataItem);
setContextStoreNumberOfSelectedRecords(contextStoreNumberOfSelectedRecords);
@ -70,6 +78,8 @@ export const JestContextStoreSetter = ({
setcontextStoreFiltersComponentState,
contextStoreFilters,
objectMetadataItem,
setContextStoreCurrentViewId,
contextStoreCurrentViewId,
]);
return isLoaded ? <>{children}</> : null;

View File

@ -27,6 +27,7 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
apolloMocks,
onInitializeRecoilSnapshot,
contextStoreTargetedRecordsRule,
contextStoreCurrentViewId,
contextStoreNumberOfSelectedRecords,
contextStoreCurrentObjectMetadataNameSingular,
contextStoreFilters,
@ -81,6 +82,7 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
}}
>
<JestContextStoreSetter
contextStoreCurrentViewId={contextStoreCurrentViewId}
contextStoreFilters={contextStoreFilters}
contextStoreTargetedRecordsRule={
contextStoreTargetedRecordsRule

View File

@ -157,7 +157,7 @@ export class MigrateRichTextContentPatchCommand extends MaintainedWorkspacesMigr
return await this.featureFlagRepository.exists({
where: {
workspaceId,
key: FeatureFlagKey.IsRichTextV2Enabled,
key: 'IS_RICH_TEXT_V2_ENABLED' as FeatureFlagKey,
value: true,
},
});