From 70eb0b58fd44828313fe65d9c2c2e8f0b7e2eae4 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 19 Feb 2026 13:44:27 +0000 Subject: [PATCH 1/3] Add example of a London pharmacy --- app/data/organisations.js | 27 +++++++++++++++++++++++++++ app/data/users.js | 16 ++++++++++++++++ app/data/vaccine-stock.js | 28 ++++++++++++++++++++++++++++ app/routes/home.js | 14 ++++++++++++-- app/views/auth/okta-sign-in.html | 1 + 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/app/data/organisations.js b/app/data/organisations.js index ce472c0b..1336e2b2 100644 --- a/app/data/organisations.js +++ b/app/data/organisations.js @@ -9050,5 +9050,32 @@ module.exports = [ } } ] + }, + { + id: "FT81513", + name: "Holborn Pharmacy", + address: { + line1: "131 Old Street", + town: "London", + postcode: "WC1 7RP" + }, + type: "Community Pharmacy", + status: "Active", + region: "Y56", + vaccines: [ + {name: "MMR", status: "enabled"}, + {name: "flu (London service)", status: "enabled"} + ], + sites: [ + { + id: "FX252", + name: "Holborn Pharmacy", + address: { + line1: "131 Old Street", + town: "London", + postcode: "WC1 7RP" + } + } + ] } ] diff --git a/app/data/users.js b/app/data/users.js index ef098328..f3b4d98c 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -13988,5 +13988,21 @@ module.exports = [ "firstName": "Sally", "lastName": "Green", admin: true + }, + // Jeremy is the lead pharmacist at a Holborn Pharmacy + // pharmacy onboarded for MMR and London flu only + { + "id": "633464144", + "email": "jeremy13.blue@nhs.net", + "firstName": "Jeremy", + "lastName": "Blue", + "organisations": [ + { + "id": "FT81513", + "permissionLevel": "Lead administrator", + "status": "Active", + "vaccinator": true + } + ] } ] diff --git a/app/data/vaccine-stock.js b/app/data/vaccine-stock.js index d1c524d3..2dc6e26a 100644 --- a/app/data/vaccine-stock.js +++ b/app/data/vaccine-stock.js @@ -408,5 +408,33 @@ module.exports = [ expiryDate: "2027-11-23" } ] + }, + { + id: "2514771", + vaccine: "flu (London service)", + vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)", + organisationId: "FT81513", // Holborn Pharmacy + siteId: "FX252", // Holborn Pharmacy + batches: [ + { + id: "46436436", + batchNumber: "LDJE-24", + expiryDate: "2026-12-04" + } + ] + }, + { + id: "463646", + vaccine: "MMR", + vaccineProduct: "MMRVaXPro", + organisationId: "FT81513", // Holborn Pharmacy + siteId: "FX252", // Holborn Pharmacy + batches: [ + { + id: "346436342", + batchNumber: "14-251", + expiryDate: "2027-01-13" + } + ] } ] diff --git a/app/routes/home.js b/app/routes/home.js index 1e9cea4f..5cd163cd 100644 --- a/app/routes/home.js +++ b/app/routes/home.js @@ -67,10 +67,13 @@ module.exports = router => { const currentUser = res.locals.currentUser const data = req.session.data - const vaccinationsRecorded = data.vaccinationsRecorded + const allVaccinationsRecorded = data.vaccinationsRecorded const dateToday = new Date() const monthToday = (dateToday.getMonth() + 1) // JavaScript dates are 0-indexed + // Vaccinations to count + let vaccinationsRecorded = [] + let sites = [] let organisations = [] @@ -78,12 +81,19 @@ module.exports = router => { // Showing all sites for now, for demo purposes sites = currentOrganisation.sites - if (sites === []) { + // Filter vaccinations to only those recorded by the current + // organisation + vaccinationsRecorded = allVaccinationsRecorded.filter((vaccination)=> vaccination.organisationId === currentOrganisation.id) + + if (sites.length === 0) { sites = [currentOrganisation] } } else { + // Include all organisations for now + vaccinationsRecorded = allVaccinationsRecorded + const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) } diff --git a/app/views/auth/okta-sign-in.html b/app/views/auth/okta-sign-in.html index df2318a1..7a0aea5b 100644 --- a/app/views/auth/okta-sign-in.html +++ b/app/views/auth/okta-sign-in.html @@ -43,6 +43,7 @@

Testing area

  • Regional lead
  • Support user
  • Lead admin at a trust with all vaccines enabled
  • +
  • Lead admin at a London pharmacy with local vaccination programme enabled only
  • From 5c5914492c29b9fda91e7986a03854757fc88a64 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 19 Feb 2026 16:25:45 +0000 Subject: [PATCH 2/3] Enable pharmacies to add national vaccine programmes --- app/data/vaccines.js | 2 + app/routes/vaccines.js | 59 +++++++++++++++++--------- app/views/vaccines/choose-vaccine.html | 35 +++++++++------ app/views/vaccines/index.html | 14 ++++++ 4 files changed, 77 insertions(+), 33 deletions(-) diff --git a/app/data/vaccines.js b/app/data/vaccines.js index 79182832..73fc9f3f 100644 --- a/app/data/vaccines.js +++ b/app/data/vaccines.js @@ -1,6 +1,7 @@ module.exports = [ { name: "COVID-19", + availableToAllSites: true, products: [ { name: "Comirnaty 3 LP.8.1", @@ -18,6 +19,7 @@ module.exports = [ }, { name: "flu", + availableToAllSites: true, products: [ { name: "Adjuvanted Trivalent Influenza Vaccine (aTIV)", diff --git a/app/routes/vaccines.js b/app/routes/vaccines.js index 8f6cb448..a7c60c96 100644 --- a/app/routes/vaccines.js +++ b/app/routes/vaccines.js @@ -4,10 +4,24 @@ module.exports = (router) => { const currentOrganisation = res.locals.currentOrganisation const data = req.session.data + const organisationVaccines = res.locals.currentOrganisation.vaccines || [] + + const vaccinesEnabledNames = organisationVaccines + .filter((vaccine) => vaccine.status === "enabled") + .map((vaccine) => vaccine.name) + + const allVaccines = data.vaccines + + const vaccinesThatCanBeRequested = allVaccines + .filter((vaccine) => vaccine.availableToAllSites) + .filter((vaccine) => !vaccinesEnabledNames.includes(vaccine.name)) + .map((vaccine) => vaccine.name) + const vaccineStock = data.vaccineStock.filter((vaccine) => vaccine.organisationId === currentOrganisation.id) res.render('vaccines/index', { - vaccineStock + vaccineStock, + vaccinesThatCanBeRequested }) }) @@ -69,40 +83,43 @@ module.exports = (router) => { const vaccinesEnabled = allVaccines.filter((vaccine) => vaccinesEnabledNames.includes(vaccine.name)) - const vaccinesDisabled = allVaccines.filter((vaccine) => !vaccinesEnabledNames.includes(vaccine.name)) + const vaccinesThatCanBeRequested = allVaccines + .filter((vaccine) => vaccine.availableToAllSites) + .filter((vaccine) => !vaccinesEnabledNames.includes(vaccine.name)) + .map((vaccine) => vaccine.name) + res.render('vaccines/choose-vaccine', { vaccinesEnabled, - vaccinesDisabled + vaccinesThatCanBeRequested }) }) - // Confirmation of a vaccine being requested - router.post('/vaccines/request', (req, res) => { + // Enabling a new vaccine type + router.post('/vaccines/enable', (req, res) => { const data = req.session.data const currentOrganisation = res.locals.currentOrganisation - const vaccinesRequested = currentOrganisation.vaccines - .filter((vaccine) => data.vaccinesRequested.includes(vaccine.name)) + const vaccinesAdded = data.vaccinesAdded - for (let vaccineRequested of vaccinesRequested) { - vaccineRequested.status = "requested" - } + for (vaccine of vaccinesAdded) { - const region = data.regions.find((region) => region.id === currentOrganisation.region) + let vaccineToEnable = currentOrganisation.vaccines.find((vaccine) => vaccine.name === vaccine) - const currentDate = new Date().toISOString() - const generatedId = "AB" + Math.floor(Math.random() * 10000000).toString() + if (vaccineToEnable) { + vaccineToEnable.status = "enabled" - region.inbox ||= [] - region.inbox.push({ - id: generatedId, - fromOrganisationId: currentOrganisation.id, - vaccinesRequested: data.vaccinesRequested, - sentOn: currentDate - }) - res.redirect('/vaccines/requested') + } else { + + currentOrganisation.vaccines.push({ + name: vaccine, + status: "enabled" + }) + } + } + + res.redirect('/vaccines/choose-vaccine') }) diff --git a/app/views/vaccines/choose-vaccine.html b/app/views/vaccines/choose-vaccine.html index da8c9dc3..91bde74b 100644 --- a/app/views/vaccines/choose-vaccine.html +++ b/app/views/vaccines/choose-vaccine.html @@ -74,23 +74,29 @@ }) }} - {% if (vaccinesDisabled | length) > 0 %} - {% call details({ summaryText: "Need to add more vaccine types?"}) %} -

    Select the vaccines you need.

    + {% if (vaccinesThatCanBeRequested | length) > 0 %} + {% set detailsHtml %} {% set requestableItems = [] %} - {% for vaccine in (vaccinesDisabled | sort(false, false, "name")) %} + {% for vaccine in (vaccinesThatCanBeRequested | sort) %} + {% set requestableItems = (requestableItems.push({ - value: vaccine.name, - text: (vaccine.name | capitaliseFirstLetter) + value: vaccine, + text: (vaccine | capitaliseFirstLetter) }), requestableItems) %} {% endfor %} -
    + + +

    You can now use RAVS to record national {{ vaccinesThatCanBeRequested | join(" and ") }} vaccinations.

    + +

    Any vaccinations you record will be sent to Manage your service (MYS) for payments.

    + +

    You will still have access to your previous system for at least 6 months so you can download reports.

    {{ checkboxes({ - name: "vaccinesRequested", - idPrefix: "vaccines-requested", + name: "vaccinesAdded", + idPrefix: "vaccines-added", fieldset: { legend: { text: "Vaccine", @@ -101,12 +107,17 @@ }) }} {{ button({ - text: "Send request", - classes: "nhsuk-button--secondary" + text: "Add", + classes: "nhsuk-button--secondary nhsuk-u-margin-bottom-0" }) }}
    + {% endset %} + + {{ details({ + summaryText: "Add other vaccines", + html: detailsHtml + }) }} - {% endcall %} {% endif %} diff --git a/app/views/vaccines/index.html b/app/views/vaccines/index.html index 37e05ad4..f3f5aa88 100644 --- a/app/views/vaccines/index.html +++ b/app/views/vaccines/index.html @@ -22,6 +22,20 @@

    Vaccines

    Add and edit vaccines for your organisation.

    + {% if (vaccinesThatCanBeRequested | length) > 0 %} + + {% set insetTextHtml %} +

    {{ tag({ text: "New", classes: "nhsuk-tag--blue"}) }}

    +

    You can now use RAVS to record national {{ vaccinesThatCanBeRequested | join(" and ") }} vaccinations. It’s free to use.

    + {% endset %} + + {{ insetText({ + html: insetTextHtml, + classes: "nhsuk-u-padding-top-1 nhsuk-u-padding-bottom-1 nhsuk-u-margin-top-1 nhsuk-u-margin-bottom-6" + }) }} + + {% endif %} + {{ button({ "text": "Add vaccine", "href": "/vaccines/choose-site" From c6d03f4dee612e99605a3771c7d77d4c4b4c1c10 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 20 Feb 2026 09:40:42 +0000 Subject: [PATCH 3/3] code style fix --- app/routes/vaccines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/vaccines.js b/app/routes/vaccines.js index a7c60c96..5ef4a696 100644 --- a/app/routes/vaccines.js +++ b/app/routes/vaccines.js @@ -103,7 +103,7 @@ module.exports = (router) => { const vaccinesAdded = data.vaccinesAdded - for (vaccine of vaccinesAdded) { + for (const vaccine of vaccinesAdded) { let vaccineToEnable = currentOrganisation.vaccines.find((vaccine) => vaccine.name === vaccine)