Files
twenty_crm/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx
Paul Rastoin aa0d8546a8 [REFACTOR][FRONT]: Remove objectMetadata and fieldMetadata sluggification (#9441)
# Introduction
For motivations and context please have a look to
https://github.com/twentyhq/twenty/pull/9394 whom this PR results from.
In this pull-request we remove any `metadataField` and `objectMetadata`
sluggification. We directly consume `objectMetadata.namePlural` and
`metadataField.name`, ***it seems like that historically the consumed
`metadataField.name`*** are we sure that we wanna change this behavior ?

## Notes
Unless I'm mistaken by reverting the `kebabcase` url formatting we might
be creating deadlinks that user could have save beforehand => Discussed
with Charles said it's controlled risk.

---------

Co-authored-by: Paul Rastoin <paulrastoin@Pauls-MacBook-Pro.local>
2025-01-08 11:31:53 +01:00

251 lines
9.8 KiB
TypeScript

import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { useCombinedGetTotalCount } from '@/object-record/multiple-objects/hooks/useCombinedGetTotalCount';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsObjectMetadataItemTableRow,
StyledObjectTableRow,
} from '@/settings/data-model/object-details/components/SettingsObjectItemTableRow';
import { SettingsObjectCoverImage } from '@/settings/data-model/objects/components/SettingsObjectCoverImage';
import { SettingsObjectInactiveMenuDropDown } from '@/settings/data-model/objects/components/SettingsObjectInactiveMenuDropDown';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { TextInput } from '@/ui/input/components/TextInput';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
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 { TableSection } from '@/ui/layout/table/components/TableSection';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyArray } from '@sniptt/guards';
import { useMemo, useState } from 'react';
import {
Button,
H2Title,
IconChevronRight,
IconPlus,
IconSearch,
Section,
UndecoratedLink,
} from 'twenty-ui';
import { SETTINGS_OBJECT_TABLE_METADATA } from '~/pages/settings/data-model/constants/SettingsObjectTableMetadata';
import { SettingsObjectTableItem } from '~/pages/settings/data-model/types/SettingsObjectTableItem';
const StyledIconChevronRight = styled(IconChevronRight)`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const StyledSearchInput = styled(TextInput)`
padding-bottom: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
export const SettingsObjects = () => {
const theme = useTheme();
const [searchTerm, setSearchTerm] = useState('');
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
const { activeObjectMetadataItems, inactiveObjectMetadataItems } =
useFilteredObjectMetadataItems();
const { totalCountByObjectMetadataItemNamePlural } = useCombinedGetTotalCount(
{
objectMetadataItems: [
...activeObjectMetadataItems,
...inactiveObjectMetadataItems,
],
},
);
const activeObjectSettingsArray = useMemo(
() =>
activeObjectMetadataItems.map(
(objectMetadataItem) =>
({
objectMetadataItem,
labelPlural: objectMetadataItem.labelPlural,
objectTypeLabel: getObjectTypeLabel(objectMetadataItem).labelText,
fieldsCount: objectMetadataItem.fields.filter(
(field) => !field.isSystem,
).length,
totalObjectCount:
totalCountByObjectMetadataItemNamePlural[
objectMetadataItem.namePlural
] ?? 0,
}) satisfies SettingsObjectTableItem,
),
[activeObjectMetadataItems, totalCountByObjectMetadataItemNamePlural],
);
const inactiveObjectSettingsArray = useMemo(
() =>
inactiveObjectMetadataItems.map(
(objectMetadataItem) =>
({
objectMetadataItem,
labelPlural: objectMetadataItem.labelPlural,
objectTypeLabel: getObjectTypeLabel(objectMetadataItem).labelText,
fieldsCount: objectMetadataItem.fields.filter(
(field) => !field.isSystem,
).length,
totalObjectCount:
totalCountByObjectMetadataItemNamePlural[
objectMetadataItem.namePlural
] ?? 0,
}) satisfies SettingsObjectTableItem,
),
[inactiveObjectMetadataItems, totalCountByObjectMetadataItemNamePlural],
);
const sortedActiveObjectSettingsItems = useSortedArray(
activeObjectSettingsArray,
SETTINGS_OBJECT_TABLE_METADATA,
);
const sortedInactiveObjectSettingsItems = useSortedArray(
inactiveObjectSettingsArray,
SETTINGS_OBJECT_TABLE_METADATA,
);
const filteredActiveObjectSettingsItems = useMemo(
() =>
sortedActiveObjectSettingsItems.filter(
(item) =>
item.labelPlural.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.objectTypeLabel.toLowerCase().includes(searchTerm.toLowerCase()),
),
[sortedActiveObjectSettingsItems, searchTerm],
);
const filteredInactiveObjectSettingsItems = useMemo(
() =>
sortedInactiveObjectSettingsItems.filter(
(item) =>
item.labelPlural.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.objectTypeLabel.toLowerCase().includes(searchTerm.toLowerCase()),
),
[sortedInactiveObjectSettingsItems, searchTerm],
);
return (
<SubMenuTopBarContainer
title="Data model"
actionButton={
<UndecoratedLink to={getSettingsPagePath(SettingsPath.NewObject)}>
<Button
Icon={IconPlus}
title="Add object"
accent="blue"
size="small"
/>
</UndecoratedLink>
}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: 'Objects',
},
]}
>
<SettingsPageContainer>
<>
<SettingsObjectCoverImage />
<Section>
<H2Title title="Existing objects" />
<StyledSearchInput
LeftIcon={IconSearch}
placeholder="Search an object..."
value={searchTerm}
onChange={setSearchTerm}
/>
<Table>
<StyledObjectTableRow>
{SETTINGS_OBJECT_TABLE_METADATA.fields.map(
(settingsObjectsTableMetadataField) => (
<SortableTableHeader
key={settingsObjectsTableMetadataField.fieldName}
fieldName={settingsObjectsTableMetadataField.fieldName}
label={settingsObjectsTableMetadataField.fieldLabel}
tableId={SETTINGS_OBJECT_TABLE_METADATA.tableId}
align={settingsObjectsTableMetadataField.align}
initialSort={SETTINGS_OBJECT_TABLE_METADATA.initialSort}
/>
),
)}
<TableHeader></TableHeader>
</StyledObjectTableRow>
{isNonEmptyArray(sortedActiveObjectSettingsItems) && (
<TableSection title="Active">
{filteredActiveObjectSettingsItems.map(
(objectSettingsItem) => (
<SettingsObjectMetadataItemTableRow
key={objectSettingsItem.objectMetadataItem.namePlural}
objectMetadataItem={
objectSettingsItem.objectMetadataItem
}
totalObjectCount={objectSettingsItem.totalObjectCount}
action={
<StyledIconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
}
link={`/settings/objects/${
objectSettingsItem.objectMetadataItem.namePlural
}`}
/>
),
)}
</TableSection>
)}
{isNonEmptyArray(inactiveObjectMetadataItems) && (
<TableSection title="Inactive">
{filteredInactiveObjectSettingsItems.map(
(objectSettingsItem) => (
<SettingsObjectMetadataItemTableRow
key={objectSettingsItem.objectMetadataItem.namePlural}
objectMetadataItem={
objectSettingsItem.objectMetadataItem
}
totalObjectCount={objectSettingsItem.totalObjectCount}
action={
<SettingsObjectInactiveMenuDropDown
isCustomObject={
objectSettingsItem.objectMetadataItem.isCustom
}
scopeKey={
objectSettingsItem.objectMetadataItem.namePlural
}
onActivate={() =>
updateOneObjectMetadataItem({
idToUpdate:
objectSettingsItem.objectMetadataItem.id,
updatePayload: { isActive: true },
})
}
onDelete={() =>
deleteOneObjectMetadataItem(
objectSettingsItem.objectMetadataItem.id,
)
}
/>
}
/>
),
)}
</TableSection>
)}
</Table>
</Section>
</>
</SettingsPageContainer>
</SubMenuTopBarContainer>
);
};