import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField'; import { SettingsObjectFieldItemTableRow, StyledObjectFieldTableRow, } from '@/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow'; import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState'; import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader'; import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray'; import { type TableMetadata } from '@/ui/layout/table/types/TableMetadata'; import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import styled from '@emotion/styled'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useMemo, useState } from 'react'; import { useRecoilState } from 'recoil'; import { FieldMetadataType } from 'twenty-shared/types'; import { IconArchive, IconFilter, IconSearch, IconSettings, } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { MenuItemToggle } from 'twenty-ui/navigation'; import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem'; import { type SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem'; import { normalizeSearchText } from '~/utils/normalizeSearchText'; const StyledSearchAndFilterContainer = styled.div` display: flex; gap: ${({ theme }) => theme.spacing(2)}; padding-bottom: ${({ theme }) => theme.spacing(2)}; width: 100%; `; const StyledSearchInput = styled(SettingsTextInput)` flex: 1; `; const SETTINGS_OBJECT_FIELD_TABLE_METADATA: TableMetadata = { tableId: 'settingsObjectDetail', fields: [ { fieldLabel: msg`Name`, fieldName: 'label', fieldType: 'string', align: 'left', }, { fieldLabel: msg`App`, fieldName: 'fieldType', fieldType: 'string', align: 'left', }, { fieldLabel: msg`Data type`, fieldName: 'dataType', fieldType: 'string', align: 'left', }, ], initialSort: { fieldName: 'label', orderBy: 'AscNullsLast', }, }; export type SettingsObjectFieldTableProps = { objectMetadataItem: ObjectMetadataItem; mode: 'view' | 'new-field'; excludeRelations?: boolean; }; // TODO: find another way than using mode which feels like it could be replaced by another pattern export const SettingsObjectFieldTable = ({ objectMetadataItem, mode, excludeRelations = false, }: SettingsObjectFieldTableProps) => { const { t } = useLingui(); const [searchTerm, setSearchTerm] = useState(''); const [showInactive, setShowInactive] = useState(true); const [showSystemFields, setShowSystemFields] = useState(false); const isAdvancedModeEnabled = useRecoilValueV2(isAdvancedModeEnabledState); const tableMetadata = SETTINGS_OBJECT_FIELD_TABLE_METADATA; const { mapFieldMetadataItemToSettingsObjectDetailTableItem } = useMapFieldMetadataItemToSettingsObjectDetailTableItem(objectMetadataItem); const [settingsObjectFields, setSettingsObjectFields] = useRecoilState( settingsObjectFieldsFamilyState({ objectMetadataItemId: objectMetadataItem.id, }), ); useEffect(() => { setSettingsObjectFields(objectMetadataItem.fields); }, [objectMetadataItem, setSettingsObjectFields]); const allObjectSettingsDetailItems = useMemo(() => { const filteredBySystem = showSystemFields ? settingsObjectFields : settingsObjectFields?.filter( (fieldMetadataItem) => !isHiddenSystemField(fieldMetadataItem), ); const fieldsToDisplay = excludeRelations ? filteredBySystem?.filter( (fieldMetadataItem) => fieldMetadataItem.type !== FieldMetadataType.RELATION && fieldMetadataItem.type !== FieldMetadataType.MORPH_RELATION, ) : filteredBySystem; return ( fieldsToDisplay?.map( mapFieldMetadataItemToSettingsObjectDetailTableItem, ) ?? [] ); }, [ settingsObjectFields, mapFieldMetadataItemToSettingsObjectDetailTableItem, excludeRelations, showSystemFields, ]); const sortedAllObjectSettingsDetailItems = useSortedArray( allObjectSettingsDetailItems, tableMetadata, ); const filteredItems = useMemo(() => { const searchNormalized = normalizeSearchText(searchTerm); return sortedAllObjectSettingsDetailItems.filter((item) => { const matchesActiveFilter = showInactive || item.fieldMetadataItem.isActive; const matchesSearch = normalizeSearchText(item.label).includes(searchNormalized) || normalizeSearchText(item.dataType).includes(searchNormalized); return matchesActiveFilter && matchesSearch; }); }, [sortedAllObjectSettingsDetailItems, searchTerm, showInactive]); return ( <> } dropdownComponents={ setShowInactive(!showInactive)} toggled={showInactive} text={t`Inactive`} toggleSize="small" /> {isAdvancedModeEnabled && ( setShowSystemFields(!showSystemFields) } toggled={showSystemFields} text={t`System fields`} toggleSize="small" /> )} } /> {tableMetadata.fields.map((item) => ( ))} {filteredItems.map((objectSettingsDetailItem) => { const status = objectSettingsDetailItem.fieldMetadataItem.isActive ? 'active' : 'disabled'; return ( ); })}
); };