Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions app/assets/javascript/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ document.addEventListener('DOMContentLoaded', () => {
// Handle reset data in background
setupResetSessionLink()

// Auto-save breast density factors when changed
setupBreastDensityFactorsAutosave()

// Reading workflow: auto-dismiss the opinion banner after a delay
const opinionBanner = document.querySelector('[data-reading-opinion-banner]')
if (opinionBanner) {
Expand Down Expand Up @@ -185,6 +188,105 @@ document.addEventListener('DOMContentLoaded', () => {
}
})

function setupBreastDensityFactorsAutosave() {
const saveTarget = document.querySelector('[data-breast-density-factors-save-url]')
if (!saveTarget) {
return
}

const saveUrl = saveTarget.dataset.breastDensityFactorsSaveUrl
if (!saveUrl) {
return
}

const checkboxSelector =
'input[name="appointment[medicalInformation][breastDensityFactors]"]'
const hrtRadioSelector =
'input[name="appointment[medicalInformation][breastDensityFactorsHrt]"]'
const checkboxes = document.querySelectorAll(checkboxSelector)
const hrtRadios = document.querySelectorAll(hrtRadioSelector)

if (checkboxes.length === 0 && hrtRadios.length === 0) {
return
}

const updateBreastDensityFactorsSummary = () => {
// Find the breast-density-factors section and update its contents summary
const section = document.getElementById('breast-density-factors')
if (!section) {
return
}

// Count selected factors
const selectedCheckboxes = document.querySelectorAll(`${checkboxSelector}:checked`)
const selectedHrtRadio = document.querySelector(`${hrtRadioSelector}:checked`)
let count = selectedCheckboxes.length
if (selectedHrtRadio && selectedHrtRadio.value === 'yes') {
count += 1
}

// Find and update the contents summary span
const summarySpan = section.querySelector('.app-details__contents-summary')
if (summarySpan) {
if (count === 0) {
summarySpan.textContent = 'No breast density factors added'
} else if (count === 1) {
summarySpan.textContent = '1 breast density factor added'
} else {
summarySpan.textContent = count + ' breast density factors added'
}
}
}

const saveFactors = async () => {
const formData = new URLSearchParams()
const selectedCheckboxes = document.querySelectorAll(`${checkboxSelector}:checked`)
const selectedHrtRadio = document.querySelector(`${hrtRadioSelector}:checked`)

selectedCheckboxes.forEach((checkbox) => {
formData.append(
'appointment[medicalInformation][breastDensityFactors]',
checkbox.value
)
})

if (selectedHrtRadio) {
formData.append(
'appointment[medicalInformation][breastDensityFactorsHrt]',
selectedHrtRadio.value
)
}

try {
const response = await fetch(saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: formData.toString()
})

if (!response.ok) {
throw new Error('Breast density factors auto-save failed')
}

// Update the summary text in the section after successful save
updateBreastDensityFactorsSummary()
} catch (error) {
console.error(error)
}
}

checkboxes.forEach((checkbox) => {
checkbox.addEventListener('change', saveFactors)
})

hrtRadios.forEach((radio) => {
radio.addEventListener('change', saveFactors)
})
}

// Quick settings modal — press backtick (`) to open settings in a modal overlay.
// On close, the page reloads to pick up any changes.
document.addEventListener('keydown', (e) => {
Expand Down
69 changes: 19 additions & 50 deletions app/lib/utils/medical-information.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,59 +429,28 @@ const summariseOtherRelevantInformation = (medicalInformation) => {

const summaries = []

// HRT summary
const hrt = medicalInformation.hrt
if (hrt) {
if (hrt.hrtQuestion === 'yes') {
summaries.push(
`Taking HRT (started ${hrt.hrtDateStarted || 'date not specified'})`
)
} else if (hrt.hrtQuestion === 'no-recently-stopped') {
if (hrt.hrtDateStopped) {
summaries.push(`Recently stopped HRT (stopped ${hrt.hrtDateStopped})`)
} else {
summaries.push('Recently stopped HRT')
}
}
// Don't add anything for 'no' - that's the default/negative state
const breastDensityFactorsRaw = medicalInformation.breastDensityFactors
const breastDensityFactors = Array.isArray(breastDensityFactorsRaw)
? breastDensityFactorsRaw
: breastDensityFactorsRaw
? [breastDensityFactorsRaw]
: []
const breastDensityFactorsHrt =
medicalInformation.breastDensityFactorsHrt ||
(breastDensityFactors.includes('hrt') ? 'yes' : undefined)

if (breastDensityFactors.includes('pregnant')) {
summaries.push('Pregnant')
}

// Pregnancy and breastfeeding summary
const pregBf = medicalInformation.pregnancyAndBreastfeeding
if (pregBf) {
// Pregnancy
if (pregBf.pregnancyStatus === 'yes') {
if (pregBf.pregnancyDueDate) {
summaries.push(`Pregnant (due ${pregBf.pregnancyDueDate})`)
} else {
summaries.push('Pregnant')
}
} else if (pregBf.pregnancyStatus === 'noButRecently') {
if (pregBf.pregnancyEndDate) {
summaries.push(`Recently pregnant (ended ${pregBf.pregnancyEndDate})`)
} else {
summaries.push('Recently pregnant')
}
}
if (breastDensityFactors.includes('breastfeeding')) {
summaries.push('Breastfeeding')
}

// Breastfeeding
if (pregBf.breastfeedingStatus === 'yes') {
if (pregBf.breastfeedingStartDate) {
summaries.push(
`Breastfeeding (started ${pregBf.breastfeedingStartDate})`
)
} else {
summaries.push('Breastfeeding')
}
} else if (pregBf.breastfeedingStatus === 'recentlyStopped') {
if (pregBf.breastfeedingStopDate) {
summaries.push(
`Recently breastfeeding (stopped ${pregBf.breastfeedingStopDate})`
)
} else {
summaries.push('Recently breastfeeding')
}
}
if (breastDensityFactorsHrt === 'yes') {
summaries.push('Taking HRT')
} else if (breastDensityFactorsHrt === 'no') {
summaries.push('Not taking HRT')
}

// Other medical information (free text)
Expand Down
135 changes: 135 additions & 0 deletions app/routes/appointments/medical-information.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,143 @@ const {
getReturnUrl,
modalBreakout
} = require('../../lib/utils/referrers')
const { getAppointmentData } = require('../../lib/utils/appointment-data')

module.exports = (router) => {
// Auto-save breast density factors when checkboxes are changed
router.post(
'/clinics/:clinicId/appointments/:appointmentId/medical-information/breast-density-factors-save',
(req, res) => {
const data = req.session.data
const postedFactors = req.body?.appointment?.medicalInformation?.breastDensityFactors
const postedHrt = req.body?.appointment?.medicalInformation?.breastDensityFactorsHrt

// No posted factors means all options are currently unchecked
const factors = Array.isArray(postedFactors)
? postedFactors
: postedFactors
? [postedFactors]
: []
const nonHrtFactors = factors.filter((factor) => factor && factor !== 'hrt')

if (!data.appointment) {
data.appointment = {}
}

if (!data.appointment.medicalInformation) {
data.appointment.medicalInformation = {}
}

data.appointment.medicalInformation.breastDensityFactors = nonHrtFactors

if (postedHrt === 'yes' || postedHrt === 'no') {
data.appointment.medicalInformation.breastDensityFactorsHrt = postedHrt
}

// Keep a draft cache outside auto-stored form keys so later form posts
// cannot accidentally clear the latest autosaved checkbox state.
if (!data._medicalInformationDraft) {
data._medicalInformationDraft = {}
}
data._medicalInformationDraft.breastDensityFactors = nonHrtFactors

if (postedHrt === 'yes' || postedHrt === 'no') {
data._medicalInformationDraft.breastDensityFactorsHrt = postedHrt
}

res.status(204).send()
}
)

// Save other medical information while preserving breast density factors
router.post(
'/clinics/:clinicId/appointments/:appointmentId/medical-information/other-medical-information-save',
(req, res) => {
const { clinicId, appointmentId } = req.params
const data = req.session.data
const referrerChain = req.query.referrerChain
const scrollTo = req.query.scrollTo

const appointmentMedicalInformation = data?.appointment?.medicalInformation
const postedBreastDensityFactors =
appointmentMedicalInformation?.breastDensityFactors
const postedBreastDensityFactorsHrt =
appointmentMedicalInformation?.breastDensityFactorsHrt
const cachedBreastDensityFactors =
data?._medicalInformationDraft?.breastDensityFactors
const cachedBreastDensityFactorsHrt =
data?._medicalInformationDraft?.breastDensityFactorsHrt

const normalisedPostedBreastDensityFactors =
Array.isArray(postedBreastDensityFactors)
? postedBreastDensityFactors
: postedBreastDensityFactors
? [postedBreastDensityFactors]
: undefined

if (normalisedPostedBreastDensityFactors) {
if (!data._medicalInformationDraft) {
data._medicalInformationDraft = {}
}
data._medicalInformationDraft.breastDensityFactors =
normalisedPostedBreastDensityFactors.filter((factor) => factor && factor !== 'hrt')
}

if (postedBreastDensityFactorsHrt === 'yes' || postedBreastDensityFactorsHrt === 'no') {
if (!data._medicalInformationDraft) {
data._medicalInformationDraft = {}
}
data._medicalInformationDraft.breastDensityFactorsHrt =
postedBreastDensityFactorsHrt
}

// If this submission did not include factors, keep the last saved values
if (!normalisedPostedBreastDensityFactors)
{
const savedAppointmentData = getAppointmentData(data, clinicId, appointmentId)
const savedBreastDensityFactors =
savedAppointmentData?.appointment?.medicalInformation?.breastDensityFactors

if (cachedBreastDensityFactors)
{
data.appointment.medicalInformation.breastDensityFactors =
cachedBreastDensityFactors
}
else if (savedBreastDensityFactors)
{
data.appointment.medicalInformation.breastDensityFactors =
savedBreastDensityFactors
}
}

if (!postedBreastDensityFactorsHrt)
{
const savedAppointmentData = getAppointmentData(data, clinicId, appointmentId)
const savedBreastDensityFactorsHrt =
savedAppointmentData?.appointment?.medicalInformation?.breastDensityFactorsHrt

if (cachedBreastDensityFactorsHrt)
{
data.appointment.medicalInformation.breastDensityFactorsHrt =
cachedBreastDensityFactorsHrt
}
else if (savedBreastDensityFactorsHrt)
{
data.appointment.medicalInformation.breastDensityFactorsHrt =
savedBreastDensityFactorsHrt
}
}

const returnUrl = getReturnUrl(
`/clinics/${clinicId}/appointments/${appointmentId}/review-medical-information`,
referrerChain,
scrollTo
)

res.redirect(modalBreakout(returnUrl))
}
)

// Save breast features (includes converting JSON string to structured data)
router.post(
'/clinics/:clinicId/appointments/:appointmentId/medical-information/record-breast-features/save',
Expand Down
Loading