From 2bc7974da97db772e63385d72948df7633518133 Mon Sep 17 00:00:00 2001 From: Ngan Phan Date: Mon, 7 Oct 2024 04:06:51 -0700 Subject: [PATCH 1/4] fix: Improve Usability of Adding Options via Return Key for Multi-Select Field (#7450) Fixes #6602 This is the approach that I followed based on these comments https://github.com/twentyhq/twenty/issues/6602#issuecomment-2356870311, https://github.com/twentyhq/twenty/issues/6602#issuecomment-2330737907 - Create forward ref in `` component - Create ref to select text in parent component `` and pass it to `` --------- Co-authored-by: Lucas Bordeau --- .../SettingsDataModelFieldSelectForm.tsx | 8 +------- ...SettingsDataModelFieldSelectFormOptionRow.tsx | 7 ++++--- .../SettingsServerlessFunctionNewForm.tsx | 10 +++++----- .../modules/ui/input/components/TextInput.tsx | 16 ++++++++++++---- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx index 9d3c97628f2e..128aab7ab6fd 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx @@ -1,6 +1,5 @@ import styled from '@emotion/styled'; import { DropResult } from '@hello-pangea/dnd'; -import { useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { IconPlus } from 'twenty-ui'; import { z } from 'zod'; @@ -79,7 +78,6 @@ const StyledButton = styled(LightButton)` export const SettingsDataModelFieldSelectForm = ({ fieldMetadataItem, }: SettingsDataModelFieldSelectFormProps) => { - const [focusedOptionId, setFocusedOptionId] = useState(''); const { initialDefaultValue, initialOptions } = useSelectSettingsFormInitialValues({ fieldMetadataItem }); @@ -190,10 +188,6 @@ export const SettingsDataModelFieldSelectForm = ({ const newOptions = getOptionsWithNewOption(); setFormValue('options', newOptions); - - const lastOptionId = newOptions[newOptions.length - 1].id; - - setFocusedOptionId(lastOptionId); }; return ( @@ -227,7 +221,7 @@ export const SettingsDataModelFieldSelectForm = ({ { const nextOptions = toSpliced( options, diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx index 121aa15e0e07..d9be3fff7e0e 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx @@ -33,7 +33,7 @@ type SettingsDataModelFieldSelectFormOptionRowProps = { onRemoveAsDefault?: () => void; onInputEnter?: () => void; option: FieldMetadataItemOption; - focused?: boolean; + isNewRow?: boolean; }; const StyledRow = styled.div` @@ -67,7 +67,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({ onRemoveAsDefault, onInputEnter, option, - focused, + isNewRow, }: SettingsDataModelFieldSelectFormOptionRowProps) => { const theme = useTheme(); @@ -129,10 +129,11 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({ value: getOptionValueFromLabel(label), }) } - focused={focused} RightIcon={isDefault ? IconCheck : undefined} maxLength={OPTION_VALUE_MAXIMUM_LENGTH} onInputEnter={handleInputEnter} + autoFocusOnMount={isNewRow} + autoSelectOnMount={isNewRow} /> diff --git a/packages/twenty-front/src/modules/ui/input/components/TextInput.tsx b/packages/twenty-front/src/modules/ui/input/components/TextInput.tsx index b0841eaa710f..8e31f598822d 100644 --- a/packages/twenty-front/src/modules/ui/input/components/TextInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/TextInput.tsx @@ -14,7 +14,8 @@ export type TextInputProps = TextInputV2ComponentProps & { disableHotkeys?: boolean; onInputEnter?: () => void; dataTestId?: string; - focused?: boolean; + autoFocusOnMount?: boolean; + autoSelectOnMount?: boolean; }; export const TextInput = ({ @@ -22,7 +23,8 @@ export const TextInput = ({ onBlur, onInputEnter, disableHotkeys = false, - focused, + autoFocusOnMount, + autoSelectOnMount, dataTestId, ...props }: TextInputProps) => { @@ -31,11 +33,17 @@ export const TextInput = ({ const [isFocused, setIsFocused] = useState(false); useEffect(() => { - if (focused === true) { + if (autoFocusOnMount === true) { inputRef.current?.focus(); setIsFocused(true); } - }, [focused]); + }, [autoFocusOnMount]); + + useEffect(() => { + if (autoSelectOnMount === true) { + inputRef.current?.select(); + } + }, [autoSelectOnMount]); const { goBackToPreviousHotkeyScope, From ce676f699d621bd7dddd520567fdbaa311a0c4c3 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Mon, 7 Oct 2024 13:45:29 +0200 Subject: [PATCH 2/4] Add opened section (#7265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When object is not part of the workspace favorite list, we want to show it in the "opened section" while its record page is accessed. This PR: - adds a new component `NavigationDrawerOpenedSection` - makes workflow versions and runs not system object + creates a prefilled view index for these - do not create workspace favorites for these so these do not appear in the workspace section Capture d’écran 2024-09-26 à 11 45 25 --- .../components/WorkspaceFavorites.tsx | 27 ++------- .../components/MainNavigationDrawerItems.tsx | 3 + ...ObjectMetadataItemsInWorkspaceFavorites.ts | 35 ++++++++++++ .../NavigationDrawerOpenedSection.tsx | 57 +++++++++++++++++++ .../useIsMenuNavbarDisplayed.test.tsx | 40 ------------- .../layout/hooks/useIsMenuNavbarDisplayed.ts | 6 -- .../data-seed-dev-workspace.command.ts | 10 +++- .../utils/should-seed-workspace-favorite.ts | 9 +++ .../demo-objects-prefill-data.ts | 3 +- .../standard-objects-prefill-data.ts | 10 +++- .../standard-objects-prefill-data/view.ts | 24 +++++--- .../views/companies-all.view.ts | 2 +- .../views/notes-all.view.ts | 2 +- .../views/opportunities-all.view.ts | 2 +- .../views/opportunity-by-stage.view.ts | 2 +- .../views/people-all.view.ts | 2 +- .../views/tasks-all.view.ts | 2 +- .../views/tasks-by-status.view.ts | 2 +- .../views/workflow-runs-all.view.ts | 56 ++++++++++++++++++ .../views/workflow-versions-all.view.ts | 56 ++++++++++++++++++ .../views/workflows-all.view.ts | 2 +- .../constants/standard-field-ids.ts | 3 + .../workflow-run.workspace-entity.ts | 29 ++++++++-- .../workflow-version.workspace-entity.ts | 19 ++++++- .../workflow.workspace-entity.ts | 1 + 25 files changed, 310 insertions(+), 94 deletions(-) create mode 100644 packages/twenty-front/src/modules/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites.ts create mode 100644 packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerOpenedSection.tsx delete mode 100644 packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useIsMenuNavbarDisplayed.test.tsx delete mode 100644 packages/twenty-front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts create mode 100644 packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view.ts diff --git a/packages/twenty-front/src/modules/favorites/components/WorkspaceFavorites.tsx b/packages/twenty-front/src/modules/favorites/components/WorkspaceFavorites.tsx index b89290526c98..cf106211405b 100644 --- a/packages/twenty-front/src/modules/favorites/components/WorkspaceFavorites.tsx +++ b/packages/twenty-front/src/modules/favorites/components/WorkspaceFavorites.tsx @@ -1,7 +1,6 @@ -import { useFavorites } from '@/favorites/hooks/useFavorites'; +import { useFilteredObjectMetadataItemsForWorkspaceFavorites } from '@/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites'; import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems'; import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader'; -import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; @@ -9,34 +8,18 @@ import { View } from '@/views/types/View'; export const WorkspaceFavorites = () => { const { records: views } = usePrefetchedData(PrefetchKey.AllViews); - const loading = useIsPrefetchLoading(); - - const { workspaceFavorites } = useFavorites(); - - const workspaceFavoriteIds = new Set( - workspaceFavorites.map((favorite) => favorite.recordId), - ); - const favoriteViewObjectMetadataIds = views.reduce((acc, view) => { - if (workspaceFavoriteIds.has(view.id)) { - acc.push(view.objectMetadataId); - } - return acc; - }, []); - - const { objectMetadataItems } = useFilteredObjectMetadataItems(); - - const objectMetadataItemsToDisplay = objectMetadataItems.filter((item) => - favoriteViewObjectMetadataIds.includes(item.id), - ); + const { activeObjectMetadataItems: objectMetadataItemsToDisplay } = + useFilteredObjectMetadataItemsForWorkspaceFavorites(); + const loading = useIsPrefetchLoading(); if (loading) { return ; } return ( { )} + {isWorkspaceFavoriteEnabled && } + {isWorkspaceFavoriteEnabled ? ( diff --git a/packages/twenty-front/src/modules/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites.ts b/packages/twenty-front/src/modules/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites.ts new file mode 100644 index 000000000000..1c8abefe350e --- /dev/null +++ b/packages/twenty-front/src/modules/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites.ts @@ -0,0 +1,35 @@ +import { useFavorites } from '@/favorites/hooks/useFavorites'; +import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; +import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; +import { View } from '@/views/types/View'; + +export const useFilteredObjectMetadataItemsForWorkspaceFavorites = () => { + const { records: views } = usePrefetchedData(PrefetchKey.AllViews); + + const { workspaceFavorites } = useFavorites(); + + const workspaceFavoriteIds = new Set( + workspaceFavorites.map((favorite) => favorite.recordId), + ); + + const favoriteViewObjectMetadataIds = new Set( + views.reduce((acc, view) => { + if (workspaceFavoriteIds.has(view.id)) { + acc.push(view.objectMetadataId); + } + return acc; + }, []), + ); + + const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); + + const activeObjectMetadataItemsInWorkspaceFavorites = + activeObjectMetadataItems.filter((item) => + favoriteViewObjectMetadataIds.has(item.id), + ); + + return { + activeObjectMetadataItems: activeObjectMetadataItemsInWorkspaceFavorites, + }; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerOpenedSection.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerOpenedSection.tsx new file mode 100644 index 000000000000..fb17b643078f --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerOpenedSection.tsx @@ -0,0 +1,57 @@ +import { useParams } from 'react-router-dom'; + +import { useFilteredObjectMetadataItemsForWorkspaceFavorites } from '@/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites'; +import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems'; +import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader'; +import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading'; +import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; +import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; +import { View } from '@/views/types/View'; + +export const NavigationDrawerOpenedSection = () => { + const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); + const filteredActiveObjectMetadataItems = activeObjectMetadataItems.filter( + (item) => !item.isRemote, + ); + + const { records: views } = usePrefetchedData(PrefetchKey.AllViews); + const loading = useIsPrefetchLoading(); + + const currentObjectNamePlural = useParams().objectNamePlural; + + const { activeObjectMetadataItems: workspaceFavoritesObjectMetadataItems } = + useFilteredObjectMetadataItemsForWorkspaceFavorites(); + + if (!currentObjectNamePlural) { + return; + } + + const objectMetadataItem = filteredActiveObjectMetadataItems.find( + (item) => item.namePlural === currentObjectNamePlural, + ); + + if (!objectMetadataItem) { + return; + } + + const shouldDisplayObjectInOpenedSection = + !workspaceFavoritesObjectMetadataItems + .map((item) => item.id) + .includes(objectMetadataItem.id); + + if (loading) { + return ; + } + + return ( + shouldDisplayObjectInOpenedSection && ( + + ) + ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useIsMenuNavbarDisplayed.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useIsMenuNavbarDisplayed.test.tsx deleted file mode 100644 index 990ff743bbf6..000000000000 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useIsMenuNavbarDisplayed.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as reactRouterDom from 'react-router-dom'; - -import { useIsMenuNavbarDisplayed } from '../useIsMenuNavbarDisplayed'; - -jest.mock('react-router-dom', () => ({ - useLocation: jest.fn(), -})); - -const setupMockLocation = (pathname: string) => { - jest.spyOn(reactRouterDom, 'useLocation').mockReturnValueOnce({ - pathname, - state: undefined, - key: '', - search: '', - hash: '', - }); -}; - -describe('useIsMenuNavbarDisplayed', () => { - it('Should return true for paths starting with "/companies"', () => { - setupMockLocation('/companies'); - - const result = useIsMenuNavbarDisplayed(); - expect(result).toBeTruthy(); - }); - - it('Should return true for paths starting with "/companies/"', () => { - setupMockLocation('/companies/test-some-subpath'); - - const result = useIsMenuNavbarDisplayed(); - expect(result).toBeTruthy(); - }); - - it('Should return false for paths not starting with "/companies"', () => { - setupMockLocation('/test-path'); - - const result = useIsMenuNavbarDisplayed(); - expect(result).toBeFalsy(); - }); -}); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts deleted file mode 100644 index 08f6103f310d..000000000000 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useLocation } from 'react-router-dom'; - -export const useIsMenuNavbarDisplayed = () => { - const currentPath = useLocation().pathname; - return currentPath.match(/^\/companies(\/.*)?$/) !== null; -}; diff --git a/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts b/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts index 50113a1c95b4..5f212a025a1d 100644 --- a/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts +++ b/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts @@ -40,6 +40,7 @@ import { DataSourceService } from 'src/engine/metadata-modules/data-source/data- import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; +import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { viewPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/view'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; @@ -219,7 +220,14 @@ export class DataSeedWorkspaceCommand extends CommandRunner { await seedWorkspaceFavorites( viewDefinitionsWithId - .filter((view) => view.key === 'INDEX') + .filter( + (view) => + view.key === 'INDEX' && + shouldSeedWorkspaceFavorite( + view.objectMetadataId, + objectMetadataMap, + ), + ) .map((view) => view.id), entityManager, dataSourceMetadata.schema, diff --git a/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts new file mode 100644 index 000000000000..ae7d9b6ff9f3 --- /dev/null +++ b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts @@ -0,0 +1,9 @@ +import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; + +export const shouldSeedWorkspaceFavorite = ( + objectMetadataId, + objectMetadataMap, +): boolean => + objectMetadataId !== + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].id && + objectMetadataId !== objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].id; diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data.ts index 226e5557c82b..73b36137db03 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data.ts @@ -2,6 +2,7 @@ import { DataSource, EntityManager } from 'typeorm'; import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { companyPrefillDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/company'; import { opportunityPrefillDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/opportunity'; import { personPrefillDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/person'; @@ -42,7 +43,7 @@ export const demoObjectsPrefillData = async ( await seedWorkspaceFavorites( viewDefinitionsWithId - .filter((view) => view.key === 'INDEX') + .filter((view) => view.key === 'INDEX' && shouldSeedWorkspaceFavorite(view.objectMetadataId, objectMetadataMap)) .map((view) => view.id), entityManager, schemaName, diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts index 7198fe4ef1e6..9584474f4377 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts @@ -2,6 +2,7 @@ import { DataSource, EntityManager } from 'typeorm'; import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { companyPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/company'; import { personPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/person'; import { viewPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/view'; @@ -45,7 +46,14 @@ export const standardObjectsPrefillData = async ( await seedWorkspaceFavorites( viewDefinitionsWithId - .filter((view) => view.key === 'INDEX') + .filter( + (view) => + view.key === 'INDEX' && + shouldSeedWorkspaceFavorite( + view.objectMetadataId, + objectMetadataMap, + ), + ) .map((view) => view.id), entityManager, schemaName, diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/view.ts index a4f230eaca32..3b7e47b80d90 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/view.ts @@ -9,6 +9,8 @@ import { opportunitiesByStageView } from 'src/engine/workspace-manager/standard- import { peopleAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view'; import { tasksAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view'; import { tasksByStatusView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view'; +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'; export const viewPrefillData = async ( @@ -18,14 +20,20 @@ export const viewPrefillData = async ( isWorkflowEnabled: boolean, ) => { const viewDefinitions = [ - await companiesAllView(objectMetadataMap), - await peopleAllView(objectMetadataMap), - await opportunitiesAllView(objectMetadataMap), - await opportunitiesByStageView(objectMetadataMap), - await notesAllView(objectMetadataMap), - await tasksAllView(objectMetadataMap), - await tasksByStatusView(objectMetadataMap), - ...(isWorkflowEnabled ? [await workflowsAllView(objectMetadataMap)] : []), + companiesAllView(objectMetadataMap), + peopleAllView(objectMetadataMap), + opportunitiesAllView(objectMetadataMap), + opportunitiesByStageView(objectMetadataMap), + notesAllView(objectMetadataMap), + tasksAllView(objectMetadataMap), + tasksByStatusView(objectMetadataMap), + ...(isWorkflowEnabled + ? [ + workflowsAllView(objectMetadataMap), + workflowVersionsAllView(objectMetadataMap), + workflowRunsAllView(objectMetadataMap), + ] + : []), ]; const viewDefinitionsWithId = viewDefinitions.map((viewDefinition) => ({ diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view.ts index ff26eab35631..73aad80dbba6 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view.ts @@ -5,7 +5,7 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const companiesAllView = async ( +export const companiesAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view.ts index 97354aa1f2ff..fec9f63b0ed0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view.ts @@ -5,7 +5,7 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const notesAllView = async ( +export const notesAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view.ts index 4dfad89ad4a7..b3a57bf4de61 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view.ts @@ -2,7 +2,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const opportunitiesAllView = async ( +export const opportunitiesAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view.ts index c67a5945b814..82d3b479122c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view.ts @@ -2,7 +2,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const opportunitiesByStageView = async ( +export const opportunitiesByStageView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view.ts index 9fbddef9109b..53492e7b9dda 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/people-all.view.ts @@ -5,7 +5,7 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const peopleAllView = async ( +export const peopleAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view.ts index 3d9a0ffadf71..29ed1ce476f8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-all.view.ts @@ -5,7 +5,7 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const tasksAllView = async ( +export const tasksAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view.ts index fe392c181305..304b3ed0112e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-by-status.view.ts @@ -5,7 +5,7 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const tasksByStatusView = async ( +export const tasksByStatusView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view.ts new file mode 100644 index 000000000000..d7bc3775d9c3 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view.ts @@ -0,0 +1,56 @@ +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; +import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; + +export const workflowRunsAllView = ( + objectMetadataMap: Record, +) => { + return { + name: 'All Workflow Runs', + objectMetadataId: objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].id, + type: 'table', + key: 'INDEX', + position: 0, + icon: 'IconHistory', + kanbanFieldMetadataId: '', + filters: [], + fields: [ + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].fields[ + WORKFLOW_RUN_STANDARD_FIELD_IDS.name + ], + position: 0, + isVisible: true, + size: 210, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].fields[ + WORKFLOW_RUN_STANDARD_FIELD_IDS.status + ], + position: 1, + isVisible: true, + size: 150, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].fields[ + WORKFLOW_RUN_STANDARD_FIELD_IDS.startedAt + ], + position: 2, + isVisible: true, + size: 150, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].fields[ + WORKFLOW_RUN_STANDARD_FIELD_IDS.endedAt + ], + position: 3, + isVisible: true, + size: 150, + }, + ], + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view.ts new file mode 100644 index 000000000000..fff64745d579 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view.ts @@ -0,0 +1,56 @@ +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WORKFLOW_VERSION_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; +import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; + +export const workflowVersionsAllView = ( + objectMetadataMap: Record, +) => { + return { + name: 'All Workflow Versions', + objectMetadataId: objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].id, + type: 'table', + key: 'INDEX', + position: 0, + icon: 'IconVersions', + kanbanFieldMetadataId: '', + filters: [], + fields: [ + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].fields[ + WORKFLOW_VERSION_STANDARD_FIELD_IDS.name + ], + position: 0, + isVisible: true, + size: 210, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].fields[ + WORKFLOW_VERSION_STANDARD_FIELD_IDS.status + ], + position: 1, + isVisible: true, + size: 150, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].fields[ + WORKFLOW_VERSION_STANDARD_FIELD_IDS.trigger + ], + position: 2, + isVisible: true, + size: 150, + }, + { + fieldMetadataId: + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].fields[ + WORKFLOW_VERSION_STANDARD_FIELD_IDS.steps + ], + position: 3, + isVisible: true, + size: 150, + }, + ], + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view.ts index 8eba4bdc4442..330d68523529 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view.ts @@ -2,7 +2,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { WORKFLOW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -export const workflowsAllView = async ( +export const workflowsAllView = ( objectMetadataMap: Record, ) => { return { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts index d9359e6eb595..6243016b8f73 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts @@ -412,11 +412,13 @@ export const WORKFLOW_STANDARD_FIELD_IDS = { }; export const WORKFLOW_RUN_STANDARD_FIELD_IDS = { + name: '20202020-b840-4253-aef9-4e5013694587', workflowVersion: '20202020-2f52-4ba8-8dc4-d0d6adb9578d', workflow: '20202020-8c57-4e7f-84f5-f373f68e1b82', startedAt: '20202020-a234-4e2d-bd15-85bcea6bb183', endedAt: '20202020-e1c1-4b6b-bbbd-b2beaf2e159e', status: '20202020-6b3e-4f9c-8c2b-2e5b8e6d6f3b', + position: '20202020-7802-4c40-ae89-1f506fe3365c', createdBy: '20202020-6007-401a-8aa5-e6f38581a6f3', output: '20202020-7be4-4db2-8ac6-3ff0d740843d', }; @@ -426,6 +428,7 @@ export const WORKFLOW_VERSION_STANDARD_FIELD_IDS = { workflow: '20202020-afa3-46c3-91b0-0631ca6aa1c8', trigger: '20202020-4eae-43e7-86e0-212b41a30b48', status: '20202020-5a34-440e-8a25-39d8c3d1d4cf', + position: '20202020-791d-4950-ab28-0e704767ae1c', runs: '20202020-1d08-46df-901a-85045f18099a', steps: '20202020-5988-4a64-b94a-1f9b7b989039', }; diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts index 388d5f1293c9..347536f57a82 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts @@ -41,15 +41,25 @@ export type WorkflowRunOutput = { @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.workflowRun, namePlural: 'workflowRuns', - labelSingular: 'workflowRun', - labelPlural: 'WorkflowRuns', + labelSingular: 'Workflow Run', + labelPlural: 'Workflow Runs', description: 'A workflow run', + labelIdentifierStandardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.name, + icon: 'IconHistory', }) @WorkspaceGate({ featureFlag: FeatureFlagKey.IsWorkflowEnabled, }) -@WorkspaceIsSystem() export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity { + @WorkspaceField({ + standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.name, + type: FieldMetadataType.TEXT, + label: 'Name', + description: 'Name of the workflow run', + icon: 'IconText', + }) + name: string; + @WorkspaceField({ standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.startedAt, type: FieldMetadataType.DATE_TIME, @@ -75,7 +85,7 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.SELECT, label: 'Workflow run status', description: 'Workflow run status', - icon: 'IconHistory', + icon: 'IconStatusChange', options: [ { value: WorkflowRunStatus.NOT_STARTED, @@ -128,6 +138,17 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() output: WorkflowRunOutput | null; + @WorkspaceField({ + standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.position, + type: FieldMetadataType.POSITION, + label: 'Position', + description: 'Workflow run position', + icon: 'IconHierarchy2', + }) + @WorkspaceIsSystem() + @WorkspaceIsNullable() + position: number | null; + // Relations @WorkspaceRelation({ standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.workflowVersion, diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts index 80545fe7897d..5ec2b4ade7da 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts @@ -61,8 +61,8 @@ const WorkflowVersionStatusOptions = [ @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.workflowVersion, namePlural: 'workflowVersions', - labelSingular: 'WorkflowVersion', - labelPlural: 'WorkflowVersions', + labelSingular: 'Workflow Version', + labelPlural: 'Workflow Versions', description: 'A workflow version', icon: 'IconVersions', labelIdentifierStandardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.name, @@ -70,7 +70,6 @@ const WorkflowVersionStatusOptions = [ @WorkspaceGate({ featureFlag: FeatureFlagKey.IsWorkflowEnabled, }) -@WorkspaceIsSystem() export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.name, @@ -86,6 +85,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.RAW_JSON, label: 'Version trigger', description: 'Json object to provide trigger', + icon: 'IconSettingsAutomation', }) @WorkspaceIsNullable() trigger: WorkflowTrigger | null; @@ -95,6 +95,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.RAW_JSON, label: 'Version steps', description: 'Json object to provide steps', + icon: 'IconSettingsAutomation', }) @WorkspaceIsNullable() steps: WorkflowStep[] | null; @@ -104,11 +105,23 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.SELECT, label: 'Version status', description: 'The workflow version status', + icon: 'IconStatusChange', options: WorkflowVersionStatusOptions, defaultValue: "'DRAFT'", }) status: WorkflowVersionStatus; + @WorkspaceField({ + standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.position, + type: FieldMetadataType.POSITION, + label: 'Position', + description: 'Workflow version position', + icon: 'IconHierarchy2', + }) + @WorkspaceIsSystem() + @WorkspaceIsNullable() + position: number | null; + // Relations @WorkspaceRelation({ standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.workflow, diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts index 98dd40417e35..167f449740ff 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts @@ -84,6 +84,7 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.MULTI_SELECT, label: 'Statuses', description: 'The current statuses of the workflow versions', + icon: 'IconStatusChange', options: WorkflowStatusOptions, }) @WorkspaceIsNullable() From b5d14868304416e0469fddca240de48bf822c569 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Mon, 7 Oct 2024 15:00:47 +0200 Subject: [PATCH 3/4] Fix currency input (#7469) Fix https://github.com/twentyhq/twenty/issues/7458 --- .../ui/field/display/components/CurrencyDisplay.tsx | 8 ++++---- .../modules/ui/field/input/components/CurrencyInput.tsx | 1 + .../src/engine/utils/should-seed-workspace-favorite.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx index 3fbb5960f2de..f2af5ae75803 100644 --- a/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx +++ b/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx @@ -5,6 +5,7 @@ import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMeta import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes'; import { formatAmount } from '~/utils/format/formatAmount'; import { isDefined } from '~/utils/isDefined'; +import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; type CurrencyDisplayProps = { currencyValue: FieldCurrencyValue | null | undefined; @@ -29,10 +30,9 @@ export const CurrencyDisplay = ({ currencyValue }: CurrencyDisplayProps) => { ? SETTINGS_FIELD_CURRENCY_CODES[currencyValue?.currencyCode]?.Icon : null; - const amountToDisplay = - currencyValue?.amountMicros != null - ? currencyValue?.amountMicros / 1000000 - : null; + const amountToDisplay = isUndefinedOrNull(currencyValue?.amountMicros) + ? null + : currencyValue?.amountMicros / 1000000; if (!shouldDisplayCurrency) { return {0}; diff --git a/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx index a25fe10cacc6..052f47954202 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx @@ -144,6 +144,7 @@ export const CurrencyInput = ({ placeholder={placeholder} autoFocus={autoFocus} value={value} + unmask /> ); diff --git a/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts index ae7d9b6ff9f3..9668a7a4eda4 100644 --- a/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts +++ b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts @@ -5,5 +5,5 @@ export const shouldSeedWorkspaceFavorite = ( objectMetadataMap, ): boolean => objectMetadataId !== - objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion].id && - objectMetadataId !== objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun].id; + objectMetadataMap[STANDARD_OBJECT_IDS.workflowVersion]?.id && + objectMetadataId !== objectMetadataMap[STANDARD_OBJECT_IDS.workflowRun]?.id; From 7bdbbcf72e7d54dce711dc664fec5dcfd0b66670 Mon Sep 17 00:00:00 2001 From: Weiko Date: Mon, 7 Oct 2024 15:20:45 +0200 Subject: [PATCH 4/4] Add delete name column from standard object tables (#7470) Following this https://github.com/twentyhq/twenty/pull/7428 we now need to fix existing workspaces thanks to this migration command. This command will fetch all standard objects and their fields then filter out tables that don't have that column OR objects that have an existing fieldMetadata "name" of type TEXT and delete the rest. --- ...e-column-standard-object-tables.command.ts | 121 ++++++++++++++++++ .../0-31/0-31-upgrade-version.command.ts | 7 + .../0-31/0-31-upgrade-version.module.ts | 2 + 3 files changed, 130 insertions(+) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command.ts new file mode 100644 index 000000000000..91576604e642 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command.ts @@ -0,0 +1,121 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; + +import { + ActiveWorkspacesCommandOptions, + ActiveWorkspacesCommandRunner, +} from 'src/database/commands/active-workspaces.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; + +@Command({ + name: 'upgrade-0.31:delete-name-column-standard-object-tables', + description: 'Delete name column from standard object tables', +}) +export class DeleteNameColumnStandardObjectTablesCommand extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + _passedParam: string[], + options: ActiveWorkspacesCommandOptions, + workspaceIds: string[], + ): Promise { + this.logger.log('Running command to fix migration'); + + for (const workspaceId of workspaceIds) { + this.logger.log(`Running command for workspace ${workspaceId}`); + + try { + this.logger.log( + chalk.green(`Deleting name columns from workspace ${workspaceId}.`), + ); + + const standardObjects = await this.objectMetadataRepository.find({ + where: { + isCustom: false, + workspaceId, + }, + relations: ['fields'], + }); + + const dataSource = + await this.twentyORMGlobalManager.getDataSourceForWorkspace( + workspaceId, + ); + + dataSource.transaction(async (entityManager) => { + const queryRunner = entityManager.queryRunner; + + for (const standardObject of standardObjects) { + if (options.dryRun) { + this.logger.log( + chalk.yellow( + `Dry run mode enabled. Skipping deletion of name column for workspace ${workspaceId} and table ${standardObject.nameSingular}.`, + ), + ); + continue; + } + + const nameColumnExists = await queryRunner?.hasColumn( + standardObject.nameSingular, + 'name', + ); + + const nameFieldMetadataExists = standardObject.fields.some( + (field) => + field.name === 'name' && field.type === FieldMetadataType.TEXT, + ); + + if (nameFieldMetadataExists) { + this.logger.log( + chalk.yellow( + `Name field exists for workspace ${workspaceId} and table ${standardObject.nameSingular}. Skipping deletion.`, + ), + ); + continue; + } + + if (!nameColumnExists) { + this.logger.log( + chalk.yellow( + `Name column does not exist for workspace ${workspaceId} and table ${standardObject.nameSingular}. Skipping deletion.`, + ), + ); + continue; + } + + await queryRunner?.dropColumn(standardObject.nameSingular, 'name'); + } + }); + } catch (error) { + this.logger.log( + chalk.red( + `Running command on workspace ${workspaceId} failed with error: ${error}`, + ), + ); + continue; + } finally { + this.logger.log( + chalk.green(`Finished running command for workspace ${workspaceId}.`), + ); + + await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( + workspaceId, + ); + } + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts index 1ab48a059db3..247e8cd4d20d 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts @@ -7,6 +7,7 @@ import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-work import { AddIndexKeyToTasksAndNotesViewsCommand } from 'src/database/commands/upgrade-version/0-31/0-31-add-index-key-to-tasks-and-notes-views.command'; import { BackfillWorkspaceFavoritesCommand } from 'src/database/commands/upgrade-version/0-31/0-31-backfill-workspace-favorites.command'; import { CleanViewsAssociatedWithOutdatedObjectsCommand } from 'src/database/commands/upgrade-version/0-31/0-31-clean-views-associated-with-outdated-objects.command'; +import { DeleteNameColumnStandardObjectTablesCommand } from 'src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; @@ -26,6 +27,7 @@ export class UpgradeTo0_31Command extends ActiveWorkspacesCommandRunner { private readonly backfillWorkspaceFavoritesCommand: BackfillWorkspaceFavoritesCommand, private readonly cleanViewsAssociatedWithOutdatedObjectsCommand: CleanViewsAssociatedWithOutdatedObjectsCommand, private readonly addIndexKeyToTasksAndNotesViewsCommand: AddIndexKeyToTasksAndNotesViewsCommand, + private readonly deleteNameColumnStandardObjectTablesCommand: DeleteNameColumnStandardObjectTablesCommand, ) { super(workspaceRepository); } @@ -58,5 +60,10 @@ export class UpgradeTo0_31Command extends ActiveWorkspacesCommandRunner { options, workspaceIds, ); + await this.deleteNameColumnStandardObjectTablesCommand.executeActiveWorkspacesCommand( + passedParam, + options, + workspaceIds, + ); } } diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts index d53dc237e96a..e1f731cb5ba4 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts @@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AddIndexKeyToTasksAndNotesViewsCommand } from 'src/database/commands/upgrade-version/0-31/0-31-add-index-key-to-tasks-and-notes-views.command'; import { BackfillWorkspaceFavoritesCommand } from 'src/database/commands/upgrade-version/0-31/0-31-backfill-workspace-favorites.command'; import { CleanViewsAssociatedWithOutdatedObjectsCommand } from 'src/database/commands/upgrade-version/0-31/0-31-clean-views-associated-with-outdated-objects.command'; +import { DeleteNameColumnStandardObjectTablesCommand } from 'src/database/commands/upgrade-version/0-31/0-31-delete-name-column-standard-object-tables.command'; import { UpgradeTo0_31Command } from 'src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; @@ -20,6 +21,7 @@ import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manage BackfillWorkspaceFavoritesCommand, CleanViewsAssociatedWithOutdatedObjectsCommand, AddIndexKeyToTasksAndNotesViewsCommand, + DeleteNameColumnStandardObjectTablesCommand, ], }) export class UpgradeTo0_31CommandModule {}