Morph-front-settings (#13872)
# Implement Morph Frontend Settings Possiblity to create a morh relation from the Settings ! ## Overview Morph object system with new frontend settings UI for configuring morph relations. ## What Changed - **New Components**: `SettingsDataModelFieldMorphRelationForm`, `SettingsMorphRelationMultiSelect` - **Form System**: React Hook Form + Zod validation for morph relation configuration - **Preview System**: Live preview of field configurations with validation - **State Management**: Enhanced component state handling for complex form interactions ## Technical Details - **Architecture**: Restructured field input/display components for better maintainability - **Types**: Added comprehensive TypeScript types for morph relations (`isFieldMorphRelation`, etc.) - **Validation**: Dynamic validation rules based on field types and relation patterns - **Performance**: Optimized rendering with proper state management --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+118
-23
@@ -4,15 +4,22 @@ import styled from '@emotion/styled';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/components/SettingsDataModelObjectTypeTag';
|
||||
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
|
||||
import { OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display';
|
||||
import {
|
||||
IconBox,
|
||||
OverflowingTextWithTooltip,
|
||||
useIcons,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export type SettingsDataModelObjectSummaryProps = {
|
||||
export type SettingsDataModelObjectPreviewProps = {
|
||||
className?: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItems: Pick<
|
||||
ObjectMetadataItem,
|
||||
'icon' | 'labelSingular' | 'labelPlural' | 'isCustom' | 'isRemote'
|
||||
>[];
|
||||
pluralizeLabel?: boolean;
|
||||
};
|
||||
|
||||
const StyledObjectSummary = styled.div`
|
||||
const StyledObjectPreview = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -24,36 +31,124 @@ const StyledObjectName = styled.div`
|
||||
max-width: 60%;
|
||||
`;
|
||||
|
||||
const StyledOverflowingTextWithTooltip = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledNumber = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
export const SettingsDataModelObjectSummary = ({
|
||||
className,
|
||||
const StyledSeparator = styled.div`
|
||||
align-self: stretch;
|
||||
background: ${({ theme }) => theme.background.quaternary};
|
||||
height: 1px;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type SettingsDataModelObjectPreviewItemProps = {
|
||||
objectMetadataItem: Pick<
|
||||
ObjectMetadataItem,
|
||||
'icon' | 'labelSingular' | 'labelPlural' | 'isCustom' | 'isRemote'
|
||||
>;
|
||||
pluralizeLabel: boolean;
|
||||
index: number;
|
||||
};
|
||||
|
||||
const SettingsDataModelObjectPreviewItem = ({
|
||||
objectMetadataItem,
|
||||
pluralizeLabel = true,
|
||||
}: SettingsDataModelObjectSummaryProps) => {
|
||||
index,
|
||||
}: SettingsDataModelObjectPreviewItemProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const ObjectIcon = getIcon(objectMetadataItem.icon);
|
||||
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
|
||||
|
||||
return (
|
||||
<StyledObjectSummary className={className}>
|
||||
<StyledObjectName>
|
||||
<StyledIconContainer>
|
||||
<ObjectIcon size={theme.icon.size.sm} stroke={theme.icon.stroke.md} />
|
||||
</StyledIconContainer>
|
||||
<OverflowingTextWithTooltip
|
||||
text={
|
||||
pluralizeLabel
|
||||
? objectMetadataItem.labelPlural
|
||||
: objectMetadataItem.labelSingular
|
||||
}
|
||||
/>
|
||||
</StyledObjectName>
|
||||
<SettingsDataModelObjectTypeTag objectTypeLabel={objectTypeLabel} />
|
||||
</StyledObjectSummary>
|
||||
<>
|
||||
{index > 0 && <StyledSeparator />}
|
||||
<StyledObjectPreview key={`${objectMetadataItem.labelSingular}-${index}`}>
|
||||
<StyledObjectName>
|
||||
<StyledIconContainer>
|
||||
<ObjectIcon
|
||||
size={theme.icon.size.sm}
|
||||
stroke={theme.icon.stroke.md}
|
||||
/>
|
||||
</StyledIconContainer>
|
||||
<OverflowingTextWithTooltip
|
||||
text={
|
||||
pluralizeLabel
|
||||
? objectMetadataItem.labelPlural
|
||||
: objectMetadataItem.labelSingular
|
||||
}
|
||||
/>
|
||||
</StyledObjectName>
|
||||
<SettingsDataModelObjectTypeTag objectTypeLabel={objectTypeLabel} />
|
||||
</StyledObjectPreview>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SettingsDataModelObjectPreviewOtherObjects = ({
|
||||
selected,
|
||||
}: {
|
||||
selected: number;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<>
|
||||
<StyledSeparator />
|
||||
<StyledObjectPreview key={`other-objects`}>
|
||||
<StyledObjectName>
|
||||
<StyledIconContainer>
|
||||
<IconBox
|
||||
size={theme.icon.size.sm}
|
||||
stroke={theme.icon.stroke.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</StyledIconContainer>
|
||||
<StyledOverflowingTextWithTooltip>
|
||||
<OverflowingTextWithTooltip text={`Other objects`} />
|
||||
</StyledOverflowingTextWithTooltip>
|
||||
</StyledObjectName>
|
||||
<StyledNumber>
|
||||
<OverflowingTextWithTooltip text={`${selected - 3}`} />
|
||||
</StyledNumber>
|
||||
</StyledObjectPreview>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsDataModelObjectPreview = ({
|
||||
objectMetadataItems,
|
||||
pluralizeLabel = true,
|
||||
}: SettingsDataModelObjectPreviewProps) => {
|
||||
let selected = 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
{objectMetadataItems.map((objectMetadataItem, index) => {
|
||||
selected++;
|
||||
return selected <= 3 ? (
|
||||
<SettingsDataModelObjectPreviewItem
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
pluralizeLabel={pluralizeLabel}
|
||||
index={index}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
})}
|
||||
{selected > 3 && (
|
||||
<SettingsDataModelObjectPreviewOtherObjects selected={selected} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user