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 new file mode 100644 index 000000000..0521676c7 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx @@ -0,0 +1,103 @@ +import { Button } from '@/ui/input/button/components/Button'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +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 { 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}; + cursor: pointer; + display: flex; + font-size: ${({ theme }) => theme.font.size.md}; +`; +const StyledButtonContainer = styled.div` + position: relative; + width: 100%; +`; + +const StyledDownChevron = styled(IconChevronDown)` + color: ${({ theme }) => theme.font.color.primary}; + position: absolute; + right: ${({ theme }) => theme.spacing(1.5)}; + top: 50%; + transform: translateY(-50%); +`; + +const StyledMenuItem = styled(MenuItem)<{ selected?: boolean }>` + background: ${({ theme, selected }) => + selected ? theme.background.quaternary : 'transparent'}; + cursor: pointer; +`; + +const StyledSpan = styled.span` + margin-left: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledButton = styled(Button)` + color: ${({ theme }) => theme.font.color.primary}; + padding-right: ${({ theme }) => theme.spacing(6)}; +`; + +export const SettingsDataModelNewFieldBreadcrumbDropDown = ({ + isConfigureStep, + onBreadcrumbClick, +}: SettingsDataModelNewFieldBreadcrumbDropDownProps) => { + const dropdownId = `settings-object-new-field-breadcrumb-dropdown`; + + const { closeDropdown } = useDropdown(dropdownId); + + const handleClick = (step: boolean) => { + onBreadcrumbClick(step); + closeDropdown(); + }; + const theme = useTheme(); + + return ( + + New Field - + + + {isConfigureStep ? ( + + ) : ( + + )} + + } + dropdownComponents={ + + + handleClick(false)} + selected={!isConfigureStep} + /> + handleClick(true)} + selected={isConfigureStep} + /> + + + } + dropdownHotkeyScope={{ + scope: dropdownId, + }} + /> + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategories.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategories.ts new file mode 100644 index 000000000..07f8aa28c --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategories.ts @@ -0,0 +1,7 @@ +import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; + +export const SETTINGS_FIELD_TYPE_CATEGORIES: SettingsFieldTypeCategoryType[] = [ + 'Basic', + 'Relation', + 'Advanced', +]; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategoryDescriptions.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategoryDescriptions.ts new file mode 100644 index 000000000..aac9c8163 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeCategoryDescriptions.ts @@ -0,0 +1,10 @@ +import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; + +export const SETTINGS_FIELD_TYPE_CATEGORY_DESCRIPTIONS: Record< + SettingsFieldTypeCategoryType, + string +> = { + Basic: 'All the basic field types you need to start', + Advanced: 'More advanced fields for advanced projects', + Relation: 'Create a relation with another object', +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts index 3272ad990..0e6cf0cbf 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts @@ -23,6 +23,7 @@ import { import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode'; import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue'; +import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -32,6 +33,7 @@ export type SettingsFieldTypeConfig = { label: string; Icon: IconComponent; exampleValue?: unknown; + category: SettingsFieldTypeCategoryType; }; export const SETTINGS_FIELD_TYPE_CONFIGS = { @@ -39,85 +41,106 @@ export const SETTINGS_FIELD_TYPE_CONFIGS = { label: 'Unique ID', Icon: IconKey, exampleValue: '00000000-0000-0000-0000-000000000000', + category: 'Advanced', }, [FieldMetadataType.Text]: { label: 'Text', Icon: IconTextSize, exampleValue: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum magna enim, dapibus non enim in, lacinia faucibus nunc. Sed interdum ante sed felis facilisis, eget ultricies neque molestie. Mauris auctor, justo eu volutpat cursus, libero erat tempus nulla, non sodales lorem lacus a est.', + category: 'Basic', }, [FieldMetadataType.Numeric]: { label: 'Numeric', Icon: IconNumbers, exampleValue: 2000, + category: 'Basic', }, [FieldMetadataType.Number]: { label: 'Number', Icon: IconNumbers, exampleValue: 2000, + category: 'Basic', }, [FieldMetadataType.Link]: { label: 'Link', Icon: IconLink, exampleValue: { url: 'www.twenty.com', label: '' }, + category: 'Basic', }, [FieldMetadataType.Links]: { label: 'Links', Icon: IconLink, exampleValue: { primaryLinkUrl: 'twenty.com', primaryLinkLabel: '' }, + category: 'Basic', }, [FieldMetadataType.Boolean]: { label: 'True/False', Icon: IconCheck, exampleValue: true, + category: 'Basic', }, [FieldMetadataType.DateTime]: { label: 'Date and Time', Icon: IconCalendarTime, exampleValue: DEFAULT_DATE_VALUE.toISOString(), + category: 'Basic', }, [FieldMetadataType.Date]: { label: 'Date', Icon: IconCalendarEvent, exampleValue: DEFAULT_DATE_VALUE.toISOString(), + category: 'Basic', }, [FieldMetadataType.Select]: { label: 'Select', Icon: IconTag, + category: 'Basic', }, [FieldMetadataType.MultiSelect]: { label: 'Multi-select', Icon: IconTags, + category: 'Basic', }, [FieldMetadataType.Currency]: { label: 'Currency', Icon: IconCoins, exampleValue: { amountMicros: 2000000000, currencyCode: CurrencyCode.USD }, + category: 'Basic', }, [FieldMetadataType.Relation]: { label: 'Relation', Icon: IconRelationManyToMany, + category: 'Relation', + }, + [FieldMetadataType.Email]: { + label: 'Email', + Icon: IconMail, + category: 'Basic', }, - [FieldMetadataType.Email]: { label: 'Email', Icon: IconMail }, [FieldMetadataType.Emails]: { label: 'Emails', Icon: IconMail, exampleValue: { primaryEmail: 'john@twenty.com' }, + category: 'Basic', }, [FieldMetadataType.Phone]: { label: 'Phone', Icon: IconPhone, exampleValue: '+1234-567-890', + category: 'Basic', }, [FieldMetadataType.Rating]: { label: 'Rating', Icon: IconTwentyStar, exampleValue: '3', + category: 'Basic', }, [FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser, exampleValue: { firstName: 'John', lastName: 'Doe' }, + category: 'Advanced', }, [FieldMetadataType.Address]: { label: 'Address', @@ -132,20 +155,25 @@ export const SETTINGS_FIELD_TYPE_CONFIGS = { addressLat: 34.0522, addressLng: -118.2437, }, + category: 'Basic', }, [FieldMetadataType.RawJson]: { label: 'JSON', Icon: IconJson, exampleValue: { key: 'value' }, + + category: 'Basic', }, [FieldMetadataType.RichText]: { label: 'Rich Text', Icon: IconFilePencil, exampleValue: { key: 'value' }, + category: 'Basic', }, [FieldMetadataType.Actor]: { label: 'Actor', Icon: IconCreativeCommonsSa, + category: 'Basic', }, } as const satisfies Record< SettingsSupportedFieldType, diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldAboutForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldAboutForm.tsx deleted file mode 100644 index ee825e776..000000000 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldAboutForm.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import styled from '@emotion/styled'; -import { Controller, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; - -import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema'; -import { getErrorMessageFromError } from '@/settings/data-model/fields/forms/utils/errorMessages'; -import { IconPicker } from '@/ui/input/components/IconPicker'; -import { TextArea } from '@/ui/input/components/TextArea'; -import { TextInput } from '@/ui/input/components/TextInput'; - -export const settingsDataModelFieldAboutFormSchema = ( - existingLabels?: string[], -) => { - return fieldMetadataItemSchema(existingLabels || []).pick({ - description: true, - icon: true, - label: true, - }); -}; - -// Correctly infer the type from the returned schema -type SettingsDataModelFieldAboutFormValues = z.infer< - ReturnType ->; - -type SettingsDataModelFieldAboutFormProps = { - disabled?: boolean; - fieldMetadataItem?: FieldMetadataItem; - maxLength?: number; -}; - -const StyledInputsContainer = styled.div` - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; - margin-bottom: ${({ theme }) => theme.spacing(2)}; - width: 100%; -`; - -const LABEL = 'label'; - -export const SettingsDataModelFieldAboutForm = ({ - disabled, - fieldMetadataItem, - maxLength, -}: SettingsDataModelFieldAboutFormProps) => { - const { - control, - trigger, - formState: { errors }, - } = useFormContext(); - return ( - <> - - ( - onChange(iconKey)} - variant="primary" - /> - )} - /> - ( - { - onChange(e); - trigger(LABEL); - }} - error={getErrorMessageFromError(errors.label?.message)} - disabled={disabled} - maxLength={maxLength} - fullWidth - /> - )} - /> - - ( -