-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor default value for select (#5343)
In this PR, we are refactoring two things: - leverage field.defaultValue for Select and MultiSelect settings form (instead of option.isDefault) - use quoted string (ex: "'USD'") for string default values to embrace backend format --------- Co-authored-by: Thaïs Guigon <guigon.thais@gmail.com>
- Loading branch information
1 parent
7728c09
commit 8590bd7
Showing
40 changed files
with
843 additions
and
559 deletions.
There are no files selected for viewing
8 changes: 0 additions & 8 deletions
8
packages/twenty-front/src/modules/object-metadata/types/FieldMetadataOption.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 4 additions & 64 deletions
68
packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemInput.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,22 @@ | ||
import toSnakeCase from 'lodash.snakecase'; | ||
|
||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { getDefaultValueForBackend } from '@/object-metadata/utils/getDefaultValueForBackend'; | ||
import { FieldMetadataType } from '~/generated-metadata/graphql'; | ||
import { formatMetadataLabelToMetadataNameOrThrows } from '~/pages/settings/data-model/utils/format-metadata-label-to-name.util'; | ||
import { isDefined } from '~/utils/isDefined'; | ||
|
||
import { FieldMetadataOption } from '../types/FieldMetadataOption'; | ||
|
||
export const getOptionValueFromLabel = (label: string) => { | ||
// Remove accents | ||
const unaccentedLabel = label | ||
.normalize('NFD') | ||
.replace(/[\u0300-\u036f]/g, ''); | ||
// Remove special characters | ||
const noSpecialCharactersLabel = unaccentedLabel.replace( | ||
/[^a-zA-Z0-9 ]/g, | ||
'', | ||
); | ||
|
||
return toSnakeCase(noSpecialCharactersLabel).toUpperCase(); | ||
}; | ||
|
||
export const formatFieldMetadataItemInput = ( | ||
input: Partial< | ||
Pick< | ||
FieldMetadataItem, | ||
'type' | 'label' | 'defaultValue' | 'icon' | 'description' | ||
'type' | 'label' | 'defaultValue' | 'icon' | 'description' | 'options' | ||
> | ||
> & { options?: FieldMetadataOption[] }, | ||
>, | ||
) => { | ||
const options = input.options as FieldMetadataOption[] | undefined; | ||
let defaultValue = input.defaultValue; | ||
if (input.type === FieldMetadataType.MultiSelect) { | ||
defaultValue = options | ||
?.filter((option) => option.isDefault) | ||
?.map((defaultOption) => getOptionValueFromLabel(defaultOption.label)); | ||
} | ||
if (input.type === FieldMetadataType.Select) { | ||
const defaultOption = options?.find((option) => option.isDefault); | ||
defaultValue = isDefined(defaultOption) | ||
? getOptionValueFromLabel(defaultOption.label) | ||
: undefined; | ||
} | ||
|
||
// Check if options has unique values | ||
if (options !== undefined) { | ||
// Compute the values based on the label | ||
const values = options.map((option) => | ||
getOptionValueFromLabel(option.label), | ||
); | ||
|
||
if (new Set(values).size !== options.length) { | ||
throw new Error( | ||
`Options must have unique values, but contains the following duplicates ${values.join( | ||
', ', | ||
)}`, | ||
); | ||
} | ||
} | ||
|
||
const label = input.label?.trim(); | ||
|
||
return { | ||
defaultValue: | ||
isDefined(defaultValue) && input.type | ||
? getDefaultValueForBackend(defaultValue, input.type) | ||
: undefined, | ||
defaultValue: input.defaultValue, | ||
description: input.description?.trim() ?? null, | ||
icon: input.icon, | ||
label, | ||
name: label ? formatMetadataLabelToMetadataNameOrThrows(label) : undefined, | ||
options: options?.map((option, index) => ({ | ||
color: option.color, | ||
id: option.id, | ||
label: option.label.trim(), | ||
position: index, | ||
value: getOptionValueFromLabel(option.label), | ||
})), | ||
options: input.options, | ||
}; | ||
}; |
22 changes: 0 additions & 22 deletions
22
packages/twenty-front/src/modules/object-metadata/utils/getDefaultValueForBackend.ts
This file was deleted.
Oops, something went wrong.
46 changes: 46 additions & 0 deletions
46
packages/twenty-front/src/modules/object-metadata/validation-schemas/selectOptionsSchema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { z } from 'zod'; | ||
|
||
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { getOptionValueFromLabel } from '@/settings/data-model/fields/forms/utils/getOptionValueFromLabel'; | ||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema'; | ||
|
||
const selectOptionSchema = z | ||
.object({ | ||
color: themeColorSchema, | ||
id: z.string(), | ||
label: z.string().trim().min(1), | ||
position: z.number(), | ||
value: z.string(), | ||
}) | ||
.refine((option) => option.value === getOptionValueFromLabel(option.label), { | ||
message: 'Value does not match label', | ||
}) satisfies z.ZodType<FieldMetadataItemOption>; | ||
|
||
export const selectOptionsSchema = z | ||
.array(selectOptionSchema) | ||
.min(1) | ||
.refine( | ||
(options) => { | ||
const optionIds = options.map(({ id }) => id); | ||
return new Set(optionIds).size === options.length; | ||
}, | ||
{ | ||
message: 'Options must have unique ids', | ||
}, | ||
) | ||
.refine( | ||
(options) => { | ||
const optionValues = options.map(({ value }) => value); | ||
return new Set(optionValues).size === options.length; | ||
}, | ||
{ | ||
message: 'Options must have unique values', | ||
}, | ||
) | ||
.refine( | ||
(options) => | ||
[...options].sort().every((option, index) => option.position === index), | ||
{ | ||
message: 'Options positions must be sequential', | ||
}, | ||
); |
5 changes: 5 additions & 0 deletions
5
...nty-front/src/modules/object-record/record-field/validation-schemas/currencyCodeSchema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { z } from 'zod'; | ||
|
||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode'; | ||
|
||
export const currencyCodeSchema = z.nativeEnum(CurrencyCode); |
Oops, something went wrong.