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:
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,41 +1,14 @@
|
|||||||
|
import { AdvancedSettingsContentWrapperWithDot } from '@/settings/components/AdvancedSettingsContentWrapperWithDot';
|
||||||
import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations';
|
import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations';
|
||||||
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
|
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { AnimatedExpandableContainer, IconPoint, MAIN_COLORS } from 'twenty-ui';
|
import { AnimatedExpandableContainer } 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;
|
|
||||||
`;
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledContent = styled.div`
|
const StyledContent = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIconPoint = styled(IconPoint)`
|
type DotPosition = 'top' | 'centered';
|
||||||
margin-right: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type AdvancedSettingsWrapperProps = {
|
type AdvancedSettingsWrapperProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -60,18 +33,12 @@ export const AdvancedSettingsWrapper = ({
|
|||||||
mode="scroll-height"
|
mode="scroll-height"
|
||||||
containAnimation={false}
|
containAnimation={false}
|
||||||
>
|
>
|
||||||
<StyledAdvancedWrapper>
|
<AdvancedSettingsContentWrapperWithDot
|
||||||
{!hideDot && (
|
hideDot={hideDot}
|
||||||
<StyledDotContainer dotPosition={dotPosition}>
|
dotPosition={dotPosition}
|
||||||
<StyledIconPoint
|
>
|
||||||
size={12}
|
|
||||||
color={MAIN_COLORS.yellow}
|
|
||||||
fill={MAIN_COLORS.yellow}
|
|
||||||
/>
|
|
||||||
</StyledDotContainer>
|
|
||||||
)}
|
|
||||||
<StyledContent>{children}</StyledContent>
|
<StyledContent>{children}</StyledContent>
|
||||||
</StyledAdvancedWrapper>
|
</AdvancedSettingsContentWrapperWithDot>
|
||||||
</AnimatedExpandableContainer>
|
</AnimatedExpandableContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
||||||
|
import { AdvancedSettingsContentWrapperWithDot } from '@/settings/components/AdvancedSettingsContentWrapperWithDot';
|
||||||
import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper';
|
import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper';
|
||||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||||
import { DATABASE_IDENTIFIER_MAXIMUM_LENGTH } from '@/settings/data-model/constants/DatabaseIdentifierMaximumLength';
|
import { DATABASE_IDENTIFIER_MAXIMUM_LENGTH } from '@/settings/data-model/constants/DatabaseIdentifierMaximumLength';
|
||||||
@ -47,7 +48,7 @@ type SettingsDataModelFieldIconLabelFormValues = z.infer<
|
|||||||
const StyledInputsContainer = styled.div`
|
const StyledInputsContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -86,6 +87,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
|
trigger,
|
||||||
} = useFormContext<SettingsDataModelFieldIconLabelFormValues>();
|
} = useFormContext<SettingsDataModelFieldIconLabelFormValues>();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -135,6 +137,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
onChange(value);
|
onChange(value);
|
||||||
|
trigger('label');
|
||||||
if (isLabelSyncedWithName === true) {
|
if (isLabelSyncedWithName === true) {
|
||||||
fillNameFromLabel(value);
|
fillNameFromLabel(value);
|
||||||
}
|
}
|
||||||
@ -152,8 +155,8 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
/>
|
/>
|
||||||
</StyledInputsContainer>
|
</StyledInputsContainer>
|
||||||
{canToggleSyncLabelWithName && (
|
{canToggleSyncLabelWithName && (
|
||||||
<StyledAdvancedSettingsOuterContainer>
|
<AdvancedSettingsWrapper hideDot>
|
||||||
<AdvancedSettingsWrapper>
|
<StyledAdvancedSettingsOuterContainer>
|
||||||
<StyledAdvancedSettingsContainer>
|
<StyledAdvancedSettingsContainer>
|
||||||
<StyledAdvancedSettingsSectionInputWrapper>
|
<StyledAdvancedSettingsSectionInputWrapper>
|
||||||
<StyledInputsContainer>
|
<StyledInputsContainer>
|
||||||
@ -207,27 +210,32 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
fieldMetadataItem?.isLabelSyncedWithName ?? true
|
fieldMetadataItem?.isLabelSyncedWithName ?? true
|
||||||
}
|
}
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Card rounded>
|
<AdvancedSettingsContentWrapperWithDot
|
||||||
<SettingsOptionCardContentToggle
|
hideDot={false}
|
||||||
Icon={IconRefresh}
|
dotPosition="centered"
|
||||||
title={t`Synchronize Field Label and API Name`}
|
>
|
||||||
description={t`Should changing a field's label also change the API name?`}
|
<Card rounded>
|
||||||
checked={value ?? true}
|
<SettingsOptionCardContentToggle
|
||||||
advancedMode
|
Icon={IconRefresh}
|
||||||
onChange={(value) => {
|
title={t`Synchronize Field Label and API Name`}
|
||||||
onChange(value);
|
description={t`Should changing a field's label also change the API name?`}
|
||||||
if (value === true) {
|
checked={value ?? true}
|
||||||
fillNameFromLabel(label);
|
advancedMode
|
||||||
}
|
onChange={(value) => {
|
||||||
}}
|
onChange(value);
|
||||||
/>
|
if (value === true) {
|
||||||
</Card>
|
fillNameFromLabel(label);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</AdvancedSettingsContentWrapperWithDot>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</StyledAdvancedSettingsSectionInputWrapper>
|
</StyledAdvancedSettingsSectionInputWrapper>
|
||||||
</StyledAdvancedSettingsContainer>
|
</StyledAdvancedSettingsContainer>
|
||||||
</AdvancedSettingsWrapper>
|
</StyledAdvancedSettingsOuterContainer>
|
||||||
</StyledAdvancedSettingsOuterContainer>
|
</AdvancedSettingsWrapper>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,10 +5,7 @@ const StyledInputErrorHelper = styled.div`
|
|||||||
color: ${({ theme }) => theme.color.red};
|
color: ${({ theme }) => theme.color.red};
|
||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
font-size: ${({ theme }) => theme.font.size.xs};
|
||||||
position: absolute;
|
position: absolute;
|
||||||
`;
|
top: calc(100% + ${({ theme }) => theme.spacing(0.25)});
|
||||||
|
|
||||||
const StyledErrorContainer = styled.div`
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const InputErrorHelper = ({
|
export const InputErrorHelper = ({
|
||||||
@ -16,11 +13,11 @@ export const InputErrorHelper = ({
|
|||||||
}: {
|
}: {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<StyledErrorContainer>
|
<div>
|
||||||
{children && (
|
{children && (
|
||||||
<StyledInputErrorHelper aria-live="polite">
|
<StyledInputErrorHelper aria-live="polite">
|
||||||
{children}
|
{children}
|
||||||
</StyledInputErrorHelper>
|
</StyledInputErrorHelper>
|
||||||
)}
|
)}
|
||||||
</StyledErrorContainer>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const StyledContainer = styled.div<
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: ${({ fullWidth }) => (fullWidth ? `100%` : 'auto')};
|
width: ${({ fullWidth }) => (fullWidth ? `100%` : 'auto')};
|
||||||
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledInputContainer = styled.div`
|
const StyledInputContainer = styled.div`
|
||||||
|
|||||||
Reference in New Issue
Block a user