From 7ece82840c43abc0fd897775df8b0085a3cbfa99 Mon Sep 17 00:00:00 2001 From: Victor Jack <67293106+thedon-dev@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:18:07 +0100 Subject: [PATCH] Fix: Exercice saving validation error (#774) Fixed the error that keeps previous deleted questions before saving Co-authored-by: Rotimi Best --- .../course/components/exercise/store.test.ts | 41 +++++++++++++++++++ .../course/components/exercise/store.ts | 10 +++-- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 apps/dashboard/src/lib/features/course/components/exercise/store.test.ts diff --git a/apps/dashboard/src/lib/features/course/components/exercise/store.test.ts b/apps/dashboard/src/lib/features/course/components/exercise/store.test.ts new file mode 100644 index 00000000..533309ff --- /dev/null +++ b/apps/dashboard/src/lib/features/course/components/exercise/store.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest'; + +import type { Question } from '$features/course/types'; +import { mapZodErrorsToQuestionErrors } from './store'; + +function makeQuestion(overrides: Partial): Question { + return { + id: overrides.id ?? 'question-1', + title: overrides.title ?? 'Question', + points: overrides.points ?? 1, + questionTypeId: overrides.questionTypeId ?? 1, + questionType: overrides.questionType, + options: overrides.options ?? [], + ...overrides + } as Question; +} + +describe('mapZodErrorsToQuestionErrors', () => { + it('maps Zod question errors to the correct active question when deleted questions are present', () => { + const questions = [ + makeQuestion({ id: 'question-1', title: 'Saved question 1' }), + makeQuestion({ id: 'question-2', title: 'Deleted question', deletedAt: '2026-01-01T00:00:00.000Z' }), + makeQuestion({ id: 'question-3', title: '' }), + makeQuestion({ id: 'question-4', title: 'Saved question 4' }) + ]; + + const zodErrors = { + 'questions.1.question': 'Question text is required', + 'questions.1.options.0.label': 'Option label is required' + }; + + const result = mapZodErrorsToQuestionErrors(zodErrors, questions); + + expect(result).toEqual({ + 'question-3': { + title: 'Question text is required', + option: 'Option label is required' + } + }); + }); +}); diff --git a/apps/dashboard/src/lib/features/course/components/exercise/store.ts b/apps/dashboard/src/lib/features/course/components/exercise/store.ts index d4717b39..d97938da 100644 --- a/apps/dashboard/src/lib/features/course/components/exercise/store.ts +++ b/apps/dashboard/src/lib/features/course/components/exercise/store.ts @@ -152,13 +152,17 @@ export function mapZodErrorsToQuestionErrors( questions: Question[] ): ExerciseEditorErrors { const errors: ExerciseEditorErrors = {}; + const activeQuestions = questions.filter((question) => !question.deletedAt); + for (const [path, message] of Object.entries(zodErrors)) { const pathParts = path.split('.'); const questionIndex = pathParts[0] === 'questions' ? parseInt(pathParts[1]) : -1; - if (questionIndex >= 0 && questionIndex < questions.length) { - const question = questions[questionIndex]; - if (question && !question.deletedAt) { + if (questionIndex >= 0 && questionIndex < activeQuestions.length) { + const activeQuestion = activeQuestions[questionIndex]; + const question = questions.find((questionItem) => questionItem.id === activeQuestion.id); + + if (question) { const qErrors = errors[question.id] || {}; // Check if error is related to options