refactor: improve Settings supported field types validation (#4496)
* refactor: improve Settings supported field types validation Related to: #4084, #4295 * fix: fix wrong import
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||||
import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue';
|
import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue';
|
||||||
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
import {
|
import {
|
||||||
IconCalendarEvent,
|
IconCalendarEvent,
|
||||||
IconCheck,
|
IconCheck,
|
||||||
@ -20,11 +21,15 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|||||||
|
|
||||||
DEFAULT_DATE_VALUE.setFullYear(DEFAULT_DATE_VALUE.getFullYear() + 2);
|
DEFAULT_DATE_VALUE.setFullYear(DEFAULT_DATE_VALUE.getFullYear() + 2);
|
||||||
|
|
||||||
export const SETTINGS_FIELD_METADATA_TYPES: Partial<
|
export type SettingsFieldTypeConfig = {
|
||||||
Record<
|
label: string;
|
||||||
FieldMetadataType,
|
Icon: IconComponent;
|
||||||
{ label: string; Icon: IconComponent; defaultValue?: unknown }
|
defaultValue?: unknown;
|
||||||
>
|
};
|
||||||
|
|
||||||
|
export const SETTINGS_FIELD_TYPE_CONFIGS: Record<
|
||||||
|
SettingsSupportedFieldType,
|
||||||
|
SettingsFieldTypeConfig
|
||||||
> = {
|
> = {
|
||||||
[FieldMetadataType.Uuid]: {
|
[FieldMetadataType.Uuid]: {
|
||||||
label: 'Unique ID',
|
label: 'Unique ID',
|
||||||
@ -1,29 +1,38 @@
|
|||||||
import { SETTINGS_FIELD_METADATA_TYPES } from '@/settings/data-model/constants/SettingsFieldMetadataTypes';
|
import omit from 'lodash.omit';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SETTINGS_FIELD_TYPE_CONFIGS,
|
||||||
|
SettingsFieldTypeConfig,
|
||||||
|
} from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
|
||||||
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|
||||||
|
|
||||||
type SettingsDataModelFieldTypeSelectProps = {
|
type SettingsDataModelFieldTypeSelectProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
excludedFieldTypes?: FieldMetadataType[];
|
excludedFieldTypes?: SettingsSupportedFieldType[];
|
||||||
onChange?: ({ type }: { type: FieldMetadataType }) => void;
|
onChange?: ({ type }: { type: SettingsSupportedFieldType }) => void;
|
||||||
value?: FieldMetadataType;
|
value?: SettingsSupportedFieldType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsDataModelFieldTypeSelect = ({
|
export const SettingsDataModelFieldTypeSelect = ({
|
||||||
className,
|
className,
|
||||||
disabled,
|
disabled,
|
||||||
excludedFieldTypes,
|
excludedFieldTypes = [],
|
||||||
onChange,
|
onChange,
|
||||||
value,
|
value,
|
||||||
}: SettingsDataModelFieldTypeSelectProps) => {
|
}: SettingsDataModelFieldTypeSelectProps) => {
|
||||||
const fieldTypeOptions = Object.entries(SETTINGS_FIELD_METADATA_TYPES)
|
const fieldTypeConfigs = omit(
|
||||||
.filter(([key]) => !excludedFieldTypes?.includes(key as FieldMetadataType))
|
SETTINGS_FIELD_TYPE_CONFIGS,
|
||||||
.map<SelectOption<FieldMetadataType>>(([key, dataTypeConfig]) => ({
|
excludedFieldTypes,
|
||||||
Icon: dataTypeConfig.Icon,
|
);
|
||||||
label: dataTypeConfig.label,
|
const fieldTypeOptions = Object.entries<SettingsFieldTypeConfig>(
|
||||||
value: key as FieldMetadataType,
|
fieldTypeConfigs,
|
||||||
}));
|
).map<SelectOption<SettingsSupportedFieldType>>(([key, dataTypeConfig]) => ({
|
||||||
|
Icon: dataTypeConfig.Icon,
|
||||||
|
label: dataTypeConfig.label,
|
||||||
|
value: key as SettingsSupportedFieldType,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { v4 } from 'uuid';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||||
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||||
import {
|
import {
|
||||||
FieldMetadataType,
|
FieldMetadataType,
|
||||||
@ -17,7 +18,7 @@ type FormValues = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
type: FieldMetadataType;
|
type: SettingsSupportedFieldType;
|
||||||
} & SettingsDataModelFieldSettingsFormValues;
|
} & SettingsDataModelFieldSettingsFormValues;
|
||||||
|
|
||||||
export const fieldMetadataFormDefaultValues: FormValues = {
|
export const fieldMetadataFormDefaultValues: FormValues = {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { css, useTheme } from '@emotion/react';
|
import { css, useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { SETTINGS_FIELD_METADATA_TYPES } from '@/settings/data-model/constants/SettingsFieldMetadataTypes';
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
|
import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSettingsFieldTypeConfig';
|
||||||
import { IconTwentyStar } from '@/ui/display/icon/components/IconTwentyStar';
|
import { IconTwentyStar } from '@/ui/display/icon/components/IconTwentyStar';
|
||||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
@ -10,10 +11,10 @@ type SettingsObjectFieldDataTypeProps = {
|
|||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
Icon?: IconComponent;
|
Icon?: IconComponent;
|
||||||
label?: string;
|
label?: string;
|
||||||
value: FieldMetadataType;
|
value: SettingsSupportedFieldType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledDataType = styled.div<{ value: FieldMetadataType }>`
|
const StyledDataType = styled.div<{ value: SettingsSupportedFieldType }>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
@ -49,11 +50,16 @@ const StyledLabelContainer = styled.div`
|
|||||||
export const SettingsObjectFieldDataType = ({
|
export const SettingsObjectFieldDataType = ({
|
||||||
onClick,
|
onClick,
|
||||||
value,
|
value,
|
||||||
Icon = SETTINGS_FIELD_METADATA_TYPES[value]?.Icon ?? IconTwentyStar,
|
Icon: IconFromProps,
|
||||||
label = SETTINGS_FIELD_METADATA_TYPES[value]?.label,
|
label: labelFromProps,
|
||||||
}: SettingsObjectFieldDataTypeProps) => {
|
}: SettingsObjectFieldDataTypeProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const fieldTypeConfig = getSettingsFieldTypeConfig(value);
|
||||||
|
const Icon: IconComponent =
|
||||||
|
IconFromProps ?? fieldTypeConfig?.Icon ?? IconTwentyStar;
|
||||||
|
const label = labelFromProps ?? fieldTypeConfig?.label;
|
||||||
|
|
||||||
const StyledIcon = styled(Icon)`
|
const StyledIcon = styled(Icon)`
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import styled from '@emotion/styled';
|
|||||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||||
import { SETTINGS_FIELD_METADATA_TYPES } from '@/settings/data-model/constants/SettingsFieldMetadataTypes';
|
|
||||||
import { FieldIdentifierType } from '@/settings/data-model/types/FieldIdentifierType';
|
import { FieldIdentifierType } from '@/settings/data-model/types/FieldIdentifierType';
|
||||||
|
import { isFieldTypeSupportedInSettings } from '@/settings/data-model/utils/isFieldTypeSupportedInSettings';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
@ -49,10 +49,6 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
const Icon = getIcon(fieldMetadataItem.icon);
|
const Icon = getIcon(fieldMetadataItem.icon);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// TODO: parse with zod and merge types with FieldType (create a subset of FieldType for example)
|
|
||||||
const fieldDataTypeIsSupported =
|
|
||||||
fieldMetadataItem.type in SETTINGS_FIELD_METADATA_TYPES;
|
|
||||||
|
|
||||||
const getRelationMetadata = useGetRelationMetadata();
|
const getRelationMetadata = useGetRelationMetadata();
|
||||||
|
|
||||||
const { relationObjectMetadataItem, relationType } =
|
const { relationObjectMetadataItem, relationType } =
|
||||||
@ -61,7 +57,10 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
[fieldMetadataItem, getRelationMetadata],
|
[fieldMetadataItem, getRelationMetadata],
|
||||||
) ?? {};
|
) ?? {};
|
||||||
|
|
||||||
if (!fieldDataTypeIsSupported) return null;
|
const fieldType = fieldMetadataItem.type;
|
||||||
|
const isFieldTypeSupported = isFieldTypeSupportedInSettings(fieldType);
|
||||||
|
|
||||||
|
if (!isFieldTypeSupported) return null;
|
||||||
|
|
||||||
const RelationIcon = relationType
|
const RelationIcon = relationType
|
||||||
? RELATION_TYPES[relationType].Icon
|
? RELATION_TYPES[relationType].Icon
|
||||||
@ -97,7 +96,7 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
value={fieldMetadataItem.type}
|
value={fieldType}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<StyledIconTableCell>{ActionIcon}</StyledIconTableCell>
|
<StyledIconTableCell>{ActionIcon}</StyledIconTableCell>
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
|
export type SettingsSupportedFieldType = Exclude<
|
||||||
|
FieldMetadataType,
|
||||||
|
FieldMetadataType.Position
|
||||||
|
>;
|
||||||
@ -3,7 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|||||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||||
import { SettingsObjectFieldSelectFormValues } from '@/settings/data-model/components/SettingsObjectFieldSelectForm';
|
import { SettingsObjectFieldSelectFormValues } from '@/settings/data-model/components/SettingsObjectFieldSelectForm';
|
||||||
import { SETTINGS_FIELD_METADATA_TYPES } from '@/settings/data-model/constants/SettingsFieldMetadataTypes';
|
import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSettingsFieldTypeConfig';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
@ -39,12 +39,16 @@ export const getFieldDefaultPreviewValue = ({
|
|||||||
|
|
||||||
if (!relationLabelIdentifierFieldMetadataItem) return null;
|
if (!relationLabelIdentifierFieldMetadataItem) return null;
|
||||||
|
|
||||||
|
const { type: relationLabelIdentifierFieldType } =
|
||||||
|
relationLabelIdentifierFieldMetadataItem;
|
||||||
|
const relationFieldTypeConfig = getSettingsFieldTypeConfig(
|
||||||
|
relationLabelIdentifierFieldType,
|
||||||
|
);
|
||||||
|
|
||||||
const defaultRelationLabelIdentifierFieldValue =
|
const defaultRelationLabelIdentifierFieldValue =
|
||||||
relationLabelIdentifierFieldMetadataItem.type === FieldMetadataType.Text
|
relationLabelIdentifierFieldType === FieldMetadataType.Text
|
||||||
? relationObjectMetadataItem.labelSingular
|
? relationObjectMetadataItem.labelSingular
|
||||||
: SETTINGS_FIELD_METADATA_TYPES[
|
: relationFieldTypeConfig?.defaultValue;
|
||||||
relationLabelIdentifierFieldMetadataItem.type
|
|
||||||
]?.defaultValue;
|
|
||||||
|
|
||||||
const defaultRelationRecord = {
|
const defaultRelationRecord = {
|
||||||
[relationLabelIdentifierFieldMetadataItem.name]:
|
[relationLabelIdentifierFieldMetadataItem.name]:
|
||||||
@ -54,6 +58,7 @@ export const getFieldDefaultPreviewValue = ({
|
|||||||
return defaultRelationRecord;
|
return defaultRelationRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Other fields
|
||||||
const isLabelIdentifier =
|
const isLabelIdentifier =
|
||||||
!!fieldMetadataItem.id &&
|
!!fieldMetadataItem.id &&
|
||||||
!!fieldMetadataItem.name &&
|
!!fieldMetadataItem.name &&
|
||||||
@ -65,8 +70,9 @@ export const getFieldDefaultPreviewValue = ({
|
|||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Other fields
|
const fieldTypeConfig = getSettingsFieldTypeConfig(fieldMetadataItem.type);
|
||||||
|
|
||||||
return isLabelIdentifier && fieldMetadataItem.type === FieldMetadataType.Text
|
return isLabelIdentifier && fieldMetadataItem.type === FieldMetadataType.Text
|
||||||
? objectMetadataItem.labelSingular
|
? objectMetadataItem.labelSingular
|
||||||
: SETTINGS_FIELD_METADATA_TYPES[fieldMetadataItem.type]?.defaultValue;
|
: fieldTypeConfig?.defaultValue;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
|
||||||
|
import { isFieldTypeSupportedInSettings } from '@/settings/data-model/utils/isFieldTypeSupportedInSettings';
|
||||||
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
|
export const getSettingsFieldTypeConfig = (fieldType: FieldMetadataType) =>
|
||||||
|
isFieldTypeSupportedInSettings(fieldType)
|
||||||
|
? SETTINGS_FIELD_TYPE_CONFIGS[fieldType]
|
||||||
|
: undefined;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
|
||||||
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
|
export const isFieldTypeSupportedInSettings = (
|
||||||
|
fieldType: FieldMetadataType,
|
||||||
|
): fieldType is SettingsSupportedFieldType =>
|
||||||
|
fieldType in SETTINGS_FIELD_TYPE_CONFIGS;
|
||||||
@ -17,6 +17,7 @@ import { SettingsObjectFieldFormSection } from '@/settings/data-model/components
|
|||||||
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
|
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
|
||||||
import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect';
|
import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect';
|
||||||
import { useFieldMetadataForm } from '@/settings/data-model/fields/forms/hooks/useFieldMetadataForm';
|
import { useFieldMetadataForm } from '@/settings/data-model/fields/forms/hooks/useFieldMetadataForm';
|
||||||
|
import { isFieldTypeSupportedInSettings } from '@/settings/data-model/utils/isFieldTypeSupportedInSettings';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { IconArchive, IconSettings } from '@/ui/display/icon';
|
import { IconArchive, IconSettings } from '@/ui/display/icon';
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
@ -112,11 +113,16 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
(optionA, optionB) => optionA.position - optionB.position,
|
(optionA, optionB) => optionA.position - optionB.position,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const fieldType = activeMetadataField.type;
|
||||||
|
const isFieldTypeSupported = isFieldTypeSupportedInSettings(fieldType);
|
||||||
|
|
||||||
|
if (!isFieldTypeSupported) return;
|
||||||
|
|
||||||
initForm({
|
initForm({
|
||||||
icon: activeMetadataField.icon ?? undefined,
|
icon: activeMetadataField.icon ?? undefined,
|
||||||
label: activeMetadataField.label,
|
label: activeMetadataField.label,
|
||||||
description: activeMetadataField.description ?? undefined,
|
description: activeMetadataField.description ?? undefined,
|
||||||
type: activeMetadataField.type,
|
type: fieldType,
|
||||||
...(currencyDefaultValue ? { currency: currencyDefaultValue } : {}),
|
...(currencyDefaultValue ? { currency: currencyDefaultValue } : {}),
|
||||||
relation: {
|
relation: {
|
||||||
field: {
|
field: {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { SettingsObjectFieldFormSection } from '@/settings/data-model/components
|
|||||||
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
|
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
|
||||||
import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect';
|
import { SettingsDataModelFieldTypeSelect } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect';
|
||||||
import { useFieldMetadataForm } from '@/settings/data-model/fields/forms/hooks/useFieldMetadataForm';
|
import { useFieldMetadataForm } from '@/settings/data-model/fields/forms/hooks/useFieldMetadataForm';
|
||||||
|
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { IconSettings } from '@/ui/display/icon';
|
import { IconSettings } from '@/ui/display/icon';
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
@ -259,7 +260,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const excludedFieldTypes = [
|
const excludedFieldTypes: SettingsSupportedFieldType[] = [
|
||||||
FieldMetadataType.Currency,
|
FieldMetadataType.Currency,
|
||||||
FieldMetadataType.Email,
|
FieldMetadataType.Email,
|
||||||
FieldMetadataType.FullName,
|
FieldMetadataType.FullName,
|
||||||
|
|||||||
Reference in New Issue
Block a user