diff --git a/app/assets/sass/components/_reading.scss b/app/assets/sass/components/_reading.scss index 660fbc95..f2cefacd 100644 --- a/app/assets/sass/components/_reading.scss +++ b/app/assets/sass/components/_reading.scss @@ -11,8 +11,133 @@ padding: 10px; } +// Mammogram thumbnail grid for reading pages +.app-mammogram-thumbnails { + display: inline-grid; + grid-template-columns: auto auto; + gap: 8px; +} + +.app-mammogram-thumbnail--right { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 8px; +} + +.app-mammogram-thumbnail--left { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; +} + +// Individual image wrapper (contains image + label) +.app-mammogram-thumbnail__image-wrapper { + position: relative; + // Fixed dimensions for consistent sizing + width: 120px; + height: 150px; + background-color: #000; + overflow: hidden; + // Use flexbox for image alignment + display: flex; + align-items: flex-start; +} + +// Right breast images align right (inwards), left breast align left (inwards) +.app-mammogram-thumbnail--right .app-mammogram-thumbnail__image-wrapper { + justify-content: flex-end; +} + +.app-mammogram-thumbnail--left .app-mammogram-thumbnail__image-wrapper { + justify-content: flex-start; +} + +.app-mammogram-thumbnail__image { + max-width: 100%; + max-height: 100%; + display: block; + cursor: default; +} + +// Slight smoothing for diagram thumbnails to reduce moiré +.app-mammogram-thumbnail__image--diagram { + filter: blur(0.5px); +} + +.app-mammogram-thumbnail__label { + position: absolute; + top: 4px; + background-color: nhsuk-colour("blue"); + color: #fff; + padding: 2px 6px; + font-size: 11px; + font-weight: 600; + z-index: 1; +} + +// Labels align to outer edge - right breast labels on left, left breast labels on right +.app-mammogram-thumbnail--right .app-mammogram-thumbnail__label { + left: 4px; +} + +.app-mammogram-thumbnail--left .app-mammogram-thumbnail__label { + right: 4px; +} + +// Missing image placeholder +.app-mammogram-thumbnail__missing { + width: 100%; + height: 100%; + border: 2px dashed nhsuk-colour("grey-2"); + background-color: nhsuk-colour("grey-5"); + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; +} + +.app-mammogram-thumbnail__missing-text { + font-size: 12px; + color: nhsuk-colour("grey-2"); +} + +// Zoom overlay for thumbnails +.app-mammogram-zoom { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.95); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + cursor: default; +} + +.app-mammogram-zoom__image { + max-width: 95%; + max-height: 95%; + object-fit: contain; +} + +.app-mammogram-zoom__label { + position: absolute; + top: 16px; + left: 16px; + background-color: nhsuk-colour("blue"); + color: #fff; + padding: 8px 16px; + font-size: 14px; + font-weight: 600; +} + // Todo: should this be merged with app-status-bar.scss? // Todo: otherwise refctor so classes aren't created with & + .app-reading-status { background-color: nhsuk-shade(nhsuk-colour("blue"), 40%); color: $nhsuk-reverse-text-colour; diff --git a/app/config.js b/app/config.js index 4f7bbc59..09bafec7 100644 --- a/app/config.js +++ b/app/config.js @@ -45,6 +45,9 @@ module.exports = { urgentThreshold: 10, // 10 days and over priorityThreshold: 7, // 7 days and over mammogramImageSource: 'diagrams', // 'diagrams' or 'real' + // View order for mammogram display: 'cc-first' or 'mlo-first' + // Right breast views always on left, left breast views on right + mammogramViewOrder: 'cc-first', // Distribution of image set tags (must sum to 1.0) // These are the base weights - they get adjusted based on event context // (symptoms, imperfect images, etc.) diff --git a/app/lib/utils/reading.js b/app/lib/utils/reading.js index 6ca77880..2d4401c3 100644 --- a/app/lib/utils/reading.js +++ b/app/lib/utils/reading.js @@ -1096,6 +1096,42 @@ const createReadingBatch = (data, options) => { event?.medicalInformation.symptoms?.length > 0 ) } + + // 4. Apply complex case filter if specified + if (filters.complexOnly) { + const isComplexCase = (event) => { + const hasSymptoms = + event?.medicalInformation?.symptoms && + event?.medicalInformation.symptoms?.length > 0 + + const hasAdditionalImages = + event?.mammogramData?.metadata?.hasAdditionalImages + + const isImperfect = + event?.mammogramData?.isImperfectButBestPossible?.includes && + event.mammogramData.isImperfectButBestPossible.includes('yes') + + const isIncomplete = + event?.mammogramData?.isIncompleteMammography?.includes && + event.mammogramData.isIncompleteMammography.includes('yes') + + const hasImplants = + event?.medicalInformation?.medicalHistory + ?.breastImplantsAugmentation && + event.medicalInformation.medicalHistory.breastImplantsAugmentation + .length > 0 + + return ( + hasSymptoms || + hasAdditionalImages || + isImperfect || + isIncomplete || + hasImplants + ) + } + + events = events.filter((event) => isComplexCase(event)) + } } // Apply read type filters diff --git a/app/lib/utils/summary-list.js b/app/lib/utils/summary-list.js index 71c9fceb..5ad55484 100644 --- a/app/lib/utils/summary-list.js +++ b/app/lib/utils/summary-list.js @@ -96,6 +96,60 @@ const handleSummaryListMissingInformation = ( return processRow(input) } +/** + * Add no-border class to the last summary list row + * + * Usefule where the summary list is within a card and you want to remove the bottom border + * + * @param {object|array} input - Summary list object or rows array + * @returns {object|array} Updated summary list or rows array + */ +const removeLastRowBorder = (input) => { + if (!input) return input + + const isArrayInput = Array.isArray(input) + const rows = isArrayInput ? input : input.rows + + if (!Array.isArray(rows) || rows.length === 0) return input + + let lastRowIndex = -1 + + for (let index = rows.length - 1; index >= 0; index -= 1) { + const row = rows[index] + + if (row && typeof row === 'object' && row.key) { + lastRowIndex = index + break + } + } + + if (lastRowIndex === -1) return input + + const updatedRows = rows.map((row, index) => { + if (index !== lastRowIndex) return row + + const className = 'nhsuk-summary-list__row--no-border' + const existingClasses = row.classes || '' + const hasClass = existingClasses.split(' ').includes(className) + const classes = hasClass + ? existingClasses + : `${existingClasses} ${className}`.trim() + + return { + ...row, + classes + } + }) + + if (isArrayInput) return updatedRows + + return { + ...input, + rows: updatedRows + } +} + module.exports = { - handleSummaryListMissingInformation + handleSummaryListMissingInformation, + removeLastRowBorder } diff --git a/app/routes/reading.js b/app/routes/reading.js index eea793ac..795f57ce 100644 --- a/app/routes/reading.js +++ b/app/routes/reading.js @@ -149,6 +149,10 @@ module.exports = (router) => { filters.includeAwaitingPriors = true } + if (queryFilters.includes('complexOnly')) { + filters.complexOnly = true + } + // Create the batch try { const batch = createReadingBatch(data, { diff --git a/app/views/_includes/additional-image-details.njk b/app/views/_includes/additional-image-details.njk index f94bd114..55d94cb9 100644 --- a/app/views/_includes/additional-image-details.njk +++ b/app/views/_includes/additional-image-details.njk @@ -53,9 +53,6 @@ value: "Technical issues", text: "Technical issues" }, - { - divider: "or" - }, { value: "Other", text: "Other" diff --git a/app/views/_includes/reading/image-warnings.njk b/app/views/_includes/reading/image-warnings.njk index c03e831b..132f74f1 100644 --- a/app/views/_includes/reading/image-warnings.njk +++ b/app/views/_includes/reading/image-warnings.njk @@ -91,7 +91,7 @@
No medical information recorded.
{% else %} {{ summaryList({ - rows: rowsForSummaryList + rows: rows | removeLastRowBorder }) }} {% endif %} diff --git a/app/views/_includes/summary-lists/read-summary.njk b/app/views/_includes/summary-lists/read-summary.njk index 9584c259..3593f52e 100644 --- a/app/views/_includes/summary-lists/read-summary.njk +++ b/app/views/_includes/summary-lists/read-summary.njk @@ -215,5 +215,5 @@ {% endif %} {{ summaryList({ - rows: rows + rows: rows | removeLastRowBorder }) }} diff --git a/app/views/_includes/symptomsWarningCard.njk b/app/views/_includes/symptomsWarningCard.njk index 156bf683..1acd90d5 100644 --- a/app/views/_includes/symptomsWarningCard.njk +++ b/app/views/_includes/symptomsWarningCard.njk @@ -8,6 +8,7 @@ {{ warningCallout({ heading: 'Significant symptoms reported', - html: warningCalloutHtml + html: warningCalloutHtml, + classes: "nhsuk-u-margin-top-3 nhsuk-u-margin-bottom-5" }) }} {% endif %} diff --git a/app/views/events/images-manual-details.html b/app/views/events/images-manual-details.html index b09135eb..c5751c2f 100644 --- a/app/views/events/images-manual-details.html +++ b/app/views/events/images-manual-details.html @@ -58,33 +58,63 @@