-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added new view to select types for objects #6700
Changes from 1 commit
dbb0c5d
0c940e9
7e4c40e
015f51f
ff591e7
5204ee0
6b74b32
b69f449
be22eb9
3b7c2e7
974703f
15e3e52
2787fa7
555a754
3d3fdcf
acbf5aa
c925c3e
4d0161a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; | ||
|
||
export const SETTINGS_FIELD_TYPE_CATEGORIES: SettingsFieldTypeCategoryType[] = [ | ||
'Basic', | ||
'Relation', | ||
'Advanced', | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: 'Custom relationships and predefined relationships', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,87 +33,108 @@ export type SettingsFieldTypeConfig = { | |
label: string; | ||
Icon: IconComponent; | ||
exampleValue?: unknown; | ||
category: SettingsFieldTypeCategoryType; | ||
}; | ||
|
||
export const SETTINGS_FIELD_TYPE_CONFIGS = { | ||
[FieldMetadataType.Uuid]: { | ||
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.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', | ||
|
@@ -127,20 +149,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', | ||
Comment on lines
+164
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Remove unnecessary newline between properties |
||
}, | ||
[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, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import omit from 'lodash.omit'; | ||
import styled from '@emotion/styled'; | ||
import { Controller, useFormContext } from 'react-hook-form'; | ||
import { z } from 'zod'; | ||
|
||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { SETTINGS_FIELD_TYPE_CATEGORIES } from '@/settings/data-model/constants/SettingsFieldTypeCategories'; | ||
import { SETTINGS_FIELD_TYPE_CATEGORY_DESCRIPTIONS } from '@/settings/data-model/constants/SettingsFieldTypeCategoryDescriptions'; | ||
import { | ||
SETTINGS_FIELD_TYPE_CONFIGS, | ||
SettingsFieldTypeConfig, | ||
|
@@ -11,7 +13,10 @@ import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/field | |
import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; | ||
import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields/forms/select/hooks/useSelectSettingsFormInitialValues'; | ||
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType'; | ||
import { Select, SelectOption } from '@/ui/input/components/Select'; | ||
import { Button } from '@/ui/input/button/components/Button'; | ||
import { Section } from '@react-email/components'; | ||
import { useState } from 'react'; | ||
import { H2Title } from 'twenty-ui'; | ||
import { FieldMetadataType } from '~/generated-metadata/graphql'; | ||
|
||
export const settingsDataModelFieldTypeFormSchema = z.object({ | ||
|
@@ -23,7 +28,7 @@ export const settingsDataModelFieldTypeFormSchema = z.object({ | |
), | ||
}); | ||
|
||
type SettingsDataModelFieldTypeFormValues = z.infer< | ||
export type SettingsDataModelFieldTypeFormValues = z.infer< | ||
typeof settingsDataModelFieldTypeFormSchema | ||
>; | ||
|
||
|
@@ -35,27 +40,55 @@ type SettingsDataModelFieldTypeSelectProps = { | |
FieldMetadataItem, | ||
'defaultValue' | 'options' | 'type' | ||
>; | ||
selectedFieldType?: SettingsSupportedFieldType; | ||
setSelectedFieldType: any; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider using a more specific type than 'any' for setSelectedFieldType |
||
|
||
const StyledButton = styled(Button)` | ||
height: 32px; | ||
width: 248px; | ||
`; | ||
const StyledButtonContainer = styled.div` | ||
display: flex; | ||
gap: 16px; | ||
justify-content: flex-start; | ||
flex-wrap: wrap; | ||
`; | ||
|
||
const StyledInput = styled.input` | ||
border-radius: 0; | ||
background-color: transparent; | ||
color: ${({ theme }) => theme.font.color.primary}; | ||
font-size: ${({ theme }) => theme.font.size.md}; | ||
margin: 0; | ||
outline: none; | ||
height: 24px; | ||
padding: 0; | ||
width: ${({ theme }) => `calc(100% - ${theme.spacing(8)})`}; | ||
|
||
&::placeholder { | ||
color: ${({ theme }) => theme.font.color.light}; | ||
font-weight: ${({ theme }) => theme.font.weight.medium}; | ||
} | ||
`; | ||
|
||
export const SettingsDataModelFieldTypeSelect = ({ | ||
className, | ||
disabled, | ||
excludedFieldTypes = [], | ||
fieldMetadataItem, | ||
selectedFieldType, | ||
setSelectedFieldType, | ||
}: SettingsDataModelFieldTypeSelectProps) => { | ||
const { control } = useFormContext<SettingsDataModelFieldTypeFormValues>(); | ||
|
||
const fieldTypeConfigs: Partial< | ||
Record<SettingsSupportedFieldType, SettingsFieldTypeConfig> | ||
> = omit(SETTINGS_FIELD_TYPE_CONFIGS, excludedFieldTypes); | ||
|
||
const fieldTypeOptions = Object.entries<SettingsFieldTypeConfig>( | ||
fieldTypeConfigs, | ||
).map<SelectOption<SettingsSupportedFieldType>>(([key, dataTypeConfig]) => ({ | ||
Icon: dataTypeConfig.Icon, | ||
label: dataTypeConfig.label, | ||
value: key as SettingsSupportedFieldType, | ||
})); | ||
const [searchQuery, setSearchQuery] = useState(''); | ||
const fieldTypeConfigs = Object.entries<SettingsFieldTypeConfig>( | ||
SETTINGS_FIELD_TYPE_CONFIGS, | ||
).filter( | ||
([key, config]) => | ||
!excludedFieldTypes.includes(key as SettingsSupportedFieldType) && | ||
config.label.toLowerCase().includes(searchQuery.toLowerCase()), | ||
); | ||
|
||
const { resetDefaultValueField: resetBooleanDefaultValueField } = | ||
useBooleanSettingsFormInitialValues({ fieldMetadataItem }); | ||
|
@@ -66,8 +99,6 @@ export const SettingsDataModelFieldTypeSelect = ({ | |
const { resetDefaultValueField: resetSelectDefaultValueField } = | ||
useSelectSettingsFormInitialValues({ fieldMetadataItem }); | ||
|
||
// Reset defaultValue on type change with a valid value for the selected type | ||
// so the form does not become invalid. | ||
const resetDefaultValueField = (nextValue: SettingsSupportedFieldType) => { | ||
switch (nextValue) { | ||
case FieldMetadataType.Boolean: | ||
|
@@ -90,23 +121,54 @@ export const SettingsDataModelFieldTypeSelect = ({ | |
name="type" | ||
control={control} | ||
defaultValue={ | ||
fieldMetadataItem && fieldMetadataItem.type in fieldTypeConfigs | ||
selectedFieldType || | ||
(fieldMetadataItem && | ||
fieldMetadataItem.type in SETTINGS_FIELD_TYPE_CONFIGS | ||
? (fieldMetadataItem.type as SettingsSupportedFieldType) | ||
: FieldMetadataType.Text | ||
: FieldMetadataType.Text) | ||
} | ||
render={({ field: { onChange, value } }) => ( | ||
<Select | ||
className={className} | ||
fullWidth | ||
disabled={disabled} | ||
dropdownId="object-field-type-select" | ||
value={value} | ||
onChange={(nextValue) => { | ||
onChange(nextValue); | ||
resetDefaultValueField(nextValue); | ||
}} | ||
options={fieldTypeOptions} | ||
/> | ||
<> | ||
<Section> | ||
<StyledInput | ||
type="text" | ||
placeholder="Search a field" | ||
value={searchQuery} | ||
onChange={(e) => setSearchQuery(e.target.value)} | ||
/> | ||
</Section> | ||
{SETTINGS_FIELD_TYPE_CATEGORIES.map((category) => ( | ||
<Section key={category}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: This nested mapping and filtering could potentially be optimized for better performance |
||
<H2Title | ||
title={category} | ||
description={ | ||
SETTINGS_FIELD_TYPE_CATEGORY_DESCRIPTIONS[category] | ||
} | ||
/> | ||
<StyledButtonContainer> | ||
{fieldTypeConfigs | ||
.filter(([, config]) => config.category === category) | ||
.map(([key, config]) => ( | ||
<StyledButton | ||
key={key} | ||
onClick={() => { | ||
onChange(key as SettingsSupportedFieldType); | ||
resetDefaultValueField( | ||
key as SettingsSupportedFieldType, | ||
); | ||
setSelectedFieldType(key as SettingsSupportedFieldType); | ||
}} | ||
disabled={disabled} | ||
variant={value === key ? 'primary' : 'secondary'} | ||
title={config.label} | ||
Icon={config.Icon} | ||
size="small" | ||
/> | ||
))} | ||
</StyledButtonContainer> | ||
</Section> | ||
))} | ||
</> | ||
)} | ||
/> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type SettingsFieldTypeCategoryType = 'Basic' | 'Advanced' | 'Relation'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider using
const
instead ofexport const
for better tree-shaking