diff --git a/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx b/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx
index c57378a2e..b4d095f66 100644
--- a/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx
+++ b/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx
@@ -6,6 +6,7 @@ import { Tag } from '@/ui/display/tag/components/Tag';
import { FieldDisplay } from '@/ui/object/field/components/FieldDisplay';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { BooleanFieldInput } from '@/ui/object/field/meta-types/input/components/BooleanFieldInput';
+import { RatingFieldInput } from '@/ui/object/field/meta-types/input/components/RatingFieldInput';
import { Field } from '~/generated/graphql';
import { FieldMetadataType } from '~/generated-metadata/graphql';
@@ -147,6 +148,8 @@ export const SettingsObjectFieldPreview = ({
>
{fieldMetadata.type === FieldMetadataType.Boolean ? (
+ ) : fieldMetadata.type === FieldMetadataType.Probability ? (
+
) : (
)}
diff --git a/front/src/modules/settings/data-model/components/SettingsObjectFieldTypeSelectSection.tsx b/front/src/modules/settings/data-model/components/SettingsObjectFieldTypeSelectSection.tsx
index c81dd52c2..722b5f109 100644
--- a/front/src/modules/settings/data-model/components/SettingsObjectFieldTypeSelectSection.tsx
+++ b/front/src/modules/settings/data-model/components/SettingsObjectFieldTypeSelectSection.tsx
@@ -91,6 +91,7 @@ export const SettingsObjectFieldTypeSelectSection = ({
FieldMetadataType.Enum,
FieldMetadataType.Link,
FieldMetadataType.Number,
+ FieldMetadataType.Probability,
FieldMetadataType.Relation,
FieldMetadataType.Text,
].includes(values.type) && (
diff --git a/front/src/modules/settings/data-model/components/__stories__/SettingsObjectFieldPreview.stories.tsx b/front/src/modules/settings/data-model/components/__stories__/SettingsObjectFieldPreview.stories.tsx
index 8d62b3023..2962cfbcc 100644
--- a/front/src/modules/settings/data-model/components/__stories__/SettingsObjectFieldPreview.stories.tsx
+++ b/front/src/modules/settings/data-model/components/__stories__/SettingsObjectFieldPreview.stories.tsx
@@ -90,6 +90,16 @@ export const Number: Story = {
},
};
+export const Rating: Story = {
+ args: {
+ fieldMetadata: {
+ icon: 'IconHandClick',
+ label: 'Engagement',
+ type: FieldMetadataType.Probability,
+ },
+ },
+};
+
export const Relation: Story = {
decorators: [
(Story) => (
diff --git a/front/src/modules/settings/data-model/constants/settingsFieldMetadataTypes.ts b/front/src/modules/settings/data-model/constants/settingsFieldMetadataTypes.ts
index 8ba5a59f3..1aa96cd59 100644
--- a/front/src/modules/settings/data-model/constants/settingsFieldMetadataTypes.ts
+++ b/front/src/modules/settings/data-model/constants/settingsFieldMetadataTypes.ts
@@ -12,6 +12,7 @@ import {
IconTextSize,
IconUser,
} from '@/ui/display/icon';
+import { IconTwentyStar } from '@/ui/display/icon/components/IconTwentyStar';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { FieldMetadataType } from '~/generated-metadata/graphql';
@@ -74,9 +75,9 @@ export const settingsFieldMetadataTypes: Record<
[FieldMetadataType.Email]: { label: 'Email', Icon: IconMail },
[FieldMetadataType.Phone]: { label: 'Phone', Icon: IconPhone },
[FieldMetadataType.Probability]: {
- label: 'Probability',
- Icon: IconNumbers,
- defaultValue: 50,
+ label: 'Rating',
+ Icon: IconTwentyStar,
+ defaultValue: '3',
},
[FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser },
};
diff --git a/front/src/modules/ui/display/icon/assets/twenty-star-filled.svg b/front/src/modules/ui/display/icon/assets/twenty-star-filled.svg
new file mode 100644
index 000000000..b9b018fb7
--- /dev/null
+++ b/front/src/modules/ui/display/icon/assets/twenty-star-filled.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/front/src/modules/ui/display/icon/assets/twenty-star.svg b/front/src/modules/ui/display/icon/assets/twenty-star.svg
new file mode 100644
index 000000000..9d2200996
--- /dev/null
+++ b/front/src/modules/ui/display/icon/assets/twenty-star.svg
@@ -0,0 +1,3 @@
+
diff --git a/front/src/modules/ui/display/icon/components/IconTwentyStar.tsx b/front/src/modules/ui/display/icon/components/IconTwentyStar.tsx
new file mode 100644
index 000000000..935629c57
--- /dev/null
+++ b/front/src/modules/ui/display/icon/components/IconTwentyStar.tsx
@@ -0,0 +1,12 @@
+import { TablerIconsProps } from '@/ui/display/icon';
+
+import { ReactComponent as IconTwentyStarRaw } from '../assets/twenty-star.svg';
+
+type IconTwentyStarProps = TablerIconsProps;
+
+export const IconTwentyStar = (props: IconTwentyStarProps): JSX.Element => {
+ const size = props.size ?? 24;
+ const stroke = props.stroke ?? 2;
+
+ return ;
+};
diff --git a/front/src/modules/ui/display/icon/components/IconTwentyStarFilled.tsx b/front/src/modules/ui/display/icon/components/IconTwentyStarFilled.tsx
new file mode 100644
index 000000000..5de47046c
--- /dev/null
+++ b/front/src/modules/ui/display/icon/components/IconTwentyStarFilled.tsx
@@ -0,0 +1,16 @@
+import { TablerIconsProps } from '@/ui/display/icon';
+
+import { ReactComponent as IconTwentyStarFilledRaw } from '../assets/twenty-star-filled.svg';
+
+type IconTwentyStarFilledProps = TablerIconsProps;
+
+export const IconTwentyStarFilled = (
+ props: IconTwentyStarFilledProps,
+): JSX.Element => {
+ const size = props.size ?? 24;
+ const stroke = props.stroke ?? 2;
+
+ return (
+
+ );
+};
diff --git a/front/src/modules/ui/object/field/components/FieldInput.tsx b/front/src/modules/ui/object/field/components/FieldInput.tsx
index 2d1a01a48..242e45a7f 100644
--- a/front/src/modules/ui/object/field/components/FieldInput.tsx
+++ b/front/src/modules/ui/object/field/components/FieldInput.tsx
@@ -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) ? (
- ) : isFieldProbability(fieldDefinition) ? (
-
+ ) : isFieldRating(fieldDefinition) ? (
+
) : (
<>>
)}
diff --git a/front/src/modules/ui/object/field/hooks/useIsFieldInputOnly.ts b/front/src/modules/ui/object/field/hooks/useIsFieldInputOnly.ts
index 855fd247e..d1566823c 100644
--- a/front/src/modules/ui/object/field/hooks/useIsFieldInputOnly.ts
+++ b/front/src/modules/ui/object/field/hooks/useIsFieldInputOnly.ts
@@ -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;
}
diff --git a/front/src/modules/ui/object/field/hooks/usePersistField.ts b/front/src/modules/ui/object/field/hooks/usePersistField.ts
index 670f6da55..421abd148 100644
--- a/front/src/modules/ui/object/field/hooks/usePersistField.ts
+++ b/front/src/modules/ui/object/field/hooks/usePersistField.ts
@@ -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);
diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useProbabilityField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useRatingField.ts
similarity index 57%
rename from front/src/modules/ui/object/field/meta-types/hooks/useProbabilityField.ts
rename to front/src/modules/ui/object/field/meta-types/hooks/useRatingField.ts
index 985922c32..148cbacc2 100644
--- a/front/src/modules/ui/object/field/meta-types/hooks/useProbabilityField.ts
+++ b/front/src/modules/ui/object/field/meta-types/hooks/useRatingField.ts
@@ -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(
+ const [fieldValue, setFieldValue] = useRecoilState(
entityFieldsFamilySelector({
entityId: entityId,
fieldName: fieldName,
}),
);
- const probabilityIndex = Math.ceil((fieldValue ?? 0) / 25);
+ const rating = +(fieldValue ?? 0);
return {
fieldDefinition,
- probabilityIndex,
+ rating,
setFieldValue,
hotkeyScope,
};
diff --git a/front/src/modules/ui/object/field/meta-types/input/components/ProbabilityFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/ProbabilityFieldInput.tsx
deleted file mode 100644
index 33788cb1f..000000000
--- a/front/src/modules/ui/object/field/meta-types/input/components/ProbabilityFieldInput.tsx
+++ /dev/null
@@ -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 (
-
- );
-};
diff --git a/front/src/modules/ui/object/field/meta-types/input/components/RatingFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/RatingFieldInput.tsx
new file mode 100644
index 000000000..a35a36654
--- /dev/null
+++ b/front/src/modules/ui/object/field/meta-types/input/components/RatingFieldInput.tsx
@@ -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 (
+
+ );
+};
diff --git a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ProbabilityFieldInput.stories.tsx b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
similarity index 57%
rename from front/src/modules/ui/object/field/meta-types/input/components/__stories__/ProbabilityFieldInput.stories.tsx
rename to front/src/modules/ui/object/field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
index 68680fd8e..53ad24924 100644
--- a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ProbabilityFieldInput.stories.tsx
+++ b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
@@ -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 (
-
-
+
+
);
};
@@ -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;
+type Story = StoryObj;
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);
},
diff --git a/front/src/modules/ui/object/field/meta-types/input/components/internal/ProbabilityInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/internal/ProbabilityInput.tsx
deleted file mode 100644
index fbf3c228a..000000000
--- a/front/src/modules/ui/object/field/meta-types/input/components/internal/ProbabilityInput.tsx
+++ /dev/null
@@ -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 (
-
-
- {PROBABILITY_VALUES[probabilityIndexToShow].label}
-
-
- {PROBABILITY_VALUES.map((probability, probabilityIndexToSelect) => (
- onChange(probability.value)}
- onMouseEnter={() =>
- setHoveredProbabilityIndex(probabilityIndexToSelect)
- }
- onMouseLeave={() => setHoveredProbabilityIndex(null)}
- >
-
-
- ))}
-
-
- );
-};
diff --git a/front/src/modules/ui/object/field/meta-types/input/components/internal/RatingInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/internal/RatingInput.tsx
new file mode 100644
index 000000000..549c10841
--- /dev/null
+++ b/front/src/modules/ui/object/field/meta-types/input/components/internal/RatingInput.tsx
@@ -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(null);
+ const currentValue = hoveredValue ?? value;
+
+ return (
+
+ {Array.from({ length: RATING_LEVELS_NB }, (_, index) => {
+ const rating = index + 1;
+
+ return (
+ onChange(rating)}
+ onMouseEnter={readonly ? undefined : () => setHoveredValue(rating)}
+ onMouseLeave={readonly ? undefined : () => setHoveredValue(null)}
+ >
+
+
+ );
+ })}
+
+ );
+};
diff --git a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts
index cb189bd8d..a2015410f 100644
--- a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts
+++ b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts
@@ -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)
diff --git a/front/src/modules/ui/object/field/types/FieldMetadata.ts b/front/src/modules/ui/object/field/types/FieldMetadata.ts
index bb8a2fe86..7b17f81e9 100644
--- a/front/src/modules/ui/object/field/types/FieldMetadata.ts
+++ b/front/src/modules/ui/object/field/types/FieldMetadata.ts
@@ -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;
diff --git a/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts b/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts
index 878ae281b..44ca2a495 100644
--- a/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts
+++ b/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts
@@ -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'
diff --git a/front/src/modules/ui/object/field/types/guards/isFieldProbability.ts b/front/src/modules/ui/object/field/types/guards/isFieldProbability.ts
deleted file mode 100644
index 49d1eeb19..000000000
--- a/front/src/modules/ui/object/field/types/guards/isFieldProbability.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { FieldDefinition } from '../FieldDefinition';
-import { FieldMetadata, FieldProbabilityMetadata } from '../FieldMetadata';
-
-export const isFieldProbability = (
- field: Pick, 'type'>,
-): field is FieldDefinition =>
- field.type === 'PROBABILITY';
diff --git a/front/src/modules/ui/object/field/types/guards/isFieldProbabilityValue.ts b/front/src/modules/ui/object/field/types/guards/isFieldProbabilityValue.ts
deleted file mode 100644
index 1c989fda9..000000000
--- a/front/src/modules/ui/object/field/types/guards/isFieldProbabilityValue.ts
+++ /dev/null
@@ -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);
diff --git a/front/src/modules/ui/object/field/types/guards/isFieldRating.ts b/front/src/modules/ui/object/field/types/guards/isFieldRating.ts
new file mode 100644
index 000000000..1490150ea
--- /dev/null
+++ b/front/src/modules/ui/object/field/types/guards/isFieldRating.ts
@@ -0,0 +1,9 @@
+import { FieldMetadataType } from '~/generated-metadata/graphql';
+
+import { FieldDefinition } from '../FieldDefinition';
+import { FieldMetadata, FieldRatingMetadata } from '../FieldMetadata';
+
+export const isFieldRating = (
+ field: Pick, 'type'>,
+): field is FieldDefinition =>
+ field.type === FieldMetadataType.Probability;
diff --git a/front/src/modules/ui/object/field/types/guards/isFieldRatingValue.ts b/front/src/modules/ui/object/field/types/guards/isFieldRatingValue.ts
new file mode 100644
index 000000000..645c5a00f
--- /dev/null
+++ b/front/src/modules/ui/object/field/types/guards/isFieldRatingValue.ts
@@ -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;