Skip to content

Commit

Permalink
fix(GoalCriteria): fetch suggestion goals by versa criteria
Browse files Browse the repository at this point in the history
fix(GoalCriteria): visibility GoalCriteria trigger
fix(GoalCriteria): save editing criteria data
fix(GoalCriteria): correct criterlia title in versa criteria
  • Loading branch information
LamaEats committed Nov 27, 2023
1 parent e3130c8 commit ed16b8d
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 193 deletions.
2 changes: 1 addition & 1 deletion src/components/CriteriaForm/CriteriaForm.i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"Add": "",
"Cancel": "",
"Suggestions": "",
"These binding is already exist": ""
"This binding is already exist": ""
}
2 changes: 1 addition & 1 deletion src/components/CriteriaForm/CriteriaForm.i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"Add": "Добавить",
"Cancel": "Отменить",
"Suggestions": "Предложения",
"These binding is already exist": "Такая связка уже существует"
"This binding is already exist": "Такая связка уже существует"
}
162 changes: 83 additions & 79 deletions src/components/CriteriaForm/CriteriaForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,32 +44,62 @@ type CriteriaFormMode = 'simple' | 'goal';
export const maxPossibleWeight = 100;
export const minPossibleWeight = 1;

const schema = z.object({
id: z.string().optional(),
mode: z.enum<CriteriaFormMode, Readonly<[CriteriaFormMode, CriteriaFormMode]>>(['simple', 'goal']),
weight: z.string().optional(),
title: z.string().optional(),
selected: z
.object({
id: z.string(),
title: z.string(),
stateColor: z.number().optional(),
})
.nullish(),
});

type CriteriaFormValues = z.infer<typeof schema>;

function patchZodSchema(
interface FormValues {
mode: CriteriaFormMode;
title: string;
weight?: string;
selected?: SuggestItem;
}

function patchZodSchema<T extends FormValues>(
data: ValidityData,
checkBindingsBetweenGoals: (selectedGoalId: string) => Promise<void>,
defaultValues?: z.infer<typeof schema>,
defaultValues?: T,
) {
return schema
.merge(
return z
.discriminatedUnion('mode', [
z.object({
mode: z.literal('simple'),
id: z.string().optional(),
selected: z.object({}).optional(),
}),
z.object({
mode: z.literal('goal'),
id: z.string().optional(),
selected: z.object({
id: z.string().refine(
async (val) => {
if (defaultValues?.selected?.id === val) {
return true;
}

try {
await checkBindingsBetweenGoals(val);
return true;
} catch (_error) {
return false;
}
},
{ message: tr('This binding is already exist'), path: [] },
),
title: z.string().refine(
(val) => {
return !data.title.includes(val);
},
{ message: tr('Title must be unique') },
),
stateColor: z.number().optional(),
}),
}),
])
.and(
z.object({
/* INFO: https://github.com/colinhacks/zod#abort-early */
weight: schema.shape.weight.superRefine((val, ctx): val is string => {
title: z
.string({ required_error: tr('Title is required') })
.min(1, tr('Title must be longer than 1 symbol'))
.refine((val) => !data.title.includes(val), tr('Title must be unique')),
weight: z.string().superRefine((val, ctx): val is string => {
/* INFO: https://github.com/colinhacks/zod#abort-early */
if (!val || !val.length) {
return z.NEVER;
}
Expand All @@ -96,58 +126,12 @@ function patchZodSchema(

return z.NEVER;
}),
selected: schema.shape.selected.refine(async (val) => {
if (!val?.id) {
return true;
}

if (defaultValues?.selected?.id === val.id) {
return true;
}

try {
await checkBindingsBetweenGoals(val.id);
return true;
} catch (_error) {
return false;
}
}, tr('These binding is already exist')),
}),
)
.superRefine((val, ctx): val is Required<CriteriaFormValues> => {
if (val.mode === 'simple') {
if (!val.title) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: tr('Title is required'),
path: ['title'],
});
} else if (val.title.length < 1) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: tr('Title must be longer than 1 symbol'),
path: ['title'],
});
} else if (data.title.includes(val.selected?.title || val.title)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: tr('Title must be unique'),
path: ['title'],
});
}
}
if (val.mode === 'goal' && !val.selected?.id.length) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: tr('Goal must be selected'),
path: ['selected'],
});
}

return z.NEVER;
});
);
}

type CriteriaFormValues = FormValues & z.infer<ReturnType<typeof patchZodSchema>>;

interface CriteriaFormProps {
items: SuggestItem[];
defaultMode?: CriteriaFormMode;
Expand Down Expand Up @@ -267,15 +251,21 @@ const CriteriaWeightField = forwardRef<HTMLInputElement, WeightFieldProps>(
);
},
);
interface ErrorMessage {
message?: string;
}

interface CriteriaTitleFieldProps {
name: 'title';
value?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange: (...args: any[]) => void;
errors?: {
title?: { message?: string };
selected?: { message?: string };
title?: ErrorMessage;
selected?: {
id?: ErrorMessage;
title?: ErrorMessage;
};
};
mode: CriteriaFormMode;
selectedItem?: SuggestItem | null;
Expand Down Expand Up @@ -307,7 +297,13 @@ const CriteriaTitleField: React.FC<CriteriaTitleFieldProps> = ({

const error = useMemo(() => {
if (mode === 'goal' && selected) {
return selected;
if (selected.id) {
return selected.id;
}

if (selected.title) {
return selected.title;
}
}

if (mode === 'simple' && title) {
Expand Down Expand Up @@ -353,7 +349,6 @@ export const CriteriaForm = forwardRef<HTMLDivElement, CriteriaFormProps>(
mode: defaultMode,
title: '',
weight: '',
selected: undefined,
},
values,
mode: 'onChange',
Expand All @@ -377,22 +372,31 @@ export const CriteriaForm = forwardRef<HTMLDivElement, CriteriaFormProps>(
if (name === 'title') {
onInputChange?.(currentValues.title);

if (currentValues.selected != null && currentValues.selected.id != null) {
if (
'selected' in currentValues &&
currentValues.selected != null &&
currentValues.selected.id != null
) {
resetField('selected');
resetField('weight', { defaultValue: '' });
}
}

return;
}

if (name === 'selected' || name === 'selected.id' || name === 'selected.title') {
if (
currentValues.mode === 'goal' &&
(name === 'selected' || name === 'selected.id' || name === 'selected.title')
) {
onItemChange?.(currentValues.selected as Required<SuggestItem>);

trigger('selected');
}

if (name === 'mode') {
onModeChange?.(currentValues.mode as NonNullable<CriteriaFormMode>);
if (!!currentValues.title && !currentValues.selected) {
if (currentValues.title) {
trigger('title');
}
}
Expand All @@ -411,7 +415,7 @@ export const CriteriaForm = forwardRef<HTMLDivElement, CriteriaFormProps>(

const handleCancel = useCallback(() => {
reset({
selected: null,
selected: undefined,
title: undefined,
});
onCancel?.();
Expand Down
26 changes: 14 additions & 12 deletions src/components/GoalActivityFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
title: data.title,
weight: String(data.weight),
goalId: goal.id,
criteriaGoal: data.selected
? {
id: data.selected.id,
}
: undefined,
criteriaGoal:
'selected' in data
? {
id: data.selected.id,
}
: undefined,
});
},
[goal.id, onGoalCriteriaAdd],
Expand All @@ -100,11 +101,12 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
title: data.title,
weight: String(data.weight),
goalId: goal.id,
criteriaGoal: data.selected
? {
id: data.selected.id,
}
: undefined,
criteriaGoal:
'selected' in data
? {
id: data.selected.id,
}
: undefined,
});
},
[goal.id, onGoalCriteriaUpdate],
Expand Down Expand Up @@ -139,7 +141,7 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps

const handleValidateGoalToCriteriaBinging = useCallback(
(selectedId: string) => {
return validateGoalCriteriaBindings({ currentGoalId: goal.id, selectedGoalId: selectedId });
return validateGoalCriteriaBindings({ criteriaGoalId: selectedId, goalId: goal.id });
},
[goal.id, validateGoalCriteriaBindings],
);
Expand Down Expand Up @@ -184,7 +186,7 @@ export const GoalActivityFeed = forwardRef<HTMLDivElement, GoalActivityFeedProps
feed={goal._activityFeed}
header={
<>
{nullable(criteriaList || goal._isEditable, () => (
{nullable(criteriaList?.length || goal._isEditable, () => (
<GoalCriteria
canEdit={goal._isEditable}
onCreate={handleCreateCriteria}
Expand Down
Loading

0 comments on commit ed16b8d

Please sign in to comment.