[Issue-5772] Add sort feature on settings tables (#5787)
## Proposed Changes - Introduce a new custom hook - useTableSort to sort table content - Add test cases for the new custom hook - Integrate useTableSort hook on to the table in settings object and settings object field pages ## Related Issue https://github.com/twentyhq/twenty/issues/5772 ## Evidence https://github.com/twentyhq/twenty/assets/87609792/8be456ce-2fa5-44ec-8bbd-70fb6c8fdb30 ## Evidence after addressing review comments https://github.com/twentyhq/twenty/assets/87609792/c267e3da-72f9-4c0e-8c94-a38122d6395e ## Further comments Apologies for the large PR. Looking forward for the review --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
committed by
GitHub
parent
0f75e14ab2
commit
59e14fabb4
@ -12,23 +12,31 @@ import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDelet
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||
import { useCombinedGetTotalCount } from '@/object-record/multiple-objects/hooks/useCombinedGetTotalCount';
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import {
|
||||
SettingsObjectItemTableRow,
|
||||
SettingsObjectMetadataItemTableRow,
|
||||
StyledObjectTableRow,
|
||||
} from '@/settings/data-model/object-details/components/SettingsObjectItemTableRow';
|
||||
import { SettingsObjectCoverImage } from '@/settings/data-model/objects/SettingsObjectCoverImage';
|
||||
import { SettingsObjectInactiveMenuDropDown } from '@/settings/data-model/objects/SettingsObjectInactiveMenuDropDown';
|
||||
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
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 { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { useMemo } from 'react';
|
||||
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};
|
||||
@ -41,11 +49,71 @@ const StyledH1Title = styled(H1Title)`
|
||||
export const SettingsObjects = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { activeObjectMetadataItems, inactiveObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
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,
|
||||
);
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer>
|
||||
@ -66,51 +134,67 @@ export const SettingsObjects = () => {
|
||||
<H2Title title="Existing objects" />
|
||||
<Table>
|
||||
<StyledObjectTableRow>
|
||||
<TableHeader>Name</TableHeader>
|
||||
<TableHeader>Type</TableHeader>
|
||||
<TableHeader align="right">Fields</TableHeader>
|
||||
<TableHeader align="right">Instances</TableHeader>
|
||||
{SETTINGS_OBJECT_TABLE_METADATA.fields.map(
|
||||
(settingsObjectsTableMetadataField) => (
|
||||
<SortableTableHeader
|
||||
fieldName={settingsObjectsTableMetadataField.fieldName}
|
||||
label={settingsObjectsTableMetadataField.fieldLabel}
|
||||
tableId={SETTINGS_OBJECT_TABLE_METADATA.tableId}
|
||||
align={settingsObjectsTableMetadataField.align}
|
||||
initialSort={SETTINGS_OBJECT_TABLE_METADATA.initialSort}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<TableHeader></TableHeader>
|
||||
</StyledObjectTableRow>
|
||||
{!!activeObjectMetadataItems.length && (
|
||||
{isNonEmptyArray(sortedActiveObjectSettingsItems) && (
|
||||
<TableSection title="Active">
|
||||
{activeObjectMetadataItems.map((activeObjectMetadataItem) => (
|
||||
<SettingsObjectItemTableRow
|
||||
key={activeObjectMetadataItem.namePlural}
|
||||
objectItem={activeObjectMetadataItem}
|
||||
{sortedActiveObjectSettingsItems.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}
|
||||
/>
|
||||
}
|
||||
to={`/settings/objects/${getObjectSlug(
|
||||
activeObjectMetadataItem,
|
||||
link={`/settings/objects/${getObjectSlug(
|
||||
objectSettingsItem.objectMetadataItem,
|
||||
)}`}
|
||||
/>
|
||||
))}
|
||||
</TableSection>
|
||||
)}
|
||||
{!!inactiveObjectMetadataItems.length && (
|
||||
{isNonEmptyArray(inactiveObjectMetadataItems) && (
|
||||
<TableSection title="Inactive">
|
||||
{inactiveObjectMetadataItems.map(
|
||||
(inactiveObjectMetadataItem) => (
|
||||
<SettingsObjectItemTableRow
|
||||
key={inactiveObjectMetadataItem.namePlural}
|
||||
objectItem={inactiveObjectMetadataItem}
|
||||
{sortedInactiveObjectSettingsItems.map(
|
||||
(objectSettingsItem) => (
|
||||
<SettingsObjectMetadataItemTableRow
|
||||
key={objectSettingsItem.objectMetadataItem.namePlural}
|
||||
objectMetadataItem={
|
||||
objectSettingsItem.objectMetadataItem
|
||||
}
|
||||
totalObjectCount={objectSettingsItem.totalObjectCount}
|
||||
action={
|
||||
<SettingsObjectInactiveMenuDropDown
|
||||
isCustomObject={inactiveObjectMetadataItem.isCustom}
|
||||
scopeKey={inactiveObjectMetadataItem.namePlural}
|
||||
isCustomObject={
|
||||
objectSettingsItem.objectMetadataItem.isCustom
|
||||
}
|
||||
scopeKey={
|
||||
objectSettingsItem.objectMetadataItem.namePlural
|
||||
}
|
||||
onActivate={() =>
|
||||
updateOneObjectMetadataItem({
|
||||
idToUpdate: inactiveObjectMetadataItem.id,
|
||||
idToUpdate:
|
||||
objectSettingsItem.objectMetadataItem.id,
|
||||
updatePayload: { isActive: true },
|
||||
})
|
||||
}
|
||||
onDelete={() =>
|
||||
deleteOneObjectMetadataItem(
|
||||
inactiveObjectMetadataItem.id,
|
||||
objectSettingsItem.objectMetadataItem.id,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user