feat: rename Probability field type to Rating and update preview (#2770)
Closes #2593
This commit is contained in:
@@ -12,7 +12,7 @@ import { EmailFieldInput } from '../meta-types/input/components/EmailFieldInput'
|
||||
import { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput';
|
||||
import { NumberFieldInput } from '../meta-types/input/components/NumberFieldInput';
|
||||
import { PhoneFieldInput } from '../meta-types/input/components/PhoneFieldInput';
|
||||
import { ProbabilityFieldInput } from '../meta-types/input/components/ProbabilityFieldInput';
|
||||
import { RatingFieldInput } from '../meta-types/input/components/RatingFieldInput';
|
||||
import { RelationFieldInput } from '../meta-types/input/components/RelationFieldInput';
|
||||
import { TextFieldInput } from '../meta-types/input/components/TextFieldInput';
|
||||
import { FieldInputEvent } from '../types/FieldInputEvent';
|
||||
@@ -23,7 +23,7 @@ import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||
import { isFieldRating } from '../types/guards/isFieldRating';
|
||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
|
||||
@@ -120,8 +120,8 @@ export const FieldInput = ({
|
||||
/>
|
||||
) : isFieldBoolean(fieldDefinition) ? (
|
||||
<BooleanFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldProbability(fieldDefinition) ? (
|
||||
<ProbabilityFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldRating(fieldDefinition) ? (
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useContext } from 'react';
|
||||
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||
import { isFieldRating } from '../types/guards/isFieldRating';
|
||||
|
||||
export const useIsFieldInputOnly = () => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
if (isFieldBoolean(fieldDefinition) || isFieldProbability(fieldDefinition)) {
|
||||
if (isFieldBoolean(fieldDefinition) || isFieldRating(fieldDefinition)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldNumberValue } from '../types/guards/isFieldNumberValue';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldPhoneValue } from '../types/guards/isFieldPhoneValue';
|
||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||
import { isFieldProbabilityValue } from '../types/guards/isFieldProbabilityValue';
|
||||
import { isFieldRating } from '../types/guards/isFieldRating';
|
||||
import { isFieldRatingValue } from '../types/guards/isFieldRatingValue';
|
||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||
import { isFieldRelationValue } from '../types/guards/isFieldRelationValue';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
@@ -61,8 +61,7 @@ export const usePersistField = () => {
|
||||
isFieldBooleanValue(valueToPersist);
|
||||
|
||||
const fieldIsProbability =
|
||||
isFieldProbability(fieldDefinition) &&
|
||||
isFieldProbabilityValue(valueToPersist);
|
||||
isFieldRating(fieldDefinition) && isFieldRatingValue(valueToPersist);
|
||||
|
||||
const fieldIsNumber =
|
||||
isFieldNumber(fieldDefinition) && isFieldNumberValue(valueToPersist);
|
||||
|
||||
+13
-6
@@ -1,30 +1,37 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldProbability } from '../../types/guards/isFieldProbability';
|
||||
import { isFieldRating } from '../../types/guards/isFieldRating';
|
||||
import { FieldRatingValue } from '../../types/FieldMetadata';
|
||||
|
||||
export const useProbabilityField = () => {
|
||||
export const useRatingField = () => {
|
||||
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata('PROBABILITY', isFieldProbability, fieldDefinition);
|
||||
assertFieldMetadata(
|
||||
FieldMetadataType.Probability,
|
||||
isFieldRating,
|
||||
fieldDefinition,
|
||||
);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<number | null>(
|
||||
const [fieldValue, setFieldValue] = useRecoilState<FieldRatingValue | null>(
|
||||
entityFieldsFamilySelector({
|
||||
entityId: entityId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const probabilityIndex = Math.ceil((fieldValue ?? 0) / 25);
|
||||
const rating = +(fieldValue ?? 0);
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
probabilityIndex,
|
||||
rating,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
import { ProbabilityInput } from '@/ui/object/field/meta-types/input/components/internal/ProbabilityInput';
|
||||
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useProbabilityField } from '../../hooks/useProbabilityField';
|
||||
|
||||
import { FieldInputEvent } from './DateFieldInput';
|
||||
|
||||
export type ProbabilityFieldInputProps = {
|
||||
onSubmit?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const ProbabilityFieldInput = ({
|
||||
onSubmit,
|
||||
}: ProbabilityFieldInputProps) => {
|
||||
const { probabilityIndex } = useProbabilityField();
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const handleChange = (newValue: number) => {
|
||||
onSubmit?.(() => persistField(newValue));
|
||||
};
|
||||
|
||||
return (
|
||||
<ProbabilityInput
|
||||
probabilityIndex={probabilityIndex}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { RatingInput } from '@/ui/object/field/meta-types/input/components/internal/RatingInput';
|
||||
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useRatingField } from '../../hooks/useRatingField';
|
||||
|
||||
import { FieldInputEvent } from './DateFieldInput';
|
||||
|
||||
export type RatingFieldInputProps = {
|
||||
onSubmit?: FieldInputEvent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const RatingFieldInput = ({
|
||||
onSubmit,
|
||||
readonly,
|
||||
}: RatingFieldInputProps) => {
|
||||
const { rating } = useRatingField();
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const handleChange = (newRating: number) => {
|
||||
onSubmit?.(() => persistField(`${newRating}`));
|
||||
};
|
||||
|
||||
return (
|
||||
<RatingInput value={rating} onChange={handleChange} readonly={readonly} />
|
||||
);
|
||||
};
|
||||
+27
-27
@@ -4,16 +4,19 @@ import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
import { useProbabilityField } from '../../../hooks/useProbabilityField';
|
||||
import {
|
||||
ProbabilityFieldInput,
|
||||
ProbabilityFieldInputProps,
|
||||
} from '../ProbabilityFieldInput';
|
||||
import { useRatingField } from '../../../hooks/useRatingField';
|
||||
import { RatingFieldInput, RatingFieldInputProps } from '../RatingFieldInput';
|
||||
import { FieldRatingValue } from '../../../../types/FieldMetadata';
|
||||
|
||||
const ProbabilityFieldValueSetterEffect = ({ value }: { value: number }) => {
|
||||
const { setFieldValue } = useProbabilityField();
|
||||
const RatingFieldValueSetterEffect = ({
|
||||
value,
|
||||
}: {
|
||||
value: FieldRatingValue;
|
||||
}) => {
|
||||
const { setFieldValue } = useRatingField();
|
||||
|
||||
useEffect(() => {
|
||||
setFieldValue(value);
|
||||
@@ -22,16 +25,16 @@ const ProbabilityFieldValueSetterEffect = ({ value }: { value: number }) => {
|
||||
return <></>;
|
||||
};
|
||||
|
||||
type ProbabilityFieldInputWithContextProps = ProbabilityFieldInputProps & {
|
||||
value: number;
|
||||
type RatingFieldInputWithContextProps = RatingFieldInputProps & {
|
||||
value: FieldRatingValue;
|
||||
entityId?: string;
|
||||
};
|
||||
|
||||
const ProbabilityFieldInputWithContext = ({
|
||||
const RatingFieldInputWithContext = ({
|
||||
entityId,
|
||||
value,
|
||||
onSubmit,
|
||||
}: ProbabilityFieldInputWithContextProps) => {
|
||||
}: RatingFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -41,18 +44,18 @@ const ProbabilityFieldInputWithContext = ({
|
||||
return (
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'probability',
|
||||
label: 'Probability',
|
||||
type: 'PROBABILITY',
|
||||
fieldMetadataId: 'rating',
|
||||
label: 'Rating',
|
||||
type: FieldMetadataType.Probability,
|
||||
iconName: 'Icon123',
|
||||
metadata: {
|
||||
fieldName: 'Probability',
|
||||
fieldName: 'Rating',
|
||||
},
|
||||
}}
|
||||
entityId={entityId}
|
||||
>
|
||||
<ProbabilityFieldValueSetterEffect value={value} />
|
||||
<ProbabilityFieldInput onSubmit={onSubmit} />
|
||||
<RatingFieldValueSetterEffect value={value} />
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
</FieldContextProvider>
|
||||
);
|
||||
};
|
||||
@@ -67,11 +70,10 @@ const clearMocksDecorator: Decorator = (Story, context) => {
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/ProbabilityFieldInput',
|
||||
component: ProbabilityFieldInputWithContext,
|
||||
title: 'UI/Data/Field/Input/RatingFieldInput',
|
||||
component: RatingFieldInputWithContext,
|
||||
args: {
|
||||
value: 25,
|
||||
isPositive: true,
|
||||
value: '3',
|
||||
onSubmit: submitJestFn,
|
||||
},
|
||||
argTypes: {
|
||||
@@ -85,7 +87,7 @@ const meta: Meta = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ProbabilityFieldInputWithContext>;
|
||||
type Story = StoryObj<typeof RatingFieldInputWithContext>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
@@ -95,12 +97,10 @@ export const Submit: Story = {
|
||||
|
||||
expect(submitJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
const item = (await canvas.findByText('25%'))?.nextElementSibling
|
||||
?.firstElementChild;
|
||||
const input = canvas.getByRole('slider', { name: 'Rating' });
|
||||
const firstStar = input.firstElementChild;
|
||||
|
||||
if (item) {
|
||||
userEvent.click(item);
|
||||
}
|
||||
if (firstStar) userEvent.click(firstStar);
|
||||
|
||||
expect(submitJestFn).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledProgressBarItemContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledProgressBarItem = styled.div<{
|
||||
isFirst: boolean;
|
||||
isLast: boolean;
|
||||
isActive: boolean;
|
||||
}>`
|
||||
background-color: ${({ theme, isActive }) =>
|
||||
isActive
|
||||
? theme.font.color.secondary
|
||||
: theme.background.transparent.medium};
|
||||
border-bottom-left-radius: ${({ theme, isFirst }) =>
|
||||
isFirst ? theme.border.radius.sm : theme.border.radius.xs};
|
||||
border-bottom-right-radius: ${({ theme, isLast }) =>
|
||||
isLast ? theme.border.radius.sm : theme.border.radius.xs};
|
||||
border-top-left-radius: ${({ theme, isFirst }) =>
|
||||
isFirst ? theme.border.radius.sm : theme.border.radius.xs};
|
||||
border-top-right-radius: ${({ theme, isLast }) =>
|
||||
isLast ? theme.border.radius.sm : theme.border.radius.xs};
|
||||
height: ${({ theme }) => theme.spacing(2)};
|
||||
width: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledProgressBarContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.div`
|
||||
width: ${({ theme }) => theme.spacing(12)};
|
||||
`;
|
||||
|
||||
const PROBABILITY_VALUES = [
|
||||
{ label: '0%', value: 0 },
|
||||
{ label: '25%', value: 25 },
|
||||
{ label: '50%', value: 50 },
|
||||
{ label: '75%', value: 75 },
|
||||
{ label: '100%', value: 100 },
|
||||
];
|
||||
|
||||
type ProbabilityInputProps = {
|
||||
probabilityIndex: number | null;
|
||||
onChange: (newValue: number) => void;
|
||||
};
|
||||
|
||||
export const ProbabilityInput = ({
|
||||
onChange,
|
||||
probabilityIndex,
|
||||
}: ProbabilityInputProps) => {
|
||||
const [hoveredProbabilityIndex, setHoveredProbabilityIndex] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
|
||||
const probabilityIndexToShow =
|
||||
hoveredProbabilityIndex ?? probabilityIndex ?? 0;
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLabel>
|
||||
{PROBABILITY_VALUES[probabilityIndexToShow].label}
|
||||
</StyledLabel>
|
||||
<StyledProgressBarContainer>
|
||||
{PROBABILITY_VALUES.map((probability, probabilityIndexToSelect) => (
|
||||
<StyledProgressBarItemContainer
|
||||
key={probabilityIndexToSelect}
|
||||
onClick={() => onChange(probability.value)}
|
||||
onMouseEnter={() =>
|
||||
setHoveredProbabilityIndex(probabilityIndexToSelect)
|
||||
}
|
||||
onMouseLeave={() => setHoveredProbabilityIndex(null)}
|
||||
>
|
||||
<StyledProgressBarItem
|
||||
isActive={
|
||||
hoveredProbabilityIndex || hoveredProbabilityIndex === 0
|
||||
? probabilityIndexToSelect <= hoveredProbabilityIndex
|
||||
: probabilityIndexToSelect <= probabilityIndexToShow
|
||||
}
|
||||
key={probability.label}
|
||||
isFirst={probabilityIndexToSelect === 0}
|
||||
isLast={
|
||||
probabilityIndexToSelect === PROBABILITY_VALUES.length - 1
|
||||
}
|
||||
/>
|
||||
</StyledProgressBarItemContainer>
|
||||
))}
|
||||
</StyledProgressBarContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconTwentyStarFilled } from '@/ui/display/icon/components/IconTwentyStarFilled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledRatingIconContainer = styled.div<{ isActive?: boolean }>`
|
||||
color: ${({ isActive, theme }) =>
|
||||
isActive ? theme.font.color.secondary : theme.background.quaternary};
|
||||
display: inline-flex;
|
||||
`;
|
||||
|
||||
type RatingInputProps = {
|
||||
onChange: (newValue: number) => void;
|
||||
value: number;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
const RATING_LEVELS_NB = 5;
|
||||
|
||||
export const RatingInput = ({
|
||||
onChange,
|
||||
value,
|
||||
readonly,
|
||||
}: RatingInputProps) => {
|
||||
const theme = useTheme();
|
||||
const [hoveredValue, setHoveredValue] = useState<number | null>(null);
|
||||
const currentValue = hoveredValue ?? value;
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
role="slider"
|
||||
aria-label="Rating"
|
||||
aria-valuemax={RATING_LEVELS_NB}
|
||||
aria-valuemin={1}
|
||||
aria-valuenow={value}
|
||||
tabIndex={0}
|
||||
>
|
||||
{Array.from({ length: RATING_LEVELS_NB }, (_, index) => {
|
||||
const rating = index + 1;
|
||||
|
||||
return (
|
||||
<StyledRatingIconContainer
|
||||
key={index}
|
||||
isActive={rating <= currentValue}
|
||||
onClick={readonly ? undefined : () => onChange(rating)}
|
||||
onMouseEnter={readonly ? undefined : () => setHoveredValue(rating)}
|
||||
onMouseLeave={readonly ? undefined : () => setHoveredValue(null)}
|
||||
>
|
||||
<IconTwentyStarFilled size={theme.icon.size.md} />
|
||||
</StyledRatingIconContainer>
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
+2
-2
@@ -15,7 +15,7 @@ import { isFieldEmail } from '../../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../../types/guards/isFieldLink';
|
||||
import { isFieldLinkValue } from '../../types/guards/isFieldLinkValue';
|
||||
import { isFieldNumber } from '../../types/guards/isFieldNumber';
|
||||
import { isFieldProbability } from '../../types/guards/isFieldProbability';
|
||||
import { isFieldRating } from '../../types/guards/isFieldRating';
|
||||
import { isFieldRelation } from '../../types/guards/isFieldRelation';
|
||||
import { isFieldRelationValue } from '../../types/guards/isFieldRelationValue';
|
||||
import { isFieldText } from '../../types/guards/isFieldText';
|
||||
@@ -40,7 +40,7 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
isFieldText(fieldDefinition) ||
|
||||
isFieldDateTime(fieldDefinition) ||
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldProbability(fieldDefinition) ||
|
||||
isFieldRating(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldBoolean(fieldDefinition)
|
||||
//|| isFieldPhone(fieldDefinition)
|
||||
|
||||
@@ -61,7 +61,7 @@ export type FieldPhoneMetadata = {
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldProbabilityMetadata = {
|
||||
export type FieldRatingMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
};
|
||||
@@ -95,7 +95,7 @@ export type FieldMetadata =
|
||||
| FieldLinkMetadata
|
||||
| FieldNumberMetadata
|
||||
| FieldPhoneMetadata
|
||||
| FieldProbabilityMetadata
|
||||
| FieldRatingMetadata
|
||||
| FieldRelationMetadata
|
||||
| FieldSelectMetadata
|
||||
| FieldTextMetadata
|
||||
@@ -115,7 +115,7 @@ export type FieldCurrencyValue = {
|
||||
amountMicros: number | null;
|
||||
};
|
||||
export type FieldFullNameValue = { firstName: string; lastName: string };
|
||||
export type FieldProbabilityValue = number;
|
||||
export type FieldRatingValue = '1' | '2' | '3' | '4' | '5';
|
||||
export type FieldSelectValue = { color: ThemeColor; label: string };
|
||||
|
||||
export type FieldRelationValue = EntityForSelect | null;
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
FieldMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldPhoneMetadata,
|
||||
FieldProbabilityMetadata,
|
||||
FieldRatingMetadata,
|
||||
FieldRelationMetadata,
|
||||
FieldSelectMetadata,
|
||||
FieldTextMetadata,
|
||||
@@ -38,7 +38,7 @@ type AssertFieldMetadataFunction = <
|
||||
: E extends 'PHONE'
|
||||
? FieldPhoneMetadata
|
||||
: E extends 'PROBABILITY'
|
||||
? FieldProbabilityMetadata
|
||||
? FieldRatingMetadata
|
||||
: E extends 'RELATION'
|
||||
? FieldRelationMetadata
|
||||
: E extends 'TEXT'
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldProbabilityMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldProbability = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldProbabilityMetadata> =>
|
||||
field.type === 'PROBABILITY';
|
||||
@@ -1,8 +0,0 @@
|
||||
import { isNumber } from '@sniptt/guards';
|
||||
|
||||
import { FieldProbabilityValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add zod
|
||||
export const isFieldProbabilityValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldProbabilityValue => isNumber(fieldValue);
|
||||
@@ -0,0 +1,9 @@
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldRatingMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldRating = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldRatingMetadata> =>
|
||||
field.type === FieldMetadataType.Probability;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FieldRatingValue } from '../FieldMetadata';
|
||||
|
||||
const ratingSchema = z
|
||||
.string()
|
||||
.transform((value) => +value)
|
||||
.pipe(z.number().int().min(1).max(5));
|
||||
|
||||
export const isFieldRatingValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldRatingValue => ratingSchema.safeParse(fieldValue).success;
|
||||
Reference in New Issue
Block a user