Skip to content

Commit 0be93fe

Browse files
Merge pull request #62 from NHSDigital/initial-work-on-multiple-reads
Work on multiple reads and updated reading workflow.
2 parents bb0c3cd + a49c0b5 commit 0be93fe

38 files changed

+3464
-1002
lines changed

app/assets/sass/_button-group.scss

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Adapted from https://github.com/alphagov/govuk-frontend/blob/5786234c2fc2e89e2b6a925211ca1cfd38fda146/packages/govuk-frontend/src/govuk/objects/_button-group.scss#L3
2+
3+
// Button groups can be used to group buttons and links together as a group.
4+
//
5+
// Within a button group:
6+
//
7+
// - links are styled to line up visually with the buttons, including being
8+
// centre-aligned on mobile
9+
// - spacing between the buttons and links is handled automatically, including
10+
// when they wrap across multiple lines
11+
.app-button-group {
12+
$horizontal-gap: nhsuk-spacing(4);
13+
$vertical-gap: nhsuk-spacing(3);
14+
15+
// These need to be kept in sync with the button component's styles
16+
$button-padding: nhsuk-spacing(2);
17+
$button-shadow-size: $nhsuk-border-width-form-element;
18+
19+
$link-spacing: nhsuk-spacing(1);
20+
21+
@include nhsuk-responsive-margin(6, "bottom", $adjustment: $vertical-gap * -1);
22+
23+
// Flexbox is used to center-align links on mobile, align everything along
24+
// the baseline on tablet and above, and to removes extra whitespace that
25+
// we'd get between the buttons and links because they're inline-blocks.
26+
//
27+
// Ideally we'd use `gap` with flexbox rather than having to do it all with
28+
// margins, but unfortunately the support isn't there (yet) and @supports
29+
// doesn't play nicely with it
30+
// (https://github.com/w3c/csswg-drafts/issues/3559)
31+
display: flex;
32+
flex-direction: column;
33+
align-items: center;
34+
35+
// Give links within the button group the same font-size and line-height
36+
// as buttons.
37+
//
38+
// Because we want the focus state to be tight around the link text, we use
39+
// margins where the buttons would use padding.
40+
.nhsuk-link {
41+
@include nhsuk-font($size: 19, $line-height: 19px);
42+
display: inline-block;
43+
// Prevent links overflowing their container in IE10/11 because of bug
44+
// with align-items: center
45+
max-width: 100%;
46+
margin-top: $link-spacing;
47+
margin-bottom: $link-spacing + $vertical-gap;
48+
text-align: center;
49+
}
50+
51+
// Reduce the bottom margin to the size of the vertical gap (accommodating
52+
// the button shadow) – the 'lost' margin is moved to the button-group.
53+
.nhsuk-button {
54+
margin-bottom: $vertical-gap + $button-shadow-size;
55+
}
56+
57+
// On tablet and above, we also introduce a 'column gap' between the
58+
// buttons and links in each row and left align links
59+
@include govuk-media-query($from: tablet) {
60+
// Cancel out the column gap for the last item in each row
61+
margin-right: ($horizontal-gap * -1);
62+
63+
flex-direction: row;
64+
flex-wrap: wrap;
65+
align-items: baseline;
66+
67+
.nhsuk-button,
68+
.nhsuk-link {
69+
margin-right: $horizontal-gap;
70+
}
71+
72+
.nhsuk-link {
73+
text-align: left;
74+
}
75+
}
76+
}

app/assets/sass/_typography.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ h3 {
1717

1818
.nhsuk-link--no-visited-state a {
1919
@extend .nhsuk-link--no-visited-state;
20+
}
21+
22+
.app-link--error {
23+
color: $nhsuk-error-color;
2024
}

app/assets/sass/main.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
@import 'components/overrides';
1818

1919
@import 'components/annotation';
20-
20+
@import 'button-group';
2121

2222

2323
@import 'misc';

app/config.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ module.exports = {
3434
targetAttendancePercent: 100, // 100% of original capacity (not overbooking)
3535

3636
// Date range for generating data
37-
daysToGenerate: 5,
38-
daysBeforeToday: 2,
37+
daysToGenerate: 16,
38+
daysBeforeToday: 12,
39+
historicPeriodCount: 1, // Number of historic periods to generate
3940

4041
simulatedTime: '11:30', // 24h format
4142
},
42-
43+
reading: {
44+
blindReading: 'true', // Enable blind reading
45+
urgentThreshold: 10, // 10 days and over
46+
priorityThreshold: 7, // 7 days and over
47+
},
4348
screening: {
4449
outcomes: {
4550
screening: {

app/data/breast-screening-units.js

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ module.exports = [
1414
},
1515
phoneNumber: '01865235621',
1616
abbreviation: 'WSB',
17-
clinicTypes: ['screening', 'assessment'], // Can do both
17+
// clinicTypes: ['screening', 'assessment'], // Can do both
18+
clinicTypes: ['screening'],
1819
riskLevelHandling: [
1920
'routine',
2021
'moderate',
@@ -57,18 +58,6 @@ module.exports = [
5758
postcode: 'BN11 2DH',
5859
},
5960
},
60-
// {
61-
// id: generateId(),
62-
// name: "Horton Hospital breast unit",
63-
// type: "hospital",
64-
// isMainSite: false,
65-
// address: {
66-
// line1: "Horton General Hospital",
67-
// line2: "Oxford Road",
68-
// city: "Banbury",
69-
// postcode: "OX16 9AL"
70-
// }
71-
// },
7261
{
7362
id: '2yt5dukk', // Must be hardcoded so it matches generated data
7463
name: 'Mobile Unit JA1 CP7',
@@ -91,50 +80,28 @@ module.exports = [
9180
},
9281
],
9382
},
94-
{
95-
id: 'acxcdcnj', // Must be hardcoded so it matches generated data
96-
name: 'Mobile Unit WX71 HCR',
97-
type: 'mobile_unit',
98-
isMainSite: false,
99-
clinicTypes: ['screening'], // Can only do screening
100-
// Mobile units only handle routine screening
101-
riskLevelHandling: [
102-
'routine',
103-
],
104-
registration: 'WX71 HCR',
105-
// Override BSU session patterns for this location
106-
sessionPatterns: [
107-
{
108-
name: 'full_day',
109-
type: 'single',
110-
sessions: [
111-
{ startTime: '09:00', endTime: '17:00' },
112-
],
113-
},
114-
],
115-
},
11683
// {
117-
// id: "acjdcnj", // Must be hardcoded so it matches generated data
118-
// name: "Mobile Unit CD1 5HR",
119-
// type: "mobile_unit",
84+
// id: 'acxcdcnj', // Must be hardcoded so it matches generated data
85+
// name: 'Mobile Unit WX71 HCR',
86+
// type: 'mobile_unit',
12087
// isMainSite: false,
121-
// clinicTypes: ['screening'], // Can only do screening
88+
// clinicTypes: ['screening'], // Can only do screening
12289
// // Mobile units only handle routine screening
12390
// riskLevelHandling: [
12491
// 'routine',
12592
// ],
126-
// registration: "CD1 5HR",
93+
// registration: 'WX71 HCR',
12794
// // Override BSU session patterns for this location
12895
// sessionPatterns: [
12996
// {
13097
// name: 'full_day',
13198
// type: 'single',
13299
// sessions: [
133-
// { startTime: "09:00", endTime: "17:00" }
134-
// ]
135-
// }
136-
// ]
137-
// }
100+
// { startTime: '09:00', endTime: '17:00' },
101+
// ],
102+
// },
103+
// ],
104+
// },
138105
],
139106
},
140107
]

app/data/session-data-defaults.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const breastScreeningUnits = require('./breast-screening-units')
55
const path = require('path')
66
const fs = require('fs')
77
const { needsRegeneration } = require('../lib/utils/regenerate-data')
8+
const config = require('../config')
89

910
// Check if generated data folder exists and create if needed
1011
const generatedDataPath = path.join(__dirname, 'generated')
@@ -51,14 +52,25 @@ try {
5152
console.warn('Error loading generated data:', err)
5253
}
5354

54-
module.exports = {
55+
const defaultSettings = {
5556
darkMode: 'false',
56-
confirmNormalResults: 'false',
57+
reading: {
58+
blindReading: config.reading.blindReading,
59+
confirmNormal: 'false',
60+
showRemaining: 'true',
61+
showRepeatsTag: 'false',
62+
},
63+
}
64+
65+
module.exports = {
5766
users,
5867
currentUser: users[0],
5968
breastScreeningUnits,
6069
participants,
6170
clinics,
6271
events,
6372
generationInfo,
73+
config,
74+
settings: defaultSettings,
75+
defaultSettings
6476
}

app/data/users.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,25 @@ module.exports = [
44
{
55
firstName: 'Jane',
66
lastName: 'Hitchin',
7-
role: 'mammographer',
7+
role: 'radiologist',
88
email: 'j.hitchin4@nhs.net',
99
id: 'ae7537b3-aed1-4620-87fd-9dc5b5bdc8cb',
1010
breastScreeningUnit: 'm5ekcxvu',
1111
},
12+
{
13+
firstName: 'Claire',
14+
lastName: 'Lowen',
15+
role: 'radiologist',
16+
email: 'c.lowen45@nhs.net',
17+
id: 'e1945412-aad7-46a2-a984-d4f6f654c229',
18+
breastScreeningUnit: 'm5ekcxvu',
19+
},
20+
{
21+
firstName: 'Sonja',
22+
lastName: 'Koyanagi',
23+
role: 'radiologist',
24+
email: 's.koyangi2@nhs.net',
25+
id: '4a7c1515-294b-4e4d-81ca-eb8a64575859',
26+
breastScreeningUnit: 'm5ekcxvu',
27+
},
1228
]

app/filters/form-attributes.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ const decorateAttributes = (originalObject, data, path) => {
108108
// Set id and name attributes if not already defined
109109
obj.id = obj.id || pathParts.join('-')
110110
obj.name = obj.name || pathParts.map(s => `[${s}]`).join('')
111-
console.log("Object name:", obj.name)
112111
return obj
113112
}
114113

app/filters/nunjucks.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,57 @@ const log = (a, description = null) => {
77
return `<script>${description || ''}console.log(${JSON.stringify(a, null, '\t')});</script>`
88
}
99

10+
/**
11+
* Get user name by user ID with format options
12+
* @param {string} userId - ID of the user
13+
* @param {Object} options - Display options
14+
* @param {boolean} [options.identifyCurrentUser=false] - Whether to add "(you)" for current user
15+
* @param {string} [options.format='full'] - Name format: 'full', 'short', or 'initial'
16+
* @returns {string} User's name in requested format
17+
*/
18+
const getUsername = function(userId, options={}) {
19+
if (!userId) return '';
20+
21+
const users = this.ctx.data.users;
22+
if (!users) return userId;
23+
24+
const user = users.find(u => u.id === userId);
25+
if (!user) return userId;
26+
27+
// Format options: full (default), short (initial + surname), initial (just initials)
28+
const format = options.format || 'full';
29+
30+
let formattedName;
31+
switch (format) {
32+
case 'short':
33+
formattedName = `${user.firstName.charAt(0)}. ${user.lastName}`;
34+
break;
35+
case 'initial':
36+
formattedName = `${user.firstName.charAt(0)}${user.lastName.charAt(0)}`;
37+
break;
38+
case 'full':
39+
default:
40+
formattedName = `${user.firstName} ${user.lastName}`;
41+
}
42+
43+
const currentUser = this.ctx.data.currentUser;
44+
if (options.identifyCurrentUser && user.id === currentUser.id) {
45+
return `${formattedName} (you)`;
46+
}
47+
48+
return formattedName;
49+
}
50+
51+
/**
52+
*
53+
* @returns {Object} The context data
54+
*/
55+
const getContext = function() {
56+
return this.ctx;
57+
}
58+
1059
module.exports = {
1160
log,
61+
getUsername,
62+
getContext,
1263
}

0 commit comments

Comments
 (0)