diff --git a/app/data/returning-session-data-defaults-a11y.js b/app/data/returning-session-data-defaults-a11y.js
index 1928e1a9..9936d713 100644
--- a/app/data/returning-session-data-defaults-a11y.js
+++ b/app/data/returning-session-data-defaults-a11y.js
@@ -1,73 +1,132 @@
/*
-Provide default values for user session data. These are automatically added
-via the `autoStoreData` middleware. Values will only be added to the
-session if a value doesn't already exist. This may be useful for testing
-journeys where users are returning or logging in to an existing application.
-
-============================================================================
-
-Example usage:
-
-"full-name": "Sarah Philips",
-
-"options-chosen": [ "foo", "bar" ]
-
-============================================================================
+This is the latest version of return data for a dummy form to be used in testing the branching journey:
+created 26 March 2025
+testing: week commencing 31 March 2025
*/
module.exports = {
- highestPageId: 6,
- action: 'gogogo',
- publish: 'GOV.UK',
- authentication: 'email',
- payments: 'no',
+ /* Dummy form settings */
+ formTitle: 'Tell us about a complaint, concern or error',
+ status: 'Draft',
+
+ // Form tasks
+ isQuestionsComplete: 'no',
+ // checkAnswersDeclaration
+ isDeclarationComplete: 'no',
+ // confirmationNext
+ isConfirmationComplete: 'no',
+ // formsEmail
+ isSubmissionEmailComplete: 'no',
+ // confirmationCode
+ isConfirmationCodeComplete: 'no',
+ // privacyInformation
+ isPrivacyInformationComplete: 'no',
+ // supportDetails - emailSupport, phoneSupport, onlineSupportLink, onlineSupportText
+ isSupportDetailsComplete: 'no',
+ // makeFormLive
+ isFormLive: 'no',
+
+ // Form questions
+ highestPageId: 7,
pages: [
{
- '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'
+ pageIndex: '0',
+ type: 'select',
+ 'long-title': 'Which of these do you want to do today?',
+ 'item-list': [
+ "Report an error",
+ "Make a complaint",
+ "Raise a concern"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
+ },
+ {
+ pageIndex: '1',
+ type: 'select',
+ 'long-title': 'What does your complaint or concern relate to?',
+ 'item-list': [
+ "Service",
+ "Access",
+ "Availability",
+ "Something else"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'What is the name of your pet?',
- 'short-title': 'Pet name',
+ pageIndex: '2',
type: 'text',
- pageIndex: '1'
+ input: "multi-line-input",
+ 'long-title': 'Please give full details of your complaint or concern',
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'Where are you travelling to?',
- 'short-title': 'Destination',
- 'hint-text': 'For example Lisbon, Portugal',
+ pageIndex: '3',
+ type: 'select',
+ 'long-title': 'Have you contacted us about this before?',
+ 'item-list': [
+ "Yes",
+ "No"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
+ },
+ {
+ pageIndex: '4',
type: 'text',
- pageIndex: '2'
+ input: "single-line-input",
+ 'long-title': 'What were you trying to do when the error happend?',
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'What date do you travel?',
- 'short-title': 'Date',
- 'hint-text': 'For example 27 3 2007',
- type: 'date',
- pageIndex: '3'
+ pageIndex: '5',
+ type: 'text',
+ input: "multi-line-input",
+ 'long-title': 'Please tell us what happened when the error occurred',
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'How are you travelling?',
- 'short-title': 'Transport type',
- 'hint-text': 'For example plane, train, car.',
+ pageIndex: '6',
type: 'text',
- pageIndex: '4'
+ input: "multi-line-input",
+ 'long-title': 'What did you do, if anything, to work around the error?',
+ 'additional-guidance': 'No',
+ "questionOptional": [
+ "questionOptional"
+ ],
+ 'questionSaved': 'Yes'
},
{
- 'long-title': 'How many pets do you have?',
- 'short-title': 'Number of pets',
- type: 'number',
- pageIndex: '5'
+ pageIndex: '7',
+ type: 'select',
+ 'long-title': 'How would you rate your overall experience of using the service?',
+ 'item-list': [
+ "Very poor",
+ "Poor",
+ "Neutral",
+ "Good",
+ "Very good"
+ ],
+ 'listSettings': [
+ "oneOption"
+ ],
+ 'additional-guidance': 'No',
+ 'questionSaved': 'Yes'
}
- ],
- status: 'Draft',
- confirmationTitle: 'Your form has been submitted',
- checkAnswersTitle: 'Check your answers before submitting your form',
- formTitle: 'Take your pet abroad',
- isQuestionsComplete: 'no'
+ ]
}
diff --git a/app/data/returning-session-data-defaults.js b/app/data/returning-session-data-defaults.js
index 7bfc19d3..56c30586 100644
--- a/app/data/returning-session-data-defaults.js
+++ b/app/data/returning-session-data-defaults.js
@@ -1,28 +1,17 @@
/*
-Provide default values for user session data. These are automatically added
-via the `autoStoreData` middleware. Values will only be added to the
-session if a value doesn't already exist. This may be useful for testing
-journeys where users are returning or logging in to an existing application.
-
-============================================================================
-
-Example usage:
-
-"full-name": "Sarah Philips",
-
-"options-chosen": [ "foo", "bar" ]
-
-============================================================================
+This data is for a dummy form - it was previously used in earlier rounds of testing
+Not all the structure of the questions will work and may cause some errors throughout the prototype
*/
module.exports = {
+ /* Dummy form settings */
+ formTitle: 'Amendment form: redundancy claim for holiday pay',
+ status: 'Draft',
+
+ // Form questions
highestPageId: 6,
- action: 'gogogo',
- publish: 'GOV.UK',
- authentication: 'email',
- payments: 'no',
pages: [
{
intro: 'This is the intro',
@@ -80,10 +69,5 @@ module.exports = {
type: 'number',
pageIndex: '7'
}
- ],
- status: 'Draft',
- confirmationTitle: 'Your form has been submitted',
- checkAnswersTitle: 'Check your answers before submitting your form',
- formTitle: 'Amendment form: redundancy claim for holiday pay',
- isQuestionsComplete: 'no'
+ ]
}
diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js
index 95a505bc..779a12a1 100644
--- a/app/data/session-data-defaults.js
+++ b/app/data/session-data-defaults.js
@@ -19,55 +19,86 @@ Example usage:
module.exports = {
- // Insert values here
+ // Account settings
+ defaultUser: 'Firstname Ipsum',
- highestPageId: 0,
- action: '',
- publish: 'GOV.UK',
+ // Group settings
+ defaultGroup: 'Departmental contact forms',
+
+ // Default form data and settings
authentication: 'email',
payments: 'no',
+ publish: 'GOV.UK',
+
+ // Placeholders to avoid errors
+ // Form questions
+ highestPageId: 0,
pages: [],
+ // Form status
status: 'Draft',
- confirmationTitle: 'Your form has been submitted',
+ // Button actions value - used for ‘continue’, ‘save’, ‘delete’ or any other secondary grey button action
+ action: '',
+
+ /* STATIC CONTENT
+ ================= */
+
+ // Check your answers page
checkAnswersTitle: 'Check your answers before submitting your form',
+ // Confirmation page
+ confirmationTitle: 'Your form has been submitted',
+ /* Answer type hints and input type content */
+
+ // Person name
personNameQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your name?’',
personNameHintHint: 'You can add a short hint to help people answer the question. For example, you might need to ask people to enter their name as it’s written on an official document such as a passport or driving licence.',
personNameInputTypeTitle: 'Name fields',
+ // Company name
companyNameQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s the name of the organisation?’',
companyNameHintHint: 'You can add a short hint to help people answer the question. For example, you might need to ask people to enter the registered name of their company.',
+ // Email
emailQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your email address?’',
emailHintHint: 'You could use hint text to tell people how you’ll use their email address. For example, ‘We’ll only use your email address to contact you about your application.’',
+ // Telephone
phoneQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your phone number?’',
phoneHintHint: 'You can add a short hint to help people answer the question. For example, ‘You can provide either a home or mobile phone number.’',
+ // National Insurance number (NINo)
ninoQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your National Insurance number?’',
ninoHintHint: 'You can add a short hint to help people answer the question. For example, ‘It’s on your National Insurance card, benefit letter, payslip or P60. For example, QQ 12 34 56 C.’',
+ // Address
addressQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your address?’',
addressHintHint: 'You could use hint text to tell people how you’ll use their address. For example, ‘We’ll send your licence to this address.’',
addressInputTypeTitle: 'Address type',
- dobQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your date of birth?’',
- dobHintHint: 'You can add a short hint to help people answer the question. For a date of birth question you could use ‘For example, 27 3 1998’.',
+ // Date
dateQuestionHint: 'Ask the question the way you would in person. For example, ‘What date was your passport issued?’',
dateHintHint: 'You can add a short hint to help people answer the question. For a date question you could use ‘For example, 27 3 2007’.',
+
+ // Date of birth (DOB)
+ dobQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your date of birth?’',
+ dobHintHint: 'You can add a short hint to help people answer the question. For a date of birth question you could use ‘For example, 27 3 1998’.',
dateOfBirthInputTypeTitle: 'Date of birth',
+ // Selection - radio, checkbox or autocomplete (build only)
selectionOneOptionQuestionHint: 'Ask the question the way you would in person. For example, ‘What country do you live in?’',
selectionOneOptionHintHint: 'You can add a short hint to help people answer the question. For a question where people can only select one answer you might want to use ‘Select one option’.',
selectionMultipleOptionsQuestionHint: 'Ask the question the way you would in person. For example, ‘Which of these countries have you lived in?’',
selectionMultipleOptionsHintHint: 'You can add a short hint to help people answer the question. For a question where people can select more than one answer you might want to use ‘Select all that apply’.',
+ // Number
numberQuestionHint: 'Ask the question the way you would in person. For example, ‘How many holiday days do you get a year?’',
numberHintHint: 'You can add a short hint to help people answer the question. For example, ‘Do not include bank holidays’.',
+ // Text - single line
textSingleLineQuestionHint: 'Ask the question the way you would in person. For example, ‘What’s your reference number?’',
textSingleLineHintHint: 'You can add a short hint to help people answer the question. For example, you could say what format the answer should be in or where to find it.',
+ // Textarea - multiple line
textMultipleLinesQuestionHint: 'Ask the question the way you would in person. For example, ‘Why do you want to apply for this role?’',
textMultipleLinesHintHint: 'You can add a short hint to help people answer the question. For example, you could give a bit more detail about the information you need.',
textLengthInputTypeTitle: 'Length'
diff --git a/app/routes.js b/app/routes.js
index 5761a4da..7a1a70e4 100644
--- a/app/routes.js
+++ b/app/routes.js
@@ -90,36 +90,24 @@ router.use('/form-designer/*', function (req, res, next) {
Create a new form
================= */
-// Middleware name your form
-router.use('/form-designer/name-your-form', function (req, res, next) {
-
+// Name your form - GET
+router.get('/form-designer/name-your-form', function (req, res) {
// get the previous page URL
var previousPage = req.session.data.referer
// set the default back link
var previousPageLink = `your-form`
- var previousPageText = 'Back to create a form'
+ var previousPageText = 'Back to create your form'
- if (previousPage) {
+ // if user is creating a ‘new’ form we should change the back link
+ if (previousPage?.includes('your-forms')) {
// set the GOV.UK Forms home back link
- if (previousPage.includes('your-forms')) {
- previousPageLink = `your-forms`
- previousPageText = 'Back to your forms'
- }
+ previousPageLink = `your-forms`
+ previousPageText = 'Back to ' + (req.session.data.defaultGroup || 'your forms' )
}
-
- // make back link available in the view
- req.session.data.previousPageLink = previousPageLink
- req.session.data.previousPageText = previousPageText
-
- next();
-})
-
-// Name your form - GET
-router.get('/form-designer/name-your-form', function (req, res) {
return res.render('form-designer/name-your-form', {
- previousPageText: req.session.data.previousPageText,
- previousPageLink: req.session.data.previousPageLink
+ previousPageText,
+ previousPageLink
})
})
@@ -144,11 +132,6 @@ router.post('/form-designer/name-your-form', function (req, res) {
if(containsErrors) {
res.render('form-designer/name-your-form', { errors, errorList, containsErrors })
} else {
-
- // remove temporary back link details
- req.session.data.previousPageLink = undefined
- req.session.data.previousPageText = undefined
-
// set a success message for saving
req.session.data.successMessage = 'Your form name has been saved'
res.redirect('your-form')
@@ -204,8 +187,8 @@ router.get('/form-designer/your-form', function (req, res) {
}
return res.render('form-designer/your-form', {
- successMessage: successMessage,
- sections: sections
+ successMessage,
+ sections
})
})
@@ -504,26 +487,17 @@ Managing additional pages in a form
// Route used to check Declaration is complete - Check your answers (CYA)
router.post('/form-designer/pages/check-answers/edit', function (req, res) {
- const action = req.session.data.action
- req.session.data.action = undefined
-
+ const errors = {};
var pageId = parseInt(req.params.pageId, 10)
var pageIndex = pageId
var pageData = req.session.data.pages[pageIndex]
+ var { checkAnswersDeclaration, isDeclarationComplete } = req.session.data
- const errors = {};
- const complete = req.session.data.isDeclarationComplete
- const declarationContent = req.session.data.checkAnswersDeclaration
-
- // content to display in notification banners
- var saved = 'Your declaration has been saved'
- var savedAndComplete = 'Your declaration has been saved and marked as complete'
-
- // if no selection made, then throw an error
- if (!complete || !complete.length) {
- errors['isDeclarationComplete'] = {
- text: 'Select yes if you want to mark this task as complete',
- href: "#isDeclarationComplete"
+ // if trying to mark as complete while having too many characters, then throw an error
+ if ((checkAnswersDeclaration.length > 2000) && (isDeclarationComplete == 'yes')) {
+ errors['checkAnswersDeclaration'] = {
+ text: 'The declaration cannot be longer than 2,000 characters',
+ href: "#check-answers-declaration"
}
}
@@ -533,16 +507,7 @@ router.post('/form-designer/pages/check-answers/edit', function (req, res) {
// otherwise, show the page again with the errors set
const containsErrors = errorList.length > 0
// If there are errors on the page, redisplay it with the errors
- if (action === 'update') {
- // set a success message for saving
- req.session.data.successMessage = saved
- return res.render('form-designer/pages/check-answers/edit', {
- pageId: pageId,
- pageIndex: pageIndex,
- pageData: pageData,
- successMessage: saved
- })
- } else if(containsErrors) {
+ if(containsErrors) {
return res.render('form-designer/pages/check-answers/edit', {
pageId: pageId,
pageIndex: pageIndex,
@@ -552,37 +517,28 @@ router.post('/form-designer/pages/check-answers/edit', function (req, res) {
containsErrors
})
} else {
- if(complete === 'yes') {
+ if(isDeclarationComplete === 'yes') {
// set a success message for saving
- req.session.data.successMessage = savedAndComplete
+ req.session.data.successMessage = 'Your declaration has been saved and marked as complete'
} else {
// set a success message for saving
- req.session.data.successMessage = saved
+ req.session.data.successMessage = 'Your declaration has been saved'
}
+ req.session.data.isDeclarationComplete = isDeclarationComplete
return res.redirect('../../your-form')
}
})
// Route used to check What happens next (WHN) content has been added
router.post('/form-designer/pages/confirmation/edit', function (req, res) {
- const action = req.session.data.action
- req.session.data.action = undefined
-
- var pageId = parseInt(req.params.pageId, 10)
- var pageIndex = pageId
- var pageData = req.session.data.pages[pageIndex]
-
const errors = {};
- const whatHappensNext = req.session.data.confirmationNext
-
- // content to display in notification banners
- var saved = 'Your information about what happens next has been saved'
+ const { whatHappensNext } = req.session.data
// if no selection made, then throw an error
if (!whatHappensNext || !whatHappensNext.length) {
- errors['confirmationNext'] = {
+ errors['whatHappensNext'] = {
text: 'Enter some information about what happens next',
- href: "#confirmationNext"
+ href: "#what-happens-next"
}
}
@@ -593,24 +549,10 @@ router.post('/form-designer/pages/confirmation/edit', function (req, res) {
const containsErrors = errorList.length > 0
// If there are errors on the page, redisplay it with the errors
if(containsErrors) {
- return res.render('form-designer/pages/confirmation/edit', {
- pageId: pageId,
- pageIndex: pageIndex,
- pageData: pageData,
- errors,
- errorList,
- containsErrors
- })
- } else if (action === 'update') {
- return res.render('form-designer/pages/confirmation/edit', {
- pageId: pageId,
- pageIndex: pageIndex,
- pageData: pageData,
- successMessage: saved
- })
+ return res.render('form-designer/pages/confirmation/edit', { errors, errorList, containsErrors })
} else {
// set a success message for saving
- req.session.data.successMessage = saved
+ req.session.data.successMessage = 'Your information about what happens next has been saved'
return res.redirect('../../your-form')
}
})
@@ -641,17 +583,73 @@ 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
+ }
+ 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') {
+ // 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 +1023,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
diff --git a/app/views/form-builder-prototypes.html b/app/views/form-builder-prototypes.html
index aac8054d..5dacb604 100644
--- a/app/views/form-builder-prototypes.html
+++ b/app/views/form-builder-prototypes.html
@@ -39,19 +39,11 @@
Admin prototype
- Amendment form: redundancy claim for holiday pay return journey (Insolvency Service first form)
-
{% endblock %}
-
-{% block pageScripts %}
-
-{% endblock %}
diff --git a/app/views/form-designer/pages/edit-answer-type.html b/app/views/form-designer/pages/edit-answer-type.html
index de8065bf..1f694af7 100644
--- a/app/views/form-designer/pages/edit-answer-type.html
+++ b/app/views/form-designer/pages/edit-answer-type.html
@@ -110,14 +110,11 @@
value: "editPage"
}) }}
- {# If we are not sure what the back link is going to don’t show the bottom link #}
- {% if previousPageText !== 'Back' %}
- {% for page in data.pages -%}
+ {% for page in data.pagesCYA -%}
{% set questionTitle = page["long-title"] or "Page " + page["pageIndex"] %}
{% if page['questionOptional'] %}
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..a6493def
--- /dev/null
+++ b/app/views/form-designer/question-routes/_routes.js
@@ -0,0 +1,568 @@
+const govukPrototypeKit = require('govuk-prototype-kit')
+const router = govukPrototypeKit.requests.setupRouter()
+
+/* CREATING A NEW QUESTION ROUTE
+================================ */
+
+/* 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]
+ 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 routeStartQuestion = req.session.data.routeStartQuestion
+ var pageIndex = parseInt(routeStartQuestion, 10)
+ 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 && routeStartQuestion !== 0) {
+ errors.routeStartQuestion = {
+ text: 'Select the question you want your route to start from',
+ href: "#routeStartQuestion"
+ }
+ }
+
+ /*
+ 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
+ // 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 {
+ // 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)
+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’ - button journey
+postConditions = function (req, res) {
+ const errors = {}
+ var pageId = parseInt(req.params.pageId, 10)
+ var pageIndex = pageId
+ var pageData = req.session.data.pages[pageIndex]
+ 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) {
+ 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, 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
+ // 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`)
+ }
+}
+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]
+
+ // 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,
+ successMessage
+ })
+})
+
+// Start a ‘Route for any other answer’ - suggested question - button journey
+router.post('/form-designer/question-routes/:pageId(\\d+)/questions-routes', function (req, res) {
+ 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`)
+ }
+})
+
+
+/* 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, routeId, routeQuestion })
+ } 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"
+ }
+ } 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 = 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/question-to-skip-to', { errors, errorList, containsErrors, pageId, routeQuestion })
+ } else {
+ // 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`)
+ }
+})
+
+
+/* Delete route journeys */
+
+// 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
+ 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 (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)) == secondaryQuestion) {
+ // delete the routing
+ element.routing = undefined
+ }
+ }
+ }
+ }
+ // 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 {
+ // 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
+ }
+ }
+ }
+ // 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 {
+ // 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
diff --git a/app/views/form-designer/question-routes/are-you-sure.html b/app/views/form-designer/question-routes/are-you-sure.html
new file mode 100644
index 00000000..6b008e8e
--- /dev/null
+++ b/app/views/form-designer/question-routes/are-you-sure.html
@@ -0,0 +1,69 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% if data.action == 'deleteRoute1' %}
+{% set pageTitle = 'Are you sure you want to delete route 1?' %}
+{% else %}
+{% set pageTitle = 'Are you sure you want to delete all question ' + ((pageIndex + 1) or 'X') + '’s routes?' %}
+{% endif %}
+
+{% block pageTitle %}
+ {{ "Error: " if containsErrors }}{{pageTitle}} - GOV.UK Forms
+{% endblock %}
+
+{% block beforeContent %}
+ Back question {{ ((pageIndex + 1) or 'X') }}’s routes
+{% endblock %}
+
+{% block content %}
+
+
+
+
+ {% if containsErrors %}
+ {{ govukErrorSummary({
+ titleText: "There is a problem",
+ errorList: errorList
+ }) }}
+ {% endif %}
+
+ {% if pageData.routing.secondary and data.action == 'deleteRoute1' %}
+ {# only show the ‘important’ notification banner if the form creator is trying to delete ‘route 1’ #}
+ {{ govukNotificationBanner({
+ text: "If you delete this route, the route for any other answer will also be deleted"
+ }) }}
+ {% endif %}
+
+
+ {{ 'Question ' + ((pageIndex + 1) or 'X') + '’s routes' }}
+
+ {{ govukRadios({
+ name: "deleteRoute",
+ fieldset: {
+ legend: {
+ text: pageTitle,
+ isPageHeading: true,
+ classes: "govuk-fieldset__legend--l"
+ }
+ },
+ items: [
+ {
+ value: "Yes",
+ text: "Yes"
+ },
+ {
+ value: "No",
+ text: "No"
+ }
+ ],
+ errorMessage: { text: errors['deleteRoute'].text } if errors['deleteRoute'].text
+ }) }}
+
+ {{ govukButton({
+ text: "Save and continue"
+ }) }}
+
+
+ {{ govukSummaryList({
+ rows: [
+ {
+ key: {
+ text: "Question " + ((pageIndex + 1) or 'X')
+ },
+ value: {
+ text: pageData['long-title'] or 'Not answered'
+ },
+ actions: {
+ items: [
+ {
+ href: "new-condition",
+ text: "Change",
+ visuallyHiddenText: " question"
+ }
+ ]
+ } if not ((pageData) and (pageData.routing.skipTo) and (pageData.routing.answer))
+ }
+ ]
+ }) }}
+
+ {# need to get an idea of which page we are trying to skip to so we will hold it in a temporary variable to use for checking if an item in the ‘select’ list has been chosen #}
+ {% set temp = '' %}
+ {% for page in data.pages -%}
+ {% if (page.routing.skipTo) %}
+ {% set temp = page.routing.skipTo %}
+ {% endif %}
+ {% endfor %}
+
+ {# create a new temporary list to use in our ‘select’ component, also add the default ‘select a/an...’ #}
+ {% set tempAnswers = [{
+ value: '',
+ text: 'Select an answer'
+ }] %}
+ {% set tempQuestions = [{
+ value: '',
+ text: 'Select a question or page'
+ }] %}
+
+ {% for page in data.pages -%}
+
+ {# if this object is the current page #}
+ {% if (page.pageIndex|int) == (pageIndex) %}
+ {# add the list of possible answers to the route question to our temporary answers list #}
+ {% for item in page['item-list'] %}
+ {% set tempAnswers = (
+ tempAnswers.push({
+ value: item,
+ text: item
+ }), tempAnswers)
+ %}
+ {% endfor %}
+ {% endif %}
+
+ {# if this object is is after the route question #}
+ {% if (page.pageIndex|int) > (pageIndex + 1) %}
+ {# add all the questions after the route question to our temporary questions list #}
+ {% set tempQuestions = (
+ tempQuestions.push({
+ value: page.pageIndex,
+ text: (page.pageIndex|int + 1) + '. ' + page['long-title']
+ }), tempQuestions)
+ %}
+ {# we also need to add the CYA page to the list of options #}
+ {% if loop.last %}
+ {% set tempQuestions = (
+ tempQuestions.push({
+ value: 'cya',
+ text: data.checkAnswersTitle
+ }), tempQuestions)
+ %}
+ {% endif %}
+ {% endif %}
+ {%- endfor %}
+
+ {{ govukSelect({
+ id: "route-1-answer",
+ name: "route1Answer",
+ label: {
+ text: "is answered as",
+ classes: 'govuk-label--s'
+ },
+ value: pageData.routing.answer or data['route1Answer'],
+ items: tempAnswers,
+ errorMessage: { text: errors['route1Answer'].text } if errors['route1Answer'].text
+ }) }}
+
+ {{ govukSelect({
+ id: "route-1-end",
+ name: "route1End",
+ label: {
+ text: "skip the person to",
+ classes: 'govuk-label--s'
+ },
+ items: tempQuestions,
+ value: pageData.routing.skipTo or data['route1End'],
+ errorMessage: { text: errors['route1End'].text } if errors['route1End'].text
+ }) }}
+
+
+
+
+{% endblock %}
diff --git a/app/views/form-designer/question-routes/new-condition.html b/app/views/form-designer/question-routes/new-condition.html
index b5ac3ef8..0245a9a7 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 question route' %}
+{% 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
@@ -34,9 +36,45 @@
{{ 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.
diff --git a/app/views/form-designer/question-routes/other-answer-route.html b/app/views/form-designer/question-routes/other-answer-route.html
new file mode 100644
index 00000000..47f28153
--- /dev/null
+++ b/app/views/form-designer/question-routes/other-answer-route.html
@@ -0,0 +1,85 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% set pageTitle = 'Which question do you want to add a route from?' %}
+{% set pageHint = 'The person will be skipped forward after they’ve answered this question' %}
+
+{% 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/question-to-skip-to.html b/app/views/form-designer/question-routes/question-to-skip-to.html
new file mode 100644
index 00000000..865d4d9a
--- /dev/null
+++ b/app/views/form-designer/question-routes/question-to-skip-to.html
@@ -0,0 +1,117 @@
+{% extends "layout-govuk-forms.html" %}
+
+{% set pageTitle = 'Set question or page to skip to' %}
+{% set pageQuestion = 'Which question or page do you want to skip to?' %}
+{% set pageHint = 'This is the next question or page that people who select any other answer will see after question ' + (routeIndex + 1) + '.' %}
+
+{% block pageTitle %}
+ {{ "Error: " if containsErrors }}{{pageTitle}}: {{ data.formTitle or '[formTitle]' }} - GOV.UK Forms
+{% endblock %}
+
+{% block beforeContent %}
+
+ Back
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+ {% if containsErrors %}
+ {{ govukErrorSummary({
+ titleText: "There is a problem",
+ errorList: errorList
+ }) }}
+ {% endif %}
+
+
+ {{ 'Question ' + ((pageIndex + 1) or 'X') + '’s routes: for any other answer' }}
+
+
{{ pageTitle }}
+
+ {% set routeStart = '' %}
+ {% for page in data.pages -%}
+ {% if (page.pageIndex|int) == (routeIndex) %}
+ {% set routeStart = page['long-title'] %}
+ {% endif %}
+ {%- endfor %}
+
+ {{ govukSummaryList({
+ rows: [
+ {
+ key: {
+ text: "Question " + ((routeIndex + 1) or 'X')
+ },
+ value: {
+ text: routeStart or 'Not answered'
+ }
+ }
+ ]
+ }) }}
+
+ {# need to get an idea of which page we are trying to skip to so we will hold it in a temporary variable to use for checking if an item in the ‘select’ list has been chosen #}
+ {% set temp = '' %}
+ {% for page in data.pages -%}
+ {% if (page.routing.thenSkipTo) %}
+ {% set temp = page.routing.thenSkipTo %}
+ {% endif %}
+ {% endfor %}
+
+ {% set tempQuestions = [] %}
+ {% for page in data.pages -%}
+ {% if (page.pageIndex > (routeIndex + 1)) %}
+ {% set tempQuestions = (
+ tempQuestions.push({
+ value: page.pageIndex,
+ text: (page.pageIndex|int + 1) + '. ' + page['long-title'],
+ checked: true if (temp == page.pageIndex)
+ }), tempQuestions)
+ %}
+ {% endif %}
+ {% if loop.last %}
+ {% set tempQuestions = (
+ tempQuestions.push({
+ value: 'cya',
+ text: data.checkAnswersTitle,
+ checked: true if (temp == 'cya')
+ }), tempQuestions)
+ %}
+ {% endif %}
+ {%- endfor %}
+
+ {{ govukRadios({
+ name: "routeEnd",
+ fieldset: {
+ legend: {
+ text: pageQuestion,
+ classes: "govuk-fieldset__legend--m"
+ }
+ },
+ hint: {
+ text: pageHint
+ },
+ items: tempQuestions,
+ errorMessage: { text: errors['routeEnd'].text } if errors['routeEnd'].text
+ }) }}
+
+ {{ govukButton({
+ text: "Save and continue"
+ }) }}
+
+
+
+ {% 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 -%}
+
+ {# 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 %}
+
+ {# 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 " + ((pageIndex + 1) or 'X')
+ },
+ value: {
+ text: pageData['long-title'] or 'Not answered'
+ }
+ }
+ ]
+ }) }}
+
+ {# show the ‘Route 1’ summary card #}
+ {{ govukSummaryList({
+ card: {
+ title: {
+ text: "Route 1"
+ },
+ actions: {
+ items: [
+ {
+ href: "./conditions",
+ text: "Edit",
+ visuallyHiddenText: " route 1"
+ }
+ ]
+ }
+ },
+ rows: [
+ {
+ key: {
+ text: "If answered as"
+ },
+ value: {
+ text: pageData.routing.answer or 'Not answered'
+ }
+ },
+ {
+ key: {
+ text: "skip the person to"
+ },
+ value: {
+ 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 %}
+ {% for otherRoutePage in data.pages %}
+ {% if (otherRoutePage.pageIndex|int) == (page.routing.thenSkipTo|int) %}
+ {% set skipPersonTo -%}
+ {{ otherRoutePage.pageIndex|int + 1 }}. “{{ otherRoutePage['long-title'] }}”
+ {%- endset %}
+ {% endif %}
+ {% endfor %}
+ {% 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",
+ name: "action",
+ value: "deleteAllRoutes"
+ }) }}
+
+ {% 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 {{ (pageIndex + 2) or 'Y' }} and through the rest of the form.
+
+ {% if routeEnd != 'cya' %}
+
+ 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 " + (suggestedQuestion|int or 'Z'),
+ classes: "govuk-button--secondary"
+ }) }}
+
+