From 6c4bc4e5755869684f17b360a202b3441626e7be Mon Sep 17 00:00:00 2001 From: madresebanoo Date: Sun, 20 Sep 2026 16:35:36 +0430 Subject: [PATCH] fix(exams): prevent double-submit from creating duplicate exams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The submit button only disabled after Vue re-rendered on mutation pending state, leaving a window where a fast double-click fired onSubmit twice — creating two exam rows, with the losing request usually failing to attach its questions (an "empty" duplicate exam). --- .../admin/exams/pages/ExamFormPage.vue | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/features/admin/exams/pages/ExamFormPage.vue b/src/features/admin/exams/pages/ExamFormPage.vue index efe8b70..291e3de 100644 --- a/src/features/admin/exams/pages/ExamFormPage.vue +++ b/src/features/admin/exams/pages/ExamFormPage.vue @@ -283,8 +283,11 @@ const addExamMutation = useAddAdminExamMutation() const updateExamMutation = useUpdateAdminExamMutation() const addQuestionMutation = useAddAdminExamQuestionMutation() +const isSubmitting = ref(false) + const submitting = computed( () => + isSubmitting.value || addExamMutation.isPending.value || updateExamMutation.isPending.value || addQuestionMutation.isPending.value @@ -298,23 +301,29 @@ const postQuestionsSequentially = async (id, list) => { } const onSubmit = async () => { - const { isValid } = await validate(form.value) - const newQuestions = validateLocalQuestions() - if (!isValid || !newQuestions) return + if (isSubmitting.value) return + isSubmitting.value = true + try { + const { isValid } = await validate(form.value) + const newQuestions = validateLocalQuestions() + if (!isValid || !newQuestions) return - const examPayload = buildExamPayload(form.value) - let targetExamId = examId.value - if (isEditMode.value) { - await updateExamMutation.mutateAsync({ id: targetExamId, payload: examPayload }) - } else { - const created = await addExamMutation.mutateAsync(examPayload) - targetExamId = created?.data?.id ?? created?.id ?? targetExamId + const examPayload = buildExamPayload(form.value) + let targetExamId = examId.value + if (isEditMode.value) { + await updateExamMutation.mutateAsync({ id: targetExamId, payload: examPayload }) + } else { + const created = await addExamMutation.mutateAsync(examPayload) + targetExamId = created?.data?.id ?? created?.id ?? targetExamId + } + if (targetExamId && newQuestions.length > 0) { + await postQuestionsSequentially(targetExamId, newQuestions) + } + await queryClient.invalidateQueries({ queryKey: adminExamsKeys.all, refetchType: 'all' }) + router.push({ name: 'admin-exams' }) + } finally { + isSubmitting.value = false } - if (targetExamId && newQuestions.length > 0) { - await postQuestionsSequentially(targetExamId, newQuestions) - } - await queryClient.invalidateQueries({ queryKey: adminExamsKeys.all, refetchType: 'all' }) - router.push({ name: 'admin-exams' }) } const onCancel = () => router.push({ name: 'admin-exams' })