Fix field metadata creation page (#11285)

in this pr 
1. fixing error helper was no longer showing. unfortunately had to
resort to `formConfig.trigger`...
2. closes https://github.com/twentyhq/twenty/issues/11262
3. closes https://github.com/twentyhq/twenty/issues/11263


https://github.com/user-attachments/assets/11f763bb-3098-4b0e-bc96-8a0de3cb3c19
This commit is contained in:
Marie
2025-04-01 14:09:24 +02:00
committed by GitHub
parent 9cbc2e3df0
commit 366106bb2b
5 changed files with 99 additions and 67 deletions

View File

@ -0,0 +1,59 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconPoint } from 'twenty-ui';
const StyledWrapper = styled.div`
position: relative;
width: 100%;
`;
type DotPosition = 'top' | 'centered';
type AdvancedSettingsContentWrapperWithDotProps = {
children: React.ReactNode;
hideDot?: boolean;
dotPosition?: DotPosition;
};
const StyledDotContainer = styled.div<{ dotPosition: DotPosition }>`
display: flex;
position: absolute;
height: 100%;
left: ${({ theme }) => theme.spacing(-5)};
${({ dotPosition }) => {
if (dotPosition === 'top') {
return `
top: 0;
`;
}
return `
align-items: center;
`;
}}
`;
const StyledIconPoint = styled(IconPoint)`
margin-right: 0;
`;
export const AdvancedSettingsContentWrapperWithDot = ({
children,
hideDot = false,
dotPosition = 'centered',
}: AdvancedSettingsContentWrapperWithDotProps) => {
const theme = useTheme();
return (
<StyledWrapper>
{!hideDot && (
<StyledDotContainer dotPosition={dotPosition}>
<StyledIconPoint
size={12}
color={theme.color.yellow}
fill={theme.color.yellow}
/>
</StyledDotContainer>
)}
{children}
</StyledWrapper>
);
};

View File

@ -1,41 +1,14 @@
import { AdvancedSettingsContentWrapperWithDot } from '@/settings/components/AdvancedSettingsContentWrapperWithDot';
import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { AnimatedExpandableContainer, IconPoint, MAIN_COLORS } from 'twenty-ui';
type DotPosition = 'top' | 'centered';
const StyledAdvancedWrapper = styled.div`
position: relative;
width: 100%;
`;
const StyledDotContainer = styled.div<{ dotPosition: DotPosition }>`
display: flex;
position: absolute;
height: 100%;
left: ${({ theme }) => theme.spacing(-5)};
${({ dotPosition }) => {
if (dotPosition === 'top') {
return `
top: 0;
`;
}
return `
align-items: center;
`;
}}
`;
import { AnimatedExpandableContainer } from 'twenty-ui';
const StyledContent = styled.div`
width: 100%;
`;
const StyledIconPoint = styled(IconPoint)`
margin-right: 0;
`;
type DotPosition = 'top' | 'centered';
type AdvancedSettingsWrapperProps = {
children: React.ReactNode;
@ -60,18 +33,12 @@ export const AdvancedSettingsWrapper = ({
mode="scroll-height"
containAnimation={false}
>
<StyledAdvancedWrapper>
{!hideDot && (
<StyledDotContainer dotPosition={dotPosition}>
<StyledIconPoint
size={12}
color={MAIN_COLORS.yellow}
fill={MAIN_COLORS.yellow}
/>
</StyledDotContainer>
)}
<AdvancedSettingsContentWrapperWithDot
hideDot={hideDot}
dotPosition={dotPosition}
>
<StyledContent>{children}</StyledContent>
</StyledAdvancedWrapper>
</AdvancedSettingsContentWrapperWithDot>
</AnimatedExpandableContainer>
);
};

View File

@ -4,6 +4,7 @@ import { z } from 'zod';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
import { AdvancedSettingsContentWrapperWithDot } from '@/settings/components/AdvancedSettingsContentWrapperWithDot';
import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
import { DATABASE_IDENTIFIER_MAXIMUM_LENGTH } from '@/settings/data-model/constants/DatabaseIdentifierMaximumLength';
@ -47,7 +48,7 @@ type SettingsDataModelFieldIconLabelFormValues = z.infer<
const StyledInputsContainer = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
margin-bottom: ${({ theme }) => theme.spacing(2)};
margin-bottom: ${({ theme }) => theme.spacing(1)};
width: 100%;
`;
@ -86,6 +87,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
setValue,
watch,
formState: { errors },
trigger,
} = useFormContext<SettingsDataModelFieldIconLabelFormValues>();
const theme = useTheme();
@ -135,6 +137,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
value={value}
onChange={(value) => {
onChange(value);
trigger('label');
if (isLabelSyncedWithName === true) {
fillNameFromLabel(value);
}
@ -152,8 +155,8 @@ export const SettingsDataModelFieldIconLabelForm = ({
/>
</StyledInputsContainer>
{canToggleSyncLabelWithName && (
<StyledAdvancedSettingsOuterContainer>
<AdvancedSettingsWrapper>
<AdvancedSettingsWrapper hideDot>
<StyledAdvancedSettingsOuterContainer>
<StyledAdvancedSettingsContainer>
<StyledAdvancedSettingsSectionInputWrapper>
<StyledInputsContainer>
@ -207,27 +210,32 @@ export const SettingsDataModelFieldIconLabelForm = ({
fieldMetadataItem?.isLabelSyncedWithName ?? true
}
render={({ field: { onChange, value } }) => (
<Card rounded>
<SettingsOptionCardContentToggle
Icon={IconRefresh}
title={t`Synchronize Field Label and API Name`}
description={t`Should changing a field's label also change the API name?`}
checked={value ?? true}
advancedMode
onChange={(value) => {
onChange(value);
if (value === true) {
fillNameFromLabel(label);
}
}}
/>
</Card>
<AdvancedSettingsContentWrapperWithDot
hideDot={false}
dotPosition="centered"
>
<Card rounded>
<SettingsOptionCardContentToggle
Icon={IconRefresh}
title={t`Synchronize Field Label and API Name`}
description={t`Should changing a field's label also change the API name?`}
checked={value ?? true}
advancedMode
onChange={(value) => {
onChange(value);
if (value === true) {
fillNameFromLabel(label);
}
}}
/>
</Card>
</AdvancedSettingsContentWrapperWithDot>
)}
/>
</StyledAdvancedSettingsSectionInputWrapper>
</StyledAdvancedSettingsContainer>
</AdvancedSettingsWrapper>
</StyledAdvancedSettingsOuterContainer>
</StyledAdvancedSettingsOuterContainer>
</AdvancedSettingsWrapper>
)}
</>
);

View File

@ -5,10 +5,7 @@ const StyledInputErrorHelper = styled.div`
color: ${({ theme }) => theme.color.red};
font-size: ${({ theme }) => theme.font.size.xs};
position: absolute;
`;
const StyledErrorContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(1)};
top: calc(100% + ${({ theme }) => theme.spacing(0.25)});
`;
export const InputErrorHelper = ({
@ -16,11 +13,11 @@ export const InputErrorHelper = ({
}: {
children?: React.ReactNode;
}) => (
<StyledErrorContainer>
<div>
{children && (
<StyledInputErrorHelper aria-live="polite">
{children}
</StyledInputErrorHelper>
)}
</StyledErrorContainer>
</div>
);

View File

@ -22,6 +22,7 @@ const StyledContainer = styled.div<
display: inline-flex;
flex-direction: column;
width: ${({ fullWidth }) => (fullWidth ? `100%` : 'auto')};
position: relative;
`;
const StyledInputContainer = styled.div`