diff --git a/app/client/views/questionnaires/questionnaire_wizzard.coffee b/app/client/views/questionnaires/questionnaire_wizzard.coffee index b44b7f3..36ddbc0 100644 --- a/app/client/views/questionnaires/questionnaire_wizzard.coffee +++ b/app/client/views/questionnaires/questionnaire_wizzard.coffee @@ -1,546 +1,547 @@ @__showQuestionnaireWizzard = (data) -> Session.set 'selectedQuestionnaireWizzard', data if !data.readonly #check if this questionnaire was answered before by this patient #we do this on the client only, because we can't check it properly by the #server anyway questionIds = Questions.find( questionnaireId: data.questionnaire._id ).map (q) -> q._id count = Answers.find( visitId: data.visit._id questionId: $in: questionIds ).count() if count > 0 swal { title: 'Attention!' text: "This questionnaire was alredy filled out. To view data, please use the ‘show’ button. Do you want to proceed? Old data will be overwritten. A log entry will be created. Please state a reason." type: 'input' inputPlaceholder: "Please state a reason." showCancelButton: true confirmButtonText: 'Yes' closeOnConfirm: false }, (confirmedWithReason)-> if confirmedWithReason is false #cancel swal.close() else if !confirmedWithReason? or confirmedWithReason.length is 0 swal.showInputError("You need to state a reason!") else Meteor.call "logActivity", "reopen questionnaire (#{data.questionnaire.id}) for editing which was already filled out (patient: #{data.patient.id} visit:#{data.visit.title})", "notice", confirmedWithReason, data swal.close() doShowQuestionnaireWizzard(data) return else doShowQuestionnaireWizzard(data) else doShowQuestionnaireWizzard(data) doShowQuestionnaireWizzard = (data) -> Modal.show('questionnaireWizzard', data, keyboard: false) @__closeQuestionnaireWizzard = -> if isAFormDirty() swal { title: 'Unsaved Changes' text: "Do you want to save the changes on this page?" type: 'warning' showCancelButton: true confirmButtonText: 'Save and exit' cancelButtonText: "Exit without saving" closeOnConfirm: false }, (save) -> if save submitAllForms('close') else swal.close() Modal.hide('questionnaireWizzard') else Modal.hide('questionnaireWizzard') _numQuestions = new ReactiveVar(0) _numPages = new ReactiveVar(0) _questionIdsForPage = new ReactiveVar({}) _pageIndex = new ReactiveVar(0) _numFormsToSubmit = 0 _readonly = new ReactiveVar(false) _questionnaire = new ReactiveVar(null) _nextQuestionnaire = null _preview = new ReactiveVar(false) _lang = new ReactiveVar(null) isAFormDirty = -> if _readonly.get() or _preview.get() return false isDirty = false $("form").each () -> return if isDirty e = $(@)[0] dirty = formIsDirty(e) isDirty = dirty if dirty isDirty _goto = null submitAllForms = (goto) -> if _readonly.get() or _preview.get() throw new Error("Can't submitAllForms because _readonly == true") _goto = goto numFormsToSubmit = 0 missingAnswer = false #count forms and check for empty inputs $("form").each -> e = $(@) classes = e.attr('class') if classes? and classes.indexOf('question') > -1 numFormsToSubmit += 1 if classes? and classes.indexOf('questionForm') > -1 #check multiple subquestions forms lines = e.find('tbody tr') lines.each -> l = $(@) lineAnswered = l.find('input:checked').length > 0 if !lineAnswered missingAnswer = true l.addClass("missing-answer") else l.removeClass("missing-answer") else #check one question forms updateDoc = AutoForm.getFormValues(e.attr('id')).updateDoc if Object.keys(updateDoc).length is 0 or updateDoc?['$unset']?.value is "" missingAnswer = true e.addClass("missing-answer") else e.removeClass("missing-answer") if missingAnswer swal { title: 'missing answers' text: "You have left some questions unanswered, are you sure you want to continue?" type: 'warning' showCancelButton: true confirmButtonText: 'Yes' cancelButtonText: "Cancel" }, -> doSubmitAllForms(numFormsToSubmit) else swal.close() #possibly open swal "unsaved changes" doSubmitAllForms(numFormsToSubmit) _submittingForms = false doSubmitAllForms = (numFormsToSubmit) -> _submittingForms = true _numFormsToSubmit = numFormsToSubmit $("form").each -> e = $(@) classes = e.attr('class') if classes? and classes.indexOf('question') > -1 e.submit() formSubmitted = -> if (_numFormsToSubmit -= 1) <= 0 _submittingForms = false if _goto is 'nextPage' nextPage() else if _goto is 'previousPage' previousPage() else if _goto is 'close' Modal.hide('questionnaireWizzard') else if _goto? and _goto.pageIndex? _pageIndex.set _goto.pageIndex nextPage = -> if _pageIndex.get() is _numPages.get()-1 # deactiavted: go to next questionnaire automatically #if _nextQuestionnaire? # _pageIndex.set 0 # _questionnaire.set _nextQuestionnaire #else Modal.hide('questionnaireWizzard') else _pageIndex.set _pageIndex.get()+1 previousPage = -> index = _pageIndex.get() index -= 1 if index > 0 _pageIndex.set index autoformHooks = onSubmit: (insertDoc, updateDoc, currentDoc) -> if !_submittingForms #ignore enter press @done() return false if _preview.get() or _readonly.get() return insertDoc.visitId = currentDoc.visitId insertDoc.questionId = currentDoc.questionId insertDoc._id = currentDoc._id if currentDoc._id? #console.log "submit questionAutoform" #console.log insertDoc if insertDoc.value? and (!currentDoc.value? or (currentDoc.value? and currentDoc.value isnt insertDoc.value)) Meteor.call "upsertAnswer", insertDoc, (error) -> throwError error if error? formSubmitted() @done() false Template.questionnaireWizzard.created = -> _questionnaire.set @data.questionnaire delete @data.questionnaire if @data.readonly _readonly.set true else _readonly.set false if @data.preview _preview.set true else _preview.set false #close on escape key press $(document).on('keyup.wizzard', (e)-> e.stopPropagation() if e.keyCode is 27 #escape __closeQuestionnaireWizzard() return ) #close if user has been logged out @autorun -> if !Meteor.user() __closeQuestionnaireWizzard() self = @ @autorun -> self.subscribe("questionsForQuestionnaire", _questionnaire.get()._id) #manage nextQuestionnaire @autorun -> return if _preview.get() data = Template.currentData() validatedQuestionnaires = data.visit.validatedQuestionnaires i = 0 index = null while i < validatedQuestionnaires.length-1 && index is null q = validatedQuestionnaires[i] if q._id is _questionnaire.get()._id index = i i += 1 if index? and index < validatedQuestionnaires.length-1 _nextQuestionnaire = validatedQuestionnaires[index+1] else _nextQuestionnaire = null #collect autoformIds, count pages @autorun -> count = 0 page = 0 questionIdsForPage = {} didBreakPage = false autoformIds = [] Questions.find questionnaireId: _questionnaire.get()._id , sort: {index: 1} .forEach (q) -> if q.type isnt "description" and q._id isnt "table" and q._id isnt "table_polar" autoformIds.push q._id count += 1 if questionIdsForPage[page]? questionIdsForPage[page].push q._id else questionIdsForPage[page] = [q._id] didBreakPage = false if q.break page += 1 didBreakPage = true page -= 1 if didBreakPage _questionIdsForPage.set questionIdsForPage _numQuestions.set count _numPages.set page+1 _pageIndex.set 0 AutoForm.addHooks(autoformIds, autoformHooks, true) #determine language @autorun -> data = Template.currentData() patient = data.patient questionnaire = _questionnaire.get() if !patient #preview _lang.set null return if patient.primaryLanguage? if patient.primaryLanguage is questionnaire.primaryLanguage _lang.set null return if questionnaire.translationLanguages? and questionnaire.translationLanguages.length > 0 lang = questionnaire.translationLanguages.find (t) -> t is patient.primaryLanguage if lang? _lang.set lang return if patient.secondaryLanguage? if patient.secondaryLanguage is questionnaire.primaryLanguage _lang.set null return if questionnaire.translationLanguages? and questionnaire.translationLanguages.length > 0 lang = questionnaire.translationLanguages.find (t) -> t is patient.secondaryLanguage if lang? _lang.set lang return _lang.set null return @autorun -> Template.currentData() #must trigger reselection value = _lang.get() if !value? value = _questionnaire.get().primaryLanguage Meteor.setTimeout -> $("#source-lang option[value=#{value}]").attr('selected', true) , 100 Template.questionnaireWizzard.destroyed = -> $(document).unbind('keyup.wizzard') Session.set('selectedQuestionnaireWizzard', null) Template.questionnaireWizzard.helpers patientDescription: -> d = @patient.id if @patient.hrid d += " (#{@patient.hrid})" d userDescription: -> getUserDescription(Meteor.user()) language: -> lang = _lang.get() questionnaire = _questionnaire.get() if !lang and questionnaire.primaryLanguage? lang = questionnaire.primaryLanguage lang hasLangs: -> _questionnaire.get().primaryLanguage? langs: -> questionnaire = _questionnaire.get() tl = questionnaire.translationLanguages or [] pl = questionnaire.primaryLanguage langs = isoLangs.filter (l) -> l.code is pl or tl.indexOf(l.code) > -1 + langs = JSON.parse(JSON.stringify(langs)) _.some langs, (l) -> if l.code is pl l.suffix = "- PRIMARY LANGUAGE -" l.code is pl langs templateGestures: 'swipeleft div': (evt, templateInstance) -> nextQuestion() 'swiperight div': (evt, templateInstance) -> previousQuestion() title: -> _questionnaire.get().title questionsForPage: -> questionIdsForPage = _questionIdsForPage.get()[_pageIndex.get()] cursor = Questions.find( questionnaireId: _questionnaire.get()._id _id: {$in: questionIdsForPage} , sort: {index: 1} ) lang = _lang.get() if lang? #we need to translate all questions questions = cursor.map (q) -> q.translateTo lang return questions else #no need to translate return cursor questionnaire: -> _questionnaire.get() answerForQuestion: (visitId, questionId) -> return if _preview.get() Answers.findOne visitId: visitId questionId: questionId readonly: -> _readonly.get() preview: -> _preview.get() formType: -> if _readonly.get() "disabled" else "normal" answerFormSchema: -> schema = _id: type: String optional: true visitId: type: String optional: true questionId: type: String optional: true value: @question.getSchemaDict() new SimpleSchema(schema) doc: -> return if _preview.get() @answer or visitId: @visit._id questionId: @question._id pages: -> answers = {} questionIds = Questions.find questionnaireId: _questionnaire.get()._id .map (question) -> question._id if !_preview.get() Answers.find visitId: @visit._id questionId: {$in: questionIds} .forEach (answer) -> answers[answer.questionId] = answer activeIndex = _pageIndex.get() questionIdsForPage = _questionIdsForPage.get() pages = [] for i in [0.._numPages.get()-1] css = "" allQuestionsAnsweredInPage = true someQuestionsAnsweredInPage = false Questions.find questionnaireId: _questionnaire.get()._id _id: {$in: questionIdsForPage[i]} .forEach (question) -> return if question.type is "description" answer = answers[question._id] if question.type is "table" or question.type is "table_polar" if !answer? or answer.value.length < question.subquestions.length allQuestionsAnsweredInPage = false if answer? and answer.value.length > 0 someQuestionsAnsweredInPage = true else if !answer? allQuestionsAnsweredInPage = false if allQuestionsAnsweredInPage css = "answered" else if someQuestionsAnsweredInPage css = "answeredPartly" if i is activeIndex css += " active" pages[i] = index: i+1 css: css pages isOnFirstPage: -> _pageIndex.get() is 0 isOnLastPageOfLastQuestionnaire: -> # deactiavted: go to next questionnaire automatically _pageIndex.get() is _numPages.get()-1# and (_preview.get() or _nextQuestionnaire is null) Template.questionnaireWizzard.events "click #next": (evt, tmpl) -> if _readonly.get() or _preview.get() nextPage() else submitAllForms('nextPage') false "click #back": (evt, tmpl) -> if _readonly.get() or _preview.get() previousPage() else submitAllForms('previousPage') false "click .jumpToPage": (evt) -> pageIndex = @index-1 if isAFormDirty() swal { title: 'Unsaved Changes' text: "Do you want to save the changes on this page?" type: 'warning' showCancelButton: true confirmButtonText: 'Save' cancelButtonText: "Don't save" closeOnConfirm: false }, (save) -> if save submitAllForms(pageIndex: pageIndex) else swal.close() _pageIndex.set pageIndex else _pageIndex.set pageIndex false "click #close": (evt) -> __closeQuestionnaireWizzard() false "submit .questionForm": (evt) -> #table and table_polar if _readonly.get() or _preview.get() return evt.preventDefault() evt.stopPropagation() if @question.type is "description" formSubmitted() return answer = visitId: @visit._id questionId: @question._id value: [] _id: @answer._id if @answer? for subquestion in @question.subquestions inputs = $(evt.target).find("input[data-subquestion_code=#{subquestion.code}]:checked") if @question.selectionMode is "multi" values = [] inputs.each -> input = $(@) values.push input.data('choice_value').toString() if values.length > 0 answer.value.push code: subquestion.code value: values else #if @question.selectionMode is "single" if inputs.length > 1 throw new Meteor.Error('error when processing the values: single selection got multiple values.') else if inputs.length is 1 value = inputs.first().data('choice_value').toString() answer.value.push code: subquestion.code value: value if answer.value.length > 0 Meteor.call "upsertAnswer", answer, (error) -> throwError error if error? formSubmitted() else formSubmitted() false "change #source-lang": (evt) -> _lang.set $(evt.target).find(":selected").attr('value') diff --git a/app/client/views/questionnaires/translate_questionnaire.coffee b/app/client/views/questionnaires/translate_questionnaire.coffee index 5e21cee..99b75a2 100644 --- a/app/client/views/questionnaires/translate_questionnaire.coffee +++ b/app/client/views/questionnaires/translate_questionnaire.coffee @@ -1,177 +1,178 @@ destinationLang = new ReactiveVar('en') sourceLang = new ReactiveVar(null) _numFormsToSubmit = 0 submitAllForms = -> numFormsToSubmit = 0 $("form").each -> e = $(@) classes = e.attr('class') if classes? and classes.indexOf('translationForm') > -1 numFormsToSubmit += 1 doSubmitAllForms(numFormsToSubmit) _submittingForms = false doSubmitAllForms = (numFormsToSubmit) -> _submittingForms = true _numFormsToSubmit = numFormsToSubmit $("form").each -> e = $(@) classes = e.attr('class') if classes? and classes.indexOf('translationForm') > -1 e.submit() formSubmitted = -> if (_numFormsToSubmit -= 1) <= 0 _submittingForms = false autoformHooks = onSubmit: (insertDoc, updateDoc, currentDoc) -> if Object.keys(insertDoc).length > 0 Meteor.call "translateQuestion", @formId, insertDoc, destinationLang.get(), (error) -> throwError error if error? formSubmitted() @done() false Template.translateQuestionnaireSourceLang.rendered = -> @autorun -> Template.currentData() #must trigger reselection Meteor.setTimeout -> $("#source-lang option[value=#{sourceLang.get()}]").attr('selected', true) , 100 Template.translateQuestionnaireSourceLang.helpers langs: -> tl = @translationLanguages or [] pl = @primaryLanguage langs = isoLangs.filter (l) -> l.code is pl or tl.indexOf(l.code) > -1 + langs = JSON.parse(JSON.stringify(langs)) _.some langs, (l) -> if l.code is pl l.suffix = "- PRIMARY LANGUAGE -" l.code is pl langs Template.translateQuestionnaireSourceLang.events "change #source-lang": (evt) -> sourceLang.set $(evt.target).find(":selected").attr('value') Template.translateQuestionnaireDestinationLang.rendered = -> @autorun -> Template.currentData() #must trigger reselection Meteor.setTimeout -> $("#destination-lang option[value=#{destinationLang.get()}]").attr('selected', true) , 100 Template.translateQuestionnaireDestinationLang.helpers langs: -> pl = @primaryLanguage isoLangs.filter (l) -> l.code isnt pl Template.translateQuestionnaireDestinationLang.events "change #destination-lang": (evt) -> destinationLang.set $(evt.target).find(":selected").attr('value') Template.translateQuestionnaire.rendered = -> questionnaire = @data if !questionnaire.translationLanguages? questionnaire.translationLanguages = [] #redirect if primaryLang isn't set if !questionnaire.primaryLanguage swal { title: 'Error!' text: 'You need to set the primary language of that questionnaire before you can translate it. You will be redirected to the edit page.' type: 'warning' showCancelButton: false }, -> Session.set 'selectedQuestionId', null Router.go "editQuestionnaire", _id: questionnaire._id return #check source lang pl = questionnaire.primaryLanguage sl = sourceLang.get() if !sl? or (sl isnt pl and questionnaire.translationLanguages.indexOf(sl) is -1) sourceLang.set questionnaire.primaryLanguage #check destination lang dl = destinationLang.get() if dl is pl if pl is "en" destinationLang.set "es" else if pl is "es" destinationLang.set "en" #hook formIds autoformIds = Questions.find( questionnaireId: @data._id ).map (q) -> q._id AutoForm.addHooks(autoformIds, autoformHooks, true) return Template.translateQuestionnaire.helpers questionnaireSchema: -> schema = title: type: String id: label: 'ID' type: String optional: true new SimpleSchema(schema) hasQuestions: -> Questions.find( questionnaireId: @_id ).count() > 0 allQuestions: -> Questions.find( questionnaireId: @_id , sort: index: 1 ) editQuestionnaireQuestionOptions: -> q = @ if sourceLang.get() isnt @primaryLanguage q = @copy() q.translateTo(sourceLang.get()) question: q Template.translateQuestionnaire.events "click #submitTranslation": (evt) -> submitAllForms() "click #editQuestionnaire": (evt) -> Router.go "editQuestionnaire", _id: @_id "click #previewQuestionnaire": (evt) -> data = questionnaire: @ visit: null patient: null preview: true Modal.show('questionnaireWizzard', data, keyboard: false) false Template.editQuestionnaireQuestionTranslate.helpers doc: -> lang = destinationLang.get() if !@translations? or !@translations[lang]? @ else @translations[lang] questionTranslationFormSchema: -> new SimpleSchema(@getTranslationSchemaDict())