Prevent relation update from settings (#13099)

## Expected behavior

Described behavior regarding: (update | create) x (custom | standard) x
(icon, label, name, isSynced)

**Custom:**
- Field RELATION create: name, label, isSynced, icon should be editable
- Field RELATION update: name should not, icon label, isSynced should
- For other fields, icon, label, name, isSynced should be editable at
field creation | update

To simplify: Field RELATION name should not be editable at update

**Standards**
- Field: create does not makes sense
- Field: name should not, icon label, isSynced should (this will end up
in overrides)

To simplify, no Field RELATION edge case, name should not be editable at
update

**Note:** the FE logic is quite different as the UI is hiding some
details behind the syncWithLabel. See my comments and TODO there


## What I've tested:
(update | create) x (custom | standard) x (icon, label, name, isSynced,
description)
This commit is contained in:
Charles Bochet
2025-07-08 21:03:38 +02:00
committed by GitHub
parent c8ec44eeaf
commit 39f6f3c4bb
13 changed files with 615 additions and 150 deletions

View File

@ -13,6 +13,7 @@ import { IconPicker } from '@/ui/input/components/IconPicker';
import { TextInput } from '@/ui/input/components/TextInput';
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
AppTooltip,
@ -74,11 +75,11 @@ const StyledAdvancedSettingsContainer = styled.div`
type SettingsDataModelFieldIconLabelFormProps = {
fieldMetadataItem?: FieldMetadataItem;
maxLength?: number;
canToggleSyncLabelWithName?: boolean;
isCreationMode?: boolean;
};
export const SettingsDataModelFieldIconLabelForm = ({
canToggleSyncLabelWithName = true,
isCreationMode = false,
fieldMetadataItem,
maxLength,
}: SettingsDataModelFieldIconLabelFormProps) => {
@ -92,6 +93,8 @@ export const SettingsDataModelFieldIconLabelForm = ({
const theme = useTheme();
const label = watch('label');
const { t } = useLingui();
const labelTextInputId = `${fieldMetadataItem?.id}-label`;
@ -102,7 +105,6 @@ export const SettingsDataModelFieldIconLabelForm = ({
(isDefined(fieldMetadataItem)
? fieldMetadataItem.isLabelSyncedWithName
: true);
const label = watch('label');
const apiNameTooltipText = isLabelSyncedWithName
? t`Deactivate "Synchronize Objects Labels and API Names" to set a custom API name`
@ -115,6 +117,27 @@ export const SettingsDataModelFieldIconLabelForm = ({
});
};
const isRelation =
fieldMetadataItem?.type === FieldMetadataType.RELATION ||
fieldMetadataItem?.type === FieldMetadataType.MORPH_RELATION;
const isCustomButNotRelationField =
fieldMetadataItem?.isCustom === true && !isRelation;
// TODO: remove the custom RELATION edge case, this will result in canToggleSyncLabelWithName = isCustom
const canToggleSyncLabelWithName =
!isCreationMode && isCustomButNotRelationField;
// TODO: remove custom RELATION edge case, this will result in isNameEditEnabled = isCustom
const isNameEditEnabled =
isLabelSyncedWithName === false && isCustomButNotRelationField;
// TODO: remove custom RELATION edge case, this will result in isLabelEditEnabled = true
const isLabelEditEnabled =
isCreationMode ||
(!isCreationMode &&
(fieldMetadataItem?.isCustom === false || isCustomButNotRelationField));
return (
<>
<StyledInputsContainer>
@ -124,7 +147,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
defaultValue={fieldMetadataItem?.icon ?? 'IconUsers'}
render={({ field: { onChange, value } }) => (
<IconPicker
selectedIconKey={value ?? ''}
selectedIconKey={value ?? 'IconUsers'}
onChange={({ iconKey }) => onChange(iconKey)}
variant="primary"
/>
@ -139,19 +162,19 @@ export const SettingsDataModelFieldIconLabelForm = ({
instanceId={labelTextInputId}
placeholder={t`Employees`}
value={value}
disabled={!isLabelEditEnabled}
onChange={(value) => {
onChange(value);
trigger('label');
if (isLabelSyncedWithName === true) {
if (
isCreationMode ||
(isLabelSyncedWithName === true &&
fieldMetadataItem?.isCustom === true)
) {
fillNameFromLabel(value);
}
}}
error={getErrorMessageFromError(errors.label?.message)}
disabled={
isLabelSyncedWithName === true &&
fieldMetadataItem &&
!fieldMetadataItem?.isCustom
}
maxLength={maxLength}
fullWidth
/>
@ -176,10 +199,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
placeholder={t`employees`}
value={value}
onChange={onChange}
disabled={
(isLabelSyncedWithName ?? false) ||
!fieldMetadataItem?.isCustom
}
disabled={!isNameEditEnabled}
fullWidth
maxLength={DATABASE_IDENTIFIER_MAXIMUM_LENGTH}
RightIcon={() =>
@ -228,8 +248,20 @@ export const SettingsDataModelFieldIconLabelForm = ({
advancedMode
onChange={(value) => {
onChange(value);
if (value === true) {
if (!isDefined(fieldMetadataItem)) {
return;
}
if (value === false) {
return;
}
if (
fieldMetadataItem.isCustom === true &&
!isRelation
) {
fillNameFromLabel(label);
return;
}
}}
/>

View File

@ -215,10 +215,7 @@ export const SettingsObjectFieldEdit = () => {
<SettingsDataModelFieldIconLabelForm
fieldMetadataItem={fieldMetadataItem}
maxLength={FIELD_NAME_MAXIMUM_LENGTH}
canToggleSyncLabelWithName={
fieldMetadataItem.type !== FieldMetadataType.RELATION &&
fieldMetadataItem.isCustom === true
}
isCreationMode={false}
/>
</Section>
{

View File

@ -221,9 +221,7 @@ export const SettingsObjectNewFieldConfigure = () => {
/>
<SettingsDataModelFieldIconLabelForm
maxLength={FIELD_NAME_MAXIMUM_LENGTH}
canToggleSyncLabelWithName={
fieldType !== FieldMetadataType.RELATION
}
isCreationMode={true}
/>
</Section>
<Section>