Seed core views in dev + fixes (#14071)
Seed core views in dev --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@@ -8,16 +8,25 @@ import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { getAppPath } from '~/utils/navigation/getAppPath';
|
||||
|
||||
export const useDefaultHomePagePath = () => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlagsMap = useFeatureFlagsMap();
|
||||
|
||||
const isCoreViewEnabled =
|
||||
featureFlagsMap[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const {
|
||||
activeNonSystemObjectMetadataItems,
|
||||
alphaSortedActiveNonSystemObjectMetadataItems,
|
||||
@@ -54,14 +63,19 @@ export const useDefaultHomePagePath = () => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const views = snapshot.getLoadable(prefetchViewsState).getValue();
|
||||
const views = isCoreViewEnabled
|
||||
? snapshot
|
||||
.getLoadable(coreViewsState)
|
||||
.getValue()
|
||||
.map(convertCoreViewToView)
|
||||
: snapshot.getLoadable(prefetchViewsState).getValue();
|
||||
|
||||
return views.find(
|
||||
(view) => view.objectMetadataId === objectMetadataItemId,
|
||||
);
|
||||
};
|
||||
},
|
||||
[arePrefetchViewsLoaded],
|
||||
[arePrefetchViewsLoaded, isCoreViewEnabled],
|
||||
);
|
||||
|
||||
const firstObjectPathInfo = useMemo<ObjectPathInfo | null>(() => {
|
||||
|
||||
+16
-1
@@ -1,7 +1,11 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const prefetchIndexViewIdFromObjectMetadataItemFamilySelector =
|
||||
selectorFamily<string | undefined, { objectMetadataItemId: string }>({
|
||||
@@ -9,9 +13,20 @@ export const prefetchIndexViewIdFromObjectMetadataItemFamilySelector =
|
||||
get:
|
||||
({ objectMetadataItemId }) =>
|
||||
({ get }) => {
|
||||
const prefetchedViews = get(prefetchViewsState);
|
||||
const coreViews = get(coreViewsState);
|
||||
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const featureFlags =
|
||||
extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
|
||||
const isCoreViewEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const views = isCoreViewEnabled
|
||||
? coreViews.map(convertCoreViewToView)
|
||||
: prefetchedViews;
|
||||
|
||||
return views?.find(
|
||||
(view) =>
|
||||
view.objectMetadataId === objectMetadataItemId &&
|
||||
|
||||
+15
-1
@@ -1,7 +1,11 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const prefetchViewFromViewIdFamilySelector = selectorFamily<
|
||||
View | undefined,
|
||||
@@ -11,9 +15,19 @@ export const prefetchViewFromViewIdFamilySelector = selectorFamily<
|
||||
get:
|
||||
({ viewId }) =>
|
||||
({ get }) => {
|
||||
const prefetchedViews = get(prefetchViewsState);
|
||||
const coreViews = get(coreViewsState);
|
||||
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const featureFlags = extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
|
||||
const isCoreViewEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const views = isCoreViewEnabled
|
||||
? coreViews.map(convertCoreViewToView)
|
||||
: prefetchedViews;
|
||||
|
||||
return views?.find((view) => view.id === viewId);
|
||||
},
|
||||
});
|
||||
|
||||
+16
-1
@@ -1,6 +1,10 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const prefetchViewIdsFromObjectMetadataItemFamilySelector =
|
||||
selectorFamily<string[], { objectMetadataItemId: string }>({
|
||||
@@ -8,9 +12,20 @@ export const prefetchViewIdsFromObjectMetadataItemFamilySelector =
|
||||
get:
|
||||
({ objectMetadataItemId }) =>
|
||||
({ get }) => {
|
||||
const prefetchedViews = get(prefetchViewsState);
|
||||
const coreViews = get(coreViewsState);
|
||||
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const featureFlags =
|
||||
extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
|
||||
const isCoreViewEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const views = isCoreViewEnabled
|
||||
? coreViews.map(convertCoreViewToView)
|
||||
: prefetchedViews;
|
||||
|
||||
return views
|
||||
.filter((view) => view.objectMetadataId === objectMetadataItemId)
|
||||
.map((view) => view.id);
|
||||
|
||||
+14
-1
@@ -1,13 +1,26 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { selector } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const prefetchViewLengthSelector = selector<number>({
|
||||
key: 'prefetchViewLengthSelector',
|
||||
get: ({ get }) => {
|
||||
const prefetchedViews = get(prefetchViewsState);
|
||||
const coreViews = get(coreViewsState);
|
||||
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const featureFlags = extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const views = isCoreViewEnabled
|
||||
? coreViews.map(convertCoreViewToView)
|
||||
: prefetchedViews;
|
||||
|
||||
return views?.length ?? 0;
|
||||
},
|
||||
});
|
||||
|
||||
+14
-1
@@ -1,7 +1,11 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const prefetchViewsFromObjectMetadataItemFamilySelector = selectorFamily<
|
||||
View[],
|
||||
@@ -11,9 +15,18 @@ export const prefetchViewsFromObjectMetadataItemFamilySelector = selectorFamily<
|
||||
get:
|
||||
({ objectMetadataItemId }) =>
|
||||
({ get }) => {
|
||||
const prefetchedViews = get(prefetchViewsState);
|
||||
const coreViews = get(coreViewsState);
|
||||
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const featureFlags = extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
|
||||
const isCoreViewEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const views = isCoreViewEnabled
|
||||
? coreViews.map(convertCoreViewToView)
|
||||
: prefetchedViews;
|
||||
|
||||
const filteredViews = views.filter(
|
||||
(view) => view.objectMetadataId === objectMetadataItemId,
|
||||
|
||||
@@ -14,6 +14,7 @@ import { detectTimeFormat } from '@/localization/utils/detectTimeFormat';
|
||||
import { detectTimeZone } from '@/localization/utils/detectTimeZone';
|
||||
import { getDateFormatFromWorkspaceDateFormat } from '@/localization/utils/getDateFormatFromWorkspaceDateFormat';
|
||||
import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTimeFormatFromWorkspaceTimeFormat';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { SOURCE_LOCALE, type APP_LOCALES } from 'twenty-shared/translations';
|
||||
@@ -38,6 +39,7 @@ export const useLoadCurrentUser = () => {
|
||||
);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setDateTimeFormat = useSetRecoilState(dateTimeFormatState);
|
||||
const setCoreViews = useSetRecoilState(coreViewsState);
|
||||
|
||||
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
|
||||
|
||||
@@ -128,6 +130,10 @@ export const useLoadCurrentUser = () => {
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(workspace) && isDefined(workspace.views)) {
|
||||
setCoreViews(workspace.views);
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
workspaceMember,
|
||||
@@ -137,6 +143,7 @@ export const useLoadCurrentUser = () => {
|
||||
getCurrentUser,
|
||||
isOnAWorkspace,
|
||||
setAvailableWorkspaces,
|
||||
setCoreViews,
|
||||
setCurrentUser,
|
||||
setCurrentUserWorkspace,
|
||||
setCurrentWorkspace,
|
||||
|
||||
+2
@@ -52,6 +52,7 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
|
||||
displayValue: 'test',
|
||||
viewFilterGroupId: 'group-1',
|
||||
positionInViewFilterGroup: 0,
|
||||
subFieldName: null,
|
||||
};
|
||||
|
||||
const mockCoreViewFilter: Omit<CoreViewFilter, 'workspaceId'> = {
|
||||
@@ -65,6 +66,7 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
|
||||
viewId: allCompaniesCoreView.id,
|
||||
positionInViewFilterGroup: 0,
|
||||
viewFilterGroupId: 'group-1',
|
||||
subFieldName: null,
|
||||
};
|
||||
|
||||
const mockView = {
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { extractFeatureFlagMapFromWorkspace } from '@/workspace/utils/extractFeatureFlagMapFromWorkspace';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
|
||||
import { type FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useFeatureFlagsMap = (): Record<FeatureFlagKey, boolean> => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const currentWorkspaceFeatureFlags = currentWorkspace?.featureFlags;
|
||||
|
||||
const initialFeatureFlags = buildRecordFromKeysWithSameValue(
|
||||
Object.values(FeatureFlagKey),
|
||||
false,
|
||||
);
|
||||
|
||||
if (!currentWorkspaceFeatureFlags) {
|
||||
return initialFeatureFlags;
|
||||
}
|
||||
|
||||
return currentWorkspaceFeatureFlags.reduce((acc, featureFlag) => {
|
||||
acc[featureFlag.key] = featureFlag.value;
|
||||
return acc;
|
||||
}, initialFeatureFlags);
|
||||
return extractFeatureFlagMapFromWorkspace(currentWorkspace);
|
||||
};
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { type CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
|
||||
|
||||
export const extractFeatureFlagMapFromWorkspace = (
|
||||
currentWorkspace: CurrentWorkspace | null,
|
||||
): Record<FeatureFlagKey, boolean> => {
|
||||
const currentWorkspaceFeatureFlags = currentWorkspace?.featureFlags;
|
||||
|
||||
const initialFeatureFlags = buildRecordFromKeysWithSameValue(
|
||||
Object.values(FeatureFlagKey),
|
||||
false,
|
||||
);
|
||||
|
||||
if (!currentWorkspaceFeatureFlags) {
|
||||
return initialFeatureFlags;
|
||||
}
|
||||
|
||||
return currentWorkspaceFeatureFlags.reduce((acc, featureFlag) => {
|
||||
acc[featureFlag.key] = featureFlag.value;
|
||||
return acc;
|
||||
}, initialFeatureFlags);
|
||||
};
|
||||
+1
-1
@@ -58,7 +58,7 @@ export const seedFeatureFlags = async (
|
||||
{
|
||||
key: FeatureFlagKey.IS_CORE_VIEW_ENABLED,
|
||||
workspaceId: workspaceId,
|
||||
value: false,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
|
||||
+20
@@ -1,5 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { type DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
@@ -16,12 +19,14 @@ import { ROCKET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seed
|
||||
import { SURVEY_RESULT_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/survey-results-object-seed.constant';
|
||||
import { type FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
|
||||
import { type ObjectMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/object-metadata-seed.type';
|
||||
import { prefillCoreViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-core-views';
|
||||
|
||||
@Injectable()
|
||||
export class DevSeederMetadataService {
|
||||
constructor(
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly fieldMetadataService: FieldMetadataService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
) {}
|
||||
|
||||
private readonly workspaceConfigs: Record<
|
||||
@@ -97,6 +102,8 @@ export class DevSeederMetadataService {
|
||||
fieldMetadataSeeds: fieldConfig.seeds,
|
||||
});
|
||||
}
|
||||
|
||||
await this.seedCoreViews(workspaceId);
|
||||
}
|
||||
|
||||
private async seedCustomObject({
|
||||
@@ -143,4 +150,17 @@ export class DevSeederMetadataService {
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
private async seedCoreViews(workspaceId: string): Promise<void> {
|
||||
const mainDataSource = this.typeORMService.getMainDataSource();
|
||||
|
||||
if (!isDefined(mainDataSource)) {
|
||||
throw new Error('Could not connect to main data source');
|
||||
}
|
||||
|
||||
const createdObjectMetadata =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||
|
||||
await prefillCoreViews(mainDataSource, workspaceId, createdObjectMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-13
@@ -13,7 +13,6 @@ import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ViewDefinition } from 'src/engine/workspace-manager/standard-objects-prefill-data/types/view-definition.interface';
|
||||
import { companiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view';
|
||||
import { customAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/custom-all.view';
|
||||
import { notesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view';
|
||||
import { opportunitiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view';
|
||||
import { opportunitiesTableByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-table-by-stage.view';
|
||||
@@ -24,6 +23,7 @@ import { tasksByStatusView } from 'src/engine/workspace-manager/standard-objects
|
||||
import { workflowRunsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view';
|
||||
import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view';
|
||||
import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view';
|
||||
import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
import { convertViewFilterOperandToCoreOperand } from 'src/modules/view/utils/convert-view-filter-operand-to-core-operand.util';
|
||||
|
||||
export const prefillCoreViews = async (
|
||||
@@ -31,14 +31,6 @@ export const prefillCoreViews = async (
|
||||
workspaceId: string,
|
||||
objectMetadataItems: ObjectMetadataEntity[],
|
||||
): Promise<View[]> => {
|
||||
const customObjectMetadataItems = objectMetadataItems.filter(
|
||||
(item) => item.isCustom,
|
||||
);
|
||||
|
||||
const customViews = customObjectMetadataItems.map((item) =>
|
||||
customAllView(item, true),
|
||||
);
|
||||
|
||||
const views = [
|
||||
companiesAllView(objectMetadataItems, true),
|
||||
peopleAllView(objectMetadataItems, true),
|
||||
@@ -51,7 +43,6 @@ export const prefillCoreViews = async (
|
||||
workflowsAllView(objectMetadataItems, true),
|
||||
workflowVersionsAllView(objectMetadataItems, true),
|
||||
workflowRunsAllView(objectMetadataItems, true),
|
||||
...customViews,
|
||||
];
|
||||
|
||||
const queryRunner = dataSource.createQueryRunner();
|
||||
@@ -110,9 +101,9 @@ const createCoreViews = async (
|
||||
isCompact: false,
|
||||
isCustom: isCustom ?? false,
|
||||
openRecordIn:
|
||||
openRecordIn === 'SIDE_PANEL'
|
||||
? ViewOpenRecordIn.SIDE_PANEL
|
||||
: ViewOpenRecordIn.RECORD_PAGE,
|
||||
openRecordIn === ViewOpenRecordInType.RECORD_PAGE
|
||||
? ViewOpenRecordIn.RECORD_PAGE
|
||||
: ViewOpenRecordIn.SIDE_PANEL,
|
||||
kanbanAggregateOperation,
|
||||
kanbanAggregateOperationFieldMetadataId,
|
||||
workspaceId,
|
||||
|
||||
Reference in New Issue
Block a user