diff --git a/assess2/vue-src/src/basicstore.js b/assess2/vue-src/src/basicstore.js index 34db500c32..53a2f0c22a 100644 --- a/assess2/vue-src/src/basicstore.js +++ b/assess2/vue-src/src/basicstore.js @@ -13,6 +13,7 @@ export const store = reactive({ inTransit: false, autoSaving: false, errorMsg: null, + blankOverride: false, confirmObj: null, lastLoaded: [], inProgress: false, @@ -239,6 +240,8 @@ export const actions = { } response = this.processSettings(response); this.copySettings(response); + // reset blankOverride when a new question is loaded + store.blankOverride = false; }) .fail((xhr, textStatus, errorThrown) => { this.handleError(textStatus === 'parsererror' ? 'parseerror' : 'noserver'); @@ -401,11 +404,47 @@ export const actions = { changedWork = true; } } + // reset override when all parts are now answered + for (const key of Object.keys(changedQuestions)) { + const qn = parseInt(key); + const question = store.assessInfo.questions[qn]; + const totalParts = question.answeights ? question.answeights.length : question.parts.length; + const answeredParts = changedQuestions[qn].length; + if (answeredParts === totalParts) { + store.blankOverride = false; + store.errorMsg = null; + break; + } + } + // blank-parts warning (only once): if some but not all parts are answered + for (const key of Object.keys(changedQuestions)) { + const qn = parseInt(key); + const question = store.assessInfo.questions[qn]; + const totalParts = question.answeights ? question.answeights.length : question.parts.length; + const answeredParts = changedQuestions[qn].length; + if (answeredParts > 0 && answeredParts < totalParts) { + // first warning: block and set override + if (!store.blankOverride) { + store.errorMsg = 'blankparts'; + store.inTransit = false; + store.blankOverride = true; + return; + } + // override present: clear the warning and allow submission through + store.errorMsg = null; + store.inTransit = false; + break; + } + } if (Object.keys(changedQuestions).length === 0 && !changedWork && !endattempt) { store.errorMsg = 'nochange'; store.inTransit = false; return; } + // Do not clear blankOverride here; only clear it after successful submission + + // Clone changedQuestions before AJAX for later blankOverride logic + const submittedChangedQuestions = { ...changedQuestions }; window.MQeditor.resetEditor(); window.imathasAssess.clearTips(); @@ -510,6 +549,21 @@ export const actions = { return; } else { store.errorMsg = null; + // only reset override if submission was not partial + let wasPartial = false; + for (const key in submittedChangedQuestions) { + const qn = parseInt(key); + const question = store.assessInfo.questions[qn]; + const totalParts = question.answeights ? question.answeights.length : question.parts.length; + const answeredParts = submittedChangedQuestions[qn].length; + if (answeredParts > 0 && answeredParts < totalParts) { + wasPartial = true; + break; + } + } + if (!wasPartial) { + store.blankOverride = false; + } } if (response.saved_autosaves) { this.markAutosavesDone(); diff --git a/assess2/vue-src/src/components/ErrorDialog.vue b/assess2/vue-src/src/components/ErrorDialog.vue index b5e019e61a..dd2ddc8862 100644 --- a/assess2/vue-src/src/components/ErrorDialog.vue +++ b/assess2/vue-src/src/components/ErrorDialog.vue @@ -53,6 +53,10 @@ export default { return (typeof this.errormsg === 'string'); }, errorTitle () { + // show a "Warning" title for blankparts + if (this.errormsg === 'blankparts') { + return this.$t('Warning'); + } return this.isError ? this.$t('error.error') : this.errormsg.title; }, errorMsg () { diff --git a/assess2/vue-src/src/locales/en.json b/assess2/vue-src/src/locales/en.json index 73f49a68ab..9313c6f8ab 100644 --- a/assess2/vue-src/src/locales/en.json +++ b/assess2/vue-src/src/locales/en.json @@ -166,6 +166,7 @@ }, "error": { "error": "Error", + "warning": "Warning", "invalid_password": "The password you entered was invalid", "invalid_aid": "Invalid assessment ID", "no_access": "You must be a student, teacher, or tutor to access this assessment", @@ -187,6 +188,7 @@ "lti_no_session": "Your session expired. Please go back to your LMS and open the assignment again.", "fast_regen": "Hey, how about slowing down and trying the problem before hitting Get a Similar Question? Wait 5 seconds before trying again.", "nochange": "Your answers have not changed since your last submission.", + "blankparts": "Some parts are unanswered; submitting again will bypass this warning.", "noserver": "The site is not responding", "parseerror": "Server sent an invalid response", "livepoll_wrongquestion": "Submitted question is not the current question.",