Implement object fields and settings new layout (#7979)

### Description

- This PR has as the base branch the TWNTY-5491 branch, but we also had
to include updates from the main branch, and currently, there are
conflicts in the TWNTY-5491, that cause errors on typescript in this PR,
so, we can update once the conflicts are resolved on the base branch,
but the functionality can be reviewed anyway
- We Implemented a new layout of object details settings and new, the
data is auto-saved in `Settings `tab of object detail
- There is no indication to the user that data are saved automatically
in the design, currently we are disabling the form

### Demo\

<https://www.loom.com/share/4198c0aa54b5450780a570ceee574838?sid=b4ef0a42-2d41-435f-9f5f-1b16816939f7>

### Refs

#TWNTY-5491

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com>
Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
gitstart-app[bot]
2024-11-07 14:50:53 +01:00
committed by GitHub
parent 3be30651b7
commit 7bab65b569
25 changed files with 496 additions and 391 deletions

View File

@ -2,9 +2,71 @@ import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { ObjectFields } from '@/settings/data-model/object-details/components/tabs/ObjectFields';
import { ObjectIndexes } from '@/settings/data-model/object-details/components/tabs/ObjectIndexes';
import { ObjectSettings } from '@/settings/data-model/object-details/components/tabs/ObjectSettings';
import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/components/SettingsDataModelObjectTypeTag';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { AppPath } from '@/types/AppPath';
import { isDefined } from 'twenty-ui';
import { SettingsObjectDetailPageContent } from '~/pages/settings/data-model/SettingsObjectDetailPageContent';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue } from 'recoil';
import {
Button,
H3Title,
IconCodeCircle,
IconListDetails,
IconPlus,
IconSettings,
IconTool,
isDefined,
MAIN_COLORS,
UndecoratedLink,
} from 'twenty-ui';
import { updatedObjectSlugState } from '~/pages/settings/data-model/states/updatedObjectSlugState';
const StyledTabListContainer = styled.div`
align-items: center;
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: ${({ theme }) => theme.spacing(10)};
.tab-list {
padding-left: 0px;
}
.tab-list > div {
padding: ${({ theme }) => theme.spacing(3) + ' 0'};
}
`;
const StyledContentContainer = styled.div`
flex: 1;
width: 100%;
padding-left: 0;
`;
const StyledObjectTypeTag = styled(SettingsDataModelObjectTypeTag)`
box-sizing: border-box;
height: ${({ theme }) => theme.spacing(5)};
margin-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledTitleContainer = styled.div`
display: flex;
`;
const TAB_LIST_COMPONENT_ID = 'object-details-tab-list';
const FIELDS_TAB_ID = 'fields';
const SETTINGS_TAB_ID = 'settings';
const INDEXES_TAB_ID = 'indexes';
export const SettingsObjectDetailPage = () => {
const navigate = useNavigate();
@ -13,18 +75,115 @@ export const SettingsObjectDetailPage = () => {
const { findActiveObjectMetadataItemBySlug } =
useFilteredObjectMetadataItems();
const activeObjectMetadataItem =
findActiveObjectMetadataItemBySlug(objectSlug);
const [updatedObjectSlug, setUpdatedObjectSlug] = useRecoilState(
updatedObjectSlugState,
);
const objectMetadataItem =
findActiveObjectMetadataItemBySlug(objectSlug) ??
findActiveObjectMetadataItemBySlug(updatedObjectSlug);
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
const activeTabId = useRecoilValue(activeTabIdState);
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
const isUniqueIndexesEnabled = useIsFeatureEnabled(
'IS_UNIQUE_INDEXES_ENABLED',
);
useEffect(() => {
if (!activeObjectMetadataItem) navigate(AppPath.NotFound);
}, [activeObjectMetadataItem, navigate]);
if (objectSlug === updatedObjectSlug) setUpdatedObjectSlug('');
if (!isDefined(objectMetadataItem)) navigate(AppPath.NotFound);
}, [
objectMetadataItem,
navigate,
objectSlug,
updatedObjectSlug,
setUpdatedObjectSlug,
]);
if (!isDefined(activeObjectMetadataItem)) return <></>;
if (!isDefined(objectMetadataItem)) return <></>;
const tabs = [
{
id: FIELDS_TAB_ID,
title: 'Fields',
Icon: IconListDetails,
hide: false,
},
{
id: SETTINGS_TAB_ID,
title: 'Settings',
Icon: IconSettings,
hide: false,
},
{
id: INDEXES_TAB_ID,
title: 'Indexes',
Icon: IconCodeCircle,
hide: !isAdvancedModeEnabled || !isUniqueIndexesEnabled,
pill: <IconTool size={12} color={MAIN_COLORS.yellow} />,
},
];
const renderActiveTabContent = () => {
switch (activeTabId) {
case FIELDS_TAB_ID:
return <ObjectFields objectMetadataItem={objectMetadataItem} />;
case SETTINGS_TAB_ID:
return <ObjectSettings objectMetadataItem={objectMetadataItem} />;
case INDEXES_TAB_ID:
return <ObjectIndexes objectMetadataItem={objectMetadataItem} />;
default:
return <></>;
}
};
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
return (
<SettingsObjectDetailPageContent
objectMetadataItem={activeObjectMetadataItem}
/>
<SubMenuTopBarContainer
title={
<StyledTitleContainer>
<H3Title title={objectMetadataItem.labelPlural} />
<StyledObjectTypeTag objectTypeLabel={objectTypeLabel} />
</StyledTitleContainer>
}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Objects', href: '/settings/objects' },
{
children: objectMetadataItem.labelPlural,
},
]}
actionButton={
activeTabId === FIELDS_TAB_ID && (
<UndecoratedLink to={'./new-field/select'}>
<Button
title="New Field"
variant="primary"
size="small"
accent="blue"
Icon={IconPlus}
/>
</UndecoratedLink>
)
}
>
<SettingsPageContainer>
<StyledTabListContainer>
<TabList
tabListId={TAB_LIST_COMPONENT_ID}
tabs={tabs}
className="tab-list"
/>
</StyledTabListContainer>
<StyledContentContainer>
{renderActiveTabContent()}
</StyledContentContainer>
</SettingsPageContainer>
</SubMenuTopBarContainer>
);
};