Skip to content

Commit cc6413f

Browse files
Refactor screening workflow and other improvements (#124)
1 parent d16b9a3 commit cc6413f

File tree

64 files changed

+4049
-403
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4049
-403
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
.tmp
77
/public
88
app/data/generated/
9-
reference
109

1110
# Runtime data
1211
pids
@@ -38,6 +37,11 @@ pids
3837
connect.lock
3938
typings
4039

40+
# LLMs
41+
reference
42+
.github/copilot-instructions.md
43+
*.instructions.md
44+
4145
# Logs
4246
logs
4347
*.log
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// app/assets/javascript/expanded-state-tracker.js
2+
3+
// Generic expanded state tracker
4+
// Tracks open/closed state of elements with .js-track-expanded class
5+
6+
(function() {
7+
'use strict'
8+
9+
const STORAGE_KEY_PREFIX = 'expanded-sections-'
10+
const PENDING_EXPAND_KEY_PREFIX = 'pending-expand-'
11+
12+
function getStorageKey() {
13+
return STORAGE_KEY_PREFIX + window.location.pathname
14+
}
15+
16+
function getPendingExpandKey() {
17+
return PENDING_EXPAND_KEY_PREFIX + window.location.pathname
18+
}
19+
20+
function saveExpandedState() {
21+
const trackableElements = document.querySelectorAll('.js-track-expanded')
22+
const expandedSections = []
23+
24+
trackableElements.forEach(element => {
25+
if (element.hasAttribute('open')) {
26+
expandedSections.push(element.id)
27+
}
28+
})
29+
30+
sessionStorage.setItem(getStorageKey(), JSON.stringify(expandedSections))
31+
}
32+
33+
function restoreExpandedState() {
34+
const storedState = sessionStorage.getItem(getStorageKey())
35+
if (storedState) {
36+
try {
37+
const expandedSections = JSON.parse(storedState)
38+
expandedSections.forEach(sectionId => {
39+
const element = document.getElementById(sectionId)
40+
if (element && element.classList.contains('js-track-expanded')) {
41+
element.setAttribute('open', 'open')
42+
}
43+
})
44+
} catch (e) {
45+
console.warn('Failed to restore expanded state:', e)
46+
}
47+
}
48+
49+
// Also check for pending expand sections
50+
const pendingExpandState = sessionStorage.getItem(getPendingExpandKey())
51+
if (pendingExpandState) {
52+
try {
53+
const sectionsToExpand = JSON.parse(pendingExpandState)
54+
sectionsToExpand.forEach(sectionId => {
55+
const element = document.getElementById(sectionId)
56+
if (element && element.classList.contains('js-track-expanded')) {
57+
element.setAttribute('open', 'open')
58+
}
59+
})
60+
// Clear pending expand state after using it
61+
sessionStorage.removeItem(getPendingExpandKey())
62+
} catch (e) {
63+
console.warn('Failed to restore pending expand state:', e)
64+
}
65+
}
66+
}
67+
68+
function clearExpandedStateIfNeeded() {
69+
const path = window.location.pathname
70+
71+
// Clear if on main event page (not sub-pages)
72+
if (path.match(/^\/clinics\/[^\/]+\/events\/[^\/]+\/?$/)) {
73+
clearAllExpandedStates()
74+
}
75+
}
76+
77+
function clearAllExpandedStates() {
78+
Object.keys(sessionStorage).forEach(key => {
79+
if (key.startsWith(STORAGE_KEY_PREFIX) || key.startsWith(PENDING_EXPAND_KEY_PREFIX)) {
80+
sessionStorage.removeItem(key)
81+
}
82+
})
83+
}
84+
85+
function setupEventListeners() {
86+
const trackableElements = document.querySelectorAll('.js-track-expanded')
87+
88+
trackableElements.forEach(element => {
89+
element.addEventListener('toggle', saveExpandedState)
90+
})
91+
92+
// Handle links that should expand their parent section
93+
const expandParentLinks = document.querySelectorAll('.js-expand-parent-section')
94+
expandParentLinks.forEach(link => {
95+
link.addEventListener('click', function() {
96+
// Find the parent section (closest .js-track-expanded element)
97+
const parentSection = this.closest('.js-track-expanded')
98+
if (parentSection && parentSection.id) {
99+
// Store this section ID as pending expand for the return URL
100+
const returnUrl = this.getAttribute('href')
101+
if (returnUrl) {
102+
// We need to figure out what the return URL will be
103+
// For now, assume it returns to the current page
104+
const currentPath = window.location.pathname
105+
const pendingExpandSections = [parentSection.id]
106+
sessionStorage.setItem(PENDING_EXPAND_KEY_PREFIX + currentPath, JSON.stringify(pendingExpandSections))
107+
console.log('Marked section for expansion on return:', parentSection.id)
108+
}
109+
}
110+
})
111+
})
112+
}
113+
114+
// Initialize on page load
115+
document.addEventListener('DOMContentLoaded', function() {
116+
clearExpandedStateIfNeeded()
117+
restoreExpandedState()
118+
setupEventListeners()
119+
})
120+
121+
// Expose clear function globally for clear data link
122+
window.clearAllExpandedStates = clearAllExpandedStates
123+
124+
})()

app/assets/javascript/main.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// ES6 or Vanilla JavaScript
1+
// app/assets/javascript/main.js
22

3+
// ES6 or Vanilla JavaScript
34

45
document.addEventListener('DOMContentLoaded', () => {
56

6-
77
// Inline check in without requiring page reload
88
const checkInLinks = document.querySelectorAll('.js-check-in-link')
99

@@ -13,6 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
1313
const link = e.currentTarget
1414
const clinicId = link.dataset.clinicId
1515
const eventId = link.dataset.eventId
16+
const showAppointmentLink = link.dataset.showAppointmentLink === 'true'
1617

1718
try {
1819
const response = await fetch(
@@ -32,11 +33,24 @@ document.addEventListener('DOMContentLoaded', () => {
3233
// Find the containing element by data attribute
3334
const container = document.querySelector(`[data-event-status-container="${eventId}"]`)
3435
if (container) {
35-
container.innerHTML = `
36+
let html = `
3637
<strong class="nhsuk-tag">
3738
Checked in
3839
</strong>
3940
`
41+
42+
// Todo: this link should include the participant's name in hidden text
43+
44+
// Add appointment link if enabled
45+
if (showAppointmentLink) {
46+
html += `
47+
<p class="nhsuk-u-margin-top-2 nhsuk-u-margin-bottom-2">
48+
<a href="/clinics/${clinicId}/events/${eventId}/start?event[workflowStatus][appointment]=started">Start appointment</a>
49+
</p>
50+
`
51+
}
52+
53+
container.innerHTML = html
4054
}
4155

4256
// Close any open modal (for modal-based check-ins)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// app/assets/javascript/scroll-to-section.js
2+
3+
// Generic scroll-to handling for all pages
4+
document.addEventListener('DOMContentLoaded', function() {
5+
const urlParams = new URLSearchParams(window.location.search)
6+
const scrollTo = urlParams.get('scrollTo')
7+
8+
if (scrollTo) {
9+
const targetSection = document.getElementById(scrollTo)
10+
if (targetSection) {
11+
// Open the section if it's a details element
12+
if (targetSection.tagName === 'DETAILS') {
13+
targetSection.setAttribute('open', 'open')
14+
}
15+
16+
// Scroll to the section
17+
setTimeout(() => {
18+
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
19+
}, 100)
20+
21+
// Clean up the URL (remove scrollTo parameter)
22+
const url = new URL(window.location)
23+
url.searchParams.delete('scrollTo')
24+
window.history.replaceState({}, '', url.pathname + url.search + url.hash)
25+
}
26+
}
27+
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// app/assets/sass/components/_details.scss
2+
3+
@use 'nhsuk-frontend/dist/nhsuk/core/settings' as *;
4+
@use 'nhsuk-frontend/dist/nhsuk/core/tools' as *;
5+
6+
.app-details__container {
7+
position: relative;
8+
}
9+
10+
.app-details__status {
11+
position: absolute;
12+
top: 0;
13+
right: 0;
14+
padding: 28px 32px 32px;
15+
}
16+
17+
.app-details__summary-subtitle {
18+
display: block;
19+
padding-top: 8px;
20+
// padding: 4px 4px 4px 38px; // to match the line above
21+
color: $nhsuk-text-color;
22+
}
23+
24+
.app-details__contents-summary {
25+
display: block;
26+
padding-top: 16px;
27+
}
28+
29+
.app-details__contents-summary--text {
30+
color: $nhsuk-secondary-text-color;
31+
}
32+
33+
// Hide contents summary when details is expanded
34+
details[open] .app-details__contents-summary {
35+
display: none;
36+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// app/assets/sass/components/_secondary-navigation-overrides.scss
2+
@use 'nhsuk-frontend/dist/nhsuk/core/settings' as *;
3+
@use 'nhsuk-frontend/dist/nhsuk/core/tools' as *;
4+
5+
// Fix for tick SVG in secondary navigation
6+
.app-secondary-navigation__link {
7+
// Ensure proper alignment when SVG is present
8+
display: inline-flex;
9+
align-items: center;
10+
gap: 10px; // 10px spacing between tick and text
11+
}
12+
13+
// Style the tick SVG specifically
14+
.app-secondary-navigation__link .app-icon--tick {
15+
width: 20px;
16+
height: 20px;
17+
flex-shrink: 0; // Prevent SVG from shrinking
18+
margin-right: 0; // Remove any default margin
19+
20+
// Style the tick path
21+
path {
22+
stroke: $color_nhsuk-green;
23+
stroke-width: 4;
24+
fill: none; // Stroke only, no fill
25+
}
26+
}
27+
28+
// Maintain layout for items without ticks
29+
.app-secondary-navigation__list-item {
30+
// Ensure float layout still works with flexbox links
31+
overflow: hidden;
32+
}

app/assets/sass/components/_secondary-navigation.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ $govuk-spacing4: 20px;
5050
}
5151
}
5252

53+
.app-secondary-navigation__link--disabled {
54+
color: $nhsuk-secondary-text-color;
55+
cursor: not-allowed;
56+
pointer-events: none;
57+
text-decoration: none;
58+
59+
&:hover,
60+
&:focus {
61+
text-decoration: none;
62+
}
63+
}
64+
5365
.app-secondary-navigation__list {
5466
@include nhsuk-clearfix;
5567

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@use 'nhsuk-frontend/dist/nhsuk/core/settings' as *;
2+
@use 'nhsuk-frontend/dist/nhsuk/core/tools' as *;
3+
4+
// app/assets/sass/components/_reading.scss
5+
6+
.app-status-bar {
7+
background-color: nhsuk-shade($color_nhsuk-blue, 40%);
8+
color: $color_nhsuk-white;
9+
padding: 8px 0;
10+
border-bottom: 1px solid #d8dde0;
11+
}
12+
13+
.app-status-bar a {
14+
color: $color_nhsuk-white;
15+
}
16+
17+
.app-status-bar__row {
18+
display: flex;
19+
flex-wrap: wrap;
20+
gap: 24px;
21+
align-items: center;
22+
}
23+
24+
.app-status-bar__row + .app-status-bar__row {
25+
margin-top: 8px;
26+
padding-top: 8px;
27+
border-top: 1px solid rgba(255, 255, 255, 0.2);
28+
}
29+
30+
.app-status-bar__item {
31+
display: flex;
32+
gap: 8px;
33+
align-items: center;
34+
}
35+
36+
.app-status-bar__key {
37+
@include nhsuk-typography-weight-bold($important: true);
38+
opacity: 0.9;
39+
}

app/assets/sass/components/_status.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.app-header-with-status {
22
position: relative;
3+
// min-height: 120px; // Make sure there's enough space for the status tags
34
}
45

56
.app-header-with-status__status-tag {

app/assets/sass/main.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33

44
// Components that are not in the NHS.UK frontend library
55
@forward 'components/button-menu';
6+
@forward 'components/details';
67
@forward 'components/dark-mode';
78
@forward 'components/list-border';
89
@forward 'components/modal';
910
@forward 'components/related-nav';
1011
@forward 'components/secondary-navigation';
12+
@forward 'components/secondary-navigation-overrides';
1113
@forward 'components/count';
1214
@forward 'components/forward-link';
1315
@forward 'components/status';
16+
@forward 'components/status-bar';
1417
@forward 'components/reading';
1518

1619
@forward 'components/overrides';

0 commit comments

Comments
 (0)