From 2a07922a0cbb4be23a3487d95a5382bafe8d701b Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Fri, 22 Mar 2024 11:07:24 -0700 Subject: [PATCH] Conditional Questions --- src/api/src/routes/admin-survey-router.ts | 37 +++++-- src/api/src/routes/survey-router.ts | 2 + src/web/src/components/QuestionRenderer.vue | 4 +- .../survey/components/QuestionEditor.vue | 96 ++++++++++++++++++- .../modules/survey/store/index.ts | 16 ++++ src/web/src/store/SurveyStore.ts | 21 +++- src/web/src/views/Preview.vue | 20 +++- src/web/src/views/Survey.vue | 13 ++- 8 files changed, 190 insertions(+), 19 deletions(-) diff --git a/src/api/src/routes/admin-survey-router.ts b/src/api/src/routes/admin-survey-router.ts index a85d63d..547a54e 100644 --- a/src/api/src/routes/admin-survey-router.ts +++ b/src/api/src/routes/admin-survey-router.ts @@ -21,16 +21,23 @@ adminSurveyRouter.get("/", async (req: Request, res: Response) => { list = await db("SURVEY").withSchema(DB_SCHEMA).whereIn("SID", surveys); } else list = await db("SURVEY").withSchema(DB_SCHEMA); - for (let item of list) { - item.questions = await db("QUESTION").withSchema(DB_SCHEMA).where({ SID: item.SID }).orderBy("ORD"); - item.choices = await db("JSON_DEF").withSchema(DB_SCHEMA).where({ SID: item.SID }).orderBy("TITLE"); - item.responses = await db("PARTICIPANT") - .withSchema(DB_SCHEMA) - .innerJoin("PARTICIPANT_DATA", "PARTICIPANT.TOKEN", "PARTICIPANT_DATA.TOKEN") - .where({ SID: item.SID }) - .whereNotNull("RESPONSE_DATE"); + let allQuestions = await db("QUESTION").withSchema(DB_SCHEMA).orderBy("SID", "ORD"); + let allChoices = await db("JSON_DEF").withSchema(DB_SCHEMA).orderBy("SID", "TITLE"); + let allResponses = await db("PARTICIPANT") + .withSchema(DB_SCHEMA) + .innerJoin("PARTICIPANT_DATA", "PARTICIPANT.TOKEN", "PARTICIPANT_DATA.TOKEN") + .whereNotNull("RESPONSE_DATE"); + let allConditions = await db("Q_CONDITION_TBL").withSchema(DB_SCHEMA); + for (let item of list) { + item.questions = allQuestions.filter((q) => q.SID == item.SID); + item.choices = allChoices.filter((c) => c.SID == item.SID); + item.responses = allResponses.filter((r) => r.SID == item.SID); item.choices.map((c: any) => (c.choices = JSON.parse(c.SELECTION_JSON))); + + for (let q of item.questions) { + q.conditions = allConditions.filter((c) => c.QID == q.QID); + } } res.json({ data: list }); @@ -144,6 +151,20 @@ adminSurveyRouter.put("/:SID/question/:QID/order", async (req: Request, res: Res res.json({ data: "success" }); }); +adminSurveyRouter.put("/:SID/question/:QID/conditions", async (req: Request, res: Response) => { + let { QID } = req.params; + let { conditions } = req.body; + + await db("Q_CONDITION_TBL").withSchema(DB_SCHEMA).where({ QID }).delete(); + + for (let cond of conditions) { + delete cond.COND_ID; + await db("Q_CONDITION_TBL").withSchema(DB_SCHEMA).insert(cond); + } + + res.json({ data: "success" }); +}); + adminSurveyRouter.put("/:SID/question/:QID", async (req: Request, res: Response) => { let { SID, QID } = req.params; let { ASK, OPTIONAL, ORD, TYPE, RANGE, GROUP_ID, JSON_ID, SELECT_LIMIT, choices, choiceTitle } = req.body; diff --git a/src/api/src/routes/survey-router.ts b/src/api/src/routes/survey-router.ts index 1cc9024..57608d1 100644 --- a/src/api/src/routes/survey-router.ts +++ b/src/api/src/routes/survey-router.ts @@ -37,6 +37,7 @@ surveyRouter.get( } for (let q of questions) { + q.conditions = await db("Q_CONDITION_TBL").withSchema(DB_SCHEMA).where({ QID: q.QID }); if (q.JSON_ID) { q.choices = choices.find((c) => c.JSON_ID == q.JSON_ID)?.choices; } @@ -68,6 +69,7 @@ surveyRouter.get( let questions = await db("QUESTION").withSchema(DB_SCHEMA).where({ SID: token }).orderBy("ORD"); for (let q of questions) { + q.conditions = await db("Q_CONDITION_TBL").withSchema(DB_SCHEMA).where({ QID: q.QID }); if (q.JSON_ID) { q.choices = choices.find((c) => c.JSON_ID == q.JSON_ID)?.choices; } diff --git a/src/web/src/components/QuestionRenderer.vue b/src/web/src/components/QuestionRenderer.vue index 2708f5b..95925ad 100644 --- a/src/web/src/components/QuestionRenderer.vue +++ b/src/web/src/components/QuestionRenderer.vue @@ -99,10 +99,10 @@ - + {{ sub.ASK }} - + - Question {{ index + 1 }}: ({{ question.TYPE }}) + + Question {{ index + 1 }}: ({{ question.TYPE }}) - {{ question.conditions.length }} conditions {{ question.ASK }} @@ -76,6 +85,57 @@ + + + + + + mdi-close + + +
+ + + + + + + + + +
+ + +
+
+
+
+ + Add Condition + +
+ Save + + close +
+
+
+
diff --git a/src/web/src/modules/administration/modules/survey/store/index.ts b/src/web/src/modules/administration/modules/survey/store/index.ts index 6c340ff..9125b2e 100644 --- a/src/web/src/modules/administration/modules/survey/store/index.ts +++ b/src/web/src/modules/administration/modules/survey/store/index.ts @@ -177,6 +177,22 @@ export const useAdminSurveyStore = defineStore("adminSurvey", { .catch(); } }, + + async saveQuestionConditions(question: { QID: number; conditions: any[] }) { + let conditions = question.conditions; + + if (this.survey) { + await api + .secureCall("put", `${ADMIN_SURVEY_URL}/${this.survey.SID}/question/${question.QID}/conditions`, { + conditions, + }) + .then(async (resp) => { + await this.loadSurveys(); + this.selectById(this.survey?.SID || 0); + }) + .catch(); + } + }, }, }); diff --git a/src/web/src/store/SurveyStore.ts b/src/web/src/store/SurveyStore.ts index 98ddaf4..584a6aa 100644 --- a/src/web/src/store/SurveyStore.ts +++ b/src/web/src/store/SurveyStore.ts @@ -43,7 +43,8 @@ export const useSurveyStore = defineStore("survey", { q.answer = null; q.isValid = () => { if (q.OPTIONAL == 1) return true; - if (q.TYPE == 'text') return true; + if (q.TYPE == "text") return true; + if (!q.isVisible) return true; if (q.subQuestions) { for (let sub of q.subQuestions) { @@ -60,6 +61,24 @@ export const useSurveyStore = defineStore("survey", { if (trimAnswer && trimAnswer.length > 0) return true; return false; }; + q.isVisible = true; + q.checkConditions = () => { + let newVisible = true; + + for (let cond of q.conditions) { + let parentQuestion = value.questions.find((q: any) => q.QID == cond.CQID); + + if (parentQuestion && parentQuestion.answer) { + if (parentQuestion.answer == cond.TVALUE) { + } else { + newVisible = false; + } + } else { + newVisible = false; + } + } + return newVisible; + }; } this.survey = value; diff --git a/src/web/src/views/Preview.vue b/src/web/src/views/Preview.vue index 574032c..263f4d9 100644 --- a/src/web/src/views/Preview.vue +++ b/src/web/src/views/Preview.vue @@ -13,7 +13,8 @@
-

{{ survey.survey.PAGE_INTRO }}

@@ -30,7 +31,11 @@
- +
@@ -113,6 +118,7 @@ export default { let v = true; for (let q of this.survey.questions) { + q.isVisible = q.checkConditions(); let qv = q.isValid(); v = v && qv; } @@ -143,6 +149,16 @@ export default { renderMarkdown(input) { return RenderMarkdown(input); }, + getQuestionNumber(question) { + let index = 0; + for (let q of this.survey.questions) { + if (q.QID == question.QID) return index; + + if (q.isVisible) index++; + } + + return index; + }, }, }; diff --git a/src/web/src/views/Survey.vue b/src/web/src/views/Survey.vue index 592a495..30cb149 100644 --- a/src/web/src/views/Survey.vue +++ b/src/web/src/views/Survey.vue @@ -30,7 +30,7 @@
- +
@@ -123,6 +123,7 @@ export default { let v = true; for (let q of this.survey.questions) { + q.isVisible = q.checkConditions(); let qv = q.isValid(); v = v && qv; } @@ -156,6 +157,16 @@ export default { }); } }, + getQuestionNumber(question) { + let index = 0; + for (let q of this.survey.questions) { + if (q.QID == question.QID) return index; + + if (q.isVisible) index++; + } + + return index; + }, }, };