Skip to content
Merged
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
125 changes: 125 additions & 0 deletions app/assets/sass/components/_reading.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
36 changes: 36 additions & 0 deletions app/lib/utils/reading.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
56 changes: 55 additions & 1 deletion app/lib/utils/summary-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions app/routes/reading.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
3 changes: 0 additions & 3 deletions app/views/_includes/additional-image-details.njk
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@
value: "Technical issues",
text: "Technical issues"
},
{
divider: "or"
},
{
value: "Other",
text: "Other"
Expand Down
2 changes: 1 addition & 1 deletion app/views/_includes/reading/image-warnings.njk
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<div class="nhsuk-grid-column-two-thirds">
{{ insetText({
html: warningsHtml,
classes: "nhsuk-u-margin-top-6"
classes: ""
}) }}
</div>
</div>
Expand Down
20 changes: 2 additions & 18 deletions app/views/_includes/summary-lists/medical-info-summary.njk
Original file line number Diff line number Diff line change
Expand Up @@ -214,26 +214,10 @@
}) %}
{% endif %}

{# Final summary list rows #}
{# Rebuild rows so we can add a no-border class to the final row #}
{% set rowsForSummaryList = [] %}
{% for row in rows %}
{% if loop.last %}
{% set rowsForSummaryList = rowsForSummaryList | push({
key: row.key,
value: row.value,
actions: row.actions,
classes: "nhsuk-summary-list__row--no-border"
}) %}
{% else %}
{% set rowsForSummaryList = rowsForSummaryList | push(row) %}
{% endif %}
{% endfor %}

{% if rowsForSummaryList | length == 0 %}
{% if rows | length == 0 %}
<p class="nhsuk-body">No medical information recorded.</p>
{% else %}
{{ summaryList({
rows: rowsForSummaryList
rows: rows | removeLastRowBorder
}) }}
{% endif %}
2 changes: 1 addition & 1 deletion app/views/_includes/summary-lists/read-summary.njk
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,5 @@
{% endif %}

{{ summaryList({
rows: rows
rows: rows | removeLastRowBorder
}) }}
3 changes: 2 additions & 1 deletion app/views/_includes/symptomsWarningCard.njk
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Loading