+{% endblock %}
+
+{% block pageScripts %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/views/form-designer/question-routes/new-condition-BACKUP.html b/app/views/form-designer/question-routes/new-condition-BACKUP.html
new file mode 100644
index 00000000..b5ac3ef8
--- /dev/null
+++ b/app/views/form-designer/question-routes/new-condition-BACKUP.html
@@ -0,0 +1,66 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% set pageTitle = 'Add a question route' %}
+
+{% block pageTitle %}
+ {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
+{% endblock %}
+
+{% block beforeContent %}
+ {% if previousPage and ('your-questions' in previousPage) %}
+
+ Back to your questions
+
+ {% else %}
+
+ Back
+
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+{% endblock %}
+
+{% block pageScripts %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/views/form-designer/question-routes/new-condition.html b/app/views/form-designer/question-routes/new-condition.html
index b5ac3ef8..0bd6e201 100644
--- a/app/views/form-designer/question-routes/new-condition.html
+++ b/app/views/form-designer/question-routes/new-condition.html
@@ -1,6 +1,6 @@
{% extends "layout-govuk-forms.html" %}
-{% set pageTitle = 'Add a question route' %}
+{% set pageTitle = 'Add a route from a question' %}
{% block pageTitle %}
{{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
@@ -34,9 +34,52 @@
{{ pageTitle }}
- You can send people directly to the question or page you want, based on their previous answer. This means they’ll only see questions that are relevant to them.
+ You can set up a route so if someone selects a specific answer to a question they’ll skip forward to a later question, or the end of the form.
+ {% for page in data.pages -%}
+ {% set tempQuestions = {
+ 'name': page['long-title']
+ } %}
+ {%- endfor %}
+
+ {% if data.pages.length >= 2 %}
+
diff --git a/app/views/form-designer/question-routes/routes-summary.html b/app/views/form-designer/question-routes/routes-summary.html
new file mode 100644
index 00000000..84ea4e66
--- /dev/null
+++ b/app/views/form-designer/question-routes/routes-summary.html
@@ -0,0 +1,109 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% set pageTitle = 'Question ' + (data.routeStartQuestion or 'X') + '’s routes' %}
+
+{% block pageTitle %}
+ {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
+{% endblock %}
+
+{% block beforeContent %}
+
+ Back
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+{% endblock %}
+
+{% block pageScripts %}
+
+{% endblock %}
\ No newline at end of file
From a18caee7ea5b58a2f8f7a377342adbf326a34e48 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 11:28:11 +0000
Subject: [PATCH 02/61] Updated session data to match latest page data
---
.../returning-session-data-defaults-a11y.js | 56 +++++++++++++------
1 file changed, 38 insertions(+), 18 deletions(-)
diff --git a/app/data/returning-session-data-defaults-a11y.js b/app/data/returning-session-data-defaults-a11y.js
index 1928e1a9..41ada837 100644
--- a/app/data/returning-session-data-defaults-a11y.js
+++ b/app/data/returning-session-data-defaults-a11y.js
@@ -25,44 +25,64 @@ module.exports = {
payments: 'no',
pages: [
{
+ pageIndex: '0',
+ type: 'text',
'long-title': 'What type of animal is your pet?',
- 'short-title': 'Animal type',
'hint-text': 'For example a bird, cat, dog.',
- type: 'text',
- pageIndex: '0'
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'What is the name of your pet?',
- 'short-title': 'Pet name',
+ pageIndex: '1',
type: 'text',
- pageIndex: '1'
+ 'long-title': 'What is the name of your pet?',
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
+ pageIndex: '2',
+ type: 'text',
'long-title': 'Where are you travelling to?',
- 'short-title': 'Destination',
'hint-text': 'For example Lisbon, Portugal',
- type: 'text',
- pageIndex: '2'
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
+ pageIndex: '3',
+ type: 'date',
'long-title': 'What date do you travel?',
- 'short-title': 'Date',
'hint-text': 'For example 27 3 2007',
- type: 'date',
- pageIndex: '3'
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
+ pageIndex: '4',
+ type: 'text',
'long-title': 'How are you travelling?',
- 'short-title': 'Transport type',
'hint-text': 'For example plane, train, car.',
- type: 'text',
- pageIndex: '4'
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'How many pets do you have?',
- 'short-title': 'Number of pets',
+ pageIndex: '5',
+ type: 'select',
+ 'long-title': "Which of these countries have you lived in?",
+ 'item-list': [
+ "England",
+ "Wales"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
+ },
+ {
+ pageIndex: '6',
type: 'number',
- pageIndex: '5'
+ 'long-title': 'How many pets do you have?',
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
}
],
status: 'Draft',
From 9dbd5690ded9c20fb2632390fd6a54711155e299 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 11:28:35 +0000
Subject: [PATCH 03/61] Updated check your answers preview to only show the
questions someone has seen
---
app/views/form-designer/preview/check-answers.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/form-designer/preview/check-answers.html b/app/views/form-designer/preview/check-answers.html
index 892c37c0..4adf57fb 100644
--- a/app/views/form-designer/preview/check-answers.html
+++ b/app/views/form-designer/preview/check-answers.html
@@ -18,7 +18,7 @@
- {% for page in data.pages -%}
+ {% for page in data.pagesCYA -%}
{% set questionTitle = page["long-title"] or "Page " + page["pageIndex"] %}
{% if page['questionOptional'] %}
From b62f13d602bf0f5fe6c0862186e6b0043d455344 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 11:29:18 +0000
Subject: [PATCH 04/61] Joined up routing creation screens, with simple errors
---
.../form-designer/question-routes/_routes.js | 86 +++++++++++++++++++
1 file changed, 86 insertions(+)
create mode 100644 app/views/form-designer/question-routes/_routes.js
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
new file mode 100644
index 00000000..79a5db29
--- /dev/null
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -0,0 +1,86 @@
+const govukPrototypeKit = require('govuk-prototype-kit')
+const router = govukPrototypeKit.requests.setupRouter()
+
+/* CREATING A NEW QUESTION ROUTE
+================================ */
+
+// Create a new question route - button journeys
+router.get('/form-designer/question-routes/route-start', function (req, res) {
+ const errors = {}
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var routeStartQuestion = req.session.data.routeStartQuestion
+
+ // If the no question to start the route has been selected, create an error to be displayed to the user
+ if (!routeStartQuestion?.length) {
+ errors.routeStartQuestion = {
+ text: 'Select the question you want your route to start from',
+ href: "#route-start-question"
+ }
+ }
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/new-condition', { errors, errorList, containsErrors })
+ } else {
+ // go to add conditions to the route
+ res.redirect(`conditions`)
+ }
+})
+
+// Create route 1 conditions - button journeys
+router.get('/form-designer/question-routes/route-conditions', function (req, res) {
+ const errors = {}
+ var { pages, routeStartQuestion, route1Answer, route1End } = req.session.data
+
+ /*
+ for page in req.session.data.pages
+ if page.pageIndex === routeStartQuestion
+ page.push(
+ routes: {
+ routeAnswer: route1Answer,
+ routeEnd: route1End
+ })
+ clear (routeStartQuestion, route1Answer, route1End)
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ if ((parseInt(element.pageIndex, 10) + 1) == routeStartQuestion) {
+ element.routing = { 'answer': route1Answer, 'skipTo': route1End }
+ }
+ }
+
+ // If the ‘is answered as’ input has not been selected, create an error to be displayed to the user
+ if (!route1Answer?.length) {
+ errors.route1Answer = {
+ text: 'Select the answer to base this route on',
+ href: "#route-1-answer"
+ }
+ }
+ // If the ‘skip the person’ to input has not been selected, create an error to be displayed to the user
+ if (!route1End?.length) {
+ errors.route1End = {
+ text: 'Select the question or page to skip to',
+ href: "#route-1-end"
+ }
+ }
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/conditions', { errors, errorList, containsErrors })
+ } else {
+ // go to question X’s routes summary screen
+ res.redirect(`routes-summary`)
+ }
+})
+
+module.exports = router
\ No newline at end of file
From 9a334f808a9c410ebd4ce74fab997ecda2219e9e Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 11:29:59 +0000
Subject: [PATCH 05/61] Updated preview routing to create CYA version of pages
and to use new routing functionality in previews
---
app/routes.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 3 deletions(-)
diff --git a/app/routes.js b/app/routes.js
index 5761a4da..89a620f5 100644
--- a/app/routes.js
+++ b/app/routes.js
@@ -641,17 +641,68 @@ router.get('/form-designer/pages/:pageId/view', function (req, res) {
// Routing for new-tab page previews, set to a specific page
router.post('/form-designer/preview/:pageId(\\d+)', function (req, res) {
+ // this is used to check if the user has clicked ‘change’ link on the CYA screen - we add a URL query ‘cya=true’
var cya = req.session.data.cya
- req.session.data.cya = undefined
+ // this clears the temporary URL query so it can be reused on another ‘change’ link
+ req.session.data.cya = undefined
+
var pageId = req.params.pageId
var pageIndex = parseInt(pageId)
const isLastQuestionPage = pageIndex === (req.session.data.pages.length - 1)
+ var pages = req.session.data.pages
+ var pageAnswer = req.session.data[pageIndex]
+
+ var pagesCYA = req.session.data.pagesCYA
+
+ if (!pagesCYA) {
+ pagesCYA = []
+ }
+
+ // set variable for the next page, with default
+ var nextPage = `${pageIndex + 1}`
+ // for all pages in the form
+ for (let index = 0; index < pages.length; index++) {
+ const page = pages[index];
+ // get the current page
+ if (page.pageIndex == pageIndex) {
+ // if current page has routing attribute
+ if (page.routing) {
+ if (page.routing.answer != pageAnswer) {
+ // if the answer is being changed and doesn’t skip remove the CYA change link function to force user to go through the rest of the journey
+ cya = 'false'
+ }
+ if (page.routing.answer == pageAnswer) {
+ // override the nextPage to the one in routing
+ nextPage = page.routing.skipTo
+ }
+ }
+ // check if we are changing an answer
+ if (cya !== 'true') {
+ // check current page against pagesCYA array - if it exists don’t add it
+ var pageExists = 'false';
+ for (let andex = 0; andex < pagesCYA.length; andex++) {
+ const answeredPage = pagesCYA[andex];
+ if (answeredPage.pageIndex == page.pageIndex) {
+ pageExists = 'true' // the page exists
+ }
+ }
+ // ignore existing pages
+ if (pageExists !== 'true') {
+ // add this answered question to our check your answers page
+ pagesCYA.push(page)
+ }
+ }
+ }
+ }
+ // set the session data so we can use it
+ req.session.data.pagesCYA = pagesCYA
+
// if last question in form OR user clicked on change link from CYA, then go to CYA
- if(isLastQuestionPage || cya === 'true') {
+ if(isLastQuestionPage || nextPage == 'cya' || cya === 'true') {
return res.redirect('check-answers')
} else {
- return res.redirect(`${pageIndex + 1}`)
+ return res.redirect(nextPage)
}
})
@@ -1025,4 +1076,7 @@ router.use('/pages', require('./views/form-designer/pages/\_routes'))
/* Use the routes file in product-pages for groups and members routes */
router.use('/product-pages', require('./views/product-pages/\_routes'))
+/* Use the routes file in question-routes for adding routing and branching to questions */
+router.use('/question-routes', require('./views/form-designer/question-routes/\_routes'))
+
module.exports = router
\ No newline at end of file
From c377e26415a730a7cc8e886dd1b915a7c66e2a03 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 15:02:26 +0000
Subject: [PATCH 06/61] Added playback of routes to question list view
---
app/views/form-designer/your-questions.html | 26 ++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/app/views/form-designer/your-questions.html b/app/views/form-designer/your-questions.html
index 4792bfe4..a25fe6d8 100644
--- a/app/views/form-designer/your-questions.html
+++ b/app/views/form-designer/your-questions.html
@@ -83,7 +83,6 @@
From ed25ae5cf7eb237a7385888c60aa293ae955a36c Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 15:02:50 +0000
Subject: [PATCH 07/61] removed unnecessary file
---
.../question-routes/new-condition-BACKUP.html | 66 -------------------
1 file changed, 66 deletions(-)
delete mode 100644 app/views/form-designer/question-routes/new-condition-BACKUP.html
diff --git a/app/views/form-designer/question-routes/new-condition-BACKUP.html b/app/views/form-designer/question-routes/new-condition-BACKUP.html
deleted file mode 100644
index b5ac3ef8..00000000
--- a/app/views/form-designer/question-routes/new-condition-BACKUP.html
+++ /dev/null
@@ -1,66 +0,0 @@
-{% extends "layout-govuk-forms.html" %}
-
-{% set pageTitle = 'Add a question route' %}
-
-{% block pageTitle %}
- {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
-{% endblock %}
-
-{% block beforeContent %}
- {% if previousPage and ('your-questions' in previousPage) %}
-
- Back to your questions
-
- {% else %}
-
- Back
-
- {% endif %}
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
-{% endblock %}
-
-{% block pageScripts %}
-
-{% endblock %}
\ No newline at end of file
From e74d7b28867eceaa62beb7fb479e8025a611a4fe Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 15:03:04 +0000
Subject: [PATCH 08/61] Added new skip to page for testing
---
.../question-routes/question-to-skip-to.html | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 app/views/form-designer/question-routes/question-to-skip-to.html
diff --git a/app/views/form-designer/question-routes/question-to-skip-to.html b/app/views/form-designer/question-routes/question-to-skip-to.html
new file mode 100644
index 00000000..86c7800c
--- /dev/null
+++ b/app/views/form-designer/question-routes/question-to-skip-to.html
@@ -0,0 +1,98 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% set pageTitle = 'Set question or page to skip to' %}
+
+{% block pageTitle %}
+ {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
+{% endblock %}
+
+{% block beforeContent %}
+
+ Back
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+{% endblock %}
+
+{% block pageScripts %}
+
+{% endblock %}
\ No newline at end of file
From 005b09766d1fae455d57fe6b837f6ecf25a47759 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 15:03:39 +0000
Subject: [PATCH 09/61] Fixed issues with comparisons
---
app/views/form-designer/question-routes/conditions.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/form-designer/question-routes/conditions.html b/app/views/form-designer/question-routes/conditions.html
index 78044545..d3084bdf 100644
--- a/app/views/form-designer/question-routes/conditions.html
+++ b/app/views/form-designer/question-routes/conditions.html
@@ -33,7 +33,7 @@
{{ pageTitle }}
{% set tempAnswers = [] %}
{% set tempQuestions = [] %}
{% for page in data.pages -%}
- {% if (page.pageIndex|int + 1) > data.routeStartQuestion + 1 %}
+ {% if (page.pageIndex|int + 1) > data.routeStartQuestion|int + 1 %}
{% set tempQuestions = (tempQuestions.push(page), tempQuestions) %}
{% endif %}
{% if (page.pageIndex|int + 1) == data.routeStartQuestion %}
From a3479d4acee30ccc5eef83ce9bfd72b43f01aa01 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Thu, 13 Mar 2025 15:04:07 +0000
Subject: [PATCH 10/61] Updated summary screen to new content a button/link
based journey
---
.../question-routes/routes-summary.html | 21 +++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/app/views/form-designer/question-routes/routes-summary.html b/app/views/form-designer/question-routes/routes-summary.html
index 84ea4e66..b87a753e 100644
--- a/app/views/form-designer/question-routes/routes-summary.html
+++ b/app/views/form-designer/question-routes/routes-summary.html
@@ -15,7 +15,7 @@
{% block content %}
-
From 8ab131085d9144bafbf165bd576ac19ca932e417 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 13:47:06 +0000
Subject: [PATCH 15/61] Changed dummy questions so it is quicker to test and
develop
---
.../returning-session-data-defaults-a11y.js | 30 +++++++++----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/app/data/returning-session-data-defaults-a11y.js b/app/data/returning-session-data-defaults-a11y.js
index 41ada837..6e892bee 100644
--- a/app/data/returning-session-data-defaults-a11y.js
+++ b/app/data/returning-session-data-defaults-a11y.js
@@ -49,31 +49,31 @@ module.exports = {
},
{
pageIndex: '3',
- type: 'date',
- 'long-title': 'What date do you travel?',
- 'hint-text': 'For example 27 3 2007',
+ type: 'select',
+ 'long-title': "Which of these countries have you lived in?",
+ 'item-list': [
+ "England",
+ "Wales"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
'additional-guidance': 'No',
'questionSaved': 'Yes'
},
{
pageIndex: '4',
- type: 'text',
- 'long-title': 'How are you travelling?',
- 'hint-text': 'For example plane, train, car.',
+ type: 'date',
+ 'long-title': 'What date do you travel?',
+ 'hint-text': 'For example 27 3 2007',
'additional-guidance': 'No',
'questionSaved': 'Yes'
},
{
pageIndex: '5',
- type: 'select',
- 'long-title': "Which of these countries have you lived in?",
- 'item-list': [
- "England",
- "Wales"
- ],
- 'listSettings': [
- "oneOption"
- ],
+ type: 'text',
+ 'long-title': 'How are you travelling?',
+ 'hint-text': 'For example plane, train, car.',
'additional-guidance': 'No',
'questionSaved': 'Yes'
},
From 4d78b4c2b79c2921b50080bb4ee10139fbecc9dd Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 13:48:01 +0000
Subject: [PATCH 16/61] Added new route for any other answer playback. Made it
easier to use pageData instead of new session item
---
.../question-routes/routes-summary.html | 159 ++++++++++++++++--
1 file changed, 141 insertions(+), 18 deletions(-)
diff --git a/app/views/form-designer/question-routes/routes-summary.html b/app/views/form-designer/question-routes/routes-summary.html
index b87a753e..c535c256 100644
--- a/app/views/form-designer/question-routes/routes-summary.html
+++ b/app/views/form-designer/question-routes/routes-summary.html
@@ -1,9 +1,9 @@
{% extends "layout-govuk-forms.html" %}
-{% set pageTitle = 'Question ' + (data.routeStartQuestion or 'X') + '’s routes' %}
+{% set pageTitle = 'Question ' + ((pageIndex + 1) or 'X') + '’s routes' %}
{% block pageTitle %}
- {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
+ {{ "Error: " if containsErrors }}{{ pageTitle }}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
{% endblock %}
{% block beforeContent %}
@@ -27,32 +27,43 @@
{{ data.formTitle or '[formTitle]' }}
{{ pageTitle }}
- {% set routeStart = '' %}
{% set routeEnd = '' %}
+ {% set routeEndText = '' %}
+ {% set countQs = 0 %}
+ {# loop to get ‘Route 1’ skip to question text to display in the summary card #}
{% for page in data.pages -%}
- {% if (page.pageIndex|int + 1) == data.routeStartQuestion %}
- {% set routeStart = page['long-title'] %}
+
+ {# quick way for us to get if there are enough questions left that a for any other answer route would work or should be offered #}
+ {% if (page.pageIndex|int+1) > (pageIndex|int+1) %}
+ {% set countQs = countQs + 1 %}
{% endif %}
- {% if (page.pageIndex|int + 1) == data.route1End %}
- {% set routeEnd = (page.pageIndex|int + 1) + '. ' + page['long-title'] %}
- {% elif data.route1End === 'cya' %}
- {% set routeEnd = data.checkAnswersTitle %}
+
+ {# set the skip to question content to an existing question from the form #}
+ {% if (page.pageIndex|int + 1) == (pageData.routing.skipTo|int + 1) %}
+ {% set routeEndText = (page.pageIndex|int + 1) + '. ' + page['long-title'] %}
+ {% set routeEnd = (page.pageIndex|int + 1) %}
+ {# set the skip to content to be check your answers default title #}
+ {% elif pageData.routing.skipTo === 'cya' %}
+ {% set routeEndText = data.checkAnswersTitle %}
+ {% set routeEnd = 'cya' %}
{% endif %}
{%- endfor %}
+ {# show the question the routes are associated with #}
{{ govukSummaryList({
rows: [
{
key: {
- text: "Question " + (data.routeStartQuestion or 'X')
+ text: "Question " + ((pageIndex + 1) or 'X')
},
value: {
- text: routeStart or 'Not answered'
+ text: pageData['long-title'] or 'Not answered'
}
}
]
}) }}
+ {# show the ‘Route 1’ summary card #}
{{ govukSummaryList({
card: {
title: {
@@ -74,7 +85,7 @@
text: "skip the person to"
},
value: {
- text: routeEnd or 'Not answered'
+ text: routeEndText or 'Not answered'
}
}
]
}) }}
+ {# show ‘Route for any other answer’ summary card if one has been added #}
+ {% if pageData.routing.secondary === 'true' %}
+
+ {# we need a way to get the question that we want to suggest to start the for any other answer route from #}
+ {% set continueTo = '' %}
+ {% set thenAfter = '0' %}
+ {% set thenAfterText = '' %}
+ {% set skipPersonTo = '' %}
+ {% for page in data.pages %}
+ {% if page.pageIndex == (pageData.pageIndex|int + 1) %}
+ {% set continueTo = (page.pageIndex|int + 1) + '. ' + page['long-title'] %}
+ {% endif %}
+ {# find the secondary route in the questions list #}
+ {% if page.routing and (page.routing.baseQuestion == (pageData.pageIndex)) %}
+ {% set thenAfter = page.pageIndex|int %}
+ {% set thenAfterText = (page.pageIndex|int + 1) + '. ' + page['long-title'] %}
+ {% if page.routing.thenSkipTo == 'cya' %}
+ {% set skipPersonTo = data.checkAnswersTitle %}
+ {% else %}
+ {% set skipPersonTo = (page.routing.thenSkipTo|int + 1) + '. ' + page['long-title'] %}
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+
+ {{ govukSummaryList({
+ card: {
+ title: {
+ text: "Route for any other answer"
+ },
+ actions: {
+ items: [
+ {
+ href: "delete-secondary-route",
+ text: "Delete",
+ visuallyHiddenText: " route for any other answer"
+ }
+ ]
+ }
+ },
+ rows: [
+ {
+ key: {
+ text: "Continue to"
+ },
+ value: {
+ text: continueTo or 'Next question'
+ }
+ },
+ {
+ key: {
+ text: "Then after"
+ },
+ value: {
+ text: thenAfterText or 'Not answered'
+ },
+ actions: {
+ items: [
+ {
+ href: thenAfter + "/other-answer-route",
+ text: "Change",
+ visuallyHiddenText: " question to skip from"
+ }
+ ]
+ }
+ },
+ {
+ key: {
+ text: "skip the person to"
+ },
+ value: {
+ text: skipPersonTo or 'Not answered'
+ },
+ actions: {
+ items: [
+ {
+ href: thenAfter + "/question-to-skip-to",
+ text: "Change",
+ visuallyHiddenText: " skip the person to"
+ }
+ ]
+ }
+ }
+ ]
+ }) }}
+
+ {{ govukButton({
+ text: "Delete all routes",
+ classes: "govuk-button--warning"
+ }) }}
+
+ {% else %}
+
+ {# we need a way to get the question that we want to suggest to start the for any other answer route from #}
+ {% set suggestedQuestion = '' %}
+ {% set suggestedQuestionText = '' %}
+ {% for page in data.pages %}
+ {% if (routeEnd != 'cya') or (countQs >= 2) %}
+ {% if page.pageIndex == pageData.routing.skipTo|int - 1 %}
+ {% set suggestedQuestion = (page.pageIndex|int + 1) %}
+ {% set suggestedQuestionText = (page.pageIndex|int + 1) + ', ‘' + page['long-title'] + '’' %}
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+
If people select any other answer
- People who select any other answer will continue to [question 3] and through the rest of the form.
+ People who select any other answer will continue to question {{ (pageIndex + 2) or 'Y' }} and through the rest of the form.
+ {% if routeEnd != 'cya' %}
- You can change this by adding a route from [3, ‘What are the company details?’].
+ You can change this by adding a route from {{ suggestedQuestionText or 'Z, ‘Example question’' }}.
+ {# if not the last question, and the previous question isn’t the last question #}
+
+
+
{{ govukButton({
- text: "Add a route from [question 3]",
+ text: "Add a route from question " + (suggestedQuestion|int or 'Z'),
classes: "govuk-button--secondary"
}) }}
{% endif %}
{%- endfor %}
From 06acc0f4879abf84a91e428c90fed8c5a91a8b96 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 13:49:29 +0000
Subject: [PATCH 18/61] Added routing to handle multiple routes. (lots of
comments to explain code used)
---
.../form-designer/question-routes/_routes.js | 263 ++++++++++++++++--
1 file changed, 238 insertions(+), 25 deletions(-)
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
index 1f4acb8c..e81d7a1d 100644
--- a/app/views/form-designer/question-routes/_routes.js
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -4,16 +4,36 @@ const router = govukPrototypeKit.requests.setupRouter()
/* CREATING A NEW QUESTION ROUTE
================================ */
-// Create a new question route - button journeys
-router.get('/form-designer/question-routes/route-start', function (req, res) {
- const errors = {}
+/* Route 1 */
+
+// Render question route start
+getStartQuestion = function (req, res) {
var pageId = parseInt(req.params.pageId, 10)
var pageIndex = pageId
var pageData = req.session.data.pages[pageIndex]
- var routeStartQuestion = req.session.data.routeStartQuestion
+ return res.render('form-designer/question-routes/new-condition', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData
+ })
+}
+router.get('/form-designer/question-routes/new-condition', getStartQuestion)
+router.get('/form-designer/question-routes/:pageId(\\d+)/new-condition', getStartQuestion)
+
+// Create a new question route - button journey
+// this POST also creates our ‘pageId’ to use throughout the editing and creating of routes attached to it
+postStartQuestion = function (req, res) {
+ const errors = {}
+ var pageId = parseInt(req.params.pageId, 10)
+ var routeStartQuestion = parseInt(req.session.data.routeStartQuestion, 10)
+ var pageIndex = routeStartQuestion
+
+ if (pageId) {
+ pageIndex = pageId
+ }
// If the no question to start the route has been selected, create an error to be displayed to the user
- if (!routeStartQuestion?.length) {
+ if (!routeStartQuestion) {
errors.routeStartQuestion = {
text: 'Select the question you want your route to start from',
href: "#route-start-question"
@@ -29,31 +49,62 @@ router.get('/form-designer/question-routes/route-start', function (req, res) {
res.render('form-designer/question-routes/new-condition', { errors, errorList, containsErrors })
} else {
// go to add conditions to the route
- res.redirect(`conditions`)
+ res.redirect(`${pageIndex}/conditions`)
}
+}
+router.post('/form-designer/question-routes/route-start', postStartQuestion)
+router.post('/form-designer/question-routes/:pageId(\\d+)/route-start', postStartQuestion)
+
+// Render ‘Route 1’ answer and skip to conditions
+router.get('/form-designer/question-routes/:pageId(\\d+)/conditions', function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ return res.render('form-designer/question-routes/conditions', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData
+ })
})
-// Create route 1 conditions - button journeys
-router.get('/form-designer/question-routes/route-conditions', function (req, res) {
+// Create ‘Route 1’ - button journey
+postConditions = function (req, res) {
const errors = {}
- var { pages, routeStartQuestion, route1Answer, route1End } = req.session.data
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var { pages, route1Answer, route1End } = req.session.data
/*
- for page in req.session.data.pages
- if page.pageIndex === routeStartQuestion
+ we need to loop through our forms questions
+ for page in req.session.data.pages
+ find if the routeStart is the same as the current page loop
+ if page.pageIndex === routeStartQuestion
+ now we need to check if routing already exists on our base question
+ if page.routing
+ we just want to update the existing keys
+ routing.answer = route1Answer
+ routing.skipTo = route1End
+ else
+ we need to create a new key object for routing
page.push(
- routes: {
+ routing: {
routeAnswer: route1Answer,
routeEnd: route1End
})
- clear (routeStartQuestion, route1Answer, route1End)
+ clear (routeStartQuestion, route1Answer, route1End)
*/
- for (let index = 0; index < pages.length; index++) {
- const element = pages[index];
- if ((parseInt(element.pageIndex, 10) + 1) == routeStartQuestion) {
- element.routing = { 'answer': route1Answer, 'skipTo': route1End }
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ if ((parseInt(element.pageIndex, 10)) == pageIndex) {
+ if (element.routing) {
+ element.routing.answer = route1Answer
+ element.routing.skipTo = route1End
+ } else {
+ element.routing = { 'answer': route1Answer, 'skipTo': route1End }
+ }
}
- }
+ }
// If the ‘is answered as’ input has not been selected, create an error to be displayed to the user
if (!route1Answer?.length) {
@@ -78,27 +129,189 @@ router.get('/form-designer/question-routes/route-conditions', function (req, res
if(containsErrors) {
res.render('form-designer/question-routes/conditions', { errors, errorList, containsErrors })
} else {
+ // reset our temporary route session data
+ req.session.data.routeStartQuestion = undefined
+ req.session.data.route1Answer = undefined
+ req.session.data.route1End = undefined
// go to question X’s routes summary screen
res.redirect(`routes-summary`)
}
+}
+router.post('/form-designer/question-routes/route-conditions', postConditions)
+router.post('/form-designer/question-routes/:pageId(\\d+)/route-conditions', postConditions)
+
+
+/* Questions routes summary screen */
+
+// Render questions routes summary page
+router.get('/form-designer/question-routes/:pageId(\\d+)/routes-summary', function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ return res.render('form-designer/question-routes/routes-summary', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData
+ })
})
-// Create a secondary route - button journeys
-router.get('/form-designer/question-routes/questions-routes', function (req, res) {
- var { pages } = req.session.data
+// Start a ‘Route for any other answer’ - suggested question - button journey
+router.post('/form-designer/question-routes/:pageId(\\d+)/questions-routes', function (req, res) {
// if button pressed
// get the suggested question (based on the route 1 end)
- var startQuestion = req.session.data.suggestedQuestion
- req.session.data.suggestedQuestion = undefined // reset the suggestedQuestion
+ var { pages, suggestedQuestion } = req.session.data
+ req.session.data.suggestedQuestion = undefined // reset the session data for suggestedQuestion
// add the first part of routing to the suggested question
for (let index = 0; index < pages.length; index++) {
const element = pages[index];
- if ((parseInt(element.pageIndex, 10) + 1) == startQuestion) {
+ if ((parseInt(element.pageIndex, 10)) == suggestedQuestion) {
element.routing = { 'noAnswer': 'true' } // add a routing element to the question
}
}
// go to add a secondary route to this question
- res.redirect(`question-to-skip-to`)
+ res.redirect(`${suggestedQuestion}/question-to-skip-to`)
+})
+
+
+/* For any other answer route */
+
+// Render ‘Route for any other answer’ start question
+getRouteOtherAnswer = function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var routeId = parseInt(req.params.routeQuestion, 10)
+ var routeData = req.session.data.pages[routeId]
+ return res.render('form-designer/question-routes/other-answer-route', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData,
+ routeId: routeId,
+ routeData: routeData
+ })
+}
+router.get('/form-designer/question-routes/:pageId(\\d+)/other-answer-route', getRouteOtherAnswer)
+router.get('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/other-answer-route', getRouteOtherAnswer)
+
+// Start a ‘Route for any other answer’ - custom question - button journey
+postRouteOtherAnswerStart = function (req, res) {
+ const errors = {}
+ var routeId = parseInt(req.params.routeQuestion, 10)
+ var routeQuestion = parseInt(req.session.data.routeQuestion, 10)
+ var { pages } = req.session.data
+
+ /*
+ we need to check if the routeId (original route question) is the same as the newly chosen one
+ if routeId !== routeQuestion
+ we then need to find the routeId page from the form
+ for page in req.session.data.pages
+ if page.pageIndex == routeId
+ finally we can ‘delete’ the routing key object
+ delete page.routing
+ */
+ if (routeId !== routeQuestion) {
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ // add routing value with end point to the relevant question
+ if ((parseInt(element.pageIndex, 10)) == routeId) {
+ delete element.routing
+ }
+ }
+ }
+
+ // If the no question to start the route has been selected, create an error to be displayed to the user
+ if (!routeQuestion) {
+ errors.routeQuestion = {
+ text: 'Select the question you want to add your route from',
+ href: "#routeQuestion"
+ }
+ }
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/other-answer-route', { errors, errorList, containsErrors })
+ } else {
+ if(!routeId) {
+ // go to add conditions to the route
+ res.redirect(`${routeQuestion}/question-to-skip-to`)
+ } else {
+ // if we are editing an existing ‘route for any other answer’ we need to update the routeQuestion
+ res.redirect(`../${routeQuestion}/question-to-skip-to`)
+ }
+ }
+}
+router.post('/form-designer/question-routes/:pageId(\\d+)/other-answer-route-start', postRouteOtherAnswerStart)
+router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/other-answer-route-start', postRouteOtherAnswerStart)
+
+// Render ‘Route for any other answer’ end page question
+router.get('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/question-to-skip-to', function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var routeQuestion = parseInt(req.params.routeQuestion, 10)
+ var routeIndex = routeQuestion
+ return res.render('form-designer/question-routes/question-to-skip-to', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData,
+ routeIndex: routeIndex
+ })
+})
+
+// Finish creating ‘Route for any other answer’ - button journey
+router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/secondary-skip-end', function (req, res) {
+ const errors = {}
+ var { routeEnd, pages } = req.session.data
+ var routeQuestion = parseInt(req.params.routeQuestion, 10)
+ var pageId = parseInt(req.params.pageId, 10)
+
+ // If the no question to start the route has been selected, create an error to be displayed to the user
+ if (!routeEnd?.length) {
+ errors.routeEnd = {
+ text: 'Select the question you want to skip the person to',
+ href: "#routeEnd"
+ }
+ }
+
+ /*
+ now add the routing to the correct question
+ for page in req.session.data.pages
+ if page.pageIndex == routeQuestion
+ page.routing = { 'skipTo': routeEnd }
+
+ clear routeQuestion and routeEnd just before we redirect
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ // add routing value with end point to the relevant question
+ if ((parseInt(element.pageIndex, 10)) == routeQuestion) {
+ element.routing = { 'baseQuestion': pageId, 'noAnswer': true, 'thenSkipTo': routeEnd }
+ }
+ // we also need to add a value to the routing value of the original question
+ if ((parseInt(element.pageIndex, 10)) == pageId) {
+ element.routing.secondary = 'true'
+ element.routing.secondaryQuestion = element.pageIndex
+ }
+ }
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/question-to-skip-to', { errors, errorList, containsErrors })
+ } else {
+ // reset our temporary route session data
+ req.session.data.routeQuestion = undefined
+ req.session.data.routeEnd = undefined
+ // go to back to question routes summary screen
+ res.redirect(`../routes-summary`)
+ }
})
module.exports = router
\ No newline at end of file
From b75c4b8900860fc3f7a30c07b14a93c95c6c1e28 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 13:49:52 +0000
Subject: [PATCH 19/61] Added route for any other routing to question order in
preview
---
app/routes.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/app/routes.js b/app/routes.js
index 89a620f5..241678bf 100644
--- a/app/routes.js
+++ b/app/routes.js
@@ -661,6 +661,7 @@ router.post('/form-designer/preview/:pageId(\\d+)', function (req, res) {
// set variable for the next page, with default
var nextPage = `${pageIndex + 1}`
+
// for all pages in the form
for (let index = 0; index < pages.length; index++) {
const page = pages[index];
@@ -676,6 +677,10 @@ router.post('/form-designer/preview/:pageId(\\d+)', function (req, res) {
// override the nextPage to the one in routing
nextPage = page.routing.skipTo
}
+ if (page.routing.thenSkipTo) {
+ // override the nextPage to the ‘Route for any other answer’
+ nextPage = page.routing.thenSkipTo
+ }
}
// check if we are changing an answer
if (cya !== 'true') {
From 6f39acdc7d074b9bccff0765bf7d743414a94e92 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 14:15:32 +0000
Subject: [PATCH 20/61] Added return journey check to add a question route to
see if a route exists for the question
---
.../form-designer/question-routes/_routes.js | 37 +++++++++++++++----
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
index e81d7a1d..211a9d0f 100644
--- a/app/views/form-designer/question-routes/_routes.js
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -23,14 +23,10 @@ router.get('/form-designer/question-routes/:pageId(\\d+)/new-condition', getStar
// Create a new question route - button journey
// this POST also creates our ‘pageId’ to use throughout the editing and creating of routes attached to it
postStartQuestion = function (req, res) {
- const errors = {}
- var pageId = parseInt(req.params.pageId, 10)
+ const errors = {}
var routeStartQuestion = parseInt(req.session.data.routeStartQuestion, 10)
var pageIndex = routeStartQuestion
-
- if (pageId) {
- pageIndex = pageId
- }
+ var { pages } = req.session.data
// If the no question to start the route has been selected, create an error to be displayed to the user
if (!routeStartQuestion) {
@@ -40,6 +36,25 @@ postStartQuestion = function (req, res) {
}
}
+ /*
+ set a temporary check variable to hold if the page exists
+ var yuppityYup = false
+ we first need to loop through the existing data.pages
+ for (page in data.pages)
+ check if the selected page already has a route
+ if ((page.pageIndex == pageIndex) && (page.routing))
+ yuppityYup = true
+ */
+ var yuppityYup = false
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ if ((parseInt(element.pageIndex, 10)) == pageIndex) {
+ if (element.routing) {
+ yuppityYup = true
+ }
+ }
+ }
+
// Convert the errors into a list, so we can use it in the template
const errorList = Object.values(errors)
// If there are no errors, redirect the user to the next page
@@ -48,8 +63,14 @@ postStartQuestion = function (req, res) {
if(containsErrors) {
res.render('form-designer/question-routes/new-condition', { errors, errorList, containsErrors })
} else {
- // go to add conditions to the route
- res.redirect(`${pageIndex}/conditions`)
+ // if selected question already has a route we want to take the user to the question routes summary page
+ if (yuppityYup) {
+ // tkae user to question routes summary page
+ res.redirect(`${pageIndex}/routes-summary`)
+ } else {
+ // go to add answer and skip to conditions for the route
+ res.redirect(`${pageIndex}/conditions`)
+ }
}
}
router.post('/form-designer/question-routes/route-start', postStartQuestion)
From 2b5658086283e0c574ed104828f6d848b8a7133a Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 14:20:27 +0000
Subject: [PATCH 21/61] Changed to use nunjucks component
---
.../question-routes/new-condition.html | 60 +++++++++----------
1 file changed, 27 insertions(+), 33 deletions(-)
diff --git a/app/views/form-designer/question-routes/new-condition.html b/app/views/form-designer/question-routes/new-condition.html
index 0bd6e201..f9d401e4 100644
--- a/app/views/form-designer/question-routes/new-condition.html
+++ b/app/views/form-designer/question-routes/new-condition.html
@@ -1,6 +1,8 @@
{% extends "layout-govuk-forms.html" %}
{% set pageTitle = 'Add a route from a question' %}
+{% set pageQuestion = 'Which question do you want to add a route from?' %}
+{% set pageHint = 'A route can only start from a question where people select one item from a list. You can only add one route from each question.' %}
{% block pageTitle %}
{{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
@@ -37,42 +39,34 @@
{{ pageTitle }}
You can set up a route so if someone selects a specific answer to a question they’ll skip forward to a later question, or the end of the form.
+ {% if data.pages.length >= 2 %}
+
+ {% set tempQuestions = [] %}
{% for page in data.pages -%}
- {% set tempQuestions = {
- 'name': page['long-title']
- } %}
+ {% if page['type'] == 'select' and page['listSettings'].includes('oneOption') and not loop.last %}
+ {% set tempQuestions = (
+ tempQuestions.push({
+ value: page.pageIndex,
+ text: (page.pageIndex|int + 1) + '. ' + page['long-title'],
+ checked: true if data.routeStartQuestion == (page.pageIndex|int + 1)
+ }), tempQuestions)
+ %}
+ {% endif %}
{%- endfor %}
- {% if data.pages.length >= 2 %}
-
-
-
+ {{ govukRadios({
+ name: "routeQuestion",
+ fieldset: {
+ legend: {
+ text: pageQuestion,
+ classes: "govuk-fieldset__legend--m"
+ }
+ },
+ hint: {
+ text: pageHint
+ },
+ items: tempQuestions
+ }) }}
{{ govukButton({
text: "Continue"
From 698dd328044c8dfe1ee60c8c2a65699156c9c5f9 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 18 Mar 2025 15:34:39 +0000
Subject: [PATCH 22/61] Fixed bug to allow first question to be the start of a
route
---
app/views/form-designer/question-routes/_routes.js | 2 +-
app/views/form-designer/question-routes/new-condition.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
index 211a9d0f..f131727e 100644
--- a/app/views/form-designer/question-routes/_routes.js
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -29,7 +29,7 @@ postStartQuestion = function (req, res) {
var { pages } = req.session.data
// If the no question to start the route has been selected, create an error to be displayed to the user
- if (!routeStartQuestion) {
+ if (!routeStartQuestion && routeStartQuestion !== 0) {
errors.routeStartQuestion = {
text: 'Select the question you want your route to start from',
href: "#route-start-question"
diff --git a/app/views/form-designer/question-routes/new-condition.html b/app/views/form-designer/question-routes/new-condition.html
index f9d401e4..e00f39b5 100644
--- a/app/views/form-designer/question-routes/new-condition.html
+++ b/app/views/form-designer/question-routes/new-condition.html
@@ -55,7 +55,7 @@
{{ govukButton({
text: "Delete all routes",
- classes: "govuk-button--warning"
+ classes: "govuk-button--warning",
+ name: "action",
+ value: "deleteAllRoutes"
}) }}
{% else %}
From 1d2e1ca6139b061249a0999b892ff6ffaff982c5 Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 25 Mar 2025 11:15:36 +0000
Subject: [PATCH 35/61] Added routing to allow route deletion
---
.../form-designer/question-routes/_routes.js | 348 ++++++++++++++----
1 file changed, 282 insertions(+), 66 deletions(-)
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
index b5131de2..0d2a82d6 100644
--- a/app/views/form-designer/question-routes/_routes.js
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -32,7 +32,7 @@ postStartQuestion = function (req, res) {
if (!routeStartQuestion && routeStartQuestion !== 0) {
errors.routeStartQuestion = {
text: 'Select the question you want your route to start from',
- href: "#route-start-question"
+ href: "#routeStartQuestion"
}
}
@@ -94,38 +94,7 @@ postConditions = function (req, res) {
var pageId = parseInt(req.params.pageId, 10)
var pageIndex = pageId
var pageData = req.session.data.pages[pageIndex]
- var { pages, route1Answer, route1End } = req.session.data
-
- /*
- we need to loop through our forms questions
- for page in req.session.data.pages
- find if the routeStart is the same as the current page loop
- if page.pageIndex === routeStartQuestion
- now we need to check if routing already exists on our base question
- if page.routing
- we just want to update the existing keys
- routing.answer = route1Answer
- routing.skipTo = route1End
- else
- we need to create a new key object for routing
- page.push(
- routing: {
- routeAnswer: route1Answer,
- routeEnd: route1End
- })
- clear (routeStartQuestion, route1Answer, route1End)
- */
- for (let index = 0; index < pages.length; index++) {
- const element = pages[index];
- if ((parseInt(element.pageIndex, 10)) == pageIndex) {
- if (element.routing) {
- element.routing.answer = route1Answer
- element.routing.skipTo = route1End
- } else {
- element.routing = { 'answer': route1Answer, 'skipTo': route1End }
- }
- }
- }
+ var { pages, route1Answer, route1End, action } = req.session.data
// If the ‘is answered as’ input has not been selected, create an error to be displayed to the user
if (!route1Answer?.length) {
@@ -148,12 +117,55 @@ postConditions = function (req, res) {
// otherwise, show the page again with the errors set
const containsErrors = errorList.length > 0
if(containsErrors) {
- res.render('form-designer/question-routes/conditions', { errors, errorList, containsErrors })
+ res.render('form-designer/question-routes/conditions', { errors, errorList, containsErrors, pageId, pageIndex, pageData })
+ } else if (action?.length && (action === 'deleteRoute1')) {
+ // reset our temporary route session data
+ req.session.data.routeStartQuestion = undefined
+ req.session.data.route1Answer = undefined
+ req.session.data.route1End = undefined
+ // reset action
+ req.session.data.action = undefined
+ /*
+ we need to redirect the user to the are you sure you want to delete this route page, if the user selects yes on that screen we can start deleting the routes
+ */
+ res.redirect(`are-you-sure?action=` + action)
} else {
+ /*
+ we need to loop through our forms questions
+ for page in req.session.data.pages
+ find if the routeStart is the same as the current page loop
+ if page.pageIndex === routeStartQuestion
+ now we need to check if routing already exists on our base question
+ if page.routing
+ we just want to update the existing keys
+ routing.answer = route1Answer
+ routing.skipTo = route1End
+ else
+ we need to create a new key object for routing
+ page.push(
+ routing: {
+ routeAnswer: route1Answer,
+ routeEnd: route1End
+ })
+ clear (routeStartQuestion, route1Answer, route1End)
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ if ((parseInt(element.pageIndex, 10)) == pageIndex) {
+ if (element.routing) {
+ element.routing.answer = route1Answer
+ element.routing.skipTo = route1End
+ } else {
+ element.routing = { 'answer': route1Answer, 'skipTo': route1End }
+ }
+ }
+ }
// reset our temporary route session data
req.session.data.routeStartQuestion = undefined
req.session.data.route1Answer = undefined
req.session.data.route1End = undefined
+ // reset action
+ req.session.data.action = undefined
// go to question X’s routes summary screen
res.redirect(`routes-summary`)
}
@@ -178,19 +190,42 @@ router.get('/form-designer/question-routes/:pageId(\\d+)/routes-summary', functi
// Start a ‘Route for any other answer’ - suggested question - button journey
router.post('/form-designer/question-routes/:pageId(\\d+)/questions-routes', function (req, res) {
- // if button pressed
- // get the suggested question (based on the route 1 end)
- var { pages, suggestedQuestion } = req.session.data
- req.session.data.suggestedQuestion = undefined // reset the session data for suggestedQuestion
- // add the first part of routing to the suggested question
- for (let index = 0; index < pages.length; index++) {
- const element = pages[index];
- if ((parseInt(element.pageIndex, 10)) == suggestedQuestion) {
- element.routing = { 'noAnswer': 'true' } // add a routing element to the question
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+
+ // get the suggested question (based on the ‘route 1’ end)
+ var { pages, suggestedQuestion, action } = req.session.data
+
+ /*
+ check if delete button is pressed
+ if action && action == deleteAllRoutes
+ now we need to find the questions with the route 1 and route for any other answer
+ and then we can delete these
+ */
+ if (action?.length && (action === 'deleteAllRoutes')) {
+ // reset action
+ req.session.data.action = undefined
+ /*
+ need to take the user to an are you sure page?
+ just now it will delete the routes and redirect them to ‘add and edit your questions’ page
+ */
+ // go to form questions list
+ res.redirect(`are-you-sure`)
+ } else {
+ // add the first part of routing to the suggested question
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ if ((parseInt(element.pageIndex, 10)) == suggestedQuestion) {
+ element.routing = { 'noAnswer': 'true' } // add a routing element to the question
+ }
}
+ // reset the session data for suggestedQuestion
+ req.session.data.suggestedQuestion = undefined
+ // reset action
+ req.session.data.action = undefined
+ // go to add a secondary route to this question
+ res.redirect(`${suggestedQuestion}/question-to-skip-to`)
}
- // go to add a secondary route to this question
- res.redirect(`${suggestedQuestion}/question-to-skip-to`)
})
@@ -254,7 +289,7 @@ postRouteOtherAnswerStart = function (req, res) {
// otherwise, show the page again with the errors set
const containsErrors = errorList.length > 0
if(containsErrors) {
- res.render('form-designer/question-routes/other-answer-route', { errors, errorList, containsErrors })
+ res.render('form-designer/question-routes/other-answer-route', { errors, errorList, containsErrors, routeId, routeQuestion })
} else {
if(!routeId) {
// go to add conditions to the route
@@ -296,26 +331,26 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/s
text: 'Select the question you want to skip the person to',
href: "#routeEnd"
}
- }
-
- /*
- now add the routing to the correct question
- for page in req.session.data.pages
- if page.pageIndex == routeQuestion
- page.routing = { 'skipTo': routeEnd }
+ } else {
+ /*
+ now add the routing to the correct question
+ for page in req.session.data.pages
+ if page.pageIndex == routeQuestion
+ page.routing = { 'skipTo': routeEnd }
- clear routeQuestion and routeEnd just before we redirect
- */
- for (let index = 0; index < pages.length; index++) {
- const element = pages[index];
- // add routing value with end point to the relevant question
- if ((parseInt(element.pageIndex, 10)) == routeQuestion) {
- element.routing = { 'baseQuestion': pageId, 'noAnswer': true, 'thenSkipTo': routeEnd }
- }
- // we also need to add a value to the routing value of the original question
- if ((parseInt(element.pageIndex, 10)) == pageId) {
- element.routing.secondary = 'true'
- element.routing.secondaryQuestion = element.pageIndex
+ clear routeQuestion and routeEnd just before we redirect
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ // add routing value with end point to the relevant question
+ if ((parseInt(element.pageIndex, 10)) == routeQuestion) {
+ element.routing = { 'baseQuestion': pageId, 'noAnswer': true, 'thenSkipTo': routeEnd }
+ }
+ // we also need to add a value to the routing value of the original question
+ if ((parseInt(element.pageIndex, 10)) == pageId) {
+ element.routing.secondary = 'true'
+ element.routing.secondaryQuestion = routeQuestion
+ }
}
}
@@ -325,7 +360,7 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/s
// otherwise, show the page again with the errors set
const containsErrors = errorList.length > 0
if(containsErrors) {
- res.render('form-designer/question-routes/question-to-skip-to', { errors, errorList, containsErrors })
+ res.render('form-designer/question-routes/question-to-skip-to', { errors, errorList, containsErrors, pageId, routeQuestion })
} else {
// reset our temporary route session data
req.session.data.routeQuestion = undefined
@@ -335,4 +370,185 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/s
}
})
+
+/* Delete route journeys */
+
+// Render are you sure you want to delete ‘Route 1’
+router.get('/form-designer/question-routes/:pageId(\\d+)/are-you-sure', function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ return res.render('form-designer/question-routes/are-you-sure', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData
+ })
+})
+
+// Check if we should delete ‘Route 1’ - button journey
+router.post('/form-designer/question-routes/:pageId(\\d+)/are-you-sure', function (req, res) {
+ const errors = {}
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var { deleteRoute, pages, action } = req.session.data
+
+ // If the ‘skip the person’ to input has not been selected, create an error to be displayed to the user
+ if (!deleteRoute?.length) {
+ if (action?.length && (action === 'deleteRoute1')) {
+ errors.deleteRoute = {
+ text: 'Select ‘Yes’ if you want to delete route 1',
+ href: "#deleteRoute"
+ }
+ } else {
+ errors.deleteRoute = {
+ text: 'Select ‘Yes’ to delete this question’s routes',
+ href: "#deleteRoute"
+ }
+ }
+ }
+
+ // reset action
+ req.session.data.action = undefined
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/are-you-sure', { errors, errorList, containsErrors, pageId, pageIndex, pageData })
+ } else if (deleteRoute?.length && (deleteRoute === 'Yes')) {
+ /*
+ go through all of the forms questions
+ for page in data.pages
+ check if question has a secondary route
+ if pageData.routing.secondary
+ get the secondary route start question
+ delete ‘route 1’
+ we now need to find the secondary route start question
+ for page in data.pages
+ if secondary route start question
+ delete ‘route for any other answer’
+ now we can take the user back to their list of questions
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ var secondaryQuestion = ''
+
+ // get current question
+ if ((parseInt(element.pageIndex, 10)) == pageIndex) {
+ // check if question has a secondary route
+ if (element.routing.secondary) {
+ // temporarily store the skipTo question in a variable
+ secondaryQuestion = element.routing.secondaryQuestion
+ }
+ // delete the routing
+ element.routing = undefined
+ }
+
+ // if there is a secondary route we need to delete this too
+ if (secondaryQuestion?.length) {
+ // re-loop through forms questions to find the ‘route for any other answer’ start question
+ for (let secondaryIndex = 0; secondaryIndex < pages.length; secondaryIndex++) {
+ const element = pages[secondaryIndex];
+ // get secondaryQuestion
+ if ((parseInt(element.pageIndex, 10)) == (parseInt(secondaryQuestion, 10))) {
+ // delete the routing
+ element.routing = undefined
+ }
+ }
+ }
+ }
+ // reset temporary page answer
+ req.session.data.deleteRoute = undefined
+ // go to question X’s routes summary screen
+ res.redirect(`../../your-questions`)
+ } else {
+ // reset temporary page answer
+ req.session.data.deleteRoute = undefined
+ // if we are not deleting the route take the person back to the question routes summary
+ res.redirect(`routes-summary`)
+ }
+})
+
+// Render are you sure you want to delete ‘Route 1’
+router.get('/form-designer/question-routes/:pageId(\\d+)/delete-secondary-route', function (req, res) {
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ return res.render('form-designer/question-routes/delete-secondary-route', {
+ pageId: pageId,
+ pageIndex: pageIndex,
+ pageData: pageData
+ })
+})
+
+// Check if we should delete ‘Route for any other answer’ - button journey
+router.post('/form-designer/question-routes/:pageId(\\d+)/delete-secondary-route', function (req, res) {
+ const errors = {}
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ var { deleteRouteOtherAnswer, pages } = req.session.data
+
+ // If the ‘skip the person’ to input has not been selected, create an error to be displayed to the user
+ if (!deleteRouteOtherAnswer?.length) {
+ errors.deleteRouteOtherAnswer = {
+ text: 'Select if you want to delete route for any other answer',
+ href: "#deleteRouteOtherAnswer"
+ }
+ }
+
+ // Convert the errors into a list, so we can use it in the template
+ const errorList = Object.values(errors)
+ // If there are no errors, redirect the user to the next page
+ // otherwise, show the page again with the errors set
+ const containsErrors = errorList.length > 0
+ if(containsErrors) {
+ res.render('form-designer/question-routes/delete-secondary-route', { errors, errorList, containsErrors, pageId, pageIndex, pageData })
+ } else if (deleteRouteOtherAnswer?.length && (deleteRouteOtherAnswer === 'Yes')) {
+ // reset temporary page answer
+ req.session.data.deleteRouteOtherAnswer = undefined
+ /*
+ go through all of the forms questions
+ for page in data.pages
+ find this question with route
+ if page.routing.baseQuestion == pageIndex
+ delete ‘route for any other answer’
+ now we can take the user back to their list of questions
+ */
+ for (let index = 0; index < pages.length; index++) {
+ const element = pages[index];
+ var baseQuestion = ''
+
+ // get current question
+ if (element.routing && ((parseInt(element.routing.baseQuestion, 10)) == pageIndex)) {
+ // temporarily store the baseQuestion question in a variable
+ baseQuestion = element.routing.baseQuestion
+ // delete the routing
+ element.routing = undefined
+ }
+
+ // now find the ‘route 1’ start question
+ for (let primaryIndex = 0; primaryIndex < pages.length; primaryIndex++) {
+ const page = pages[primaryIndex];
+ // get skipToQuestion
+ if (page.routing && ((parseInt(page.pageIndex, 10)) == (parseInt(baseQuestion, 10)))) {
+ // delete the routing
+ page.routing.secondary = undefined
+ page.routing.secondaryQuestion = undefined
+ }
+ }
+ }
+ // go to question X’s routes summary screen
+ res.redirect(`routes-summary`)
+ } else {
+ // reset temporary page answer
+ req.session.data.deleteRouteOtherAnswer = undefined
+ // if we are not deleting the route take the person back to the question routes summary
+ res.redirect(`routes-summary`)
+ }
+})
+
module.exports = router
\ No newline at end of file
From aa64397f9f2a243c1b16a86785255c4f04172c9e Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Tue, 25 Mar 2025 15:51:57 +0000
Subject: [PATCH 36/61] Fix inability to update page question text
---
app/views/form-designer/pages/_routes.js | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/app/views/form-designer/pages/_routes.js b/app/views/form-designer/pages/_routes.js
index 85cd15bb..f136b80a 100644
--- a/app/views/form-designer/pages/_routes.js
+++ b/app/views/form-designer/pages/_routes.js
@@ -414,21 +414,19 @@ router.post('/form-designer/pages/:pageId(\\d+)/edit', function (req, res) {
const errors = {};
- if (!pageData['long-title']) {
- const title = req.session.data['long-title']
-
- // if no question text given, then throw an error
- if (!title || !title.length) {
- errors['long-title'] = {
- text: 'Enter a question',
- href: "#long-title"
- }
- // otherwise add question text to pageData
- } else {
- pageData['long-title'] = req.session.data['long-title']
+ var title = req.session.data['long-title']
+
+ // if no question text given, then throw an error
+ if (!title || !title.length) {
+ errors['long-title'] = {
+ text: 'Enter a question',
+ href: "#long-title"
}
- req.session.data['long-title'] = undefined
+ // otherwise add question text to pageData
+ } else {
+ pageData['long-title'] = req.session.data['long-title']
}
+ req.session.data['long-title'] = undefined
// if hint text is added, add it to pageData
if (req.session.data['hint-text']) {
From 61182e8b504bede6d68cba99a482c69afb9e6b7a Mon Sep 17 00:00:00 2001
From: Chris Cameron
Date: Wed, 26 Mar 2025 12:03:04 +0000
Subject: [PATCH 37/61] Added successMessage banners to creation and deletion
of routing journeys
---
.../form-designer/question-routes/_routes.js | 22 +++++++++++++++----
.../question-routes/routes-summary.html | 7 ++++++
2 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/app/views/form-designer/question-routes/_routes.js b/app/views/form-designer/question-routes/_routes.js
index 0d2a82d6..a10a5735 100644
--- a/app/views/form-designer/question-routes/_routes.js
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -166,6 +166,8 @@ postConditions = function (req, res) {
req.session.data.route1End = undefined
// reset action
req.session.data.action = undefined
+ // set a success message to declare that ‘route 1’ has been saved
+ req.session.data.successMessage = "Route 1 has been saved"
// go to question X’s routes summary screen
res.redirect(`routes-summary`)
}
@@ -181,10 +183,16 @@ router.get('/form-designer/question-routes/:pageId(\\d+)/routes-summary', functi
var pageId = parseInt(req.params.pageId, 10)
var pageIndex = pageId
var pageData = req.session.data.pages[pageIndex]
+
+ // get success message and then clear session data (so it doesn’t hang around)
+ var successMessage = req.session.data.successMessage
+ req.session.data.successMessage = undefined
+
return res.render('form-designer/question-routes/routes-summary', {
pageId: pageId,
pageIndex: pageIndex,
- pageData: pageData
+ pageData: pageData,
+ successMessage
})
})
@@ -365,6 +373,8 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/s
// reset our temporary route session data
req.session.data.routeQuestion = undefined
req.session.data.routeEnd = undefined
+ // set a success message to declare that ‘route for any other answer’ has been saved
+ req.session.data.successMessage = "Route for any other answer has been saved"
// go to back to question routes summary screen
res.redirect(`../routes-summary`)
}
@@ -373,7 +383,7 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/:routeQuestion(\\d+)/s
/* Delete route journeys */
-// Render are you sure you want to delete ‘Route 1’
+// Render are you sure you want to delete ‘Route 1’ or all routes
router.get('/form-designer/question-routes/:pageId(\\d+)/are-you-sure', function (req, res) {
var pageId = parseInt(req.params.pageId, 10)
var pageIndex = pageId
@@ -448,12 +458,12 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/are-you-sure', functio
}
// if there is a secondary route we need to delete this too
- if (secondaryQuestion?.length) {
+ if (typeof secondaryQuestion !== 'undefined') {
// re-loop through forms questions to find the ‘route for any other answer’ start question
for (let secondaryIndex = 0; secondaryIndex < pages.length; secondaryIndex++) {
const element = pages[secondaryIndex];
// get secondaryQuestion
- if ((parseInt(element.pageIndex, 10)) == (parseInt(secondaryQuestion, 10))) {
+ if ((parseInt(element.pageIndex, 10)) == secondaryQuestion) {
// delete the routing
element.routing = undefined
}
@@ -462,6 +472,8 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/are-you-sure', functio
}
// reset temporary page answer
req.session.data.deleteRoute = undefined
+ // set a success message to declare that routes have been deleted
+ req.session.data.successMessage = "Question " + (pageIndex + 1) + "’s routes have been deleted"
// go to question X’s routes summary screen
res.redirect(`../../your-questions`)
} else {
@@ -541,6 +553,8 @@ router.post('/form-designer/question-routes/:pageId(\\d+)/delete-secondary-route
}
}
}
+ // set a success message to declare that ‘route for any other answer’ has been deleted
+ req.session.data.successMessage = "Route for any other answer has been deleted"
// go to question X’s routes summary screen
res.redirect(`routes-summary`)
} else {
diff --git a/app/views/form-designer/question-routes/routes-summary.html b/app/views/form-designer/question-routes/routes-summary.html
index 9386bcc8..e0c0ad12 100644
--- a/app/views/form-designer/question-routes/routes-summary.html
+++ b/app/views/form-designer/question-routes/routes-summary.html
@@ -17,6 +17,13 @@