diff --git a/app/assets/sass/components/_calendar-table.scss b/app/assets/sass/components/_calendar-table.scss index eae3d13..d27448c 100644 --- a/app/assets/sass/components/_calendar-table.scss +++ b/app/assets/sass/components/_calendar-table.scss @@ -57,6 +57,7 @@ .--date-selected .calendar-table-date { background-color: $nhsuk-focus-colour; + outline: 2px solid $nhsuk-input-border-colour; } .--schedule-start { diff --git a/app/assets/sass/components/_session-slots.scss b/app/assets/sass/components/_session-slots.scss index f38879f..1ca0a53 100644 --- a/app/assets/sass/components/_session-slots.scss +++ b/app/assets/sass/components/_session-slots.scss @@ -42,6 +42,7 @@ display: flex; align-items: first baseline; padding-top: nhsuk-px-to-rem(8px); + padding-bottom: nhsuk-px-to-rem(8px); } .slot.is-hold {} diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index 40b2268..b7be8bb 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -77,6 +77,8 @@ module.exports = { duration: '10', bookableSlotCount: 36, nonBookableSlotCount: 6, + standardSlotCount: 0, + heldSlotCount: 36, bookableBreakdown: [ { label: 'Special appointment', @@ -175,18 +177,16 @@ module.exports = { duration: '10', bookableSlotCount: 36, nonBookableSlotCount: 6, + standardSlotCount: 28, + heldSlotCount: 8, bookableBreakdown: [ { - label: 'Held for rebooking', + label: 'Rebooking', count: 4 }, { label: 'Special appointment', count: 4 - }, - { - label: 'Standard', - count: 28 } ], nonBookableBreakdown: [ diff --git a/app/routes/clinic-sessions.js b/app/routes/clinic-sessions.js index a0111f8..f8725d9 100644 --- a/app/routes/clinic-sessions.js +++ b/app/routes/clinic-sessions.js @@ -11,7 +11,7 @@ const { combineSlotsHandler, divideSlotsHandler } = require('./slot-structure-handlers') -const { deleteSessionMapImage, saveSessionMapImage, sessionMapImageExists } = require('../lib/session-map-svg') +const { buildSessionMapSvg, deleteSessionMapImage, saveSessionMapImage, sessionMapImageExists } = require('../lib/session-map-svg') /* /app/views/sessions/ @@ -83,6 +83,20 @@ router.post('/sessions/03-slot-structure', function (req, res) { res.redirect('/sessions/04-check-template') }) +router.get('/sessions/04-check-template', function (req, res) { + if (!req.session.data.createSessionTemplate) { + req.session.data.createSessionTemplate = {} + } + + const { svg, description } = buildSessionMapSvg(buildSessionSlots(req.session.data.createSessionTemplate)) + + // A data URI keeps the preview out of the image directory, so nothing is left to clean up + res.render('sessions/04-check-template', { + mapImageSrc: `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`, + mapImageAlt: description + }) +}) + // Saves the draft as a template, overwriting the template being edited if there is one router.post('/sessions/save-template', function (req, res) { const template = req.session.data.createSessionTemplate || {} @@ -184,6 +198,8 @@ router.post('/sessions/mark-hold-web-rebooking', holdHandler({ reason: 'online b router.post('/sessions/mark-hold-technical-recall', holdHandler({ reason: 'technical recall', ...sessionsSlotStructure })) router.post('/sessions/mark-hold-generic', holdHandler(sessionsSlotStructure)) router.post('/sessions/mark-special-appointment', specialAppointmentHandler(sessionsSlotStructure)) +router.post('/sessions/mark-admin', nonBookableHandler({ reason: 'admin', ...sessionsSlotStructure })) +router.post('/sessions/mark-downtime', nonBookableHandler({ reason: 'downtime', ...sessionsSlotStructure })) router.post('/sessions/mark-non-bookable-generic', nonBookableHandler(sessionsSlotStructure)) router.post('/sessions/combine-slots', combineSlotsHandler(sessionsSlotStructure)) router.post('/sessions/divide-slots', divideSlotsHandler(sessionsSlotStructure)) diff --git a/app/routes/slot-structure-handlers.js b/app/routes/slot-structure-handlers.js index ee468be..49ff93a 100644 --- a/app/routes/slot-structure-handlers.js +++ b/app/routes/slot-structure-handlers.js @@ -56,11 +56,12 @@ const buildSessionSlots = (sessionData) => { const nonBookableSlotCount = sumSpans(slots.filter((slot) => slot.isNonBookable)) sessionData.bookableSlotCount = bookableSlotCount sessionData.nonBookableSlotCount = nonBookableSlotCount + sessionData.heldSlotCount = sumSpans(slots.filter((slot) => (slot.isHold || slot.isSpecialAppointment) && !slot.isNonBookable)) const capitalise = (text) => text.charAt(0).toUpperCase() + text.slice(1) const slotTypeLabel = (slot) => { - if (slot.isNonBookable) return slot.nonBookableReason ? capitalise(slot.nonBookableReason) : 'Non-bookable' - if (slot.isHold) return slot.holdReason ? `Held for ${slot.holdReason}` : 'Held' + if (slot.isNonBookable) return slot.nonBookableReason ? capitalise(slot.nonBookableReason) : 'Other non-bookable' + if (slot.isHold) return slot.holdReason ? `${slot.holdReason}` : 'Other hold' if (slot.isSpecialAppointment) return 'Special appointment' return 'Standard' } @@ -78,12 +79,14 @@ const buildSessionSlots = (sessionData) => { const secondIndex = order.indexOf(second.label) return (firstIndex === -1 ? order.length : firstIndex) - (secondIndex === -1 ? order.length : secondIndex) }) - sessionData.bookableBreakdown = breakdown(slots.filter((slot) => !slot.isNonBookable), [ - 'Held for rebooking', 'Held for online booking', 'Held for technical recall', - 'Special appointment', 'Held', 'Standard' + const bookableItems = breakdown(slots.filter((slot) => !slot.isNonBookable), [ + 'Standard', 'Special appointment', 'Rebooking', 'Online booking', 'Technical recall', 'Held' ]) + const standardItem = bookableItems.find((item) => item.label === 'Standard') + sessionData.standardSlotCount = standardItem ? standardItem.count : 0 + sessionData.bookableBreakdown = bookableItems.filter((item) => item.label !== 'Standard') sessionData.nonBookableBreakdown = breakdown(slots.filter((slot) => slot.isNonBookable), [ - 'Staff break', 'Staff meeting', 'Non-bookable' + 'Staff break', 'Staff meeting', 'Admin', 'Downtime', 'Non-bookable' ]) return { diff --git a/app/views/create-capacity-from-zero/clinic-summary.html b/app/views/create-capacity-from-zero/clinic-summary.html index eebe484..c431d0f 100644 --- a/app/views/create-capacity-from-zero/clinic-summary.html +++ b/app/views/create-capacity-from-zero/clinic-summary.html @@ -84,17 +84,18 @@
- A schedule is a block of confirmed time and capacity (usually agreed as a rota). Each schedule has a start date and an end date, for example a 6 week block. -
++ There are no schedules in this clinic at the moment. Schedules will appear here when you have created them. +
+ + + + Create a schedule + + +| + | Clinic | -+ | Unit and location | -- Scheduled timespan + | + Current end date | -+ | Capacity |
|---|---|---|---|---|---|---|---|
|
- Mereford central ongoing - NNN-NNN-systemid-20260501 + Mereford central ongoing |
Mereford and Wensley Screening Centre Mereford and Wensley District Hospital |
- 7 Sep 2026 to 27 Nov 2026 + 15 Dec 2026 |
- 1500 slots available - of which 150 held + 1500 slots available |
||||
|
- Drummond van visits - NNN-NNN-systemid-20260501 + Drummond van visits |
Drummond East Mereford Community Hub |
- 17 Nov 2026 to 24 Dec 2026 + 24 Dec 2026 |
No sessions assigned
@@ -186,123 +182,23 @@ Active clinics | ||||
|
- Pankhurst van visits - NNN-NNN-systemid-20261028 + Pankhurst van visits |
Pankhurst Riverside Sports Centre |
- 17 Sep 2026 to 27 Oct 2026 + 27 Dec 2026 |
- {{ padlockSvg | safe }}
- 1500 slots - + 1500 slots available |
{{ template.note }}
- {% endif %} -+ A session represents one “clinic day” of slots. Common slot patterns can be captured in session templates for easy reuse. +
+{{ template.note }}
+ {% endif %} +
- {% endif %}
-
+ {% endif %}
{{ template.note }}
- {% endif %} -+ There are no session templates at the moment. Templates will appear here when you have created them. +
+ + + + Create a session template + - {% endif %}
- {% endif %}
- {{ template.note }}
+ {% endif %}
+ {% endif %}
{{ bookableSlotCount }} bookable slots
@@ -260,6 +216,8 @@{{ data.createSessionTemplate.bookableSlotCount }} bookable slots
- {% endif %} - {% if data.createSessionTemplate.nonBookableBreakdown | length %} -{{ data.createSessionTemplate.nonBookableSlotCount }} non-bookable slots
- {% endif %} + +{{ data.createSessionTemplate.standardSlotCount }} standard slots
+ + {% if data.createSessionTemplate.bookableBreakdown | length %} +