New Settings Layout (#6867)

#### \
Description

- **Added "Exit Settings" Back Button**: Introduced a new back button
labeled "Exit Settings" for easy navigation back to the app content.
- **Implemented Settings Navbar Breadcrumb**: A breadcrumb navigation
bar for each settings page has been added to improve navigation within
the settings. This ensures users can easily trace their location within
the settings.
- **Persistent CTA Zone**: The Call-to-Action (CTA) zone at the top of
the page now remains visible even when the user scrolls down, preventing
unresponsive behavior and improving accessibility.
- **Page Title**: The page title has been added to each settings page
and separated from the breadcrumb, following the app's layout standards.
- we could not reproduce the Billing and CMR Migrations settings on the
app, but we updated the files according, please let's us know if is
there any way to log in with access to these pages, or if is everything
ok.
- Some breadcrumbs are not following the Figma, are following the
current app sections isntead, because we would need to change the
sidebar structure, and we should not do it on this PR

### Demo


<https://www.loom.com/share/21b20a2cd2f3471e94d61563c9901b19?sid=9dc49456-6cae-48e1-9149-8d706f00ab65>

### Refs:

#6144

Fixes #6144

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
gitstart-app[bot]
2024-09-17 18:29:00 +02:00
committed by GitHub
parent fe4ca2133d
commit c42ea57b97
45 changed files with 504 additions and 323 deletions

View File

@ -7,7 +7,6 @@ import { z } from 'zod';
import { useCreateOneObjectMetadataItem } from '@/object-metadata/hooks/useCreateOneObjectMetadataItem';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsDataModelObjectAboutForm,
@ -20,7 +19,6 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
const newObjectFormSchema = settingsDataModelObjectAboutFormSchema;
@ -72,17 +70,18 @@ export const SettingsNewObject = () => {
<FormProvider {...formConfig}>
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{ children: 'New' },
]}
/>
}
title="New Object"
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{ children: 'New' },
]}
actionButton={
<SaveAndCancelButtons
isSaveDisabled={!canSave}
@ -93,7 +92,6 @@ export const SettingsNewObject = () => {
}
>
<SettingsPageContainer>
<SettingsHeaderContainer></SettingsHeaderContainer>
<Section>
<H2Title
title="About"

View File

@ -8,7 +8,6 @@ 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 { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
import styled from '@emotion/styled';
import { isNonEmptyArray } from '@sniptt/guards';
@ -49,14 +48,15 @@ export const SettingsObjectDetailPageContent = ({
return (
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{ children: objectMetadataItem.labelPlural },
]}
/>
}
title={objectMetadataItem.labelPlural}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Objects', href: '/settings/objects' },
{ children: objectMetadataItem.labelPlural },
]}
>
<SettingsPageContainer>
<Section>

View File

@ -13,7 +13,6 @@ import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdat
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsDataModelObjectAboutForm,
@ -30,7 +29,6 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Button } from '@/ui/input/button/components/Button';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
const objectEditFormSchema = z
.object({})
@ -110,35 +108,36 @@ export const SettingsObjectEdit = () => {
<FormProvider {...formConfig}>
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{
children: activeObjectMetadataItem.labelPlural,
href: `${settingsObjectsPagePath}/${objectSlug}`,
},
{ children: 'Edit' },
]}
/>
title="Edit"
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{
children: activeObjectMetadataItem.labelPlural,
href: `${settingsObjectsPagePath}/${objectSlug}`,
},
{ children: 'Edit Object' },
]}
actionButton={
activeObjectMetadataItem.isCustom && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
onCancel={() =>
navigate(`${settingsObjectsPagePath}/${objectSlug}`)
}
onSave={formConfig.handleSubmit(handleSave)}
/>
)
}
>
<SettingsPageContainer>
<SettingsHeaderContainer>
{activeObjectMetadataItem.isCustom && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
onCancel={() =>
navigate(`${settingsObjectsPagePath}/${objectSlug}`)
}
onSave={formConfig.handleSubmit(handleSave)}
/>
)}
</SettingsHeaderContainer>
<Section>
<H2Title
title="About"

View File

@ -26,13 +26,14 @@ import { SettingsDataModelFieldIconLabelForm } from '@/settings/data-model/field
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema';
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Button } from '@/ui/input/button/components/Button';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
@ -173,23 +174,24 @@ export const SettingsObjectFieldEdit = () => {
<FormProvider {...formConfig}>
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: '/settings/objects',
styles: { minWidth: 'max-content' },
},
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
styles: { maxWidth: '60%' },
},
{ children: activeMetadataField.label },
]}
/>
}
title={activeMetadataField?.label}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: 'Objects',
href: '/settings/objects',
},
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{
children: activeMetadataField.label,
},
]}
actionButton={
shouldDisplaySaveAndCancel && (
<SaveAndCancelButtons

View File

@ -9,11 +9,12 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { AppPath } from '@/types/AppPath';
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 { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { useRecoilState } from 'recoil';
import { SettingsObjectFieldTable } from '~/pages/settings/data-model/SettingsObjectFieldTable';
@ -86,18 +87,18 @@ export const SettingsObjectNewFieldStep1 = () => {
return (
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: 'New Field' },
]}
/>
}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: 'New Field' },
]}
actionButton={
!activeObjectMetadataItem.isRemote && (
<SaveAndCancelButtons

View File

@ -19,7 +19,6 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { View } from '@/views/types/View';
import { ViewType } from '@/views/types/ViewType';
import { useApolloClient } from '@apollo/client';
@ -41,6 +40,7 @@ type SettingsDataModelNewFieldFormValues = z.infer<
const StyledH1Title = styled(H1Title)`
margin-bottom: 0;
padding-top: ${({ theme }) => theme.spacing(3)};
`;
export const SettingsObjectNewFieldStep2 = () => {
const navigate = useNavigate();
@ -177,30 +177,24 @@ export const SettingsObjectNewFieldStep2 = () => {
>
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: '/settings/objects',
styles: { minWidth: 'max-content' },
},
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
styles: { maxWidth: '50%' },
},
{
children: (
<SettingsDataModelNewFieldBreadcrumbDropDown
isConfigureStep={isConfigureStep}
onBreadcrumbClick={setIsConfigureStep}
/>
),
},
]}
/>
}
links={[
{
children: 'Objects',
href: '/settings/objects',
},
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{
children: (
<SettingsDataModelNewFieldBreadcrumbDropDown
isConfigureStep={isConfigureStep}
onBreadcrumbClick={setIsConfigureStep}
/>
),
},
]}
actionButton={
!activeObjectMetadataItem.isRemote && (
<SaveAndCancelButtons

View File

@ -1,24 +1,25 @@
import { ReactFlowProvider } from 'reactflow';
import { SettingsDataModelOverview } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverview';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { IconHierarchy2 } from 'twenty-ui';
export const SettingsObjectOverview = () => {
return (
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Data model', href: '/settings/objects' },
{
children: 'Overview',
},
]}
/>
}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Objects', href: '/settings/objects' },
{
children: 'Overview',
},
]}
>
<ReactFlowProvider>
<SettingsDataModelOverview />

View File

@ -145,6 +145,15 @@ export const SettingsObjects = () => {
/>
</UndecoratedLink>
}
links={[
{
children: 'Workspace',
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: 'Objects',
},
]}
>
<SettingsPageContainer>
<>

View File

@ -30,7 +30,6 @@ export type Story = StoryObj<typeof SettingsObjectNewFieldStep1>;
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('Settings');
await canvas.findByText('Objects');
await canvas.findByText('Companies');
await canvas.findByText('Check deactivated fields');