Files
twenty/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx
Félix Malfait 9d32e63111 Continue Frontend localization (#9909)
Translation more content on the frontend
2025-01-29 17:36:28 +01:00

258 lines
10 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 { 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 { Trans, useLingui } from '@lingui/react/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { useMemo, useState } from 'react';
import {
Button,
H2Title,
IconChevronRight,
IconPlus,
IconSearch,
Section,
UndecoratedLink,
} from 'twenty-ui';
import { GET_SETTINGS_OBJECT_TABLE_METADATA } from '~/pages/settings/data-model/constants/SettingsObjectTableMetadata';
import { SettingsObjectTableItem } from '~/pages/settings/data-model/types/SettingsObjectTableItem';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
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 { t } = useLingui();
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,
GET_SETTINGS_OBJECT_TABLE_METADATA,
);
const sortedInactiveObjectSettingsItems = useSortedArray(
inactiveObjectSettingsArray,
GET_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={t`Data model`}
actionButton={
<UndecoratedLink to={getSettingsPath(SettingsPath.NewObject)}>
<Button
Icon={IconPlus}
title={t`Add object`}
accent="blue"
size="small"
/>
</UndecoratedLink>
}
links={[
{
children: <Trans>Workspace</Trans>,
href: getSettingsPath(SettingsPath.Workspace),
},
{ children: <Trans>Objects</Trans> },
]}
>
<SettingsPageContainer>
<>
<SettingsObjectCoverImage />
<Section>
<H2Title title={t`Existing objects`} />
<StyledSearchInput
LeftIcon={IconSearch}
placeholder={t`Search for an object...`}
value={searchTerm}
onChange={setSearchTerm}
/>
<Table>
<StyledObjectTableRow>
{GET_SETTINGS_OBJECT_TABLE_METADATA.fields.map(
(settingsObjectsTableMetadataField) => (
<SortableTableHeader
key={settingsObjectsTableMetadataField.fieldName}
fieldName={settingsObjectsTableMetadataField.fieldName}
label={t(settingsObjectsTableMetadataField.fieldLabel)}
tableId={GET_SETTINGS_OBJECT_TABLE_METADATA.tableId}
align={settingsObjectsTableMetadataField.align}
initialSort={
GET_SETTINGS_OBJECT_TABLE_METADATA.initialSort
}
/>
),
)}
<TableHeader></TableHeader>
</StyledObjectTableRow>
{isNonEmptyArray(sortedActiveObjectSettingsItems) && (
<TableSection title={t`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={getSettingsPath(SettingsPath.ObjectDetail, {
objectNamePlural:
objectSettingsItem.objectMetadataItem.namePlural,
})}
/>
),
)}
</TableSection>
)}
{isNonEmptyArray(inactiveObjectMetadataItems) && (
<TableSection title={t`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>
);
};