From 8f40e2a1c64d59f7b9ffded2d6db3493af4104c5 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 13:19:23 +0100 Subject: [PATCH 01/37] Initial work on pharmacy chain mode --- app/routes.js | 1 + app/routes/pharmacies.js | 55 +++++++++ app/views/home/index.html | 2 +- app/views/includes/header.html | 15 +++ app/views/pharmacies/index.html | 58 +++++++++ app/views/pharmacies/pharmacy.html | 23 ++++ app/views/pharmacies/select.html | 186 +++++++++++++++++++++++++++++ 7 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 app/routes/pharmacies.js create mode 100644 app/views/pharmacies/index.html create mode 100644 app/views/pharmacies/pharmacy.html create mode 100644 app/views/pharmacies/select.html diff --git a/app/routes.js b/app/routes.js index db5607e4..c794b938 100644 --- a/app/routes.js +++ b/app/routes.js @@ -51,6 +51,7 @@ require('./routes/user-profile')(router) require('./routes/vaccines')(router) require('./routes/reports')(router) require('./routes/records')(router) +require('./routes/pharmacies')(router) require('./routes/prototype-admin')(router) require('./routes/lists')(router) require('./routes/support')(router) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js new file mode 100644 index 00000000..e9375d92 --- /dev/null +++ b/app/routes/pharmacies.js @@ -0,0 +1,55 @@ +const { getPharmaciesBelongingToOrganisation, getPharmacyChains, getOrganisation } = require('../lib/ods'); + +const sortByNameThenPostcode = (getPostcode = (item) => item.postcode) => (a, b) => { + if (a.name < b.name) return -1 + if (a.name > b.name) return 1 + const postcodeA = getPostcode(a) + const postcodeB = getPostcode(b) + if (postcodeA < postcodeB) return -1 + return 1 +} + +module.exports = router => { + + router.get('/pharmacies', (req, res) => { + const data = req.session.data + const currentUser = res.locals.currentUser + + const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) + + const organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) + + + res.render('pharmacies/index', { + organisations + }) + }) + + router.get('/pharmacies/select', async (req, res) => { + const data = req.session.data + const id = req.params.id + + let pharmacies = await getPharmaciesBelongingToOrganisation("P15J") + + pharmacies = pharmacies.sort(sortByNameThenPostcode((item) => item.address.postcode)) + + + res.render('pharmacies/select', { + pharmacies + }) + }) + + + router.get('/pharmacies/:id', (req, res) => { + const data = req.session.data + const id = req.params.id + + const organisation = data.organisations.find((organisation) => organisation.id === id) + + res.render('pharmacies/pharmacy', { + organisation + }) + }) + + +} diff --git a/app/views/home/index.html b/app/views/home/index.html index fad8e48b..662883cd 100644 --- a/app/views/home/index.html +++ b/app/views/home/index.html @@ -13,7 +13,7 @@ {% include "includes/notification.html" %} -

{% if currentOrganisation %}{{ currentOrganisation.name }}{% else %}Overview{% endif %}

+

{% if currentOrganisation %}{{ currentOrganisation.name }}{% else %}Peak pharmacies{% endif %}

{% if currentOrganisation and totalVaccinationsRecorded == 0 %} diff --git a/app/views/includes/header.html b/app/views/includes/header.html index 7b92876d..8881592f 100644 --- a/app/views/includes/header.html +++ b/app/views/includes/header.html @@ -11,6 +11,21 @@ active: (currentSection == "home") }), navigationItems) %} + + {% if data.currentMode == "reports" %} + {% set navigationItems = (navigationItems.push({ + href: "/pharmacies", + text: "Pharmacies", + active: (currentSection == "pharmacies") + }), navigationItems) %} + + {% set navigationItems = (navigationItems.push({ + href: "/users", + text: "Manage users", + active: (currentSection == "user-admin") + }), navigationItems) %} + {% endif %} + {% if currentOrganisation %} {% set navigationItems = (navigationItems.push({ href: "/record-vaccinations", diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html new file mode 100644 index 00000000..1fe6d32d --- /dev/null +++ b/app/views/pharmacies/index.html @@ -0,0 +1,58 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} + +{% block content %} +
+
+ +

Pharmacies

+ +

Add a new pharmacy or manage an existing pharmacy.

+ + {{ button({ + text: "Add pharmacy", + href: "/pharmacies/select" + }) }} + + + + + + + + + + + + + + {% for organisation in organisations %} + + + + + + + {% endfor %} + + +
Current pharmacies
+ Name + + Vaccines + + Users +
+ {{ organisation.name }} ({{ organisation.id}}) + + COVID-19 + + 1 + + Deactivate +
+
+
+ +{% endblock %} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html new file mode 100644 index 00000000..a50639f2 --- /dev/null +++ b/app/views/pharmacies/pharmacy.html @@ -0,0 +1,23 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} + +{% set pageName = organisation.name %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

{{ pageName }}

+ + +
+
+ +{% endblock %} diff --git a/app/views/pharmacies/select.html b/app/views/pharmacies/select.html new file mode 100644 index 00000000..0ff4eb6d --- /dev/null +++ b/app/views/pharmacies/select.html @@ -0,0 +1,186 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies" + }) }} +{% endblock %} + +{% block content %} +
+
+
+ + {% set items = [] %} + + {% for pharmacy in pharmacies %} + {% set items = (items.push({ + text: pharmacy.name + ", " + pharmacy.address.postcode + " (" + pharmacy.id + ")", + value: pharmacy.id + }), items) %} + {% endfor %} + + + {% call fieldset({ + legend: { + text: "Select pharmacies", + size: "l", + classes: "nhsuk-u-margin-bottom-5" + } + }) %} + + {% if (pharmacies | length) > 1 %} + {{ checkboxes({ + idPrefix: "pharmacy-select-all", + name: "pharmacyIds", + values: data.pharmacyIds, + formGroup: { + classes: "nhsuk-u-margin-bottom-2" + }, + items: [ + { + text: "Select all " + (pharmacies | length), + value: "", + attributes: { + "data-select-all": "true" + } + }, + { + divider: "or" + } + ] + }) }} + {% endif %} + + {% if (pharmacies | length) > 20 %} + {{ input({ + id: "pharmacy-search", + name: "pharmacySearch", + type: "search", + label: { + text: "Search" + }, + classes: "nhsuk-input--width-20", + attributes: { + "data-module": "app-filter-checkboxes" + }, + formGroup: { + classes: "nhsuk-u-margin-bottom-4" + } + }) }} + {% endif %} + + +
+ {{ checkboxes({ + id: "pharmacy-ids", + name: "pharmacyIds", + values: data.pharmacyIds, + items: items + }) }} +
+ + {% endcall %} + + + {{ button({ + text: "Continue" + }) }} + +
+
+
+ + + + +{% endblock %} From 41a628b6992d688ed36b570d9ddb4bbb3dd658d9 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 14:01:28 +0100 Subject: [PATCH 02/37] More work in progress --- app/data/session-data-defaults.js | 5 +- app/routes/pharmacies.js | 24 ++++++ app/views/includes/header.html | 4 +- app/views/pharmacies/check-selection.html | 58 ++++++++++++++ app/views/pharmacies/pharmacy.html | 65 +++++++++++++++ app/views/pharmacies/users/index.html | 52 ++++++++++++ app/views/pharmacies/users/new.html | 98 +++++++++++++++++++++++ 7 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 app/views/pharmacies/check-selection.html create mode 100644 app/views/pharmacies/users/index.html create mode 100644 app/views/pharmacies/users/new.html diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index c0027bf0..e06dba75 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -15,8 +15,9 @@ module.exports = { vaccineStock: vaccineStock, lists: [], nhsNumberKnown: "yes", - currentUserId: "2387441662601", - currentOrganisationId: "RW3", + currentUserId: "6424325235325", + currentOrganisationId: null, + currentMode: "reports", vaccinationsRecorded: vaccinationsRecorded, // These are the options for extracting CSV reports diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index e9375d92..f71ba910 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -39,6 +39,30 @@ module.exports = router => { }) }) + router.get('/pharmacies/check-selection', async (req, res) => { + const data = req.session.data + + let pharmacies = await getPharmaciesBelongingToOrganisation("P15J") + + pharmacies = pharmacies.filter((pharmacy) => { + return data.pharmacyIds.includes(pharmacy.id) + }).sort(sortByNameThenPostcode()) + + + res.render('pharmacies/check-selection', { + pharmacies + }) + }) + + router.get('/pharmacies/users',(req, res) => { + const data = req.session.data + const users = data.users.slice(10, 20) + + res.render('pharmacies/users/index', { + users + }) + }) + router.get('/pharmacies/:id', (req, res) => { const data = req.session.data diff --git a/app/views/includes/header.html b/app/views/includes/header.html index 8881592f..58e9a25a 100644 --- a/app/views/includes/header.html +++ b/app/views/includes/header.html @@ -20,9 +20,9 @@ }), navigationItems) %} {% set navigationItems = (navigationItems.push({ - href: "/users", + href: "/pharmacies/users", text: "Manage users", - active: (currentSection == "user-admin") + active: (currentSection == "pharmacies-users") }), navigationItems) %} {% endif %} diff --git a/app/views/pharmacies/check-selection.html b/app/views/pharmacies/check-selection.html new file mode 100644 index 00000000..e1596c75 --- /dev/null +++ b/app/views/pharmacies/check-selection.html @@ -0,0 +1,58 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} +{% set pageName = "Check your list of pharmacies" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/select" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

{{ pageName }}

+ +

You have selected {{ pharmacies | length | plural("pharmacy") }}.

+ + + + + + + + + + + {% for pharmacy in pharmacies %} + + + + + {% endfor %} + + +
Pharmacy
{{ pharmacy.name }}, {{ pharmacy.address.postcode }} ({{ pharmacy.id }}) + {% if pharmacies | length > 1 %} +
+ + {{ button({ + text: "Remove", + classes: "nhsuk-button--small nhsuk-button--secondary nhsuk-u-margin-bottom-0" + }) }} +
+ {% endif %} +
+ +
+ {{ button({ + text: "Continue" + }) }} +
+ +
+
+ +{% endblock %} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index a50639f2..7ce47a11 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -16,6 +16,71 @@

{{ pageName }}

+ {{ summaryList({ + rows: [ + { + key: { + text: "ODS code" + }, + value: { + text: organisation.id + } + }, + { + key: { + text: "Address" + }, + value: { + text: organisation.address.line1 + ", " + organisation.address.town + ", " + organisation.address.postcode + } + } + ] + }) }} + +

Users

+ + {{ button({ + text: "Add user" + }) }} + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Permission level + + Vaccinator + + Status +
+ Jane Smith + + Lead administrator + + Yes + + Active + +
diff --git a/app/views/pharmacies/users/index.html b/app/views/pharmacies/users/index.html new file mode 100644 index 00000000..bd940b52 --- /dev/null +++ b/app/views/pharmacies/users/index.html @@ -0,0 +1,52 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies-users" %} + +{% block content %} +
+
+ +

Users

+ +

Add a new user or manage an existing pharmacy.

+ + {{ button({ + text: "Add user", + href: "/pharmacies/users/new" + }) }} + + + + + + + + + + + + + {% for user in users %} + + + + + + {% endfor %} + + +
Current users
+ Name + + Email +
+ {{ user.firstName }} {{ user.lastName }} + + {{ user.email }} + + Deactivate +
+
+
+ +{% endblock %} diff --git a/app/views/pharmacies/users/new.html b/app/views/pharmacies/users/new.html new file mode 100644 index 00000000..c6ebdf50 --- /dev/null +++ b/app/views/pharmacies/users/new.html @@ -0,0 +1,98 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies-users" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/users" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

Add user

+ +
+ + {{ input({ + "label": { + "text": "First name" + }, + "id": "first-name", + "name": "firstName", + classes: "nhsuk-input--width-20", + value: data.firstName, + "errorMessage": { + "text": firstNameError + } if firstNameError + }) }} + + {{ input({ + "label": { + "text": "Last name" + }, + "id": "last-name", + "name": "lastName", + classes: "nhsuk-input--width-20", + value: data.lastName, + "errorMessage": { + "text": lastNameError + } if lastNameError + }) }} + + {{ input({ + "label": { + "text": "Email address", + size: "s" + }, + "id": "email", + "name": "email", + type: "email", + value: data.email + }) }} + + {{ checkboxes({ + idPrefix: "example", + name: "example", + fieldset: { + legend: { + text: "Which pharmacy do you want to add them to?", + size: "s", + isPageHeading: true + } + }, + items: [ + { + value: "all", + text: "All – add them as a super admin for Peak Pharmacy" + }, + { + divider: "or" + }, + { + text: "Peak Pharmacy (P141)" + }, + { + text: "Peak Pharmacy (P931)" + }, + { + text: "Peak Pharmacy (P291)" + }, + { + text: "etc" + } + ] + }) }} + + + {{ button({ + "text": "Continue" + }) }} +
+ +
+
+ +{% endblock %} From d319388ce83a1c4301e3c4ea05c9b0d3daf276ee Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 14:13:12 +0100 Subject: [PATCH 03/37] More work in progress --- app/routes/pharmacies.js | 10 +++ app/views/pharmacies/pharmacy.html | 2 +- app/views/pharmacies/users/index.html | 2 +- app/views/pharmacies/users/user.html | 100 ++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 app/views/pharmacies/users/user.html diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index f71ba910..bd6d3173 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -63,6 +63,16 @@ module.exports = router => { }) }) + router.get('/pharmacies/users/:id',(req, res) => { + const data = req.session.data + const id = req.params.id + const user = data.users.find((user) => user.id === id) + + res.render('pharmacies/users/user', { + user + }) + }) + router.get('/pharmacies/:id', (req, res) => { const data = req.session.data diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index 7ce47a11..4769b4a0 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -65,7 +65,7 @@

Users

- Jane Smith + Jane Smith Lead administrator diff --git a/app/views/pharmacies/users/index.html b/app/views/pharmacies/users/index.html index bd940b52..0c530fb2 100644 --- a/app/views/pharmacies/users/index.html +++ b/app/views/pharmacies/users/index.html @@ -33,7 +33,7 @@

Users

{% for user in users %} - {{ user.firstName }} {{ user.lastName }} + {{ user.firstName }} {{ user.lastName }} {{ user.email }} diff --git a/app/views/pharmacies/users/user.html b/app/views/pharmacies/users/user.html new file mode 100644 index 00000000..6c008800 --- /dev/null +++ b/app/views/pharmacies/users/user.html @@ -0,0 +1,100 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies-users" %} + +{% set pageName = user.firstName + " " + user.lastName %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/users" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

{{ pageName }}

+ + {{ summaryList({ + rows: [ + { + key: { + text: "Email" + }, + value: { + text: user.email + } + } + ] + }) }} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Pharmacies
+ Name + + Permission level + + Vaccinator + + Status +
+ Peak Pharmacy (P112) + + Lead administrator + + Yes + + Active + + Remove +
+ Peak Pharmacy (P9241) + + Recorder + + Yes + + Active + + Remove +
+ +

Deactivate user from all pharmacies

+ +
+
+ +{% endblock %} From 2c13df8d800a65f9f828d1e67e194a0fb0ae137a Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 14:28:55 +0100 Subject: [PATCH 04/37] Add lead administrators page --- app/routes/pharmacies.js | 9 ++++ app/views/pharmacies/add-lead-admins.html | 60 +++++++++++++++++++++++ app/views/pharmacies/check-selection.html | 1 + 3 files changed, 70 insertions(+) create mode 100644 app/views/pharmacies/add-lead-admins.html diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index bd6d3173..408bb722 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -63,6 +63,15 @@ module.exports = router => { }) }) + router.get('/pharmacies/add-lead-admins',(req, res) => { + const data = req.session.data + const users = data.users.slice(10, 20) + + res.render('pharmacies/add-lead-admins', { + users + }) + }) + router.get('/pharmacies/users/:id',(req, res) => { const data = req.session.data const id = req.params.id diff --git a/app/views/pharmacies/add-lead-admins.html b/app/views/pharmacies/add-lead-admins.html new file mode 100644 index 00000000..269a35b0 --- /dev/null +++ b/app/views/pharmacies/add-lead-admins.html @@ -0,0 +1,60 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} +{% set pageName = "Add lead administrators" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/check-selection" + }) }} +{% endblock %} + +{% block content %} +
+
+

{{ pageName }}

+ +

Who do you want to get be a lead administrator on RAVS for all {{ data.pharmacyIds | length }} pharmacies?

+ + {% set items = [] %} + + {% set items = (items.push({ + text: "Me (" + currentUser.firstName + " " + currentUser.lastName + ")", + value: user.id + }), items) %} + + {% for user in (users | sort(false, false, "firstName")) %} + {% set items = (items.push({ + text: user.firstName + " " + user.lastName, + value: user.id + }), items) %} + {% endfor %} + + {% set items = (items.push({ + divider: "or" + }), items) %} + + {% set items = (items.push({ + text: "No one, I’ll add lead administrators later", + value: "", + exclusive: true + }), items) %} + + {{ checkboxes({ + fieldset: { + legend: { + text: "Users", + size: "m" + } + }, + items: items + }) }} + + {{ button({ + text: "Continue" + }) }} + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/check-selection.html b/app/views/pharmacies/check-selection.html index e1596c75..9d99d841 100644 --- a/app/views/pharmacies/check-selection.html +++ b/app/views/pharmacies/check-selection.html @@ -48,6 +48,7 @@

{{ pageName }}

{{ button({ + href: "/pharmacies/add-lead-admins", text: "Continue" }) }}
From 93e50b2632e332fa933ca7219ae8d502f2b1a710 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 15:25:19 +0100 Subject: [PATCH 05/37] Some more progress --- app/views/home/index.html | 2 +- app/views/pharmacies/add-lead-admins.html | 153 ++++++++++++++++++++-- app/views/pharmacies/index.html | 14 +- app/views/pharmacies/pharmacy.html | 1 + app/views/pharmacies/select.html | 10 +- app/views/pharmacies/users/index.html | 4 - app/views/pharmacies/users/user.html | 13 +- 7 files changed, 167 insertions(+), 30 deletions(-) diff --git a/app/views/home/index.html b/app/views/home/index.html index 662883cd..a5a5e58f 100644 --- a/app/views/home/index.html +++ b/app/views/home/index.html @@ -13,7 +13,7 @@ {% include "includes/notification.html" %} -

{% if currentOrganisation %}{{ currentOrganisation.name }}{% else %}Peak pharmacies{% endif %}

+

{% if currentOrganisation %}{{ currentOrganisation.name }}{% else %}PCT Healthcare{% endif %}

{% if currentOrganisation and totalVaccinationsRecorded == 0 %} diff --git a/app/views/pharmacies/add-lead-admins.html b/app/views/pharmacies/add-lead-admins.html index 269a35b0..e555f8d6 100644 --- a/app/views/pharmacies/add-lead-admins.html +++ b/app/views/pharmacies/add-lead-admins.html @@ -14,13 +14,69 @@

{{ pageName }}

-

Who do you want to get be a lead administrator on RAVS for all {{ data.pharmacyIds | length }} pharmacies?

+
+ +

Who do you want to assign as lead administrators for all {{ data.pharmacyIds | length }} pharmacies?

+ + {% call details({ + summaryText: "What is a lead administrator?", + classes: "nhsuk-expander" + }) %} +

Lead administrators are responsible for setting up the pharmacy's users and vaccines.

+ +

They will get a Welcome email telling them how to log into RAVS. Once logged in at ABC pharmacy, they will be able to:

+ +
    +
  • add more users, with different permission levels
  • +
  • add vaccines
  • +
  • create reports
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Permission levelRecord and edit vaccinationsAdd and manage vaccinesCreate reportsAdd and manage users
Lead administratorYesYesYesYes
AdministratorYesYesYesNo
RecorderYesNoNoNo
+ {% endcall %} {% set items = [] %} {% set items = (items.push({ text: "Me (" + currentUser.firstName + " " + currentUser.lastName + ")", - value: user.id + value: user.id, + hint: { + text: "Do not add yourself if you do not need to record vaccinations at these pharmacies" + } }), items) %} {% for user in (users | sort(false, false, "firstName")) %} @@ -30,30 +86,101 @@

{{ pageName }}

}), items) %} {% endfor %} - {% set items = (items.push({ - divider: "or" - }), items) %} - - {% set items = (items.push({ - text: "No one, I’ll add lead administrators later", - value: "", - exclusive: true - }), items) %} - {{ checkboxes({ fieldset: { legend: { - text: "Users", + text: "Existing lead administrators", size: "m" } }, items: items }) }} +
+ {{ button({ + id: "add-lead-admin", + text: "Add a new lead administrator", + type: "button", + classes: "nhsuk-button--secondary", + attributes: { + "data-add-another-add": "true" + } + }) }} +
+ + + {{ button({ text: "Continue" }) }} +
+
diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html index 1fe6d32d..ecf76961 100644 --- a/app/views/pharmacies/index.html +++ b/app/views/pharmacies/index.html @@ -8,15 +8,15 @@

Pharmacies

-

Add a new pharmacy or manage an existing pharmacy.

+

Add new pharmacies or manage an existing pharmacy.

{{ button({ - text: "Add pharmacy", + text: "Add pharmacies", href: "/pharmacies/select" }) }} - + - + @@ -39,14 +39,14 @@

Pharmacies

{{ organisation.name }} ({{ organisation.id}}) - {% endfor %} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index 4769b4a0..23811652 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -82,6 +82,7 @@

Users

Current pharmaciesCurrent pharmacies ({{ organisations | length }})
@@ -28,7 +28,7 @@

Pharmacies

Users
- COVID-19 + COVID-19, flu 1 +
+

Deactivate this pharmacy

diff --git a/app/views/pharmacies/select.html b/app/views/pharmacies/select.html index 0ff4eb6d..11aa9abe 100644 --- a/app/views/pharmacies/select.html +++ b/app/views/pharmacies/select.html @@ -1,6 +1,7 @@ {% extends 'layout.html' %} {% set currentSection = "pharmacies" %} +{% set pageName = "Add pharmacies" %} {% block beforeContent %} {{ backLink({ @@ -13,6 +14,13 @@
+

{{ pageName }}

+ +

There are {{ (pharmacies | length) + 7 }} active pharmacies in your company according to the NHS Organisation Data Service (ODS).

+ +

{{ (pharmacies | length) }} of them are not yet using RAVS and can be added below.

+ + {% set items = [] %} {% for pharmacy in pharmacies %} @@ -26,7 +34,7 @@ {% call fieldset({ legend: { text: "Select pharmacies", - size: "l", + size: "m", classes: "nhsuk-u-margin-bottom-5" } }) %} diff --git a/app/views/pharmacies/users/index.html b/app/views/pharmacies/users/index.html index 0c530fb2..c7c66a3c 100644 --- a/app/views/pharmacies/users/index.html +++ b/app/views/pharmacies/users/index.html @@ -25,7 +25,6 @@

Users

Email - @@ -38,9 +37,6 @@

Users

{{ user.email }} - - Deactivate - {% endfor %} diff --git a/app/views/pharmacies/users/user.html b/app/views/pharmacies/users/user.html index 6c008800..50bfb9a3 100644 --- a/app/views/pharmacies/users/user.html +++ b/app/views/pharmacies/users/user.html @@ -35,11 +35,11 @@

{{ pageName }}

- + @@ -86,12 +86,17 @@

{{ pageName }}

Active
PharmaciesAccess and permission levels
- Name + Pharmacy Permission level @@ -69,7 +69,7 @@

{{ pageName }}

Active
- Remove + Deactivate
- Remove + Deactivate
+ {{ button({ + text: "Add to another pharmacy", + classes: "nhsuk-button--secondary" + }) }} +

Deactivate user from all pharmacies

From e4a8c67d65b2868541b35f04c0be197cf6e8842d Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 18:07:37 +0100 Subject: [PATCH 06/37] Update add another so that none are shown by default --- app/assets/javascript/add-another.js | 37 ++++++++++++++++++----- app/views/pharmacies/add-lead-admins.html | 24 +++++---------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/assets/javascript/add-another.js b/app/assets/javascript/add-another.js index cfb86296..23844906 100644 --- a/app/assets/javascript/add-another.js +++ b/app/assets/javascript/add-another.js @@ -14,6 +14,7 @@ import { Component } from 'nhsuk-frontend' * - Add `data-add-another-item="N"` to each item section (where N is the item index: 1, 2, 3, etc.) * - Add `data-add-another-add` to the "Add another" button (hidden by default) * - Add `data-add-another-remove="N"` to the "Remove" button within each section (hidden by default) + * - Optionally add `data-add-another-min="0"` to allow starting with no items visible (default is 1) * * @augments Component */ @@ -29,11 +30,13 @@ export class AddAnother extends Component { this.$items = Array.from(this.$root.querySelectorAll('[data-add-another-item]')) this.$addButton = this.$root.querySelector('[data-add-another-add]') this.$addButtonWrapper = this.$addButton?.closest('.nhsuk-button-group') + this.minItems = parseInt(this.$root.dataset.addAnotherMin ?? '1', 10) this.initializeItemVisibility() this.setupAddButton() this.setupRemoveButtons() this.updateAddButtonVisibility() + this.updateAddButtonText() this.updateRemoveButtonVisibility() } @@ -71,17 +74,18 @@ export class AddAnother extends Component { */ initializeItemVisibility() { // Find the last item with values - let lastFilledIndex = 0 + let lastFilledIndex = -1 this.$items.forEach(($item, index) => { if (this.hasInputValues($item)) { lastFilledIndex = index } }) - // Show items up to and including the last filled one (minimum 1) + // Show items up to and including the last filled one (respecting minItems) // Hide all items after that + const minVisibleIndex = this.minItems - 1 this.$items.forEach(($item, index) => { - if (index <= lastFilledIndex) { + if (index <= lastFilledIndex || index <= minVisibleIndex) { $item.hidden = false } else { $item.hidden = true @@ -152,6 +156,7 @@ export class AddAnother extends Component { } this.updateAddButtonVisibility() + this.updateAddButtonText() this.updateRemoveButtonVisibility() } @@ -163,8 +168,8 @@ export class AddAnother extends Component { removeItem(index) { const visibleItems = this.getVisibleItems() - // Don't remove if only one item is visible - if (visibleItems.length <= 1) { + // Don't remove if at minimum items + if (visibleItems.length <= this.minItems) { return } @@ -187,16 +192,34 @@ export class AddAnother extends Component { } this.updateAddButtonVisibility() + this.updateAddButtonText() this.updateRemoveButtonVisibility() } + /** + * Update the add button text based on number of visible items + * Uses data-add-another-text-first for the first item, + * data-add-another-text-another for subsequent items + */ + updateAddButtonText() { + if (!this.$addButton) return + + const firstText = this.$addButton.dataset.addAnotherTextFirst + const anotherText = this.$addButton.dataset.addAnotherTextAnother + + if (!firstText || !anotherText) return + + const visibleItems = this.getVisibleItems() + this.$addButton.textContent = visibleItems.length === 0 ? firstText : anotherText + } + /** * Update visibility of remove buttons based on number of visible items - * Remove buttons should only be visible when there are 2+ items + * Remove buttons should only be visible when there are more than minItems */ updateRemoveButtonVisibility() { const visibleItems = this.getVisibleItems() - const showRemoveButtons = visibleItems.length >= 2 + const showRemoveButtons = visibleItems.length > this.minItems this.$items.forEach($item => { const $removeButton = $item.querySelector('[data-add-another-remove]') diff --git a/app/views/pharmacies/add-lead-admins.html b/app/views/pharmacies/add-lead-admins.html index e555f8d6..c8e7da2c 100644 --- a/app/views/pharmacies/add-lead-admins.html +++ b/app/views/pharmacies/add-lead-admins.html @@ -14,7 +14,7 @@

{{ pageName }}

- +

Who do you want to assign as lead administrators for all {{ data.pharmacyIds | length }} pharmacies?

@@ -97,23 +97,13 @@

{{ pageName }}

}) }}
- {{ button({ - id: "add-lead-admin", - text: "Add a new lead administrator", - type: "button", - classes: "nhsuk-button--secondary", - attributes: { - "data-add-another-add": "true" - } - }) }} -
- +
{{ button({ From e08d33392a66fd0a0647d7f8885c596b6c70ec79 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 24 Apr 2026 18:15:07 +0100 Subject: [PATCH 07/37] Add a user to a pharmacy --- app/routes/pharmacies.js | 12 +++ app/views/pharmacies/add-users.html | 119 ++++++++++++++++++++++++++++ app/views/pharmacies/pharmacy.html | 3 +- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 app/views/pharmacies/add-users.html diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 408bb722..8e0ecfc6 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -83,6 +83,18 @@ module.exports = router => { }) + router.get('/pharmacies/:id/add-users',(req, res) => { + const data = req.session.data + const users = data.users.slice(10, 20) + const id = req.params.id + const organisation = data.organisations.find((organisation) => organisation.id === id) + + res.render('pharmacies/add-users', { + users, + organisation + }) + }) + router.get('/pharmacies/:id', (req, res) => { const data = req.session.data const id = req.params.id diff --git a/app/views/pharmacies/add-users.html b/app/views/pharmacies/add-users.html new file mode 100644 index 00000000..09ae6b96 --- /dev/null +++ b/app/views/pharmacies/add-users.html @@ -0,0 +1,119 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} +{% set pageName = "Add user to " + organisation.name %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/" + organisation.id + }) }} +{% endblock %} + +{% block content %} +
+
+

{{ pageName }}

+ +

You can add an existing user to the pharmacy, or invite a new user.

+ + + + {% set items = [] %} + + {% for user in (users | sort(false, false, "firstName")) %} + {% set items = (items.push({ + text: user.firstName + " " + user.lastName, + value: user.id + }), items) %} + {% endfor %} + + {{ checkboxes({ + fieldset: { + legend: { + text: "Existing users", + size: "m" + } + }, + items: items + }) }} + +
+ + {# Ordinal suffixes for numbers 1-10 #} + + {% for index in range(0, 1) %} +
+

New user details

+ + + + {{ input({ + label: { + text: "First name" + }, + id: "first-name-" + (index + 1), + name: "firstName", + classes: "nhsuk-input--width-20", + value: data.firstName[index] + }) }} + + {{ input({ + label: { + text: "Last name" + }, + id: "last-name-" + (index + 1), + name: "lastName", + classes: "nhsuk-input--width-20", + value: data.lastName[index] + }) }} + + {{ input({ + label: { + text: "Email address" + }, + hint: { + html: "Use a personal nhs.net email, not a pharmacy email.
For example, joe.bloggs1@nhs.net" + }, + id: "email-" + (index + 1), + name: "email", + type: "email", + value: data.email[index] + }) }} +
+ {% endfor %} + + + + + {{ button({ + text: "Continue" + }) }} + + + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index 23811652..1610e168 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -40,7 +40,8 @@

{{ pageName }}

Users

{{ button({ - text: "Add user" + href: "/pharmacies/" + organisation.id + "/add-users", + text: "Add users" }) }} From 339a3a1681b2dd10c4f818d67bbfe30fc8c50b43 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 30 Apr 2026 11:13:20 +0100 Subject: [PATCH 08/37] Make more of the pages work --- app/data/users.js | 16 ++- app/routes/pharmacies.js | 108 ++++++++++++++- app/views/pharmacies/add-user-check.html | 125 ++++++++++++++++++ .../pharmacies/add-user-permission-level.html | 96 ++++++++++++++ app/views/pharmacies/add-user.html | 92 +++++++++++++ app/views/pharmacies/add-users.html | 119 ----------------- app/views/pharmacies/check-selection.html | 4 +- app/views/pharmacies/index.html | 7 +- app/views/pharmacies/pharmacy.html | 36 +++-- 9 files changed, 461 insertions(+), 142 deletions(-) create mode 100644 app/views/pharmacies/add-user-check.html create mode 100644 app/views/pharmacies/add-user-permission-level.html create mode 100644 app/views/pharmacies/add-user.html delete mode 100644 app/views/pharmacies/add-users.html diff --git a/app/data/users.js b/app/data/users.js index f3b4d98c..0ef9d0a6 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -316,12 +316,26 @@ module.exports = [ } ] }, + { + "id": "34634617277", + "email": "peter.orange@nhs.net", + "organisations": [ + { + "id": "RCY", + "permissionLevel": "Recorder", + "status": "Active", + "vaccinator": true + } + ], + "firstName": "Peter", + "lastName": "Orange" + }, { "id": "1394978032564", "email": "ocean.merritt@nhs.net", "organisations": [ { - "id": "RCY", + "id": "FR4V56", "permissionLevel": "Recorder", "status": "Active", "vaccinator": true diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 8e0ecfc6..4014a1e0 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -19,9 +19,19 @@ module.exports = router => { const organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) + let organisationUserCounts = {} + + for (const organisation of organisations) { + organisationUserCounts[organisation.id] = data.users + .filter((user) => (user.organisations || []) + .find((orgPermission) => orgPermission.id === organisation.id) + ).length + } + res.render('pharmacies/index', { - organisations + organisations, + organisationUserCounts }) }) @@ -83,28 +93,116 @@ module.exports = router => { }) - router.get('/pharmacies/:id/add-users',(req, res) => { + router.get('/pharmacies/:id/add-user',(req, res) => { const data = req.session.data const users = data.users.slice(10, 20) const id = req.params.id const organisation = data.organisations.find((organisation) => organisation.id === id) - res.render('pharmacies/add-users', { + res.render('pharmacies/add-user', { users, organisation }) }) + router.get('/pharmacies/:id/add-user-permission-level',(req, res) => { + const data = req.session.data + const id = req.params.id + const organisation = data.organisations.find((organisation) => organisation.id === id) + let existingUser + + if (data.userId) { + existingUser = data.users.find((user) => user.id === data.userId) + } + + res.render('pharmacies/add-user-permission-level', { + organisation, + existingUser + }) + }) + + router.get('/pharmacies/:id/add-user-check',(req, res) => { + const data = req.session.data + const id = req.params.id + const organisation = data.organisations.find((organisation) => organisation.id === id) + let existingUser + + if (data.userId) { + existingUser = data.users.find((user) => user.id === data.userId) + } + + res.render('pharmacies/add-user-check', { + organisation, + existingUser + }) + }) + + router.get('/pharmacies/:id/user-added',(req, res) => { + const data = req.session.data + const id = req.params.id + const organisation = data.organisations.find((organisation) => organisation.id === id) + let existingUser + + if (data.userId) { + existingUser = data.users.find((user) => user.id === data.userId) + + existingUser.organisations.push({ + id: organisation.id, + status: 'Active', + vaccinator: (data.vaccinator === 'yes'), + permissionLevel: data.permissionLevel + }) + } else { + + data.users.push({ + id: Math.floor(Math.random() * 10000000).toString(), + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + organisations: [ + { + id: organisation.id, + status: 'Invited', + vaccinator: (data.vaccinator === 'yes'), + permissionLevel: data.permissionLevel + } + ] + }) + + } + + // Reset data + req.session.data.email = '' + req.session.data.firstName = '' + req.session.data.lastName = '' + req.session.data.permissionLevel = '' + req.session.data.vaccinator = '' + + res.redirect(`/pharmacies/${organisation.id}?added=true`) + }) + router.get('/pharmacies/:id', (req, res) => { const data = req.session.data const id = req.params.id const organisation = data.organisations.find((organisation) => organisation.id === id) + const userOrganisationPermissions = {} + + const users = data.users + .filter((user) => (user.organisations || []) + .find((orgPermission) => orgPermission.id === organisation.id) + ) + + for (const user of users) { + userOrganisationPermissions[user.id] = user.organisations.find((userOrganisation) => userOrganisation.id === organisation.id) + } + res.render('pharmacies/pharmacy', { - organisation + organisation, + users, + userOrganisationPermissions }) }) - } diff --git a/app/views/pharmacies/add-user-check.html b/app/views/pharmacies/add-user-check.html new file mode 100644 index 00000000..b7e8c577 --- /dev/null +++ b/app/views/pharmacies/add-user-check.html @@ -0,0 +1,125 @@ +{% extends 'layout.html' %} + +{% set pageName = "Check" %} +{% set currentSection = "pharmacies" %} + + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/" + organisation.id + "/add-user-permission-level", + text: "Back" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

Check and {% if existingUserWithSameEmail %}reactivate{% else %}add{% endif %} user to {{ organisation.name }} ({{ organisation.id }})

+ + {% set nameText %} + {% if existingUser %} + {{ existingUser.firstName }} {{ existingUser.lastName }} + {% else %} + {{ data.firstName }} {{ data.lastName }} + {% endif %} + {% endset %} + + {{ summaryList({ + rows: [ + { + key: { + text: "Name" + }, + value: { + text: nameText + }, + actions: { + items: [ + { + href: "/pharmacies/" + organisation.id + "/add-user", + text: "Change", + visuallyHiddenText: "name" + } + ] + } + }, + { + key: { + text: "Email address" + }, + value: { + html: (existingUser.email if existingUser else data.email) + }, + actions: { + items: [ + { + href: "/pharmacies/" + organisation.id + "/add-user", + text: "Change", + visuallyHiddenText: "email address" + } + ] + } + }, + { + key: { + text: "Vaccinator" + }, + value: { + html: (data.vaccinator | capitalize) + }, + actions: { + items: [ + { + href: "/pharmacies/" + organisation.id + "/add-user-permission-level", + text: "Change", + visuallyHiddenText: "vaccinator status" + } + ] + } + }, + { + key: { + text: "Permission level" + }, + value: { + html: data.permissionLevel + }, + actions: { + items: [ + { + href: "/pharmacies/" + organisation.id + "/add-user-permission-level", + text: "Change", + visuallyHiddenText: "permission level" + } + ] + } + } + ] + }) }} + + + {% if existingUser %} +

{{ existingUser.firstName }} will be sent this email:

+ {% else %} +

{{ data.firstName }} will receive this welcome email with information about activating an account:

+ {% endif %} + +
+ {% if existingUserWithSameEmail %} + {% include "user-admin/_reactivation-email.html" %} + {% else %} + {% include "user-admin/_welcome-email.html" %} + {% endif %} +
+ +
+ {{ button({ + "text": "Confirm and send" + }) }} + + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/add-user-permission-level.html b/app/views/pharmacies/add-user-permission-level.html new file mode 100644 index 00000000..bf0ee1cc --- /dev/null +++ b/app/views/pharmacies/add-user-permission-level.html @@ -0,0 +1,96 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} +{% set pageName = "Adding user to " + organisation.name %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/" + organisation.id + "/add-user" + }) }} +{% endblock %} + +{% block content %} +
+
+

+ Adding + {% if existingUser %} + {{ existingUser.firstName }} {{ existingUser.lastName }} + {% else %} + {{ data.firstName }} {{ data.lastName }} + {% endif %} + to {{ organisation.name }} ({{ organisation.id }}) +

+ +
+ + {{ radios({ + name: "permissionLevel", + idPrefix: "permission-level", + fieldset: { + legend: { + text: "Permission level", + size: "s" + } + }, + value: data.permissionLevel, + items: [ + { + value: "Recorder", + text: "Recorder", + hint: { + text: "Record vaccinations only" + } + }, + { + value: "Administrator", + text: "Administrator", + hint: { + text: "Record vaccinations, create reports and manage vaccines" + } + }, + { + value: "Lead administrator", + text: "Lead administrator", + hint: { + text: "Record vaccinations, create reports, manage vaccines and users" + } + } + ] + }) }} + + {{ radios({ + "name": "vaccinator", + "fieldset": { + "legend": { + "text": "Are they a vaccinator?", + size: "s" + } + }, + hint: { + text: "Vaccination records include the name of the person who gave the vaccination" + }, + value: data.vaccinator, + "items": [ + { + "value": "yes", + "text": "Yes", + id: "vaccinator" + }, + { + "value": "no", + "text": "No" + } + ] + }) }} + + + {{ button({ + text: "Continue" + }) }} + + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/add-user.html b/app/views/pharmacies/add-user.html new file mode 100644 index 00000000..547aa66b --- /dev/null +++ b/app/views/pharmacies/add-user.html @@ -0,0 +1,92 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies" %} +{% set pageName = "Add user to " + organisation.name + " (" + organisation.id + ")" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/" + organisation.id + }) }} +{% endblock %} + +{% block content %} +
+
+

{{ pageName }}

+ +

You can add an existing user to the pharmacy, or invite a new user.

+ +
+ + {% set items = [] %} + + {% for user in (users | sort(false, false, "firstName")) %} + {% set items = (items.push({ + text: user.firstName + " " + user.lastName, + value: user.id + }), items) %} + {% endfor %} + + {% set items = (items.push({ + divider: "or" + }), items) %} + + {% set newUserHtml %} + {{ input({ + label: { + text: "First name" + }, + name: "firstName", + classes: "nhsuk-input--width-20", + value: data.firstName + }) }} + + {{ input({ + label: { + text: "Last name" + }, + name: "lastName", + classes: "nhsuk-input--width-20", + value: data.lastName + }) }} + + {{ input({ + label: { + text: "Email address" + }, + name: "email", + type: "email", + value: data.email + }) }} + {% endset %} + + {% set items = (items.push({ + text: "Add a new user", + value: "add-new", + conditional: { + html: newUserHtml + } + }), items) %} + + {{ radios({ + name: "userId", + value: data.userId, + fieldset: { + legend: { + text: "User", + size: "m" + } + }, + items: items + }) }} + + {{ button({ + text: "Continue" + }) }} + + + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/add-users.html b/app/views/pharmacies/add-users.html deleted file mode 100644 index 09ae6b96..00000000 --- a/app/views/pharmacies/add-users.html +++ /dev/null @@ -1,119 +0,0 @@ -{% extends 'layout.html' %} - -{% set currentSection = "pharmacies" %} -{% set pageName = "Add user to " + organisation.name %} - -{% block beforeContent %} - {{ backLink({ - href: "/pharmacies/" + organisation.id - }) }} -{% endblock %} - -{% block content %} -
-
-

{{ pageName }}

- -

You can add an existing user to the pharmacy, or invite a new user.

- -
- - {% set items = [] %} - - {% for user in (users | sort(false, false, "firstName")) %} - {% set items = (items.push({ - text: user.firstName + " " + user.lastName, - value: user.id - }), items) %} - {% endfor %} - - {{ checkboxes({ - fieldset: { - legend: { - text: "Existing users", - size: "m" - } - }, - items: items - }) }} - -
- - {# Ordinal suffixes for numbers 1-10 #} - - {% for index in range(0, 1) %} -
-

New user details

- - - - {{ input({ - label: { - text: "First name" - }, - id: "first-name-" + (index + 1), - name: "firstName", - classes: "nhsuk-input--width-20", - value: data.firstName[index] - }) }} - - {{ input({ - label: { - text: "Last name" - }, - id: "last-name-" + (index + 1), - name: "lastName", - classes: "nhsuk-input--width-20", - value: data.lastName[index] - }) }} - - {{ input({ - label: { - text: "Email address" - }, - hint: { - html: "Use a personal nhs.net email, not a pharmacy email.
For example, joe.bloggs1@nhs.net" - }, - id: "email-" + (index + 1), - name: "email", - type: "email", - value: data.email[index] - }) }} -
- {% endfor %} - - - - - {{ button({ - text: "Continue" - }) }} - - - - -
-
-{% endblock %} diff --git a/app/views/pharmacies/check-selection.html b/app/views/pharmacies/check-selection.html index 9d99d841..cefd8cd4 100644 --- a/app/views/pharmacies/check-selection.html +++ b/app/views/pharmacies/check-selection.html @@ -48,8 +48,8 @@

{{ pageName }}

{{ button({ - href: "/pharmacies/add-lead-admins", - text: "Continue" + href: "/pharmacies", + text: "Confirm and add" }) }} diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html index ecf76961..8558a7d5 100644 --- a/app/views/pharmacies/index.html +++ b/app/views/pharmacies/index.html @@ -28,8 +28,6 @@

Pharmacies

- - @@ -42,11 +40,8 @@

Pharmacies

COVID-19, flu - {% endfor %} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index 1610e168..01813d85 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -33,15 +33,27 @@

{{ pageName }}

value: { text: organisation.address.line1 + ", " + organisation.address.town + ", " + organisation.address.postcode } + }, + { + key: { + text: "Vaccines enabled" + }, + value: { + text: "COVID-19, flu" + } } ] }) }}

Users

+ + +
+
{{ button({ - href: "/pharmacies/" + organisation.id + "/add-users", - text: "Add users" + href: "/pharmacies/" + organisation.id + "/add-user", + text: "Add user" }) }}
Users
- 1 + {{ organisationUserCounts[organisation.id] }}
@@ -59,27 +71,33 @@

Users

- - + + {% for user in users %} - + {% endfor %}
Status + Actions +
- Jane Smith + + {{ user.firstName }} {{ user.lastName }} + - Lead administrator + {{ userOrganisationPermissions[user.id].permissionLevel }} - Yes + {{ "Yes" if userOrganisationPermissions[user.id].vaccinator else "No" }} - Active + {{ userOrganisationPermissions[user.id].status }} + + Changepermission level for {{ user.firstName }} {{ user.lastName }}
From 6916ff9b40e52dc71b5897595c29ff752a6752d5 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 30 Apr 2026 14:17:29 +0100 Subject: [PATCH 09/37] Add ability to add pharmacies --- app/data/organisations.js | 33 ++++++++++++++++++ app/data/users.js | 11 +++--- app/routes/pharmacies.js | 41 ++++++++++++++++++++++- app/views/pharmacies/check-selection.html | 3 +- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/app/data/organisations.js b/app/data/organisations.js index 0427c632..962b698a 100644 --- a/app/data/organisations.js +++ b/app/data/organisations.js @@ -8484,9 +8484,22 @@ module.exports = [ postcode: "LS2 7UE" } }, + { + id: 'P15951', + name: 'MediCare Health Ltd', + address: { + line1: '28 High Street', + town: 'London', + postcode: 'N5 1PL' + }, + type: 'Pharmacy HQ', + status: 'Active', + region: "Y56" + }, { id: 'FX9141', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '28 High Street', town: 'London', @@ -8514,6 +8527,7 @@ module.exports = [ { id: 'FX4825', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '104 Bow Street', town: 'London', @@ -8541,6 +8555,7 @@ module.exports = [ { id: 'FX7314', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '99 Flowers Road', town: 'London', @@ -8568,6 +8583,7 @@ module.exports = [ { id: 'FX9151', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '12 Church Road', town: 'London', @@ -8595,6 +8611,7 @@ module.exports = [ { id: 'FQ2525', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '1 Granary Road', town: 'London', @@ -8622,6 +8639,7 @@ module.exports = [ { id: 'FW1941', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '8 Manchester Road', town: 'London', @@ -8649,6 +8667,7 @@ module.exports = [ { id: 'FP9824', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '12 John Robinson Road', town: 'London', @@ -8676,6 +8695,7 @@ module.exports = [ { id: 'FP1812', name: 'MediCare Pharmacy', + companyId: 'P15951', address: { line1: '18 Church Road', town: 'London', @@ -8703,6 +8723,7 @@ module.exports = [ { id: "FA7K23", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "45 High Street", town: "Manchester", @@ -8730,6 +8751,7 @@ module.exports = [ { id: "FG2R56", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "78 Queen Street", town: "Birmingham", @@ -8757,6 +8779,7 @@ module.exports = [ { id: "FH9P12", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "23 Station Road", town: "Leeds", @@ -8784,6 +8807,7 @@ module.exports = [ { id: "FJ4M89", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "156 Market Street", town: "Liverpool", @@ -8811,6 +8835,7 @@ module.exports = [ { id: "FK5N34", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "89 Park Lane", town: "Bristol", @@ -8838,6 +8863,7 @@ module.exports = [ { id: "FL7Q67", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "34 Castle Street", town: "Edinburgh", @@ -8865,6 +8891,7 @@ module.exports = [ { id: "FM8R23", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "67 Main Street", town: "Glasgow", @@ -8892,6 +8919,7 @@ module.exports = [ { id: "FN9S45", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "112 Church Road", town: "Cardiff", @@ -8919,6 +8947,7 @@ module.exports = [ { id: "FP2T78", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "45 Bridge Street", town: "Newcastle", @@ -8946,6 +8975,7 @@ module.exports = [ { id: "FQ3U12", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "28 Victoria Road", town: "Sheffield", @@ -8973,6 +9003,7 @@ module.exports = [ { id: "FR4V56", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "91 Oxford Street", town: "Nottingham", @@ -9000,6 +9031,7 @@ module.exports = [ { id: "FS5W89", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "15 King Street", town: "Leicester", @@ -9027,6 +9059,7 @@ module.exports = [ { id: "FT6X34", name: "MediCare Pharmacy", + companyId: 'P15951', address: { line1: "203 London Road", town: "Southampton", diff --git a/app/data/users.js b/app/data/users.js index 0ef9d0a6..673bec4c 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -174,8 +174,8 @@ module.exports = [ "firstName": "Jeremy", "lastName": "Blue" }, - // Amanda White is a lead admin for - // a chain of pharmacies + // Amanda White is a group administrator for the + // MediCare Health Ltd chain of pharmacies { "firstName": "Amanda", "lastName": "White", @@ -183,10 +183,9 @@ module.exports = [ "email": "amanda.white@nhs.net", "organisations": [ { - "id": "FX9141", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false + "id": "P15951", + "permissionLevel": "Group administrator", + "status": "Active" }, { "id": "FX4825", diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 4014a1e0..d8c3ec12 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -15,9 +15,13 @@ module.exports = router => { const data = req.session.data const currentUser = res.locals.currentUser + // TODO: get this from the current login + // rather than hardcode it + const companyId = 'P15951' + const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) - const organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) + const organisations = data.organisations.filter((organisation) => organisation.companyId === companyId).sort(sortByNameThenPostcode()) let organisationUserCounts = {} @@ -64,6 +68,41 @@ module.exports = router => { }) }) + // Actually add the pharmacies + router.post('/pharmacies/added', async (req, res) => { + const data = req.session.data + + // TODO: get this from the current login + // rather than hardcode it + const companyId = 'P15951' + + let pharmacies = await getPharmaciesBelongingToOrganisation("P15J") + + pharmacies = pharmacies.filter((pharmacy) => { + return data.pharmacyIds.includes(pharmacy.id) + }).sort(sortByNameThenPostcode()) + + for (const pharmacy of pharmacies) { + + data.organisations.push({ + id: pharmacy.id, + name: pharmacy.name, + type: 'Community Pharmacy', + companyId: companyId, + status: 'Active', + vaccines: [], + sites: [ + { + id: pharmacy.id, + name: pharmacy.name + } + ] + }) + } + + res.redirect('/pharmacies?added=true') + }) + router.get('/pharmacies/users',(req, res) => { const data = req.session.data const users = data.users.slice(10, 20) diff --git a/app/views/pharmacies/check-selection.html b/app/views/pharmacies/check-selection.html index cefd8cd4..f95b8cd4 100644 --- a/app/views/pharmacies/check-selection.html +++ b/app/views/pharmacies/check-selection.html @@ -46,9 +46,8 @@

{{ pageName }}

-
+ {{ button({ - href: "/pharmacies", text: "Confirm and add" }) }}
From 0ff48306740db8035881b786d0de6addef3f374f Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 30 Apr 2026 14:19:03 +0100 Subject: [PATCH 10/37] Fix address --- app/routes/pharmacies.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index d8c3ec12..e02003c5 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -89,6 +89,7 @@ module.exports = router => { name: pharmacy.name, type: 'Community Pharmacy', companyId: companyId, + address: pharmacy.address, status: 'Active', vaccines: [], sites: [ From 388e6eb9392fa705c672a3aa27c66f26630076bb Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 30 Apr 2026 14:44:36 +0100 Subject: [PATCH 11/37] Fix adding users to pharmacies --- app/routes/pharmacies.js | 13 ++++++++----- app/views/pharmacies/index.html | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index e02003c5..4ebde354 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -14,6 +14,7 @@ module.exports = router => { router.get('/pharmacies', (req, res) => { const data = req.session.data const currentUser = res.locals.currentUser + const added = req.query.added // TODO: get this from the current login // rather than hardcode it @@ -35,7 +36,8 @@ module.exports = router => { res.render('pharmacies/index', { organisations, - organisationUserCounts + organisationUserCounts, + added }) }) @@ -101,7 +103,7 @@ module.exports = router => { }) } - res.redirect('/pharmacies?added=true') + res.redirect(`/pharmacies?added=${pharmacies.length}`) }) router.get('/pharmacies/users',(req, res) => { @@ -181,11 +183,11 @@ module.exports = router => { const data = req.session.data const id = req.params.id const organisation = data.organisations.find((organisation) => organisation.id === id) - let existingUser + const existingUser = data.users.find((user) => user.id === data.userId) - if (data.userId) { - existingUser = data.users.find((user) => user.id === data.userId) + if (existingUser) { + existingUser.organisations ||= [] existingUser.organisations.push({ id: organisation.id, status: 'Active', @@ -212,6 +214,7 @@ module.exports = router => { } // Reset data + req.session.data.userId = '' req.session.data.email = '' req.session.data.firstName = '' req.session.data.lastName = '' diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html index 8558a7d5..d16445c5 100644 --- a/app/views/pharmacies/index.html +++ b/app/views/pharmacies/index.html @@ -6,6 +6,20 @@
+ {% if added %} + {% set html %} +

+ {{ added }} pharmacies added +

+

You can now add or assign users to these pharmacies.

+ {% endset %} + + {{ notificationBanner({ + html: html, + type: "success" + }) }} + {% endif %} +

Pharmacies

Add new pharmacies or manage an existing pharmacy.

From 9ece65934b4805a212df51128da39962101e6b4a Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Thu, 30 Apr 2026 15:05:44 +0100 Subject: [PATCH 12/37] Extract checkbox count javascript --- .../javascript/checkbox-selected-count.js | 61 +++++++++++++++++++ app/assets/javascript/main.js | 2 + app/views/pharmacies/select.html | 21 +------ 3 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 app/assets/javascript/checkbox-selected-count.js diff --git a/app/assets/javascript/checkbox-selected-count.js b/app/assets/javascript/checkbox-selected-count.js new file mode 100644 index 00000000..b72b945c --- /dev/null +++ b/app/assets/javascript/checkbox-selected-count.js @@ -0,0 +1,61 @@ +import { Component } from 'nhsuk-frontend' + +/** + * Checkbox Selected Count component + * + * Displays a count of selected checkboxes within a container with pluralization support. + * + * Usage: + * - Add `data-module="app-checkboxes-count-select"` to a wrapper element + * - Add an element with pluralization attributes: + * `

` + * - Checkboxes with `data-select-all` attribute are excluded from the count + * + * @augments Component + */ +export class CheckboxSelectedCount extends Component { + static elementType = HTMLElement + + /** + * Name for the component used when initialising using data-module attributes + */ + static moduleName = 'app-checkboxes-count-select' + + /** + * @param {Element | null} $root - HTML element to use for component + */ + constructor($root) { + super($root) + + this.$countDisplay = this.$root.querySelector('[data-i18n-selected-count\\.one]') + this.$checkboxes = this.$root.querySelectorAll('input[type="checkbox"]') + + if (this.$countDisplay) { + this.singularLabel = this.$countDisplay.getAttribute('data-i18n-selected-count.one') || 'selected' + this.pluralLabel = this.$countDisplay.getAttribute('data-i18n-selected-count.many') || 'selected' + this.setupEventListeners() + this.updateCount() + } + } + + /** + * Set up change event listeners on all checkboxes + */ + setupEventListeners() { + this.$checkboxes.forEach(($checkbox) => { + $checkbox.addEventListener('change', () => this.updateCount()) + }) + } + + /** + * Update the count display with the number of selected checkboxes + */ + updateCount() { + const checkedCount = this.$root.querySelectorAll( + 'input[type="checkbox"]:checked:not([data-select-all])' + ).length + + const label = checkedCount === 1 ? this.singularLabel : this.pluralLabel + this.$countDisplay.textContent = `${checkedCount} ${label} selected` + } +} diff --git a/app/assets/javascript/main.js b/app/assets/javascript/main.js index d101693a..6ee6b810 100644 --- a/app/assets/javascript/main.js +++ b/app/assets/javascript/main.js @@ -5,11 +5,13 @@ import { import { AddAnother } from './add-another.js' import { Autocomplete } from './autocomplete.js' +import { CheckboxSelectedCount } from './checkbox-selected-count.js' // Initiate NHS.UK frontend components on page load document.addEventListener('DOMContentLoaded', () => { createAll(AddAnother) createAll(Autocomplete) + createAll(CheckboxSelectedCount) }) diff --git a/app/views/pharmacies/select.html b/app/views/pharmacies/select.html index 11aa9abe..9d2d71fe 100644 --- a/app/views/pharmacies/select.html +++ b/app/views/pharmacies/select.html @@ -12,7 +12,7 @@ {% block content %}
-
+

{{ pageName }}

@@ -92,6 +92,7 @@

{{ pageName }}

{% endcall %} +

{{ button({ text: "Continue" @@ -104,23 +105,6 @@

{{ pageName }}

- {% endblock %} diff --git a/app/views/pharmacies/select.html b/app/views/pharmacies/select.html index d665ca85..b95a88d3 100644 --- a/app/views/pharmacies/select.html +++ b/app/views/pharmacies/select.html @@ -55,6 +55,7 @@

{{ pageName }}

text: "Select all " + (pharmacies | length), value: "", attributes: { + "data-module": "app-checkbox-select-all", "data-select-all": "true" } }, @@ -105,37 +106,4 @@

{{ pageName }}

- - - {% endblock %} diff --git a/app/views/reports/choose-site.html b/app/views/reports/choose-site.html index aed4f930..42709b1a 100644 --- a/app/views/reports/choose-site.html +++ b/app/views/reports/choose-site.html @@ -25,6 +25,7 @@ text: "All " + ((sites|length) if sites else (organisations|length)) + " " + ("sites" if sites else "organisations"), value: "all", attributes: { + "data-module": "app-checkbox-select-all", "data-select-all": "true" } }), items) %} @@ -74,38 +75,5 @@
- - {% endblock %} From 04b0398b72b8ccfe6ca36073182fc4ce6e4d46ca Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 1 May 2026 15:07:35 +0100 Subject: [PATCH 20/37] Add radios filter --- app/assets/javascript/main.js | 2 + app/assets/javascript/radios-filter.js | 68 ++++++++++++++++ .../components/_scrollable-container.scss | 19 +++++ app/assets/sass/main.scss | 27 ++----- app/views/apply/pharmacies.html | 2 +- app/views/pharmacies/add-user.html | 77 ++++++++++++++----- app/views/pharmacies/select.html | 2 +- 7 files changed, 153 insertions(+), 44 deletions(-) create mode 100644 app/assets/javascript/radios-filter.js create mode 100644 app/assets/sass/components/_scrollable-container.scss diff --git a/app/assets/javascript/main.js b/app/assets/javascript/main.js index 32aa42e4..c7464192 100644 --- a/app/assets/javascript/main.js +++ b/app/assets/javascript/main.js @@ -8,6 +8,7 @@ import { Autocomplete } from './autocomplete.js' import { CheckboxFilter } from './checkbox-filter.js' import { CheckboxSelectAll } from './checkbox-select-all.js' import { CheckboxSelectedCount } from './checkbox-selected-count.js' +import { RadiosFilter } from './radios-filter.js' // Initiate NHS.UK frontend components on page load document.addEventListener('DOMContentLoaded', () => { @@ -16,6 +17,7 @@ document.addEventListener('DOMContentLoaded', () => { createAll(CheckboxFilter) createAll(CheckboxSelectAll) createAll(CheckboxSelectedCount) + createAll(RadiosFilter) }) diff --git a/app/assets/javascript/radios-filter.js b/app/assets/javascript/radios-filter.js new file mode 100644 index 00000000..e9dcf236 --- /dev/null +++ b/app/assets/javascript/radios-filter.js @@ -0,0 +1,68 @@ +import { Component } from 'nhsuk-frontend' + +/** + * Radios Filter component + * + * Filters a list of radio buttons based on search input. + * + * Usage: + * - Add `data-module="app-radios-filter"` to a search input element + * - The component will filter `.nhsuk-radios__item` elements within the same form or fieldset + * - Radio items with `data-no-filter` on their input are not filtered (e.g. "add new" options) + * + * @augments Component + */ +export class RadiosFilter extends Component { + static elementType = HTMLInputElement + + /** + * Name for the component used when initialising using data-module attributes + */ + static moduleName = 'app-radios-filter' + + /** + * @param {Element | null} $root - HTML input element to use for component + */ + constructor($root) { + super($root) + + this.$form = this.$root.closest('fieldset') || this.$root.closest('form') || document.body + this.$radioItems = this.$form.querySelectorAll('.nhsuk-radios__item') + + this.$root.addEventListener('input', () => this.filter()) + } + + /** + * Filter radio items based on the search input value + */ + filter() { + const searchTerm = this.$root.value.toLowerCase().trim().replace(/[.()]/g, '') + const searchWords = searchTerm.split(/\s+/).filter(word => word.length > 0) + + console.log('Filtering radios with search term:', searchTerm) + + console.log('Radio items:', this.$radioItems) + + this.$radioItems.forEach(($item) => { + const $label = $item.querySelector('.nhsuk-radios__label') + const $radio = $item.querySelector('.nhsuk-radios__input') + + if (!$label || !$radio) return + + // Skip items marked as excluded from filtering (e.g. "add new" options) + if ($radio.hasAttribute('data-no-filter')) return + + const labelText = $label.textContent.toLowerCase().replace(/[.()]/g, '') + const labelWords = labelText.split(/\s+/) + const matches = searchWords.length === 0 || searchWords.every(searchWord => + labelWords.some(labelWord => labelWord.startsWith(searchWord)) + ) + + if (matches) { + $item.removeAttribute('hidden') + } else { + $item.setAttribute('hidden', '') + } + }) + } +} diff --git a/app/assets/sass/components/_scrollable-container.scss b/app/assets/sass/components/_scrollable-container.scss new file mode 100644 index 00000000..81f21313 --- /dev/null +++ b/app/assets/sass/components/_scrollable-container.scss @@ -0,0 +1,19 @@ +.app-scrollable-container { + max-height: 500px; + overflow-y: scroll; + margin-bottom: nhsuk-spacing(4); + + // Force scrollbar to always be visible on WebKit browsers (Chrome, Safari) + &::-webkit-scrollbar { + width: $nhsuk-border-width-inset-text; + } + + &::-webkit-scrollbar-track { + background: $nhsuk-border-colour; + } + + &::-webkit-scrollbar-thumb { + background: $nhsuk-input-border-colour; + border-radius: 4px; + } +} diff --git a/app/assets/sass/main.scss b/app/assets/sass/main.scss index 5a8af5a1..7ea6cd22 100755 --- a/app/assets/sass/main.scss +++ b/app/assets/sass/main.scss @@ -24,6 +24,7 @@ @import 'components/tag'; @import 'components/numbered-heading'; @import 'components/inset-text'; +@import 'components/scrollable-container'; @import '../../components/secondary-navigation/_secondary-navigation'; @@ -31,11 +32,13 @@ // Add your custom CSS/Sass styles below... /////////////////////////////////////////// -// Ensure hidden attribute works on button groups (which have display: flex) -.nhsuk-button-group[hidden] { +// Ensure hidden attribute works on button groups and radios (which have display: flex) +.nhsuk-button-group[hidden], +.nhsuk-radios__item[hidden] { display: none; } + .nhsuk-header--left .nhsuk-header__navigation-list { justify-content: normal; } @@ -145,23 +148,3 @@ border-radius: 3px; .nhsuk-checkboxes__item[hidden] { display: none; } - -.app-checkboxes--scrollable-container { - max-height: 500px; - overflow-y: scroll; - margin-bottom: nhsuk-spacing(4); - - // Force scrollbar to always be visible on WebKit browsers (Chrome, Safari) - &::-webkit-scrollbar { - width: $nhsuk-border-width-inset-text; - } - - &::-webkit-scrollbar-track { - background: $nhsuk-border-colour; - } - - &::-webkit-scrollbar-thumb { - background: $nhsuk-input-border-colour; - border-radius: 4px; - } -} \ No newline at end of file diff --git a/app/views/apply/pharmacies.html b/app/views/apply/pharmacies.html index 156a4696..8d65fa40 100644 --- a/app/views/apply/pharmacies.html +++ b/app/views/apply/pharmacies.html @@ -90,7 +90,7 @@

Select pharmacies

}) }} {% endif %} -
+
{{ checkboxes({ id: "pharmacy-ids", name: "pharmacyIds", diff --git a/app/views/pharmacies/add-user.html b/app/views/pharmacies/add-user.html index 547aa66b..28c334a5 100644 --- a/app/views/pharmacies/add-user.html +++ b/app/views/pharmacies/add-user.html @@ -27,9 +27,7 @@

{{ pageName }}

}), items) %} {% endfor %} - {% set items = (items.push({ - divider: "or" - }), items) %} + {% set newUserHtml %} {{ input({ @@ -60,25 +58,64 @@

{{ pageName }}

}) }} {% endset %} - {% set items = (items.push({ - text: "Add a new user", - value: "add-new", - conditional: { - html: newUserHtml + + + {% call fieldset({ + legend: { + text: "User", + size: "m", + classes: "nhsuk-u-margin-bottom-5" } - }), items) %} - - {{ radios({ - name: "userId", - value: data.userId, - fieldset: { - legend: { - text: "User", - size: "m" + }) %} + + {% if (users | length) > 10 %} + {{ input({ + id: "user-search", + name: "userSearch", + type: "search", + label: { + text: "Search" + }, + classes: "nhsuk-input--width-20", + attributes: { + "data-module": "app-radios-filter" + }, + formGroup: { + classes: "nhsuk-u-margin-bottom-4" } - }, - items: items - }) }} + }) }} + {% endif %} + +
+ {{ radios({ + name: "userId", + value: data.userId, + items: items + }) }} +
+ + {{ radios({ + name: "userId", + value: data.userId, + items: [ + { + divider: "or" + }, + { + text: "Add a new user", + value: "add-new", + id: "add-new", + attributes: { + "data-no-filter": "" + }, + conditional: { + html: newUserHtml + } + } + ] + }) }} + + {% endcall %} {{ button({ text: "Continue" diff --git a/app/views/pharmacies/select.html b/app/views/pharmacies/select.html index b95a88d3..ed3b05e4 100644 --- a/app/views/pharmacies/select.html +++ b/app/views/pharmacies/select.html @@ -85,7 +85,7 @@

{{ pageName }}

{% endif %} -
+
{{ checkboxes({ id: "pharmacy-ids", name: "pharmacyIds", From e9e59fd6597eb62103da9b744ad97891b0868e61 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 1 May 2026 15:22:25 +0100 Subject: [PATCH 21/37] Add success notification banner --- app/routes/pharmacies.js | 5 ++++- app/views/pharmacies/pharmacy.html | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 5e8cf0fa..f9e1b6e6 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -227,6 +227,8 @@ module.exports = router => { router.get('/pharmacies/:id', (req, res) => { const data = req.session.data const id = req.params.id + const added = req.query.added + const organisation = data.organisations.find((organisation) => organisation.id === id) @@ -244,7 +246,8 @@ module.exports = router => { res.render('pharmacies/pharmacy', { organisation, users, - userOrganisationPermissions + userOrganisationPermissions, + added }) }) diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index d28ef25b..ddb64ce5 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -14,6 +14,19 @@
+ {% if added %} + {% set html %} +

+ User added +

+ {% endset %} + + {{ notificationBanner({ + html: html, + type: "success" + }) }} + {% endif %} +

{{ pageName }}

{{ summaryList({ From 56666581d5519f8b369272fa105e4a936057cb57 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 1 May 2026 15:36:52 +0100 Subject: [PATCH 22/37] Add email addresses and make region fixed height --- app/assets/javascript/radios-filter.js | 13 +++++++----- .../components/_scrollable-container.scss | 4 ++++ app/routes/pharmacies.js | 7 ++++++- app/views/pharmacies/add-user.html | 7 +++++-- app/views/pharmacies/users/new.html | 21 +++++-------------- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/assets/javascript/radios-filter.js b/app/assets/javascript/radios-filter.js index e9dcf236..b5540722 100644 --- a/app/assets/javascript/radios-filter.js +++ b/app/assets/javascript/radios-filter.js @@ -36,8 +36,8 @@ export class RadiosFilter extends Component { * Filter radio items based on the search input value */ filter() { - const searchTerm = this.$root.value.toLowerCase().trim().replace(/[.()]/g, '') - const searchWords = searchTerm.split(/\s+/).filter(word => word.length > 0) + const searchTerm = this.$root.value.toLowerCase().trim() + const searchWords = searchTerm.split(/[\s.@()]+/).filter(word => word.length > 0) console.log('Filtering radios with search term:', searchTerm) @@ -52,10 +52,13 @@ export class RadiosFilter extends Component { // Skip items marked as excluded from filtering (e.g. "add new" options) if ($radio.hasAttribute('data-no-filter')) return - const labelText = $label.textContent.toLowerCase().replace(/[.()]/g, '') - const labelWords = labelText.split(/\s+/) + const $hint = $item.querySelector('.nhsuk-radios__hint') + const labelText = $label.textContent.toLowerCase() + const hintText = $hint ? $hint.textContent.toLowerCase() : '' + const combinedText = `${labelText} ${hintText}` + const combinedWords = combinedText.split(/[\s.@()]+/).filter(word => word.length > 0) const matches = searchWords.length === 0 || searchWords.every(searchWord => - labelWords.some(labelWord => labelWord.startsWith(searchWord)) + combinedWords.some(word => word.startsWith(searchWord)) ) if (matches) { diff --git a/app/assets/sass/components/_scrollable-container.scss b/app/assets/sass/components/_scrollable-container.scss index 81f21313..e62f6b59 100644 --- a/app/assets/sass/components/_scrollable-container.scss +++ b/app/assets/sass/components/_scrollable-container.scss @@ -17,3 +17,7 @@ border-radius: 4px; } } + +.app-scrollable-container--fixed-height { + min-height: 500px; +} diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index f9e1b6e6..73063c72 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -108,13 +108,18 @@ module.exports = router => { router.get('/pharmacies/users',(req, res) => { const data = req.session.data - const users = data.users.slice(10, 20) + const users = data.users.slice(2, 30) res.render('pharmacies/users/index', { users }) }) + router.get('/pharmacies/users/new',(req, res) => { + + res.render('pharmacies/users/new') + }) + router.get('/pharmacies/add-lead-admins',(req, res) => { const data = req.session.data const users = data.users.slice(10, 20) diff --git a/app/views/pharmacies/add-user.html b/app/views/pharmacies/add-user.html index 28c334a5..9d5a3833 100644 --- a/app/views/pharmacies/add-user.html +++ b/app/views/pharmacies/add-user.html @@ -23,7 +23,10 @@

{{ pageName }}

{% for user in (users | sort(false, false, "firstName")) %} {% set items = (items.push({ text: user.firstName + " " + user.lastName, - value: user.id + value: user.id, + hint: { + text: user.email + } }), items) %} {% endfor %} @@ -86,7 +89,7 @@

{{ pageName }}

}) }} {% endif %} -
+
{{ radios({ name: "userId", value: data.userId, diff --git a/app/views/pharmacies/users/new.html b/app/views/pharmacies/users/new.html index c6ebdf50..e601611f 100644 --- a/app/views/pharmacies/users/new.html +++ b/app/views/pharmacies/users/new.html @@ -44,8 +44,7 @@

Add user

{{ input({ "label": { - "text": "Email address", - size: "s" + "text": "Email address" }, "id": "email", "name": "email", @@ -53,35 +52,25 @@

Add user

value: data.email }) }} - {{ checkboxes({ + {{ radios({ idPrefix: "example", name: "example", fieldset: { legend: { - text: "Which pharmacy do you want to add them to?", - size: "s", + text: "Permission level", isPageHeading: true } }, items: [ { value: "all", - text: "All – add them as a super admin for Peak Pharmacy" + text: "Group admin for Peak Pharmacy" }, { divider: "or" }, { - text: "Peak Pharmacy (P141)" - }, - { - text: "Peak Pharmacy (P931)" - }, - { - text: "Peak Pharmacy (P291)" - }, - { - text: "etc" + text: "I’ll add them to individual pharmacies" } ] }) }} From 499ae2c326ecd5c8d9f0f32320d43264bde6c0a9 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 8 May 2026 13:35:54 +0100 Subject: [PATCH 23/37] Rename tab to "By pharmacy" --- app/views/home/_by-organisation.html | 4 ++-- app/views/home/index.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/home/_by-organisation.html b/app/views/home/_by-organisation.html index 5096e321..442cee25 100644 --- a/app/views/home/_by-organisation.html +++ b/app/views/home/_by-organisation.html @@ -1,9 +1,9 @@ - + - {% for totalByOrganisation in (totalsByOrganisation | sort(false, false, "organisationName")) %} + {% for totalByOrganisation in (totalsByPharmacy | sort(false, false, "organisationName")) %} {% set organisation = data.organisations | findById(totalByOrganisation.organisationId) %}
By organisationBy pharmacy
- Organisation + Pharmacy Today diff --git a/app/views/home/index.html b/app/views/home/index.html index a5a5e58f..79c019f9 100644 --- a/app/views/home/index.html +++ b/app/views/home/index.html @@ -85,7 +85,7 @@

All vaccinations

} } if (totalsBySite | length) > 1, { - label: "By organisation", + label: "By pharmacy", id: "by-organisations", panel: { html: byOrganisationHtml From 31d8343022d107b8e5ac175c382ba6b84bd27d03 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 11:44:08 +0100 Subject: [PATCH 24/37] Content changes --- app/routes/pharmacies.js | 5 +++++ app/views/pharmacies/index.html | 8 ++++++-- app/views/pharmacies/pharmacy.html | 5 +++-- app/views/pharmacies/users/new.html | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 73063c72..4c25cfa9 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -120,6 +120,11 @@ module.exports = router => { res.render('pharmacies/users/new') }) + router.post('/pharmacies/users/new-answer',(req, res) => { + + res.redirect('/pharmacies/users') + }) + router.get('/pharmacies/add-lead-admins',(req, res) => { const data = req.session.data const users = data.users.slice(10, 20) diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html index d16445c5..591eedec 100644 --- a/app/views/pharmacies/index.html +++ b/app/views/pharmacies/index.html @@ -30,7 +30,7 @@

Pharmacies

}) }} - +
Current pharmacies ({{ organisations | length }})Pharmacies added ({{ organisations | length }})
@@ -51,7 +51,11 @@

Pharmacies

{{ organisation.name }} ({{ organisation.id}})
- COVID-19, flu + {% if loop.index0 == 0 %} + Not yet added + {% else %} + COVID-19, flu + {% endif %} {{ organisationUserCounts[organisation.id] }} diff --git a/app/views/pharmacies/pharmacy.html b/app/views/pharmacies/pharmacy.html index ddb64ce5..b817a217 100644 --- a/app/views/pharmacies/pharmacy.html +++ b/app/views/pharmacies/pharmacy.html @@ -49,7 +49,7 @@

{{ pageName }}

}, { key: { - text: "Vaccines enabled" + text: "Vaccines" }, value: { text: "COVID-19, flu" @@ -58,6 +58,8 @@

{{ pageName }}

] }) }} +

Deactivate this pharmacy

+

Users

@@ -117,7 +119,6 @@

Users

{% endif %} -

Deactivate this pharmacy

diff --git a/app/views/pharmacies/users/new.html b/app/views/pharmacies/users/new.html index e601611f..9987c923 100644 --- a/app/views/pharmacies/users/new.html +++ b/app/views/pharmacies/users/new.html @@ -14,7 +14,7 @@

Add user

- + {{ input({ "label": { From 9ffb77e84bd377d4b1a067859d1be93b0f169dd6 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 11:49:53 +0100 Subject: [PATCH 25/37] Fix code style issues --- app/assets/javascript/autocomplete.js | 4 ---- app/filters.js | 17 ----------------- app/routes/pharmacies.js | 8 +------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/app/assets/javascript/autocomplete.js b/app/assets/javascript/autocomplete.js index 695a7cf2..ec0c1a85 100644 --- a/app/assets/javascript/autocomplete.js +++ b/app/assets/javascript/autocomplete.js @@ -67,10 +67,6 @@ export class Autocomplete extends Component { /** * Selected option - * - * @param {*} value - Current value - * @param {Array} options - Available options - * @returns {HTMLOptionElement} Selected option */ selectedOption(value, options) { return [].filter.call( diff --git a/app/filters.js b/app/filters.js index ce8b19fc..9d48df16 100644 --- a/app/filters.js +++ b/app/filters.js @@ -67,23 +67,6 @@ module.exports = function () { } } - /** - * Ensure a value is always returned as an array - * Useful for form fields with [] notation that may return a string if only one value - * - * @param {*} value - Value to convert to array - * @returns {Array} Value as an array - */ - filters.asArray = function(value) { - if (value === undefined || value === null) { - return [] - } - if (Array.isArray(value)) { - return value - } - return [value] - } - /* keep the following line to return your filters to the app */ return filters } diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 4c25cfa9..e03f9526 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -1,4 +1,4 @@ -const { getPharmaciesBelongingToOrganisation, getPharmacyChains, getOrganisation } = require('../lib/ods'); +const { getPharmaciesBelongingToOrganisation } = require('../lib/ods'); const sortByNameThenPostcode = (getPostcode = (item) => item.postcode) => (a, b) => { if (a.name < b.name) return -1 @@ -13,15 +13,12 @@ module.exports = router => { router.get('/pharmacies', (req, res) => { const data = req.session.data - const currentUser = res.locals.currentUser const added = req.query.added // TODO: get this from the current login // rather than hardcode it const companyId = 'P15951' - const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) - const organisations = data.organisations.filter((organisation) => organisation.companyId === companyId).sort(sortByNameThenPostcode()) let organisationUserCounts = {} @@ -42,14 +39,11 @@ module.exports = router => { }) router.get('/pharmacies/select', async (req, res) => { - const data = req.session.data - const id = req.params.id let pharmacies = await getPharmaciesBelongingToOrganisation("P15J") pharmacies = pharmacies.sort(sortByNameThenPostcode((item) => item.address.postcode)) - res.render('pharmacies/select', { pharmacies }) From 11ea5c76c81c9f0608c6920f118c2ddb05b40b75 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 13:34:46 +0100 Subject: [PATCH 26/37] Refactor "By organisation" as "By pharmacy" --- app/data/session-data-defaults.js | 2 +- app/data/users.js | 126 ------------------ app/routes/home.js | 54 +++++--- ...by-organisation.html => _by-pharmacy.html} | 2 +- app/views/home/index.html | 12 +- 5 files changed, 41 insertions(+), 155 deletions(-) rename app/views/home/{_by-organisation.html => _by-pharmacy.html} (94%) diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index e06dba75..3737b555 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -16,7 +16,7 @@ module.exports = { lists: [], nhsNumberKnown: "yes", currentUserId: "6424325235325", - currentOrganisationId: null, + currentOrganisationId: "P15951", currentMode: "reports", vaccinationsRecorded: vaccinationsRecorded, diff --git a/app/data/users.js b/app/data/users.js index 673bec4c..623144a4 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -186,132 +186,6 @@ module.exports = [ "id": "P15951", "permissionLevel": "Group administrator", "status": "Active" - }, - { - "id": "FX4825", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FX7314", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FX9151", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FQ2525", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FW1941", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FP9824", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FP1812", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FA7K23", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FG2R56", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FH9P12", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FA7K23", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FJ4M89", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FK5N34", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FL7Q67", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FM8R23", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FN9S45", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FP2T78", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FQ3U12", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FR4V56", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FS5W89", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FT6X34", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false } ] }, diff --git a/app/routes/home.js b/app/routes/home.js index bc890a75..41768f5e 100644 --- a/app/routes/home.js +++ b/app/routes/home.js @@ -75,22 +75,36 @@ module.exports = router => { let vaccinationsRecorded = [] let sites = [] + let pharmacies = [] let organisations = [] if (currentOrganisation) { - // Showing all sites for now, for demo purposes - sites = currentOrganisation.sites - // Filter vaccinations to only those recorded by the current - // organisation - vaccinationsRecorded = allVaccinationsRecorded.filter((vaccination)=> vaccination.organisationId === currentOrganisation.id) + if (currentOrganisation.type == "Pharmacy HQ") { - if (!sites.length || sites.length === 0) { - sites = [currentOrganisation] - } + pharmacies = data.organisations.filter((organisation) => organisation.companyId === currentOrganisation.id) + + vaccinationsRecorded = allVaccinationsRecorded + + + + } else { + + // Showing all sites for now, for demo purposes + sites = currentOrganisation.sites || [] + // Filter vaccinations to only those recorded by the current + // organisation + vaccinationsRecorded = allVaccinationsRecorded.filter((vaccination)=> vaccination.organisationId === currentOrganisation.id) + + if (!sites.length || sites.length === 0) { + sites = [currentOrganisation] + } + } } else { + // TODO: remove all this. + // Include all organisations for now vaccinationsRecorded = allVaccinationsRecorded @@ -98,14 +112,11 @@ module.exports = router => { organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) } - - let totalsBySite = [] - let totalsByOrganisation = [] + let totalsByPharmacy = [] let totalsByVaccine = [] let totalsByDay = [] - const totalVaccinationsRecorded = countVaccinations(vaccinationsRecorded) const totalVaccinationsRecordedToday = countVaccinations( @@ -190,28 +201,28 @@ module.exports = router => { } } - for (let organisation of organisations) { + for (let pharmacy of pharmacies) { const total = countVaccinations(vaccinationsRecorded, { - organisationId: organisation.id + organisationId: pharmacy.id }) if (total !== -1) { - totalsByOrganisation.push({ - organisationId: organisation.id, - organisationName: organisation.name, + totalsByPharmacy.push({ + organisationId: pharmacy.id, + organisationName: pharmacy.name, today: countVaccinations(vaccinationsRecorded, { date: dateToday, - organisationId: organisation.id + organisationId: pharmacy.id }), month:countVaccinations(vaccinationsRecorded, { month: dateToday, - organisationId: organisation.id + organisationId: pharmacy.id }), past7Days: countVaccinations(vaccinationsRecorded, { minDate: sevenDaysAgo, maxDate: dateToday, - organisationId: organisation.id + organisationId: pharmacy.id }), total: total }) @@ -220,6 +231,7 @@ module.exports = router => { res.render('home/index', { sites, + pharmacies, totalVaccinationsRecorded, totalVaccinationsRecordedToday, totalVaccinationsRecordedThisMonth, @@ -228,7 +240,7 @@ module.exports = router => { totalsBySite, totalsByVaccine, totalsByDay, - totalsByOrganisation + totalsByPharmacy }) }) } diff --git a/app/views/home/_by-organisation.html b/app/views/home/_by-pharmacy.html similarity index 94% rename from app/views/home/_by-organisation.html rename to app/views/home/_by-pharmacy.html index 442cee25..3aa613d0 100644 --- a/app/views/home/_by-organisation.html +++ b/app/views/home/_by-pharmacy.html @@ -20,7 +20,7 @@
diff --git a/app/views/home/index.html b/app/views/home/index.html index 79c019f9..542427ec 100644 --- a/app/views/home/index.html +++ b/app/views/home/index.html @@ -42,7 +42,6 @@

All vaccinations

{% include "home/_vaccination-totals.html" %} - {% if totalVaccinationsRecorded > 0 %} {% set byDayHtml %} @@ -57,10 +56,11 @@

All vaccinations

{% include "home/_by-site.html" %} {% endset %} - {% set byOrganisationHtml %} - {% include "home/_by-organisation.html" %} + {% set byPharmacyHtml %} + {% include "home/_by-pharmacy.html" %} {% endset %} + {{ tabs({ items: [ { @@ -86,11 +86,11 @@

All vaccinations

} if (totalsBySite | length) > 1, { label: "By pharmacy", - id: "by-organisations", + id: "by-pharmacies", panel: { - html: byOrganisationHtml + html: byPharmacyHtml } - } if (totalsByOrganisation | length) > 1 + } if (pharmacies | length) > 1 ] }) }} From e892dbfdd080a0bdc3eafce73ab3c4c1fe81e1e6 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 13:39:18 +0100 Subject: [PATCH 27/37] Remove 'currentMode' This has now been replaced with "Pharmacy HQ" parent organisations. --- app/data/session-data-defaults.js | 1 - app/routes/auth.js | 2 -- app/views/includes/header.html | 31 ++++++++++++++++--------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index 3737b555..ace71cb2 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -17,7 +17,6 @@ module.exports = { nhsNumberKnown: "yes", currentUserId: "6424325235325", currentOrganisationId: "P15951", - currentMode: "reports", vaccinationsRecorded: vaccinationsRecorded, // These are the options for extracting CSV reports diff --git a/app/routes/auth.js b/app/routes/auth.js index 7959d8fb..82de9ef0 100644 --- a/app/routes/auth.js +++ b/app/routes/auth.js @@ -74,7 +74,6 @@ module.exports = router => { const email = data.email const user = data.users.find((user) => user.email === email) - req.session.data.currentMode = "reports" req.session.data.currentOrganisationId = null req.session.data.currentUserId = user.id @@ -122,7 +121,6 @@ module.exports = router => { router.get('/sign-out', (req, res) => { req.session.data.currentUserId = null req.session.data.currentOrganisationId = null - req.session.data.currentMode = null req.session.data.email = "" res.redirect('/product-page') diff --git a/app/views/includes/header.html b/app/views/includes/header.html index 58e9a25a..7036c24f 100644 --- a/app/views/includes/header.html +++ b/app/views/includes/header.html @@ -11,8 +11,8 @@ active: (currentSection == "home") }), navigationItems) %} - - {% if data.currentMode == "reports" %} + + {% if currentOrganisation.type === "Pharmacy HQ" %} {% set navigationItems = (navigationItems.push({ href: "/pharmacies", text: "Pharmacies", @@ -26,25 +26,26 @@ }), navigationItems) %} {% endif %} - {% if currentOrganisation %} + + {% if currentOrganisation and (not currentOrganisation.type == "Pharmacy HQ") %} {% set navigationItems = (navigationItems.push({ href: "/record-vaccinations", text: "Record vaccinations", active: (currentSection == "vaccinate") }), navigationItems) %} + {% endif %} - - {% if (["Lead administrator", "Administrator"] | arrayOrStringIncludes(organisationSetting.permissionLevel)) %} - {% set navigationItems = (navigationItems.push({ - href: "/vaccines", - text: "Vaccines", - active: (currentSection == "vaccines") - }), navigationItems) %} - {% endif %} + + {% if (["Lead administrator", "Administrator"] | arrayOrStringIncludes(organisationSetting.permissionLevel)) %} + {% set navigationItems = (navigationItems.push({ + href: "/vaccines", + text: "Vaccines", + active: (currentSection == "vaccines") + }), navigationItems) %} {% endif %} - - {% if currentOrganisation %} + + {% if currentOrganisation and (not currentOrganisation.type == "Pharmacy HQ") %} {% set navigationItems = (navigationItems.push({ href: "/records", text: "Records", @@ -52,8 +53,8 @@ }), navigationItems) %} {% endif %} - - {% if (["Lead administrator", "Administrator"] | arrayOrStringIncludes(organisationSetting.permissionLevel)) or data.currentMode == "reports" %} + + {% if (["Lead administrator", "Administrator"] | arrayOrStringIncludes(organisationSetting.permissionLevel)) or currentOrganisation.type === "Pharmacy HQ" %} {% set navigationItems = (navigationItems.push({ href: "/reports", text: "Reports", From 80aba372f84b4601abd669c71225a89de8bf8829 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 13:56:31 +0100 Subject: [PATCH 28/37] Remove 'Select mode' screen This is no longer needed if we have the Pharmacy HQ screens. --- app/routes/auth.js | 27 --------------- app/views/auth/select-mode.html | 46 ------------------------- app/views/auth/select-organisation.html | 10 ------ app/views/pharmacies/users/index.html | 2 +- 4 files changed, 1 insertion(+), 84 deletions(-) delete mode 100644 app/views/auth/select-mode.html diff --git a/app/routes/auth.js b/app/routes/auth.js index 82de9ef0..3fd822a7 100644 --- a/app/routes/auth.js +++ b/app/routes/auth.js @@ -49,11 +49,6 @@ module.exports = router => { res.redirect('/regions') - } else if (organisationsUserIsAnAdminAt.length > 1) { - // They are an admin at 2 or more organisations, so - // ask them to select mode (single org or report mode) - res.redirect('/auth/select-mode') - } else { res.redirect('/auth/select-organisation') @@ -62,28 +57,6 @@ module.exports = router => { }) - - router.post('/auth/answer-select-mode', (req, res) => { - const data = req.session.data - const loginMode = data.loginMode - - if (loginMode === 'single') { - res.redirect('/auth/select-organisation?from=select-mode') - } else if (loginMode === 'create-reports') { - - const email = data.email - const user = data.users.find((user) => user.email === email) - - req.session.data.currentOrganisationId = null - req.session.data.currentUserId = user.id - - res.redirect('/home') - } else { - res.redirect('/auth/select-mode') - } - - }) - router.get('/auth/select-organisation', (req, res) => { const data = req.session.data diff --git a/app/views/auth/select-mode.html b/app/views/auth/select-mode.html deleted file mode 100644 index 65e270ab..00000000 --- a/app/views/auth/select-mode.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends 'layout.html' %} - -{% set pageName = "Sign in" %} - -{% block content %} -
-
- - - - {{ radios({ - name: "loginMode", - fieldset: { - legend: { - text: "What do you want to do?", - size: "l", - isPageHeading: true - } - }, - hint: { - text: "As an administrator at multiple pharmacies or organisations you have 2 options. To switch between these, log out and log back in again." - }, - items: [ - { - text: "Use the service at 1 organisation", - value: "single", - hint: { - text: "For example, to record vaccinations and manage vaccines" - } - }, - { - text: "Create a report across multiple pharmacies or organisations", - value: "create-reports" - } - ] - }) }} - - {{ button({ - text: "Continue" - }) }} - - -
-
- -{% endblock %} diff --git a/app/views/auth/select-organisation.html b/app/views/auth/select-organisation.html index eaa3304a..caae94ea 100644 --- a/app/views/auth/select-organisation.html +++ b/app/views/auth/select-organisation.html @@ -2,16 +2,6 @@ {% set pageName = "Choose your organisation" %} -{% block beforeContent %} - {% if from === "select-mode" %} - {{ backLink({ - text: "Back", - href: "/auth/select-mode" - }) }} - {% endif %} -{% endblock %} - - {% block content %}
diff --git a/app/views/pharmacies/users/index.html b/app/views/pharmacies/users/index.html index c7c66a3c..f0f12a29 100644 --- a/app/views/pharmacies/users/index.html +++ b/app/views/pharmacies/users/index.html @@ -8,7 +8,7 @@

Users

-

Add a new user or manage an existing pharmacy.

+

Add a new user or change the permissions of an existing user.

{{ button({ text: "Add user", From fa558eeefe115344f32ccca11fa00800be34fe0f Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 16:30:11 +0100 Subject: [PATCH 29/37] Separate site and pharmacies in reports It's a bit clearer if these are separate pages. --- app/routes/reports.js | 61 ++++++++++++------- app/views/reports/check.html | 25 ++++++-- app/views/reports/choose-pharmacies.html | 76 ++++++++++++++++++++++++ app/views/reports/choose-site.html | 16 ++--- app/views/reports/choose-vaccines.html | 2 +- 5 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 app/views/reports/choose-pharmacies.html diff --git a/app/routes/reports.js b/app/routes/reports.js index 234edeaa..2165795a 100644 --- a/app/routes/reports.js +++ b/app/routes/reports.js @@ -8,7 +8,18 @@ module.exports = (router) => { let vaccinationsRecordedCount if (currentOrganisation) { - vaccinationsRecordedCount = data.vaccinationsRecorded.filter((vaccination) => vaccination.organisationId === currentOrganisation.id).length + + if (currentOrganisation.type === "Pharmacy HQ") { + + // TODO count vaccinations recorded at any pharmacies within this group. + vaccinationsRecordedCount = 1 + + } else { + + vaccinationsRecordedCount = data.vaccinationsRecorded.filter((vaccination) => vaccination.organisationId === currentOrganisation.id).length + + } + } else { // TODO: count across all organisations you @@ -43,6 +54,16 @@ module.exports = (router) => { }) }) + router.post('/reports/choose-vaccines-answer', (req, res) => { + + if (res.locals.currentOrganisation.type === "Pharmacy HQ") { + res.redirect('/reports/choose-pharmacies') + } else { + res.redirect('/reports/choose-site') + } + + }) + router.get('/reports/choose-dates', (req, res) => { const data = req.session.data @@ -98,26 +119,22 @@ module.exports = (router) => { const currentOrganisation = res.locals.currentOrganisation const currentUser = res.locals.currentUser - let sites, organisations - - if (currentOrganisation) { - // Showing all sites for now, for demo purposes - sites = currentOrganisation.sites - - if (sites === []) { - sites = [currentOrganisation] - } + const sites = currentOrganisation.sites || [] + res.render('reports/choose-site', { + sites + }) + }) - } else { + router.get('/reports/choose-pharmacies', (req, res) => { + const data = req.session.data + const currentOrganisation = res.locals.currentOrganisation - const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) - organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) - } + const pharmacies = data.organisations.filter((organisation) => organisation.companyId === currentOrganisation.id) - res.render('reports/choose-site', { - sites, - organisations + res.render('reports/choose-pharmacies', { + pharmacies }) + }) @@ -184,11 +201,13 @@ module.exports = (router) => { const today = new Date() const days = 86400000 // number of milliseconds in a day - let sites, organisations + const pharmacyIdsToReport = data.pharmacyIdsToReport || [] + + let sites, pharmacies if (currentOrganisation) { - sites = currentOrganisation.sites + sites = (currentOrganisation.sites || []) .filter((site) => siteIds.includes(site.id)) } else { @@ -199,6 +218,8 @@ module.exports = (router) => { .filter((organisation) => siteIds.includes(organisation.id)) } + pharmacies = data.organisations.filter((pharmacy) => pharmacyIdsToReport.includes(pharmacy.id)) + const fromInput = data.from const toInput = data.to const dateOption = data.date @@ -236,7 +257,7 @@ module.exports = (router) => { res.render('reports/check', { sites, - organisations, + pharmacies, from, to }) diff --git a/app/views/reports/check.html b/app/views/reports/check.html index bd4e00ca..97546922 100644 --- a/app/views/reports/check.html +++ b/app/views/reports/check.html @@ -52,10 +52,10 @@

{{ pageName }}

{% endset %} - {% set organisationsHtml %} + {% set pharmaciesHtml %}
    - {% for organisation in organisations | sort(false, false, "name") %} -
  • {{ organisation.name }} ({{ organisation.id }})
  • + {% for pharmacy in pharmacies | sort(false, false, "name") %} +
  • {{ pharmacy.name }} ({{ pharmacy.id }})
  • {% endfor %}
{% endset %} @@ -132,7 +132,24 @@

{{ pageName }}

} ] } - } if sites, + } if (sites | length) > 0, + { + key: { + text: ("Pharmacies" if (data.pharmacyIdsToReport | length) > 1 else "Pharmacy") + }, + value: { + html: pharmaciesHtml + }, + actions: { + items: [ + { + href: "/reports/choose-pharmacies", + text: "Change", + visuallyHiddenText: "pharmacies" + } + ] + } + } if currentOrganisation.type == "Pharmacy HQ", { key: { text: ("Organisations" if (data.siteIdsToReport | length) > 1 else "Organisation") diff --git a/app/views/reports/choose-pharmacies.html b/app/views/reports/choose-pharmacies.html new file mode 100644 index 00000000..bbd552c4 --- /dev/null +++ b/app/views/reports/choose-pharmacies.html @@ -0,0 +1,76 @@ +{% extends 'layout.html' %} + +{% set pageName = "Choose pharmacies" %} + +{% set currentSection = "reports" %} + +{% block beforeContent %} + {{ backLink({ + href: "/reports/choose-vaccines", + text: "Back" + }) }} +{% endblock %} + +{% block content %} +
+
+ +
+ + {% set items = [] %} + + {% set items = (items.push({ + text: "All " + (pharmacies|length) + " pharmacies", + value: "all", + attributes: { + "data-module": "app-checkbox-select-all", + "data-select-all": "true" + } + }), items) %} + + {% set items = (items.push({ + divider: "or" + }), items) %} + + + {% for pharmacy in pharmacies | sort(false, false, "name") %} + + {% if pharmacy.address %} + {% set hint = { + text: pharmacy.address.line1 + ", " + pharmacy.address.town + ", " + pharmacy.address.postcode + } %} + {% endif %} + + {% set items = (items.push({ + value: pharmacy.id, + text: pharmacy.name + " (" + pharmacy.id + ")", + hint: hint + }), items) %} + {% endfor %} + + {{ checkboxes({ + idPrefix: "pharmacy-to-report", + name: "pharmacyIdsToReport", + fieldset: { + legend: { + text: pageName, + classes: "nhsuk-fieldset__legend--l", + isPageHeading: true + } + }, + values: data.pharmacyIdsToReport, + items: items + }) }} + + + {{ button({ + text: "Continue" + }) }} + +
+ +
+
+ +{% endblock %} + diff --git a/app/views/reports/choose-site.html b/app/views/reports/choose-site.html index 42709b1a..f95ff530 100644 --- a/app/views/reports/choose-site.html +++ b/app/views/reports/choose-site.html @@ -1,6 +1,6 @@ {% extends 'layout.html' %} -{% set pageName = ("Choose sites" if sites else "Choose organisations") %} +{% set pageName = "Choose sites" %} {% set currentSection = "reports" %} @@ -19,10 +19,10 @@ {% set items = [] %} - {% if ((sites | length) > 2) or ((organisations | length) > 2) %} + {% if ((sites | length) > 2) %} {% set items = (items.push({ - text: "All " + ((sites|length) if sites else (organisations|length)) + " " + ("sites" if sites else "organisations"), + text: "All " + (sites|length) + " " + ("sites" if sites else "site"), value: "all", attributes: { "data-module": "app-checkbox-select-all", @@ -36,17 +36,17 @@ {% endif %} - {% for siteOrOrg in ((sites if sites else organisations) | sort(false, false, "name")) %} + {% for site in (sites | sort(false, false, "name")) %} - {% if siteOrOrg.address %} + {% if site.address %} {% set hint = { - text: siteOrOrg.address.line1 + ", " + siteOrOrg.address.town + ", " + siteOrOrg.address.postcode + text: site.address.line1 + ", " + site.address.town + ", " + site.address.postcode } %} {% endif %} {% set items = (items.push({ - value: siteOrOrg.id, - text: siteOrOrg.name + " (" + siteOrOrg.id + ")", + value: site.id, + text: site.name + " (" + site.id + ")", hint: hint }), items) %} {% endfor %} diff --git a/app/views/reports/choose-vaccines.html b/app/views/reports/choose-vaccines.html index 8c60d5e4..c695571e 100644 --- a/app/views/reports/choose-vaccines.html +++ b/app/views/reports/choose-vaccines.html @@ -15,7 +15,7 @@ {% block content %}
-
+ {% set items = [] %} From 02beecd24d2f6a2869af0825735e9cdf1e382d20 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 16:39:52 +0100 Subject: [PATCH 30/37] Fix choose data page --- app/views/reports/choose-data.html | 65 +++++++++++++++++------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/app/views/reports/choose-data.html b/app/views/reports/choose-data.html index 477c66a1..5076d211 100644 --- a/app/views/reports/choose-data.html +++ b/app/views/reports/choose-data.html @@ -240,52 +240,61 @@
From 21c795a7e8d7a8f73710419a36f20caed7513fec Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:05:10 +0100 Subject: [PATCH 31/37] Update data model --- app/data/organisations.js | 16 ++++++++++++++++ app/data/users.js | 30 ++++++------------------------ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/app/data/organisations.js b/app/data/organisations.js index 962b698a..b4fa4e0f 100644 --- a/app/data/organisations.js +++ b/app/data/organisations.js @@ -8261,6 +8261,7 @@ module.exports = [ { id: 'FA424', name: 'Pickfords Pharmacy', + companyId: "P0191N", sites: [ { id: "FA424X", @@ -8285,6 +8286,7 @@ module.exports = [ { id: 'FA02S', name: 'Addlestone Pharmacy', + companyId: "P0191N", address: { line1: '92a Station Road', town: 'Addlestone', @@ -8316,6 +8318,7 @@ module.exports = [ { id: 'FVJ99', name: 'Pharmacy 4U', + companyId: "P0191N", sites: [ { id: "123535", @@ -8340,6 +8343,7 @@ module.exports = [ }, { id: 'PDL93', + companyId: "P0191N", name: 'Silverfields Chemists', sites: [ { @@ -9110,5 +9114,17 @@ module.exports = [ } } ] + }, + { + id: "P0191N", + name: 'P.W. Pharmacies Ltd', + address: { + line1: '12 High Road', + town: 'Manchester', + postcode: 'M7 1LP' + }, + type: 'Pharmacy HQ', + status: 'Active', + region: "Y56" } ] diff --git a/app/data/users.js b/app/data/users.js index 623144a4..932f4df7 100644 --- a/app/data/users.js +++ b/app/data/users.js @@ -82,35 +82,17 @@ module.exports = [ "firstName": "Phoebe", "lastName": "Black" }, - // Paulina Sloan is a lead admin for - // a chain of pharmacies + // Paulina Sloan is a group admin for + // a chain of pharmacies called + // P.W. Pharmacies Ltd { "id": "9847489647892", "email": "paulina.sloan@nhs.net", "organisations": [ { - "id": "FA424", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FA02S", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "FVJ99", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false - }, - { - "id": "PDL93", - "permissionLevel": "Lead administrator", - "status": "Active", - "vaccinator": false + "id": "P0191N", + "permissionLevel": "Group administrator", + "status": "Active" } ], "firstName": "Paulina", From b4d36a1499ed9c28bf0f2ba3e97d62a5590d50cf Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:05:15 +0100 Subject: [PATCH 32/37] Fix --- app/routes/pharmacies.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index e03f9526..727be355 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -15,9 +15,7 @@ module.exports = router => { const data = req.session.data const added = req.query.added - // TODO: get this from the current login - // rather than hardcode it - const companyId = 'P15951' + const companyId = res.locals.currentOrganisation.id const organisations = data.organisations.filter((organisation) => organisation.companyId === companyId).sort(sortByNameThenPostcode()) From c4e96611b5a65849e0229750bdcb6cbf82554d9b Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:05:21 +0100 Subject: [PATCH 33/37] Fix header --- app/views/includes/header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/includes/header.html b/app/views/includes/header.html index 7036c24f..a0f6a913 100644 --- a/app/views/includes/header.html +++ b/app/views/includes/header.html @@ -27,7 +27,7 @@ {% endif %} - {% if currentOrganisation and (not currentOrganisation.type == "Pharmacy HQ") %} + {% if currentOrganisation and (currentOrganisation.type != "Pharmacy HQ") %} {% set navigationItems = (navigationItems.push({ href: "/record-vaccinations", text: "Record vaccinations", From febab30dac286fd927e9fdbc561ef2cabc0aa1b2 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:08:20 +0100 Subject: [PATCH 34/37] Tidy up the code --- app/routes/auth.js | 4 ---- app/routes/home.js | 12 ------------ app/routes/reports.js | 15 +-------------- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/app/routes/auth.js b/app/routes/auth.js index 3fd822a7..4390d5d5 100644 --- a/app/routes/auth.js +++ b/app/routes/auth.js @@ -21,10 +21,6 @@ module.exports = router => { .filter((organisation) => organisation.status === "Active") .map((organisation) => organisation.id) - const organisationsUserIsAnAdminAt = (user.organisations || []) - .filter((organisation) => (organisation.status === "Active" && ["Lead administrator", "Administrator"].includes(organisation.permissionLevel))) - .map((organisation) => organisation.id) - const userRegionIds = (user.regions || []) .filter((organisation) => organisation.status === "Active") .map((organisation) => organisation.id) diff --git a/app/routes/home.js b/app/routes/home.js index 41768f5e..7bee500f 100644 --- a/app/routes/home.js +++ b/app/routes/home.js @@ -64,7 +64,6 @@ module.exports = router => { // Dashboard router.get('/home', (req, res) => { const currentOrganisation = res.locals.currentOrganisation - const currentUser = res.locals.currentUser const data = req.session.data const allVaccinationsRecorded = data.vaccinationsRecorded @@ -73,10 +72,8 @@ module.exports = router => { // Vaccinations to count let vaccinationsRecorded = [] - let sites = [] let pharmacies = [] - let organisations = [] if (currentOrganisation) { @@ -101,15 +98,6 @@ module.exports = router => { sites = [currentOrganisation] } } - } else { - - // TODO: remove all this. - - // Include all organisations for now - vaccinationsRecorded = allVaccinationsRecorded - - const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) - organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) } let totalsBySite = [] diff --git a/app/routes/reports.js b/app/routes/reports.js index 2165795a..3ce25df5 100644 --- a/app/routes/reports.js +++ b/app/routes/reports.js @@ -115,9 +115,7 @@ module.exports = (router) => { }) router.get('/reports/choose-site', (req, res) => { - const data = req.session.data const currentOrganisation = res.locals.currentOrganisation - const currentUser = res.locals.currentUser const sites = currentOrganisation.sites || [] res.render('reports/choose-site', { @@ -196,7 +194,6 @@ module.exports = (router) => { router.get('/reports/check', (req, res) => { const data = req.session.data const currentOrganisation = res.locals.currentOrganisation - const currentUser = res.locals.currentUser const siteIds = data.siteIdsToReport || [] const today = new Date() const days = 86400000 // number of milliseconds in a day @@ -205,19 +202,9 @@ module.exports = (router) => { let sites, pharmacies - if (currentOrganisation) { - - sites = (currentOrganisation.sites || []) + sites = (currentOrganisation.sites || []) .filter((site) => siteIds.includes(site.id)) - } else { - - const userOrganisationIds = currentUser.organisations.map((organisation) => organisation.id) - - organisations = data.organisations.filter((organisation) => userOrganisationIds.includes(organisation.id) ) - .filter((organisation) => siteIds.includes(organisation.id)) - } - pharmacies = data.organisations.filter((pharmacy) => pharmacyIdsToReport.includes(pharmacy.id)) const fromInput = data.from From 4e96faccc918933086ae721c3775bbc65144e289 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:10:23 +0100 Subject: [PATCH 35/37] Simplify code --- app/routes/pharmacies.js | 4 +--- app/routes/reports.js | 19 +++++-------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 727be355..203f76e2 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -66,9 +66,7 @@ module.exports = router => { router.post('/pharmacies/added', async (req, res) => { const data = req.session.data - // TODO: get this from the current login - // rather than hardcode it - const companyId = 'P15951' + const companyId = res.locals.currentOrganisation.id let pharmacies = await getPharmaciesBelongingToOrganisation("P15J") diff --git a/app/routes/reports.js b/app/routes/reports.js index 3ce25df5..230ac52a 100644 --- a/app/routes/reports.js +++ b/app/routes/reports.js @@ -7,24 +7,15 @@ module.exports = (router) => { const currentOrganisation = res.locals.currentOrganisation let vaccinationsRecordedCount - if (currentOrganisation) { + if (currentOrganisation.type === "Pharmacy HQ") { - if (currentOrganisation.type === "Pharmacy HQ") { - - // TODO count vaccinations recorded at any pharmacies within this group. - vaccinationsRecordedCount = 1 - - } else { - - vaccinationsRecordedCount = data.vaccinationsRecorded.filter((vaccination) => vaccination.organisationId === currentOrganisation.id).length - - } + // TODO count vaccinations recorded at any pharmacies within this group. + vaccinationsRecordedCount = 1 } else { - // TODO: count across all organisations you - // have access to - vaccinationsRecordedCount = 100 + vaccinationsRecordedCount = data.vaccinationsRecorded.filter((vaccination) => vaccination.organisationId === currentOrganisation.id).length + } res.render('reports/index', { From 13e57a0bbca5b92cd3703954c64df12d9c0c8cb4 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:21:59 +0100 Subject: [PATCH 36/37] Revert default user --- app/data/session-data-defaults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/data/session-data-defaults.js b/app/data/session-data-defaults.js index ace71cb2..c0027bf0 100644 --- a/app/data/session-data-defaults.js +++ b/app/data/session-data-defaults.js @@ -15,8 +15,8 @@ module.exports = { vaccineStock: vaccineStock, lists: [], nhsNumberKnown: "yes", - currentUserId: "6424325235325", - currentOrganisationId: "P15951", + currentUserId: "2387441662601", + currentOrganisationId: "RW3", vaccinationsRecorded: vaccinationsRecorded, // These are the options for extracting CSV reports From 1da9808df0b78869f7b3e7320c02ff76027d78d6 Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Mon, 11 May 2026 17:45:31 +0100 Subject: [PATCH 37/37] Add check page --- app/routes/pharmacies.js | 22 ++++++- app/views/pharmacies/users/check.html | 88 +++++++++++++++++++++++++++ app/views/pharmacies/users/new.html | 11 ++-- 3 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 app/views/pharmacies/users/check.html diff --git a/app/routes/pharmacies.js b/app/routes/pharmacies.js index 203f76e2..f9da70a7 100644 --- a/app/routes/pharmacies.js +++ b/app/routes/pharmacies.js @@ -111,8 +111,15 @@ module.exports = router => { }) router.post('/pharmacies/users/new-answer',(req, res) => { + const data = req.session.data + const permissionLevel = data.permissionLevel + + if (permissionLevel === "Group administrator") { + res.redirect('/pharmacies/users/check') + } else { + res.redirect('/pharmacies/users') + } - res.redirect('/pharmacies/users') }) router.get('/pharmacies/add-lead-admins',(req, res) => { @@ -124,6 +131,19 @@ module.exports = router => { }) }) + router.get('/pharmacies/users/check',(req, res) => { + + res.render('pharmacies/users/check') + }) + + router.post('/pharmacies/users/check-answer',(req, res) => { + // TODO: add the user + + res.redirect('/pharmacies/users?added=true') + }) + + + router.get('/pharmacies/users/:id',(req, res) => { const data = req.session.data const id = req.params.id diff --git a/app/views/pharmacies/users/check.html b/app/views/pharmacies/users/check.html new file mode 100644 index 00000000..4495c722 --- /dev/null +++ b/app/views/pharmacies/users/check.html @@ -0,0 +1,88 @@ +{% extends 'layout.html' %} + +{% set currentSection = "pharmacies-users" %} + +{% block beforeContent %} + {{ backLink({ + href: "/pharmacies/users/new", + text: "Back" + }) }} +{% endblock %} + +{% block content %} +
+
+ +

Check and add group administrator

+ + {{ summaryList({ + rows: [ + { + key: { + text: "Name" + }, + value: { + text: data.firstName + " " + data.lastName + }, + actions: { + items: [ + { + href: "/pharmacies/users/new", + text: "Change", + visuallyHiddenText: "name" + } + ] + } + }, + { + key: { + text: "Email address" + }, + value: { + html: data.email + }, + actions: { + items: [ + { + href: "/pharmacies/users/new", + text: "Change", + visuallyHiddenText: "email address" + } + ] + } + }, + { + key: { + text: "Permission level" + }, + value: { + html: data.permissionLevel + }, + actions: { + items: [ + { + href: "/pharmacies/users/new", + text: "Change", + visuallyHiddenText: "permission level" + } + ] + } + } + ] + }) }} + +

{{ data.firstName }} will receive this welcome email with information about activating an account:

+ +
+ {% include "user-admin/_welcome-email.html" %} +
+ + + {{ button({ + "text": "Confirm and send" + }) }} + + +
+
+{% endblock %} diff --git a/app/views/pharmacies/users/new.html b/app/views/pharmacies/users/new.html index 9987c923..91a3f1fc 100644 --- a/app/views/pharmacies/users/new.html +++ b/app/views/pharmacies/users/new.html @@ -53,8 +53,8 @@

Add user

}) }} {{ radios({ - idPrefix: "example", - name: "example", + idPrefix: "permission-level", + name: "permissionLevel", fieldset: { legend: { text: "Permission level", @@ -63,14 +63,15 @@

Add user

}, items: [ { - value: "all", - text: "Group admin for Peak Pharmacy" + value: "Group administrator", + text: "Group administrator for Peak Pharmacy" }, { divider: "or" }, { - text: "I’ll add them to individual pharmacies" + text: "I’ll add them to individual pharmacies later", + value: "none" } ] }) }}