diff --git a/app/data/appointments.js b/app/data/appointments.js new file mode 100644 index 00000000..c0181f22 --- /dev/null +++ b/app/data/appointments.js @@ -0,0 +1,240 @@ +// These are organisations set up as using RAVS +module.exports = [ + + { + id: "163473464363", + date: "today", + time: "10:00", + patient: { + nhsNumber: "9123123123", + firstName: "Alan", + lastName: "Teapot", + dateOfBirth: "1969-04-16", + contactDetails: { + mobile: "07588547704", + phone: "0111432626", + email: "alan.teapot@btinternet.com" + } + }, + vaccinations: [ + "COVID-19" + ] + }, + { + id: "923532535", + date: "today", + time: "10:30", + patient: { + nhsNumber: "9841414141", + firstName: "Keith", + lastName: "Napkin", + dateOfBirth: "1976-02-12", + contactDetails: { + mobile: "077345813941" + } + }, + vaccinations: [ + "COVID-19", + "Flu" + ] + }, + { + id: "2364364369", + date: "today", + time: "10:40", + patient: { + nhsNumber: "9841515715", + firstName: "Lynn", + lastName: "Feather", + dateOfBirth: "1963-05-23", + contactDetails: { + email: "lynn.feather@gmail.com" + } + }, + vaccinations: [ + "Flu", "RSV" + ] + }, + { + id: "3026352592", + date: "today", + time: "11:10", + cancelled: { + reason: "patient" + }, + patient: { + nhsNumber: "98357151513", + firstName: "Lorna", + lastName: "Biscuitbarrel", + dateOfBirth: "1962-08-19", + contactDetails: { + mobile: "07364824944" + } + }, + vaccinations: [ + "Flu" + ] + }, + { + id: "410059235", + date: "today", + time: "11:20", + patient: { + nhsNumber: "9847471413", + firstName: "Moira", + lastName: "Packet", + dateOfBirth: "1950-04-14", + contactDetails: { + mobile: "07623842424" + } + }, + vaccinations: [ + "COVID-19" + ] + }, + { + id: "5019581446", + date: "today", + time: "11:30", + patient: { + nhsNumber: "9841411411", + firstName: "Cliff", + lastName: "Sugarbowl", + dateOfBirth: "1957-01-23", + contactDetails: { + mobile: "07623913141" + } + }, + vaccinations: [ + "COVID-19" + ] + }, + { + id: "69263464", + date: "today", + time: "11:40", + patient: { + nhsNumber: "9841411411", + firstName: "James", + lastName: "Brown", + dateOfBirth: "1952-04-19", + contactDetails: { + mobile: "0723456123", + email: "james.brown@hotmail.com" + } + }, + vaccinations: [ + "COVID-19" + ] + }, + { + id: "7692855825", + date: "today", + time: "11:50", + patient: { + nhsNumber: "9917425141", + firstName: "Emma", + lastName: "Blue", + dateOfBirth: "1973-01-23", + contactDetails: { + mobile: "07524222525", + email: "emma.blue123@nhs.net" + } + }, + vaccinations: [ + "COVID-19", "RSV" + ] + }, + { + id: "8928558275", + date: "today", + time: "12:10", + cancelled: { + reason: "organisation" + }, + patient: { + nhsNumber: "9741851731", + firstName: "Charlie", + lastName: "Green", + dateOfBirth: "1983-06-12", + contactDetails: { + email: "charlie.green@hotmail.com" + } + }, + vaccinations: [ + "COVID-19" + ] + }, + { + id: "9255253581", + date: "today", + time: "12:40", + cancelled: { + reason: "auto" + }, + patient: { + nhsNumber: "9951736814", + firstName: "Jason", + lastName: "White", + dateOfBirth: "1998-03-19", + contactDetails: { + email: "jw225235@gmail.com" + } + }, + vaccinations: [ + "Flu" + ] + }, + { + id: "634633242", + date: "yesterday", + time: "11:20", + patient: { + nhsNumber: "9918571751", + firstName: "Danny", + lastName: "Green", + dateOfBirth: "1998-03-19", + contactDetails: { + email: "dgreen@gmail.com" + } + }, + vaccinations: [ + "Flu" + ] + }, + { + id: "10363212525", + date: "yesterday", + time: "13:20", + patient: { + nhsNumber: "99472754141", + firstName: "Jason", + lastName: "Black", + dateOfBirth: "1967-09-21", + contactDetails: { + email: "jblack@hotmail.com" + } + }, + vaccinations: [ + "Flu" + ], + vaccinationIds: ["464743636"] + }, + { + id: "64639185885", + date: "tomorrow", + time: "11:20", + patient: { + nhsNumber: "9017474141", + firstName: "Mohammed", + lastName: "Khan", + dateOfBirth: "1993-01-23", + contactDetails: { + email: "mk25325@gmail.com" + } + }, + vaccinations: [ + "Flu" + ] + } +] diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index c0027bf0..53a33eff 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -1,4 +1,5 @@ const allOrganisations = require('./all-organisations') +const appointments = require('./appointments') const featureFlags = require('./feature-flags') const organisations = require('./organisations') const users = require('./users') @@ -13,10 +14,11 @@ module.exports = { users: users, vaccines: vaccines, vaccineStock: vaccineStock, + appointments: appointments, lists: [], nhsNumberKnown: "yes", - currentUserId: "2387441662601", - currentOrganisationId: "RW3", + currentUserId: "46436346", // Jeremy Blue + currentOrganisationId: "FA424", // Pickfords Pharmacy vaccinationsRecorded: vaccinationsRecorded, // These are the options for extracting CSV reports diff --git a/app/data/users.js b/app/data/users.js index f3b4d98c..fd68212d 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -116,14 +116,14 @@ module.exports = [ "firstName": "Paulina", "lastName": "Sloan" }, - // Each pharmacy in Paulina Sloan’s chain has its own vaccinator + // Each pharmacy in Paulina Sloan’s chain has its own vaccinator, some of who are also admins { "id": "46436346", "email": "jeremy.blue@nhs.net", "organisations": [ { "id": "FA424", - "permissionLevel": "Recorder", + "permissionLevel": "Lead administrator", "status": "Active", "vaccinator": true } diff --git a/app/data/vaccine-stock.js b/app/data/vaccine-stock.js index 32927538..f703deed 100644 --- a/app/data/vaccine-stock.js +++ b/app/data/vaccine-stock.js @@ -409,6 +409,34 @@ module.exports = [ } ] }, + { + id: "7436323", + vaccine: "Flu", + vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)", + organisationId: "FA424", // Pickfords Pharmacy + siteId: "FA424X", // Pickfords Pharmacy + batches: [ + { + id: "464735235325", + batchNumber: "714-14", + expiryDate: "2026-12-14" + } + ] + }, + { + id: "9235235325", + vaccine: "COVID-19", + vaccineProduct: "Comirnaty 3 LP.8.1", + organisationId: "FA424", // Pickfords Pharmacy + siteId: "FA424X", // Pickfords Pharmacy + batches: [ + { + id: "82725252", + batchNumber: "AB-255", + expiryDate: "2027-01-22" + } + ] + }, { id: "2514771", vaccine: "flu (London service)", diff --git a/app/filters.js b/app/filters.js index ce8b19fc..dd43b8b3 100644 --- a/app/filters.js +++ b/app/filters.js @@ -18,9 +18,10 @@ module.exports = function () { filters.findById = findById - filters.dayName = function(isoDate) { + filters.dayName = function(isoDate, style = 'short') { const date = new Date(Date.parse(isoDate)) - const dateFormatter = new Intl.DateTimeFormat('en-GB', {weekday: 'short'}); + const weekdayStyle = (style === 'long') ? 'long' : 'short' + const dateFormatter = new Intl.DateTimeFormat('en-GB', {weekday: weekdayStyle}); return dateFormatter.format(date) } diff --git a/app/routes.js b/app/routes.js index db5607e4..dd32140b 100644 --- a/app/routes.js +++ b/app/routes.js @@ -43,6 +43,7 @@ router.use('/regions{*splat}', authorise({userType: 'regional'})) router.use('/support{*splat}', authorise({userType: 'admin'})) require('./routes/apply')(router) +require('./routes/appointments')(router) require('./routes/record-vaccinations')(router) require('./routes/regions')(router) require('./routes/user-admin')(router) diff --git a/app/routes/appointments.js b/app/routes/appointments.js new file mode 100644 index 00000000..d7b9d129 --- /dev/null +++ b/app/routes/appointments.js @@ -0,0 +1,68 @@ +module.exports = (router) => { + + router.get('/appointments', (req, res) => { + const data = req.session.data + + const day = 86400000 // number of milliseconds in a day + const today = (new Date()).toISOString().substring(0,10) + const yesterday = (new Date(Date.now() - day)).toISOString().substring(0,10) + const tomorrow = (new Date(Date.now() + day)).toISOString().substring(0,10) + const requestedDay = req.query.date + const allowedDays = [yesterday, today, tomorrow] + + if (requestedDay && !allowedDays.includes(requestedDay)) { + return res.redirect('/appointments') + } + + const currentDay = requestedDay || today + const currentDate = new Date(currentDay) + const previousDay = (new Date(currentDate.getTime() - day)).toISOString().substring(0,10) + const nextDay = (new Date(currentDate.getTime() + day)).toISOString().substring(0,10) + const isYesterday = currentDay === yesterday + const isTomorrow = currentDay === tomorrow + + let appointments = data.appointments + + if (currentDay === today) { + appointments = appointments.filter((appointment) => appointment.date === "today" || appointment.date === today) + } else if (nextDay === today) { + appointments = appointments.filter((appointment) => appointment.date === "yesterday" || appointment.date === today) + } else if (previousDay === today) { + appointments = appointments.filter((appointment) => appointment.date === "tomorrow" || appointment.date === today) + } else { + appointments = appointments.filter((appointment) => appointment.date === today) + } + + const scheduledAppointments = appointments.filter((appointment) => !appointment.cancelled && (appointment.vaccinationIds || []).length === 0) + + const cancelledAppointments = appointments.filter((appointment) => appointment.cancelled) + + const completedAppointments = appointments.filter((appointment) => (appointment.vaccinationIds || []).length > 0) + + let vaccinators = [] + + if (data.vaccinatorIds) { + vaccinators = data.users.filter((user) => data.vaccinatorIds.includes(user.id)) + } + + + res.render('appointments/index', { + scheduledAppointments, + cancelledAppointments, + completedAppointments, + today, + currentDay, + previousDay, + nextDay, + isYesterday, + isTomorrow, + vaccinators + }) + }) + + + router.get('/appointments/vaccinators', (req, res) => { + res.redirect('/appointments') + }) +} + diff --git a/app/routes/record-vaccinations.js b/app/routes/record-vaccinations.js index 5de6bd4c..b253cb03 100644 --- a/app/routes/record-vaccinations.js +++ b/app/routes/record-vaccinations.js @@ -53,12 +53,33 @@ module.exports = router => { }) }) + router.get('/record-vaccinations/vaccination-date', (req, res) => { + + const data = req.session.data + const appointmentId = data.appointmentId + + if (appointmentId) { + const appointment = data.appointments.find((appointment) => appointment.id === appointmentId) + + if (appointment.date === "today") { + // Set vaccination date to today and skip question + req.session.data.vaccinationToday = "yes" + return res.redirect('/record-vaccinations/delivery-team') + + } + } + + res.render('record-vaccinations/vaccination-date') + }) + router.post('/record-vaccinations/answer-date', (req, res) => { const data = req.session.data if (!data.vaccinationToday) { return res.redirect('/record-vaccinations/?showErrors=yes') } + + res.redirect('/record-vaccinations/delivery-team') }) @@ -72,6 +93,12 @@ module.exports = router => { const sitesInUse = currentOrganisation.sites.filter((site) => siteIdsWithVaccines.includes(site.id)) + // If there’s only 1 site set up (eg a pharmacy), then + // set that and skip this question. + if (sitesInUse.length === 1) { + data.siteId = sitesInUse[0].id + return res.redirect('/record-vaccinations/vaccinator') + } if (req.query.showErrors === "yes") { if (!req.session.data.siteId) { @@ -127,6 +154,14 @@ module.exports = router => { } } + // If we’re recording a vaccination from today’s appointments, and there’s + // only 1 vaccinator set as present, then set them as the vaccinator + // and skip the question. + if (data.appointmentId && Array.isArray(data.vaccinatorIds) && data.vaccinatorIds.length == 1) { + req.session.data.vaccinatorId = data.vaccinatorIds[0] + return res.redirect('/record-vaccinations/vaccine') + } + res.render('record-vaccinations/vaccinator', { vaccinatorError, otherVaccinators @@ -598,6 +633,7 @@ module.exports = router => { const yearToday = (dateToday.getFullYear()) if (data.vaccinationToday === 'yes') { + data.vaccinationDate = {} data.vaccinationDate.day = String(dayToday) data.vaccinationDate.month = String(monthToday) data.vaccinationDate.year = String(yearToday) @@ -627,6 +663,16 @@ module.exports = router => { data.lastAddedVaccinationId = generatedId + // Add the vaccination ID to the appointment so that + // we can filter the appointments list by which ones have + // been given + if (data.appointmentId) { + const appointment = data.appointments.find((appointment) => appointment.id == data.appointmentId) + + appointment.vaccinationIds ||= [] + appointment.vaccinationIds.push(generatedId) + } + res.redirect('/record-vaccinations/done') }) @@ -716,6 +762,7 @@ module.exports = router => { } if (answer === 'same-vaccination-another-patient') { + data.appointmentId = "" req.session.data.firstName = "" req.session.data.lastName = "" @@ -741,6 +788,7 @@ module.exports = router => { res.redirect('/record-vaccinations/patient-history?repeatPatient=yes&repeatVaccination=no') } else if (answer === 'different-vaccination-another-patient') { + data.appointmentId = "" req.session.data.vaccine = "" req.session.data.vaccineProduct = "" @@ -751,6 +799,22 @@ module.exports = router => { req.session.data.vaccineDose = "" res.redirect('/record-vaccinations/?repeatPatient=no&repeatVaccination=no') + + } else if (answer === 'appointments') { + data.appointmentId = "" + + req.session.data.vaccine = "" + req.session.data.vaccineProduct = "" + req.session.data.vaccineBatch = "" + req.session.data.eligibility = "" + req.session.data.nhsNumber = "" + req.session.data.healthcareWorker = "" + req.session.data.vaccineDose = "" + req.session.data.vaccinationToday = "" + req.session.data.vaccinatorId = "" + + res.redirect('/appointments') + } else { res.redirect('/record-vaccinations/done?showErrors=yes') } diff --git a/app/views/appointments/_cancelled.html b/app/views/appointments/_cancelled.html new file mode 100644 index 00000000..65abf7a7 --- /dev/null +++ b/app/views/appointments/_cancelled.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + {% for appointment in cancelledAppointments %} + + + + + + + + + + {% endfor %} + +
TimeName and NHS numberDate of birthVaccinesContact detailsCancellation reason
{{ appointment.time }}am + {{ appointment.patient.firstName }} + {{ appointment.patient.lastName }} +
{{ appointment.patient.nhsNumber }}
+
+ {{ appointment.patient.dateOfBirth | govukDate }} + + {{ appointment.vaccinations | join(", ") }} + + {% if appointment.patient.contactDetails.mobile %} + {{ appointment.patient.contactDetails.mobile }}
+ {% endif %} + {% if appointment.patient.contactDetails.email %} + {{ appointment.patient.contactDetails.email }}
+ {% endif %} +
+ {% if appointment.cancelled.reason == "patient" %} + Patient + {% elif appointment.cancelled.reason == "organisation" %} + Us + {% elif appointment.cancelled.reason == "auto" %} + Automatic + {% endif %} +
diff --git a/app/views/appointments/_scheduled.html b/app/views/appointments/_scheduled.html new file mode 100644 index 00000000..8b8317d3 --- /dev/null +++ b/app/views/appointments/_scheduled.html @@ -0,0 +1,49 @@ + + + + + + + + + + {% if currentDay == today %} + + {% endif %} + + + + + + {% for appointment in scheduledAppointments %} + + + + + + + {% if currentDay == today %} + + {% endif %} + + {% endfor %} + +
TimeName and NHS numberDate of birthVaccines bookedContact detailsAction
{{ appointment.time }}am + {{ appointment.patient.firstName }} + {{ appointment.patient.lastName }} +
{{ appointment.patient.nhsNumber }}
+
+ {{ appointment.patient.dateOfBirth | govukDate }} +
{{ appointment.patient.dateOfBirth | age }} old
+
+ {{ appointment.vaccinations | join("
") | safe }} +
+ {% if appointment.patient.contactDetails.mobile %} + {{ appointment.patient.contactDetails.mobile }}
+ {% endif %} + {% if appointment.patient.contactDetails.email %} + {{ appointment.patient.contactDetails.email }}
+ {% endif %} +
+ Record +
diff --git a/app/views/appointments/_vaccinations_given.html b/app/views/appointments/_vaccinations_given.html new file mode 100644 index 00000000..5df2278a --- /dev/null +++ b/app/views/appointments/_vaccinations_given.html @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + {% for appointment in completedAppointments %} + + + + + + + + {% endfor %} + +
TimeName and NHS numberDate of birthVaccines bookedContact details
{{ appointment.time }}am + {{ appointment.patient.firstName }} + {{ appointment.patient.lastName }} +
{{ appointment.patient.nhsNumber }}
+
+ {{ appointment.patient.dateOfBirth | govukDate }} + + {{ appointment.vaccinations | join("
") | safe }} +
+ {% if appointment.patient.contactDetails.mobile %} + {{ appointment.patient.contactDetails.mobile }}
+ {% endif %} + {% if appointment.patient.contactDetails.email %} + {{ appointment.patient.contactDetails.email }}
+ {% endif %} +
diff --git a/app/views/appointments/index.html b/app/views/appointments/index.html new file mode 100644 index 00000000..1d33883d --- /dev/null +++ b/app/views/appointments/index.html @@ -0,0 +1,87 @@ +{% extends 'layout.html' %} + +{% if currentDay == today %} + {% set pageName = "Today’s appointments" %} +{% elif nextDay == today %} + {% set pageName = "Yesterday’s appointments" %} +{% elif previousDay == today %} + {% set pageName = "Tomorrow’s appointments" %} +{% else %} + {% set pageName = "Appointments" %} +{% endif %} +{% set currentSection = "appointments" %} + + +{% block content %} +
+
+ +

{{ pageName }}

+ {{ currentDay | dayName('long') }} +

{{ currentDay | formatDate }}

+ + + +

Imported from Manage your appointments

+ +
+
+
+
+ + {{ pagination({ + previous: { + labelText: ("Yesterday" if currentDay == today else (previousDay | formatDate)), + href: "/appointments?date=" + previousDay + } if not isYesterday, + next: { + labelText: ("Tomorrow" if currentDay == today else (nextDay | formatDate)), + href: "/appointments?date=" + nextDay + } if not isTomorrow + }) }} + + {% set todayHtml %} + {% include "appointments/_scheduled.html" %} + {% endset %} + + {% set cancelledHtml %} + {% include "appointments/_cancelled.html" %} + {% endset %} + + {% set vaccinationsGivenHtml %} + {% include "appointments/_vaccinations_given.html" %} + {% endset %} + + + {{ tabs({ + items: [ + { + label: "Scheduled (" + (scheduledAppointments | length) + ")", + id: "scheduled", + panel: { + html: todayHtml + } + }, + { + label: "Completed (" + (completedAppointments | length) + ")", + id: "done", + panel: { + html: vaccinationsGivenHtml + } + } if currentDay <= today, + { + label: "Cancelled (" + (cancelledAppointments | length) + ")", + id: "cancelled", + panel: { + html: cancelledHtml + } + } + ] + }) }} + + +
+
+ + +{% endblock %} diff --git a/app/views/appointments/vaccinators.html b/app/views/appointments/vaccinators.html new file mode 100644 index 00000000..04a50559 --- /dev/null +++ b/app/views/appointments/vaccinators.html @@ -0,0 +1,62 @@ +{% extends 'layout.html' %} + +{% set currentSection = "appointments" %} +{% set pageName = "Who is vaccinating today?" %} +{% set organisationSetting = currentUser.organisations | findById(data.currentOrganisationId) %} + +{% block beforeContent %} + {{ backLink({ + href: "/appointments", + text: "Back" + }) }} +{% endblock%} + +{% block content %} +
+
+ +
+ + {% set items = [] %} + + {% if organisationSetting.vaccinator %} + {% set items = (items.push({ + value: data.currentUserId, + text: "Me (" + currentUser.firstName + " " + currentUser.lastName + ")" + }), items) %} + {% endif %} + + {% for user in otherVaccinators %} + {% set items = (items.push({ + value: user.id, + text: user.firstName + " " + user.lastName + " (" + user.email + ")" + }), items) %} + {% endfor %} + + {{ checkboxes({ + idPrefix: "vaccinator", + name: "vaccinatorIds", + fieldset: { + legend: { + text: pageName, + size: "l", + isPageHeading: "true" + } + }, + hint: { + text: "Setting this lets you save time when recording today’s vaccinations." + }, + items: items, + values: data.vaccinatorIds + }) }} + + {{ button({ + text: "Continue" + })}} +
+ +
+
+ + +{% endblock %} diff --git a/app/views/auth/okta-sign-in.html b/app/views/auth/okta-sign-in.html index 0b913e3a..482f9eda 100644 --- a/app/views/auth/okta-sign-in.html +++ b/app/views/auth/okta-sign-in.html @@ -35,6 +35,7 @@

Testing area