feat: add EnumFieldDisplay and Enum field preview (#2487)
Closes #2428 Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -32,8 +32,8 @@ const StyledSettingsObjectFieldTypeCard = styled(SettingsObjectFieldTypeCard)`
|
|||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
margin-top: ${({ theme }) => theme.spacing(4)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// TODO: remove "relation" type for now, add it back when the backend is ready.
|
// TODO: remove "enum" and "relation" types for now, add them back when the backend is ready.
|
||||||
const { RELATION: _, ...dataTypesWithoutRelation } = dataTypes;
|
const { ENUM: _ENUM, RELATION: _RELATION, ...allowedDataTypes } = dataTypes;
|
||||||
|
|
||||||
export const SettingsObjectFieldTypeSelectSection = ({
|
export const SettingsObjectFieldTypeSelectSection = ({
|
||||||
disabled,
|
disabled,
|
||||||
@ -57,12 +57,10 @@ export const SettingsObjectFieldTypeSelectSection = ({
|
|||||||
dropdownScopeId="object-field-type-select"
|
dropdownScopeId="object-field-type-select"
|
||||||
value={fieldType}
|
value={fieldType}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
options={Object.entries(dataTypesWithoutRelation).map(
|
options={Object.entries(allowedDataTypes).map(([key, dataType]) => ({
|
||||||
([key, dataType]) => ({
|
value: key as FieldMetadataType,
|
||||||
value: key as FieldMetadataType,
|
...dataType,
|
||||||
...dataType,
|
}))}
|
||||||
}),
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
{['BOOLEAN', 'DATE', 'MONEY', 'NUMBER', 'TEXT', 'URL'].includes(
|
{['BOOLEAN', 'DATE', 'MONEY', 'NUMBER', 'TEXT', 'URL'].includes(
|
||||||
fieldType,
|
fieldType,
|
||||||
|
|||||||
@ -73,6 +73,14 @@ export const Number: Story = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Select: Story = {
|
||||||
|
args: {
|
||||||
|
fieldIconKey: 'IconBuildingFactory2',
|
||||||
|
fieldLabel: 'Industry',
|
||||||
|
fieldType: FieldMetadataType.Enum,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const CustomObject: Story = {
|
export const CustomObject: Story = {
|
||||||
args: {
|
args: {
|
||||||
isObjectCustom: true,
|
isObjectCustom: true,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
IconNumbers,
|
IconNumbers,
|
||||||
IconPhone,
|
IconPhone,
|
||||||
IconPlug,
|
IconPlug,
|
||||||
|
IconTag,
|
||||||
IconTextSize,
|
IconTextSize,
|
||||||
IconUser,
|
IconUser,
|
||||||
} from '@/ui/display/icon';
|
} from '@/ui/display/icon';
|
||||||
@ -52,6 +53,11 @@ export const dataTypes: Record<
|
|||||||
Icon: IconCalendarEvent,
|
Icon: IconCalendarEvent,
|
||||||
defaultValue: defaultDateValue.toISOString(),
|
defaultValue: defaultDateValue.toISOString(),
|
||||||
},
|
},
|
||||||
|
[FieldMetadataType.Enum]: {
|
||||||
|
label: 'Select',
|
||||||
|
Icon: IconTag,
|
||||||
|
defaultValue: { color: 'green', text: 'Option 1' },
|
||||||
|
},
|
||||||
[FieldMetadataType.Currency]: {
|
[FieldMetadataType.Currency]: {
|
||||||
label: 'Currency',
|
label: 'Currency',
|
||||||
Icon: IconCoins,
|
Icon: IconCoins,
|
||||||
@ -66,5 +72,4 @@ export const dataTypes: Record<
|
|||||||
defaultValue: 50,
|
defaultValue: 50,
|
||||||
},
|
},
|
||||||
[FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser },
|
[FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser },
|
||||||
[FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug },
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
export type MetadataFieldDataType =
|
export type MetadataFieldDataType =
|
||||||
| 'BOOLEAN'
|
| 'BOOLEAN'
|
||||||
| 'DATE'
|
| 'DATE'
|
||||||
|
| 'ENUM'
|
||||||
| 'MONEY'
|
| 'MONEY'
|
||||||
| 'NUMBER'
|
| 'NUMBER'
|
||||||
| 'RELATION'
|
| 'RELATION'
|
||||||
|
|||||||
@ -35,10 +35,17 @@ const StyledTag = styled.h3<{
|
|||||||
gap: ${({ theme }) => theme.spacing(2)};
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
height: ${({ theme }) => theme.spacing(5)};
|
height: ${({ theme }) => theme.spacing(5)};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledContent = styled.span`
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
export type TagProps = {
|
export type TagProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
color: ThemeColor;
|
color: ThemeColor;
|
||||||
@ -52,6 +59,6 @@ export const Tag = ({ className, color, text, onClick }: TagProps) => (
|
|||||||
color={castToTagColor(color)}
|
color={castToTagColor(color)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{text}
|
<StyledContent>{text}</StyledContent>
|
||||||
</StyledTag>
|
</StyledTag>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -34,6 +34,17 @@ export const Default: Story = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WithLongText: Story = {
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
args: {
|
||||||
|
color: 'green',
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
container: { width: 100 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const Catalog: CatalogStory<Story, typeof Tag> = {
|
export const Catalog: CatalogStory<Story, typeof Tag> = {
|
||||||
args: { text: 'Urgent' },
|
args: { text: 'Urgent' },
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { FullNameFieldDisplay } from '@/ui/object/field/meta-types/display/components/FullNameFieldDisplay';
|
import { FullNameFieldDisplay } from '@/ui/object/field/meta-types/display/components/FullNameFieldDisplay';
|
||||||
|
import { LinkFieldDisplay } from '@/ui/object/field/meta-types/display/components/LinkFieldDisplay';
|
||||||
import { RelationFieldDisplay } from '@/ui/object/field/meta-types/display/components/RelationFieldDisplay';
|
import { RelationFieldDisplay } from '@/ui/object/field/meta-types/display/components/RelationFieldDisplay';
|
||||||
import { UuidFieldDisplay } from '@/ui/object/field/meta-types/display/components/UuidFieldDisplay';
|
import { UuidFieldDisplay } from '@/ui/object/field/meta-types/display/components/UuidFieldDisplay';
|
||||||
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
|
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
|
||||||
|
import { isFieldLink } from '@/ui/object/field/types/guards/isFieldLink';
|
||||||
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
|
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
|
||||||
|
|
||||||
import { FieldContext } from '../contexts/FieldContext';
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
@ -12,7 +14,7 @@ import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyF
|
|||||||
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
|
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
|
||||||
import { DoubleTextChipFieldDisplay } from '../meta-types/display/components/DoubleTextChipFieldDisplay';
|
import { DoubleTextChipFieldDisplay } from '../meta-types/display/components/DoubleTextChipFieldDisplay';
|
||||||
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
|
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
|
||||||
import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay';
|
import { EnumFieldDisplay } from '../meta-types/display/components/EnumFieldDisplay';
|
||||||
import { MoneyFieldDisplay } from '../meta-types/display/components/MoneyFieldDisplay';
|
import { MoneyFieldDisplay } from '../meta-types/display/components/MoneyFieldDisplay';
|
||||||
import { NumberFieldDisplay } from '../meta-types/display/components/NumberFieldDisplay';
|
import { NumberFieldDisplay } from '../meta-types/display/components/NumberFieldDisplay';
|
||||||
import { PhoneFieldDisplay } from '../meta-types/display/components/PhoneFieldDisplay';
|
import { PhoneFieldDisplay } from '../meta-types/display/components/PhoneFieldDisplay';
|
||||||
@ -23,7 +25,7 @@ import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
|||||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||||
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
|
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
|
||||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
import { isFieldEnum } from '../types/guards/isFieldEnum';
|
||||||
import { isFieldMoney } from '../types/guards/isFieldMoney';
|
import { isFieldMoney } from '../types/guards/isFieldMoney';
|
||||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||||
@ -64,6 +66,8 @@ export const FieldDisplay = () => {
|
|||||||
<ChipFieldDisplay />
|
<ChipFieldDisplay />
|
||||||
) : isFieldDoubleTextChip(fieldDefinition) ? (
|
) : isFieldDoubleTextChip(fieldDefinition) ? (
|
||||||
<DoubleTextChipFieldDisplay />
|
<DoubleTextChipFieldDisplay />
|
||||||
|
) : isFieldEnum(fieldDefinition) ? (
|
||||||
|
<EnumFieldDisplay />
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||||
|
|
||||||
|
import { useEnumField } from '../../hooks/useEnumField';
|
||||||
|
|
||||||
|
export const EnumFieldDisplay = () => {
|
||||||
|
const { fieldValue } = useEnumField();
|
||||||
|
|
||||||
|
return <Tag color={fieldValue.color} text={fieldValue.text} />;
|
||||||
|
};
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|
||||||
|
import { FieldContext } from '../../../../contexts/FieldContext';
|
||||||
|
import { FieldEnumValue } from '../../../../types/FieldMetadata';
|
||||||
|
import { useEnumField } from '../../../hooks/useEnumField';
|
||||||
|
import { EnumFieldDisplay } from '../EnumFieldDisplay';
|
||||||
|
|
||||||
|
const EnumFieldValueSetterEffect = ({ value }: { value: FieldEnumValue }) => {
|
||||||
|
const { setFieldValue } = useEnumField();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFieldValue(value);
|
||||||
|
}, [setFieldValue, value]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta = {
|
||||||
|
title: 'UI/Data/Field/Display/EnumFieldDisplay',
|
||||||
|
decorators: [
|
||||||
|
(Story, { args }) => (
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
entityId: '',
|
||||||
|
fieldDefinition: {
|
||||||
|
fieldMetadataId: 'enum',
|
||||||
|
label: 'Enum',
|
||||||
|
type: 'ENUM',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'Enum',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hotkeyScope: 'hotkey-scope',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EnumFieldValueSetterEffect value={args.value} />
|
||||||
|
<Story />
|
||||||
|
</FieldContext.Provider>
|
||||||
|
),
|
||||||
|
ComponentDecorator,
|
||||||
|
],
|
||||||
|
component: EnumFieldDisplay,
|
||||||
|
args: {
|
||||||
|
value: { color: 'purple', text: 'Lorem ipsum' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof EnumFieldDisplay>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { FieldEnumValue } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
|
||||||
|
import { FieldContext } from '../../contexts/FieldContext';
|
||||||
|
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
|
||||||
|
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
|
||||||
|
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||||
|
import { isFieldEnum } from '../../types/guards/isFieldEnum';
|
||||||
|
import { isFieldEnumValue } from '../../types/guards/isFieldEnumValue';
|
||||||
|
|
||||||
|
export const useEnumField = () => {
|
||||||
|
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||||
|
|
||||||
|
assertFieldMetadata('ENUM', isFieldEnum, fieldDefinition);
|
||||||
|
|
||||||
|
const { fieldName } = fieldDefinition.metadata;
|
||||||
|
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<FieldEnumValue>(
|
||||||
|
entityFieldsFamilySelector({
|
||||||
|
entityId: entityId,
|
||||||
|
fieldName: fieldName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const fieldEnumValue = isFieldEnumValue(fieldValue)
|
||||||
|
? fieldValue
|
||||||
|
: { color: 'green' as ThemeColor, text: '' };
|
||||||
|
|
||||||
|
const fieldInitialValue = useFieldInitialValue();
|
||||||
|
|
||||||
|
const initialValue = {
|
||||||
|
color: 'green' as ThemeColor,
|
||||||
|
text: fieldInitialValue?.isEmpty
|
||||||
|
? ''
|
||||||
|
: fieldInitialValue?.value ?? fieldEnumValue?.text ?? '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
fieldDefinition,
|
||||||
|
fieldValue: fieldEnumValue,
|
||||||
|
initialValue,
|
||||||
|
setFieldValue,
|
||||||
|
hotkeyScope,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
|
||||||
export type FieldUuidMetadata = {
|
export type FieldUuidMetadata = {
|
||||||
placeHolder: string;
|
placeHolder: string;
|
||||||
@ -94,6 +95,10 @@ export type FieldBooleanMetadata = {
|
|||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FieldEnumMetadata = {
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type FieldMetadata =
|
export type FieldMetadata =
|
||||||
| FieldBooleanMetadata
|
| FieldBooleanMetadata
|
||||||
| FieldChipMetadata
|
| FieldChipMetadata
|
||||||
@ -107,6 +112,7 @@ export type FieldMetadata =
|
|||||||
| FieldNumberMetadata
|
| FieldNumberMetadata
|
||||||
| FieldPhoneMetadata
|
| FieldPhoneMetadata
|
||||||
| FieldProbabilityMetadata
|
| FieldProbabilityMetadata
|
||||||
|
| FieldEnumMetadata
|
||||||
| FieldRelationMetadata
|
| FieldRelationMetadata
|
||||||
| FieldTextMetadata
|
| FieldTextMetadata
|
||||||
| FieldURLMetadata
|
| FieldURLMetadata
|
||||||
@ -140,3 +146,5 @@ export type FieldDoubleTextChipValue = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FieldRelationValue = EntityForSelect | null;
|
export type FieldRelationValue = EntityForSelect | null;
|
||||||
|
|
||||||
|
export type FieldEnumValue = { color: ThemeColor; text: string };
|
||||||
|
|||||||
@ -1,19 +1,25 @@
|
|||||||
export type FieldType =
|
export type FieldType =
|
||||||
|
| 'BOOLEAN'
|
||||||
| 'UUID'
|
| 'UUID'
|
||||||
| 'TEXT'
|
| 'TEXT'
|
||||||
| 'RELATION'
|
| 'RELATION'
|
||||||
| 'CHIP'
|
| 'CHIP'
|
||||||
|
| 'DATE'
|
||||||
| 'DOUBLE_TEXT_CHIP'
|
| 'DOUBLE_TEXT_CHIP'
|
||||||
| 'DOUBLE_TEXT'
|
| 'DOUBLE_TEXT'
|
||||||
| 'NUMBER'
|
|
||||||
| 'EMAIL'
|
| 'EMAIL'
|
||||||
| 'BOOLEAN'
|
| 'ENUM'
|
||||||
| 'DATE'
|
| 'MONEY_AMOUNT_V2'
|
||||||
|
| 'MONEY_AMOUNT'
|
||||||
|
| 'MONEY'
|
||||||
|
| 'NUMBER'
|
||||||
|
| 'PHONE'
|
||||||
|
| 'PROBABILITY'
|
||||||
|
| 'RELATION'
|
||||||
|
| 'TEXT'
|
||||||
|
| 'URL'
|
||||||
| 'PHONE'
|
| 'PHONE'
|
||||||
| 'URL'
|
| 'URL'
|
||||||
| 'LINK'
|
| 'LINK'
|
||||||
| 'PROBABILITY'
|
|
||||||
| 'CURRENCY'
|
| 'CURRENCY'
|
||||||
| 'MONEY_AMOUNT'
|
|
||||||
| 'MONEY'
|
|
||||||
| 'FULL_NAME';
|
| 'FULL_NAME';
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
FieldDoubleTextChipMetadata,
|
FieldDoubleTextChipMetadata,
|
||||||
FieldDoubleTextMetadata,
|
FieldDoubleTextMetadata,
|
||||||
FieldEmailMetadata,
|
FieldEmailMetadata,
|
||||||
|
FieldEnumMetadata,
|
||||||
FieldFullnameMetadata,
|
FieldFullnameMetadata,
|
||||||
FieldLinkMetadata,
|
FieldLinkMetadata,
|
||||||
FieldMetadata,
|
FieldMetadata,
|
||||||
@ -43,6 +44,8 @@ type AssertFieldMetadataFunction = <
|
|||||||
? FieldLinkMetadata
|
? FieldLinkMetadata
|
||||||
: E extends 'MONEY_AMOUNT'
|
: E extends 'MONEY_AMOUNT'
|
||||||
? FieldMoneyMetadata
|
? FieldMoneyMetadata
|
||||||
|
: E extends 'ENUM'
|
||||||
|
? FieldEnumMetadata
|
||||||
: E extends 'NUMBER'
|
: E extends 'NUMBER'
|
||||||
? FieldNumberMetadata
|
? FieldNumberMetadata
|
||||||
: E extends 'PHONE'
|
: E extends 'PHONE'
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { FieldDefinition } from '../FieldDefinition';
|
||||||
|
import { FieldEnumMetadata, FieldMetadata } from '../FieldMetadata';
|
||||||
|
|
||||||
|
export const isFieldEnum = (
|
||||||
|
field: FieldDefinition<FieldMetadata>,
|
||||||
|
): field is FieldDefinition<FieldEnumMetadata> => field.type === 'ENUM';
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { mainColors, ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
|
||||||
|
const enumColors = Object.keys(mainColors) as [ThemeColor, ...ThemeColor[]];
|
||||||
|
const enumValueSchema = z.object({
|
||||||
|
color: z.enum(enumColors),
|
||||||
|
text: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const isFieldEnumValue = (
|
||||||
|
fieldValue: unknown,
|
||||||
|
): fieldValue is z.infer<typeof enumValueSchema> =>
|
||||||
|
enumValueSchema.safeParse(fieldValue).success;
|
||||||
Reference in New Issue
Block a user