[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:
Anand Krishnan M J
2024-08-14 20:41:17 +05:30
committed by GitHub
parent 0f75e14ab2
commit 59e14fabb4
40 changed files with 1229 additions and 445 deletions

View File

@ -1,27 +1,22 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import styled from '@emotion/styled';
import { H2Title, IconMinus, IconPlus, IconSettings } from 'twenty-ui';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { H2Title, IconPlus, IconSettings } from 'twenty-ui';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsObjectFieldItemTableRow,
StyledObjectFieldTableRow,
} from '@/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState';
import { AppPath } from '@/types/AppPath';
import { Button } from '@/ui/input/button/components/Button';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
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 { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { useRecoilState } from 'recoil';
import { SettingsObjectFieldTable } from '~/pages/settings/data-model/SettingsObjectFieldTable';
const StyledSection = styled(Section)`
display: flex;
@ -43,62 +38,52 @@ export const SettingsObjectNewFieldStep1 = () => {
const activeObjectMetadataItem =
findActiveObjectMetadataItemBySlug(objectSlug);
const [settingsObjectFields] = useRecoilState(
settingsObjectFieldsFamilyState({
objectMetadataItemId: activeObjectMetadataItem?.id,
}),
);
const { activateMetadataField, deactivateMetadataField } =
useFieldMetadataItem();
const [metadataFields, setMetadataFields] = useState(
activeObjectMetadataItem?.fields ?? [],
);
const activeMetadataFields = metadataFields.filter((field) => field.isActive);
const deactivatedMetadataFields = metadataFields.filter(
(field) => !field.isActive,
);
const canSave = metadataFields.some(
const canSave = settingsObjectFields?.some(
(field, index) =>
field.isActive !== activeObjectMetadataItem?.fields[index].isActive,
);
const handleSave = async () => {
if (!activeObjectMetadataItem || !settingsObjectFields) {
return;
}
await Promise.all(
settingsObjectFields.map((fieldMetadataItem, index) => {
if (
fieldMetadataItem.isActive ===
activeObjectMetadataItem.fields[index].isActive
) {
return undefined;
}
return fieldMetadataItem.isActive
? activateMetadataField(fieldMetadataItem)
: deactivateMetadataField(fieldMetadataItem);
}),
);
navigate(`/settings/objects/${objectSlug}`);
};
useEffect(() => {
if (!activeObjectMetadataItem) {
navigate(AppPath.NotFound);
return;
}
if (!metadataFields.length)
setMetadataFields(activeObjectMetadataItem.fields);
}, [activeObjectMetadataItem, metadataFields.length, navigate]);
}, [activeObjectMetadataItem, navigate]);
if (!activeObjectMetadataItem) return null;
const handleToggleField = (fieldMetadataId: string) =>
setMetadataFields((previousFields) =>
previousFields.map((field) =>
field.id === fieldMetadataId
? { ...field, isActive: !field.isActive }
: field,
),
);
const handleSave = async () => {
await Promise.all(
metadataFields.map((metadataField, index) => {
if (
metadataField.isActive ===
activeObjectMetadataItem.fields[index].isActive
) {
return undefined;
}
return metadataField.isActive
? activateMetadataField(metadataField)
: deactivateMetadataField(metadataField);
}),
);
navigate(`/settings/objects/${objectSlug}`);
};
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SettingsPageContainer>
@ -126,58 +111,10 @@ export const SettingsObjectNewFieldStep1 = () => {
title="Check deactivated fields"
description="Before creating a custom field, check if it already exists in the deactivated section."
/>
<Table>
<StyledObjectFieldTableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Field type</TableHeader>
<TableHeader>Data type</TableHeader>
<TableHeader></TableHeader>
</StyledObjectFieldTableRow>
{!!activeMetadataFields.length && (
<TableSection isInitiallyExpanded={false} title="Active">
{activeMetadataFields.map((activeMetadataField) => (
<SettingsObjectFieldItemTableRow
key={activeMetadataField.id}
fieldMetadataItem={activeMetadataField}
isRemoteObjectField={activeObjectMetadataItem.isRemote}
ActionIcon={
isLabelIdentifierField({
fieldMetadataItem: activeMetadataField,
objectMetadataItem: activeObjectMetadataItem,
}) ? undefined : (
<LightIconButton
Icon={IconMinus}
accent="tertiary"
onClick={() =>
handleToggleField(activeMetadataField.id)
}
/>
)
}
/>
))}
</TableSection>
)}
{!!deactivatedMetadataFields.length && (
<TableSection title="Disabled">
{deactivatedMetadataFields.map((deactivatedMetadataField) => (
<SettingsObjectFieldItemTableRow
key={deactivatedMetadataField.name}
fieldMetadataItem={deactivatedMetadataField}
ActionIcon={
<LightIconButton
Icon={IconPlus}
accent="tertiary"
onClick={() =>
handleToggleField(deactivatedMetadataField.id)
}
/>
}
/>
))}
</TableSection>
)}
</Table>
<SettingsObjectFieldTable
objectMetadataItem={activeObjectMetadataItem}
mode="new-field"
/>
<StyledAddCustomFieldButton
Icon={IconPlus}
title="Add Custom Field"