diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilteredObjectMetadataItems.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilteredObjectMetadataItems.test.tsx index c42b94fbb..9bb8f0169 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilteredObjectMetadataItems.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useFilteredObjectMetadataItems.test.tsx @@ -57,6 +57,26 @@ describe('useFilteredObjectMetadataItems', () => { }); }); + it('should findObjectMetadataItemBySlug', async () => { + const { result } = renderHook( + () => { + const setMetadataItems = useSetRecoilState(objectMetadataItemsState); + setMetadataItems(mockObjectMetadataItems); + + return useFilteredObjectMetadataItems(); + }, + { + wrapper: Wrapper, + }, + ); + + act(() => { + const res = result.current.findObjectMetadataItemBySlug('people'); + expect(res).toBeDefined(); + expect(res?.namePlural).toBe('people'); + }); + }); + it('should findObjectMetadataItemById', async () => { const { result } = renderHook( () => { diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts index 2cf99ae12..61922bf55 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts @@ -27,6 +27,11 @@ export const useFilteredObjectMetadataItems = () => { ({ isActive, isSystem }) => !isActive && !isSystem, ); + const findObjectMetadataItemBySlug = (slug: string) => + objectMetadataItems.find( + (objectMetadataItem) => getObjectSlug(objectMetadataItem) === slug, + ); + const findActiveObjectMetadataItemBySlug = (slug: string) => activeObjectMetadataItems.find( (activeObjectMetadataItem) => @@ -50,6 +55,7 @@ export const useFilteredObjectMetadataItems = () => { findObjectMetadataItemByNamePlural, inactiveObjectMetadataItems, objectMetadataItems, + findObjectMetadataItemBySlug, alphaSortedActiveObjectMetadataItems, }; }; diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx index 6879667dc..a6b68c6b5 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx @@ -1,4 +1,10 @@ -import { IconArchiveOff, IconDotsVertical, IconTrash } from 'twenty-ui'; +import { + IconArchiveOff, + IconDotsVertical, + IconEye, + IconPencil, + IconTrash, +} from 'twenty-ui'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -12,6 +18,7 @@ type SettingsObjectFieldInactiveActionDropdownProps = { isCustomField?: boolean; fieldType?: FieldMetadataType; onActivate: () => void; + onEdit: () => void; onDelete: () => void; scopeKey: string; }; @@ -20,6 +27,7 @@ export const SettingsObjectFieldInactiveActionDropdown = ({ onActivate, scopeKey, onDelete, + onEdit, isCustomField, }: SettingsObjectFieldInactiveActionDropdownProps) => { const dropdownId = `${scopeKey}-settings-field-disabled-action-dropdown`; @@ -36,6 +44,11 @@ export const SettingsObjectFieldInactiveActionDropdown = ({ closeDropdown(); }; + const handleEdit = () => { + onEdit(); + closeDropdown(); + }; + const isDeletable = isCustomField; return ( @@ -47,6 +60,11 @@ export const SettingsObjectFieldInactiveActionDropdown = ({ dropdownComponents={ + navigate(linkToNavigate)} onActivate={() => activateMetadataField(fieldMetadataItem)} onDelete={() => deleteMetadataField(fieldMetadataItem)} /> diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx index 353beeee8..5e4769913 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx @@ -5,7 +5,12 @@ import pick from 'lodash.pick'; import { useEffect } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { useNavigate, useParams } from 'react-router-dom'; -import { H2Title, IconArchive, IconHierarchy2 } from 'twenty-ui'; +import { + H2Title, + IconArchive, + IconArchiveOff, + IconHierarchy2, +} from 'twenty-ui'; import { z } from 'zod'; import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem'; @@ -56,16 +61,15 @@ export const SettingsObjectFieldEdit = () => { const { enqueueSnackBar } = useSnackBar(); const { objectSlug = '', fieldSlug = '' } = useParams(); - const { findActiveObjectMetadataItemBySlug } = - useFilteredObjectMetadataItems(); + const { findObjectMetadataItemBySlug } = useFilteredObjectMetadataItems(); - const activeObjectMetadataItem = - findActiveObjectMetadataItemBySlug(objectSlug); + const objectMetadataItem = findObjectMetadataItemBySlug(objectSlug); - const { deactivateMetadataField } = useFieldMetadataItem(); - const activeMetadataField = activeObjectMetadataItem?.fields.find( - (metadataField) => - metadataField.isActive && getFieldSlug(metadataField) === fieldSlug, + const { deactivateMetadataField, activateMetadataField } = + useFieldMetadataItem(); + + const fieldMetadataItem = objectMetadataItem?.fields.find( + (fieldMetadataItem) => getFieldSlug(fieldMetadataItem) === fieldSlug, ); const getRelationMetadata = useGetRelationMetadata(); @@ -74,11 +78,11 @@ export const SettingsObjectFieldEdit = () => { const apolloClient = useApolloClient(); const { findManyRecordsQuery } = useFindManyRecordsQuery({ - objectNameSingular: activeObjectMetadataItem?.nameSingular || '', + objectNameSingular: objectMetadataItem?.nameSingular || '', }); const refetchRecords = async () => { - if (!activeObjectMetadataItem) return; + if (!objectMetadataItem) return; await apolloClient.query({ query: findManyRecordsQuery, fetchPolicy: 'network-only', @@ -89,27 +93,29 @@ export const SettingsObjectFieldEdit = () => { mode: 'onTouched', resolver: zodResolver(settingsFieldFormSchema()), values: { - icon: activeMetadataField?.icon ?? 'Icon123', - type: activeMetadataField?.type as SettingsSupportedFieldType, - label: activeMetadataField?.label ?? '', - description: activeMetadataField?.description, + icon: fieldMetadataItem?.icon ?? 'Icon', + type: fieldMetadataItem?.type as SettingsSupportedFieldType, + label: fieldMetadataItem?.label ?? '', + description: fieldMetadataItem?.description, }, }); useEffect(() => { - if (!activeObjectMetadataItem || !activeMetadataField) { + if (!objectMetadataItem || !fieldMetadataItem) { navigate(AppPath.NotFound); } - }, [activeMetadataField, activeObjectMetadataItem, navigate]); + }, [fieldMetadataItem, objectMetadataItem, navigate]); const { isDirty, isValid, isSubmitting } = formConfig.formState; const canSave = isDirty && isValid && !isSubmitting; - if (!activeObjectMetadataItem || !activeMetadataField) return null; + if (!isDefined(objectMetadataItem) || !isDefined(fieldMetadataItem)) { + return null; + } const isLabelIdentifier = isLabelIdentifierField({ - fieldMetadataItem: activeMetadataField, - objectMetadataItem: activeObjectMetadataItem, + fieldMetadataItem: fieldMetadataItem, + objectMetadataItem: objectMetadataItem, }); const handleSave = async ( @@ -125,7 +131,7 @@ export const SettingsObjectFieldEdit = () => { ) { const { relationFieldMetadataItem } = getRelationMetadata({ - fieldMetadataItem: activeMetadataField, + fieldMetadataItem: fieldMetadataItem, }) ?? {}; if (isDefined(relationFieldMetadataItem)) { @@ -145,7 +151,7 @@ export const SettingsObjectFieldEdit = () => { ); await updateOneFieldMetadataItem({ - fieldMetadataIdToUpdate: activeMetadataField.id, + fieldMetadataIdToUpdate: fieldMetadataItem.id, updatePayload: formattedInput, }); } @@ -161,12 +167,17 @@ export const SettingsObjectFieldEdit = () => { }; const handleDeactivate = async () => { - await deactivateMetadataField(activeMetadataField); + await deactivateMetadataField(fieldMetadataItem); + navigate(`/settings/objects/${objectSlug}`); + }; + + const handleActivate = async () => { + await activateMetadataField(fieldMetadataItem); navigate(`/settings/objects/${objectSlug}`); }; const shouldDisplaySaveAndCancel = - canPersistFieldMetadataItemUpdate(activeMetadataField); + canPersistFieldMetadataItemUpdate(fieldMetadataItem); return ( @@ -174,7 +185,7 @@ export const SettingsObjectFieldEdit = () => { { href: '/settings/objects', }, { - children: activeObjectMetadataItem.labelPlural, + children: objectMetadataItem.labelPlural, href: `/settings/objects/${objectSlug}`, }, { - children: activeMetadataField.label, + children: fieldMetadataItem.label, }, ]} actionButton={ @@ -210,8 +221,8 @@ export const SettingsObjectFieldEdit = () => { description="The name and icon of this field" /> @@ -219,8 +230,8 @@ export const SettingsObjectFieldEdit = () => {
@@ -229,8 +240,8 @@ export const SettingsObjectFieldEdit = () => { description="The description of this field" />
{!isLabelIdentifier && ( @@ -240,11 +251,17 @@ export const SettingsObjectFieldEdit = () => { description="Deactivate this field" />