diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index 05e852e2b..6f37c0595 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -1,6 +1,7 @@ import { lazy, Suspense } from 'react'; import { Route, Routes } from 'react-router-dom'; +import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; @@ -202,22 +203,21 @@ const SettingsIntegrationShowDatabaseConnection = lazy(() => })), ); -const SettingsObjectNewFieldStep1 = lazy(() => +const SettingsObjectNewFieldSelect = lazy(() => import( - '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep1' + '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect' ).then((module) => ({ - default: module.SettingsObjectNewFieldStep1, + default: module.SettingsObjectNewFieldSelect, })), ); -const SettingsObjectNewFieldStep2 = lazy(() => +const SettingsObjectNewFieldConfigure = lazy(() => import( - '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2' + '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldConfigure' ).then((module) => ({ - default: module.SettingsObjectNewFieldStep2, + default: module.SettingsObjectNewFieldConfigure, })), ); - const SettingsObjectFieldEdit = lazy(() => import('~/pages/settings/data-model/SettingsObjectFieldEdit').then( (module) => ({ @@ -245,7 +245,7 @@ export const SettingsRoutes = ({ isCRMMigrationEnabled, isServerlessFunctionSettingsEnabled, }: SettingsRoutesProps) => ( - + }> } /> } /> @@ -345,12 +345,12 @@ export const SettingsRoutes = ({ element={} /> } + path={SettingsPath.ObjectNewFieldSelect} + element={} /> } + path={SettingsPath.ObjectNewFieldConfigure} + element={} /> , -) => - objectMetadataItem.fields.filter( - (fieldMetadataItem) => - !fieldMetadataItem.isActive && !fieldMetadataItem.isSystem, - ); diff --git a/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx b/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx new file mode 100644 index 000000000..812a5ca67 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx @@ -0,0 +1,52 @@ +import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; +import { PageBody } from '@/ui/layout/page/PageBody'; +import { PageHeader } from '@/ui/layout/page/PageHeader'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +const StyledTitleLoaderContainer = styled.div` + margin: ${({ theme }) => theme.spacing(8, 8, 2)}; +`; + +export const SettingsSkeletonLoader = () => { + const theme = useTheme(); + return ( + + + {' '} + + } + /> + + + + + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx index 0521676c7..268cfd2cc 100644 --- a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx @@ -6,20 +6,17 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { IconChevronDown } from 'twenty-ui'; -type SettingsDataModelNewFieldBreadcrumbDropDownProps = { - isConfigureStep: boolean; - onBreadcrumbClick: (isConfigureStep: boolean) => void; -}; - const StyledContainer = styled.div` align-items: center; - color: ${({ theme }) => theme.font.color.secondary}; + color: ${({ theme }) => theme.font.color.tertiary}; cursor: pointer; display: flex; font-size: ${({ theme }) => theme.font.size.md}; `; + const StyledButtonContainer = styled.div` position: relative; width: 100%; @@ -48,19 +45,24 @@ const StyledButton = styled(Button)` padding-right: ${({ theme }) => theme.spacing(6)}; `; -export const SettingsDataModelNewFieldBreadcrumbDropDown = ({ - isConfigureStep, - onBreadcrumbClick, -}: SettingsDataModelNewFieldBreadcrumbDropDownProps) => { +export const SettingsDataModelNewFieldBreadcrumbDropDown = () => { const dropdownId = `settings-object-new-field-breadcrumb-dropdown`; - const { closeDropdown } = useDropdown(dropdownId); + const navigate = useNavigate(); + const location = useLocation(); + const { objectSlug = '' } = useParams(); + const theme = useTheme(); - const handleClick = (step: boolean) => { - onBreadcrumbClick(step); + const isConfigureStep = location.pathname.includes('/configure'); + + const handleClick = (isConfigureStep: boolean) => { + if (isConfigureStep) { + navigate(`/settings/objects/${objectSlug}/new-field/configure`); + } else { + navigate(`/settings/objects/${objectSlug}/new-field/select`); + } closeDropdown(); }; - const theme = useTheme(); return ( diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx similarity index 59% rename from packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx rename to packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx index b75d880d5..ce83da6f7 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx @@ -10,36 +10,25 @@ import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fiel import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields/forms/select/hooks/useSelectSettingsFormInitialValues'; import { SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType'; import { TextInput } from '@/ui/input/components/TextInput'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Section } from '@react-email/components'; import { useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { H2Title, IconSearch } from 'twenty-ui'; -import { z } from 'zod'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { SettingsDataModelFieldTypeFormValues } from '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect'; -export const settingsDataModelFieldTypeFormSchema = z.object({ - type: z.enum( - Object.keys(SETTINGS_FIELD_TYPE_CONFIGS) as [ - SettingsFieldType, - ...SettingsFieldType[], - ], - ), -}); - -export type SettingsDataModelFieldTypeFormValues = z.infer< - typeof settingsDataModelFieldTypeFormSchema ->; - -type SettingsDataModelFieldTypeSelectProps = { +type SettingsObjectNewFieldSelectorProps = { className?: string; excludedFieldTypes?: SettingsFieldType[]; fieldMetadataItem?: Pick< FieldMetadataItem, 'defaultValue' | 'options' | 'type' >; - onFieldTypeSelect: () => void; + + objectSlug: string; }; const StyledTypeSelectContainer = styled.div` @@ -68,12 +57,11 @@ const StyledSearchInput = styled(TextInput)` width: 100%; `; -export const SettingsDataModelFieldTypeSelect = ({ - className, +export const SettingsObjectNewFieldSelector = ({ excludedFieldTypes = [], fieldMetadataItem, - onFieldTypeSelect, -}: SettingsDataModelFieldTypeSelectProps) => { + objectSlug, +}: SettingsObjectNewFieldSelectorProps) => { const theme = useTheme(); const { control } = useFormContext(); const [searchQuery, setSearchQuery] = useState(''); @@ -112,59 +100,60 @@ export const SettingsDataModelFieldTypeSelect = ({ }; return ( - ( - -
- -
- {SETTINGS_FIELD_TYPE_CATEGORIES.map((category) => ( -
- - - {fieldTypeConfigs - .filter(([, config]) => config.category === category) - .map(([key, config]) => ( - - { - onChange(key as SettingsFieldType); - resetDefaultValueField(key as SettingsFieldType); - onFieldTypeSelect(); - }} - Icon={ - + {' '} +
+ +
+ ( + + {SETTINGS_FIELD_TYPE_CATEGORIES.map((category) => ( +
+ + + {fieldTypeConfigs + .filter(([, config]) => config.category === category) + .map(([key, config]) => ( + + + resetDefaultValueField(key as SettingsFieldType) + } + > + + } + title={config.label} /> - } - title={config.label} - /> - - ))} - -
- ))} -
- )} - /> + +
+ ))} +
+
+ ))} +
+ )} + /> + ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/__stories__/SettingsDataModelFieldTypeSelect.stories.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/__stories__/SettingsDataModelFieldTypeSelect.stories.tsx deleted file mode 100644 index dbd3ea3c9..000000000 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/__stories__/SettingsDataModelFieldTypeSelect.stories.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { expect, userEvent, within } from '@storybook/test'; -import { ComponentDecorator } from 'twenty-ui'; - -import { FieldMetadataType } from '~/generated-metadata/graphql'; -import { FormProviderDecorator } from '~/testing/decorators/FormProviderDecorator'; -import { graphqlMocks } from '~/testing/graphqlMocks'; - -import { SettingsDataModelFieldTypeSelect } from '../SettingsDataModelFieldTypeSelect'; - -const meta: Meta = { - title: - 'Modules/Settings/DataModel/Fields/Forms/SettingsDataModelFieldTypeSelect', - component: SettingsDataModelFieldTypeSelect, - decorators: [FormProviderDecorator, ComponentDecorator], - parameters: { - container: { width: 512 }, - msw: graphqlMocks, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; - -export const WithOpenSelect: Story = { - play: async () => { - const canvas = within(document.body); - - const inputField = await canvas.findByText('Text'); - - await userEvent.click(inputField); - - const input = await canvas.findByText('Unique ID'); - await userEvent.click(input); - - await userEvent.click(inputField); - }, -}; - -export const WithExcludedFieldTypes: Story = { - args: { - excludedFieldTypes: [FieldMetadataType.Uuid, FieldMetadataType.Numeric], - }, - play: async () => { - const canvas = within(document.body); - - const inputField = await canvas.findByText('Text'); - - await userEvent.click(inputField); - - await canvas.findByText('Number'); - - expect(canvas.queryByText('Unique ID')).toBeNull(); - expect(canvas.queryByText('Numeric')).toBeNull(); - }, -}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema.ts index 4d7a171f4..18ae87b97 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema.ts @@ -1,9 +1,8 @@ -import { z } from 'zod'; - import { settingsDataModelFieldDescriptionFormSchema } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldDescriptionForm'; import { settingsDataModelFieldIconLabelFormSchema } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm'; import { settingsDataModelFieldSettingsFormSchema } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard'; -import { settingsDataModelFieldTypeFormSchema } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect'; +import { z } from 'zod'; +import { settingsDataModelFieldTypeFormSchema } from '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect'; export const settingsFieldFormSchema = (existingOtherLabels?: string[]) => { return z diff --git a/packages/twenty-front/src/modules/types/SettingsPath.ts b/packages/twenty-front/src/modules/types/SettingsPath.ts index 92f152619..96efe89cd 100644 --- a/packages/twenty-front/src/modules/types/SettingsPath.ts +++ b/packages/twenty-front/src/modules/types/SettingsPath.ts @@ -12,8 +12,8 @@ export enum SettingsPath { ObjectOverview = 'objects/overview', ObjectDetail = 'objects/:objectSlug', ObjectEdit = 'objects/:objectSlug/edit', - ObjectNewFieldStep1 = 'objects/:objectSlug/new-field/step-1', - ObjectNewFieldStep2 = 'objects/:objectSlug/new-field/step-2', + ObjectNewFieldSelect = 'objects/:objectSlug/new-field/select', + ObjectNewFieldConfigure = 'objects/:objectSlug/new-field/configure', ObjectFieldEdit = 'objects/:objectSlug/:fieldSlug', NewObject = 'objects/new', NewServerlessFunction = 'functions/new', diff --git a/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx b/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx index ef767a67a..9b26f71bc 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx @@ -1,6 +1,5 @@ import styled from '@emotion/styled'; import { JSX, ReactNode } from 'react'; -import { IconComponent } from 'twenty-ui'; import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper'; import { @@ -14,7 +13,6 @@ type SubMenuTopBarContainerProps = { children: JSX.Element | JSX.Element[]; title?: string; actionButton?: ReactNode; - Icon: IconComponent; className?: string; links: BreadcrumbProps['links']; }; diff --git a/packages/twenty-front/src/modules/views/view-picker/hooks/useGetAvailableFieldsForKanban.ts b/packages/twenty-front/src/modules/views/view-picker/hooks/useGetAvailableFieldsForKanban.ts index 23e082c2c..1021a104d 100644 --- a/packages/twenty-front/src/modules/views/view-picker/hooks/useGetAvailableFieldsForKanban.ts +++ b/packages/twenty-front/src/modules/views/view-picker/hooks/useGetAvailableFieldsForKanban.ts @@ -38,7 +38,7 @@ export const useGetAvailableFieldsForKanban = () => { navigate( `/settings/objects/${getObjectSlug( objectMetadataItem, - )}/new-field/step-2?fieldType=${FieldMetadataType.Select}`, + )}/new-field/configure?fieldType=${FieldMetadataType.Select}`, ); } else { navigate(`/settings/objects`); diff --git a/packages/twenty-front/src/pages/settings/Releases.tsx b/packages/twenty-front/src/pages/settings/Releases.tsx index 60845a1b8..3429ac989 100644 --- a/packages/twenty-front/src/pages/settings/Releases.tsx +++ b/packages/twenty-front/src/pages/settings/Releases.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useState } from 'react'; import rehypeStringify from 'rehype-stringify'; import remarkParse from 'remark-parse'; import remarkRehype from 'remark-rehype'; -import { IconRocket } from 'twenty-ui'; import { unified } from 'unified'; import { visit } from 'unist-util-visit'; @@ -107,7 +106,6 @@ export const Releases = () => { return ( { return ( ( ( { return ( { return ( { return ( ( { return ( { const currentWorkspace = useRecoilValue(currentWorkspaceState); return ( { // eslint-disable-next-line react/jsx-props-no-spreading {shouldDisplayAddFieldButton && ( - +