diff --git a/app/assets/javascript/expandable-sections.js b/app/assets/javascript/expandable-sections.js index 9826e0bf..5597b924 100644 --- a/app/assets/javascript/expandable-sections.js +++ b/app/assets/javascript/expandable-sections.js @@ -1,10 +1,79 @@ // app/assets/javascript/expandable-sections.js +const BREAST_FEATURES_SECTION_ID = 'breast-features' +const CONFIRM_AFTER_IMAGING_STATUS = 'Review after imaging' +const LEGACY_CONFIRM_AFTER_IMAGING_STATUS = 'Confirm after imaging' + // Handle expandable sections with completion tracking document.addEventListener('DOMContentLoaded', function () { // console.log('Expandable sections script loaded') // Debug log const sections = document.querySelectorAll('.js-expandable-section') const completedSections = new Set() + const reviewAfterImagingField = document.querySelector( + 'input[name="event[workflowStatus][review-breast-features-after-imaging]"]' + ) + + function getReviewAfterImagingWorkflowStatus() { + if (!reviewAfterImagingField) { + return '' + } + + return reviewAfterImagingField.value || '' + } + + function setReviewAfterImagingFlag(statusValue) { + if (!reviewAfterImagingField) { + return + } + + reviewAfterImagingField.value = statusValue || '' + } + + function completeSectionAndContinue(section, index, options = {}) { + const forcedStatus = options.forcedStatus + + // Mark current section as completed + completedSections.add(index) + + // Determine current status and set new status + const currentStatus = getCurrentSectionStatus(section) + let newStatus + + if (forcedStatus) { + newStatus = forcedStatus + } else if ( + currentStatus === CONFIRM_AFTER_IMAGING_STATUS || + currentStatus === LEGACY_CONFIRM_AFTER_IMAGING_STATUS + ) { + newStatus = CONFIRM_AFTER_IMAGING_STATUS + } else if (currentStatus === 'Incomplete') { + newStatus = 'Complete' + } else if (currentStatus === 'To review') { + newStatus = 'Reviewed' + } else if (currentStatus === 'Reviewed') { + newStatus = 'Reviewed' // Keep as reviewed + } else { + newStatus = 'Complete' // Default fallback + } + + // Update the status for this section + updateSectionStatus(section, newStatus) + + // Save status to sessionStorage + const sectionId = section.getAttribute('id') + if (sectionId && window.saveSectionStatus) { + window.saveSectionStatus(sectionId, newStatus) + } + + // Close current section + section.removeAttribute('open') + + // Find and open the next incomplete section after this one + openNextIncompleteSection(index, sections, completedSections) + + // Update progress counter + updateProgress(sections, completedSections) + } sections.forEach(function (section, index) { const content = section.querySelector('.nhsuk-details__text') @@ -16,7 +85,27 @@ document.addEventListener('DOMContentLoaded', function () { // Create button container const buttonContainer = document.createElement('div') - buttonContainer.className = 'nhsuk-form-group' + buttonContainer.className = 'nhsuk-form-group nhsuk-button-group' + + const sectionId = section.getAttribute('id') + const isBreastFeaturesSection = sectionId === BREAST_FEATURES_SECTION_ID + + if (isBreastFeaturesSection) { + const reviewAfterImagingWorkflowStatus = + getReviewAfterImagingWorkflowStatus() + + if (reviewAfterImagingWorkflowStatus === 'yes') { + updateSectionStatus(section, CONFIRM_AFTER_IMAGING_STATUS) + if (window.saveSectionStatus) { + window.saveSectionStatus(sectionId, CONFIRM_AFTER_IMAGING_STATUS) + } + } else if (reviewAfterImagingWorkflowStatus === 'answered') { + updateSectionStatus(section, 'Reviewed') + if (window.saveSectionStatus) { + window.saveSectionStatus(sectionId, 'Reviewed') + } + } + } // Create button const button = document.createElement('button') @@ -30,49 +119,38 @@ document.addEventListener('DOMContentLoaded', function () { buttonContainer.appendChild(button) + if (isBreastFeaturesSection) { + const reviewAfterImagingButton = document.createElement('button') + reviewAfterImagingButton.className = + 'nhsuk-button nhsuk-button--secondary nhsuk-u-margin-bottom-0 nhsuk-u-margin-top-3' + reviewAfterImagingButton.type = 'button' + reviewAfterImagingButton.textContent = 'Review after imaging' + + buttonContainer.appendChild(reviewAfterImagingButton) + + reviewAfterImagingButton.addEventListener('click', function () { + setReviewAfterImagingFlag('yes') + completeSectionAndContinue(section, index, { + forcedStatus: CONFIRM_AFTER_IMAGING_STATUS + }) + updateButtonText(section, button) + }) + } + // Append HR first, then button container content.appendChild(hr) content.appendChild(buttonContainer) // Add click handler button.addEventListener('click', function () { - // Mark current section as completed - completedSections.add(index) - - // Determine current status and set new status - const currentStatus = getCurrentSectionStatus(section) - let newStatus - - if (currentStatus === 'Incomplete') { - newStatus = 'Complete' - } else if (currentStatus === 'To review') { - newStatus = 'Reviewed' - } else if (currentStatus === 'Reviewed') { - newStatus = 'Reviewed' // Keep as reviewed - } else { - newStatus = 'Complete' // Default fallback + if (isBreastFeaturesSection) { + setReviewAfterImagingFlag('') } - // Update the status for this section - updateSectionStatus(section, newStatus) - - // Save status to sessionStorage - const sectionId = section.getAttribute('id') - if (sectionId && window.saveSectionStatus) { - window.saveSectionStatus(sectionId, newStatus) - } + completeSectionAndContinue(section, index) // Update button text based on new status updateButtonText(section, button) - - // Close current section - section.removeAttribute('open') - - // Find and open the next incomplete section after this one - openNextIncompleteSection(index, sections, completedSections) - - // Update progress counter - updateProgress(sections, completedSections) }) } }) @@ -88,14 +166,35 @@ document.addEventListener('DOMContentLoaded', function () { button.addEventListener('click', function (event) { // Mark all sections as completed sections.forEach(function (section, index) { + const sectionId = section.getAttribute('id') + const isBreastFeaturesSection = + sectionId === BREAST_FEATURES_SECTION_ID + // Add to completed set completedSections.add(index) // Determine current status and set new status const currentStatus = getCurrentSectionStatus(section) let newStatus - - if (currentStatus === 'Incomplete') { + const reviewAfterImagingWorkflowStatus = + getReviewAfterImagingWorkflowStatus() + + if ( + isBreastFeaturesSection && + reviewAfterImagingWorkflowStatus === 'yes' + ) { + newStatus = CONFIRM_AFTER_IMAGING_STATUS + } else if ( + isBreastFeaturesSection && + reviewAfterImagingWorkflowStatus === 'answered' + ) { + newStatus = 'Reviewed' + } else if ( + currentStatus === CONFIRM_AFTER_IMAGING_STATUS || + currentStatus === LEGACY_CONFIRM_AFTER_IMAGING_STATUS + ) { + newStatus = CONFIRM_AFTER_IMAGING_STATUS + } else if (currentStatus === 'Incomplete') { newStatus = 'Complete' } else if (currentStatus === 'To review') { newStatus = 'Reviewed' @@ -109,7 +208,6 @@ document.addEventListener('DOMContentLoaded', function () { updateSectionStatus(section, newStatus) // Save status to sessionStorage - const sectionId = section.getAttribute('id') if (sectionId && window.saveSectionStatus) { window.saveSectionStatus(sectionId, newStatus) } @@ -353,7 +451,12 @@ function updateSectionStatus(section, statusText) { if (statusElement) { // console.log('Found status element:', statusElement) - statusElement.textContent = statusText + const normalisedStatusText = + statusText === LEGACY_CONFIRM_AFTER_IMAGING_STATUS + ? CONFIRM_AFTER_IMAGING_STATUS + : statusText + + statusElement.textContent = normalisedStatusText // Update the tag colour class based on status // Reset all status classes first @@ -363,11 +466,13 @@ function updateSectionStatus(section, statusText) { 'nhsuk-tag--yellow' ) - if (statusText === 'Complete' || statusText === 'Reviewed') { + if (normalisedStatusText === 'Complete' || normalisedStatusText === 'Reviewed') { + statusElement.classList.add('nhsuk-tag--green') + } else if (normalisedStatusText === CONFIRM_AFTER_IMAGING_STATUS) { statusElement.classList.add('nhsuk-tag--green') - } else if (statusText === 'Incomplete') { + } else if (normalisedStatusText === 'Incomplete') { statusElement.classList.add('nhsuk-tag--blue') - } else if (statusText === 'To review') { + } else if (normalisedStatusText === 'To review') { statusElement.classList.add('nhsuk-tag--yellow') } diff --git a/app/assets/javascript/expanded-state-tracker.js b/app/assets/javascript/expanded-state-tracker.js index 5f6ac09d..c274f437 100644 --- a/app/assets/javascript/expanded-state-tracker.js +++ b/app/assets/javascript/expanded-state-tracker.js @@ -10,6 +10,8 @@ const STORAGE_KEY_PREFIX = 'expanded-sections-' const PENDING_EXPAND_KEY_PREFIX = 'pending-expand-' const STATUS_KEY_PREFIX = 'section-statuses-' + const CONFIRM_AFTER_IMAGING_STATUS = 'Review after imaging' + const LEGACY_CONFIRM_AFTER_IMAGING_STATUS = 'Confirm after imaging' function getStorageKey() { return STORAGE_KEY_PREFIX + window.location.pathname @@ -131,7 +133,12 @@ } if (statusElement) { - statusElement.textContent = statusText + const normalisedStatusText = + statusText === LEGACY_CONFIRM_AFTER_IMAGING_STATUS + ? CONFIRM_AFTER_IMAGING_STATUS + : statusText + + statusElement.textContent = normalisedStatusText // Update the tag colour class based on status statusElement.classList.remove( @@ -140,11 +147,13 @@ 'nhsuk-tag--yellow' ) - if (statusText === 'Complete' || statusText === 'Reviewed') { + if (normalisedStatusText === 'Complete' || normalisedStatusText === 'Reviewed') { + statusElement.classList.add('nhsuk-tag--green') + } else if (normalisedStatusText === CONFIRM_AFTER_IMAGING_STATUS) { statusElement.classList.add('nhsuk-tag--green') - } else if (statusText === 'Incomplete') { + } else if (normalisedStatusText === 'Incomplete') { statusElement.classList.add('nhsuk-tag--blue') - } else if (statusText === 'To review') { + } else if (normalisedStatusText === 'To review') { statusElement.classList.add('nhsuk-tag--yellow') } } diff --git a/app/lib/utils/status.js b/app/lib/utils/status.js index 6937927e..df1a708b 100644 --- a/app/lib/utils/status.js +++ b/app/lib/utils/status.js @@ -223,6 +223,8 @@ const getStatusTagColour = (status) => { 'incomplete': 'blue', 'complete': 'green', 'to_review': 'blue', + 'review_after_imaging': 'green', + 'confirm_after_imaging': 'green', 'reviewed': 'green', // Image reading @@ -327,7 +329,11 @@ const getStatusText = (status) => { prior_requested: 'Requested', prior_received: 'Received', prior_not_available: 'Not available', - prior_not_needed: 'Not needed' + prior_not_needed: 'Not needed', + + // Task list statuses + review_after_imaging: 'Review after imaging', + confirm_after_imaging: 'Review after imaging' // "technical-recall": 'Technical recall', // "recall-for-assesment": 'Recall for assessment', diff --git a/app/routes/events.js b/app/routes/events.js index 419636f3..c670ce0a 100644 --- a/app/routes/events.js +++ b/app/routes/events.js @@ -102,6 +102,18 @@ function captureSessionEndTime(data, eventId, userId) { // } module.exports = (router) => { + const getPostImagingDestinationUrl = (data, clinicId, eventId) => { + const shouldReviewBreastFeaturesAfterImaging = + data?.event?.workflowStatus?.['review-breast-features-after-imaging'] === + 'yes' + + if (shouldReviewBreastFeaturesAfterImaging) { + return `/clinics/${clinicId}/events/${eventId}/review-after-imaging-breast-features` + } + + return `/clinics/${clinicId}/events/${eventId}/check-information` + } + // Set clinics to active in nav for all urls starting with /clinics router.use('/clinics/:clinicId/events/:eventId', (req, res, next) => { const eventId = req.params.eventId @@ -1493,6 +1505,16 @@ module.exports = (router) => { referrerChain, scrollTo ) + + if (!data.event.workflowStatus) { + data.event.workflowStatus = {} + } + + if (req.query.fromPostImagingBreastFeatures === '1') { + data.event.workflowStatus['review-breast-features-after-imaging'] = + 'answered' + } + res.redirect(modalBreakout(returnUrl)) } ) @@ -2157,6 +2179,57 @@ module.exports = (router) => { } ) + router.get( + '/clinics/:clinicId/events/:eventId/review-after-imaging-breast-features', + (req, res) => { + res.render('events/review-after-imaging-breast-features') + } + ) + + router.post( + '/clinics/:clinicId/events/:eventId/review-after-imaging-breast-features-answer', + (req, res) => { + const { clinicId, eventId } = req.params + const data = req.session.data + const choice = data?.event?.breastFeaturesAfterImagingDecision + + if (!choice) { + req.flash('error', { + text: 'Select whether to record breast features', + name: 'event[breastFeaturesAfterImagingDecision]' + }) + + return res.redirect( + `/clinics/${clinicId}/events/${eventId}/review-after-imaging-breast-features` + ) + } + + if (choice === 'yes') { + if (!data.event.workflowStatus) { + data.event.workflowStatus = {} + } + + data.event.workflowStatus['review-breast-features-after-imaging'] = + 'answered' + + return res.redirect( + urlWithReferrer( + `/clinics/${clinicId}/events/${eventId}/medical-information/record-breast-features?fromPostImagingBreastFeatures=1`, + `/clinics/${clinicId}/events/${eventId}/check-information` + ) + ) + } + + if (!data.event.workflowStatus) { + data.event.workflowStatus = {} + } + + data.event.workflowStatus['review-breast-features-after-imaging'] = + 'answered' + res.redirect(`/clinics/${clinicId}/events/${eventId}/check-information`) + } + ) + // Handle screening completion router.post( '/clinics/:clinicId/events/:eventId/imaging-answer', @@ -2205,7 +2278,7 @@ module.exports = (router) => { } data.event.workflowStatus['take-images'] = 'completed' - res.redirect(`/clinics/${clinicId}/events/${eventId}/check-information`) + res.redirect(getPostImagingDestinationUrl(data, clinicId, eventId)) } ) @@ -2784,9 +2857,7 @@ module.exports = (router) => { data.event.workflowStatus['take-images'] = 'completed' // Redirect to check information - return res.redirect( - `/clinics/${clinicId}/events/${eventId}/check-information` - ) + return res.redirect(getPostImagingDestinationUrl(data, clinicId, eventId)) } // If custom details needed, go to details page @@ -2873,7 +2944,7 @@ module.exports = (router) => { data.event.workflowStatus['take-images'] = 'completed' // Redirect to check information - res.redirect(`/clinics/${clinicId}/events/${eventId}/check-information`) + res.redirect(getPostImagingDestinationUrl(data, clinicId, eventId)) } ) @@ -3035,7 +3106,7 @@ module.exports = (router) => { data.event.workflowStatus['take-images'] = 'completed' // Redirect to check information - res.redirect(`/clinics/${clinicId}/events/${eventId}/check-information`) + res.redirect(getPostImagingDestinationUrl(data, clinicId, eventId)) } ) diff --git a/app/views/_includes/medical-information/index.njk b/app/views/_includes/medical-information/index.njk index 95281412..738996b5 100644 --- a/app/views/_includes/medical-information/index.njk +++ b/app/views/_includes/medical-information/index.njk @@ -181,6 +181,14 @@ {% set breastFeatures = event.medicalInformation.breastFeatures or rawBreastFeatures %} {% set breastFeaturesCount = breastFeatures | length %} {% set hasBreastFeatures = breastFeaturesCount > 0 %} +{% set reviewBreastFeaturesAfterImagingStatus = data.event.workflowStatus['review-breast-features-after-imaging'] %} + +{% set breastFeaturesStatus = "To review" %} +{% if reviewBreastFeaturesAfterImagingStatus == 'yes' %} + {% set breastFeaturesStatus = "Review after imaging" %} +{% elseif reviewBreastFeaturesAfterImagingStatus == 'answered' %} + {% set breastFeaturesStatus = "Reviewed" %} +{% endif %} {% set breastFeaturesContentsSummaryText %} {{ breastFeaturesCount or "No" }} breast {{ "feature" | pluralise(breastFeaturesCount) }} added @@ -214,7 +222,7 @@ subtitle: subHeading, html: breastFeaturesHtml, contentsSummary: breastFeaturesContentsSummaryText, - status: "To review" + status: breastFeaturesStatus }) }} {% endswitch %} diff --git a/app/views/events/review-after-imaging-breast-features.html b/app/views/events/review-after-imaging-breast-features.html new file mode 100644 index 00000000..755ae10d --- /dev/null +++ b/app/views/events/review-after-imaging-breast-features.html @@ -0,0 +1,47 @@ +{# app/views/events/review-after-imaging-breast-features.html #} + +{% extends parentLayout or 'layout-appointment.html' %} + +{% set pageHeading = "Review breast features" %} +{% set showNavigation = true %} +{% set activeWorkflowStep = 'check-information' %} +{% set hideBackLink = true %} +{% set gridColumn = "nhsuk-grid-column-two-thirds" %} + +{% set formAction = './review-after-imaging-breast-features-answer' %} + +{% block pageContent %} + +

+ {{ pageHeading }} +

+ + {{ radios({ + name: "event[breastFeaturesAfterImagingDecision]", + value: event.breastFeaturesAfterImagingDecision, + fieldset: { + legend: { + text: "Are there any breast features to add for " + (participant | getFullName) + "?", + classes: "nhsuk-fieldset__legend--m" + } + }, + hint: { + text: "This includes non-surgical scars, moles and other features that may appear on mammogram images" + }, + items: [ + { + value: "yes", + text: "Yes" + }, + { + value: "no", + text: "No" + } + ] + } | populateErrors) }} + + {{ button({ + text: "Continue" + }) }} + +{% endblock %} diff --git a/app/views/events/review-medical-information.html b/app/views/events/review-medical-information.html index 0972de72..112643a5 100644 --- a/app/views/events/review-medical-information.html +++ b/app/views/events/review-medical-information.html @@ -27,6 +27,11 @@

value: "completed" }) }} + {{ appHiddenInput({ + name: "event[workflowStatus][review-breast-features-after-imaging]", + value: data.event.workflowStatus['review-breast-features-after-imaging'] + }) }} +
{{ button({