From 76052998152718424f2af7385c0ac7447e7c784e Mon Sep 17 00:00:00 2001 From: Frankie Roberto Date: Fri, 20 Mar 2026 10:49:13 +0000 Subject: [PATCH 1/8] Allow users to continue to log in after signing up a chain --- app/lib/ods.js | 10 +++-- app/lib/utils/capitalise-from-ods.js | 39 ++++++++++++++++++++ app/routes/apply.js | 45 +++++++++++++++++++++++ app/routes/auth.js | 8 ++-- app/routes/home.js | 2 +- app/routes/vaccines.js | 2 + app/views/apply/check-chain.html | 6 +-- app/views/apply/check-pharmacy-chain.html | 4 +- app/views/apply/pharmacies.html | 2 +- app/views/apply/pharmacy-chain-check.html | 2 +- app/views/apply/start.html | 2 +- app/views/apply/welcome-email-chain.html | 12 +++--- app/views/auth/okta-sign-in.html | 2 +- app/views/auth/select-organisation.html | 2 +- 14 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 app/lib/utils/capitalise-from-ods.js diff --git a/app/lib/ods.js b/app/lib/ods.js index debd8b46..bb7c8b50 100644 --- a/app/lib/ods.js +++ b/app/lib/ods.js @@ -1,3 +1,5 @@ +const { capitaliseFromOds } = require('./utils/capitalise-from-ods.js') + // There are 2 different ODS APIs, the old ORD API and the newer FHIR-based API. // The ORD API is officially deprecated and may be retired in the future. @@ -32,7 +34,7 @@ async function fetchPaginatedOrganisations(queryParams) { const results = (data.Organisations || []).map(function(org) { return { id: org.OrgId, - name: org.Name, + name: capitaliseFromOds(org.Name), address: { postcode: org.PostCode } @@ -81,10 +83,10 @@ async function getOrganisation(id) { const organisation = { id: data.id, - name: data.name, + name: capitaliseFromOds(data.name), address: { - line1: data.address[0].line[0], - town: data.address[0].city, + line1: capitaliseFromOds(data.address[0].line[0]), + town: capitaliseFromOds(data.address[0].city), postcode: data.address[0].postalCode } } diff --git a/app/lib/utils/capitalise-from-ods.js b/app/lib/utils/capitalise-from-ods.js new file mode 100644 index 00000000..d43b0630 --- /dev/null +++ b/app/lib/utils/capitalise-from-ods.js @@ -0,0 +1,39 @@ +// Words that should remain lowercase (unless at the start) +const lowercaseWords = ['and', 'the', 'of', 'in', 'for', 'on', 'at', 'to', 'by', 'with', 'a', 'an']; + +// Acronyms that should remain uppercase +const acronyms = ['NHS', 'GP', 'PCN', 'CCG', 'ICB', 'CIC', 'UK', 'LLP', 'PLC']; + +module.exports.capitaliseFromOds = function(input) { + if (!input) return input; + + const words = input.toLowerCase().split(/\s+/); + + const capitalisedWords = words.map((word, index) => { + // Check if word is initials (e.g. "p.g." or "a.b.c.") + if (/^([a-z]\.)+$/i.test(word)) { + return word.toUpperCase(); + } + + // Check if word is a single letter (keep uppercase) + if (/^[a-z]$/i.test(word)) { + return word.toUpperCase(); + } + + // Check if upper-cased word is an acronym + const upperWord = word.toUpperCase(); + if (acronyms.includes(upperWord)) { + return upperWord; + } + + // Check if word should remain lowercase (but not if it's the first word) + if (index !== 0 && lowercaseWords.includes(word)) { + return word; + } + + // Capitalise first letter, rest lowercase + return word.charAt(0).toUpperCase() + word.slice(1); + }); + + return capitalisedWords.join(' '); +} diff --git a/app/routes/apply.js b/app/routes/apply.js index 3abcbed4..bad1f9ba 100644 --- a/app/routes/apply.js +++ b/app/routes/apply.js @@ -157,6 +157,51 @@ module.exports = router => { res.redirect('/apply/check-your-email') }) + // Routing after the final check answers for chains page + router.post('/apply/check-chain-answer', async (req, res) => { + const data = req.session.data + + let pharmacies = await getPharmaciesBelongingToOrganisation(data.pharmacyChainId) + + pharmacies = pharmacies.filter((pharmacy) => { + return data.pharmacyIds.includes(pharmacy.id) + }) + + let userOrganisationPermissions = [] + + for (pharmacy of pharmacies) { + + // Add the pharmacy itself as the single site + pharmacy.sites = [ + { + id: pharmacy.id, + name: pharmacy.name, + address: pharmacy.address + } + ] + + data.organisations.push(pharmacy) + userOrganisationPermissions.push({ + id: pharmacy.id, + permissionLevel: 'Lead administrator', + status: 'Active', + vaccinator: false + }) + } + + const user = { + id: Math.floor(Math.random() * 10000000).toString(), + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + organisations: userOrganisationPermissions + } + + data.users.push(user) + + res.redirect('/apply/check-your-email-chain') + }) + // Welcome email mockup router.get('/apply/welcome-email', (req, res) => { const data = req.session.data diff --git a/app/routes/auth.js b/app/routes/auth.js index 20548c23..4dbd4078 100644 --- a/app/routes/auth.js +++ b/app/routes/auth.js @@ -55,7 +55,10 @@ module.exports = router => { } else if (organisationsUserIsAnAdminAt.length > 1) { req.session.data.currentUserId = user.id - res.redirect('/auth/select-mode') + + // Skipping the select mode screen for research purposes + res.redirect('/auth/select-organisation') + // res.redirect('/auth/select-mode') } else { @@ -105,12 +108,11 @@ module.exports = router => { const data = req.session.data const email = data.email - const user = data.users.find((user) => user.email === email) + const user = res.locals.currentUser const selectedOrganisationId = req.session.data.organisationId if (selectedOrganisationId) { - req.session.data.currentUserId = user.id; req.session.data.currentOrganisationId = selectedOrganisationId; res.redirect('/survey') diff --git a/app/routes/home.js b/app/routes/home.js index 5cd163cd..bc890a75 100644 --- a/app/routes/home.js +++ b/app/routes/home.js @@ -85,7 +85,7 @@ module.exports = router => { // organisation vaccinationsRecorded = allVaccinationsRecorded.filter((vaccination)=> vaccination.organisationId === currentOrganisation.id) - if (sites.length === 0) { + if (!sites.length || sites.length === 0) { sites = [currentOrganisation] } diff --git a/app/routes/vaccines.js b/app/routes/vaccines.js index 5ef4a696..1ed8a0f0 100644 --- a/app/routes/vaccines.js +++ b/app/routes/vaccines.js @@ -105,6 +105,8 @@ module.exports = (router) => { for (const vaccine of vaccinesAdded) { + currentOrganisation.vaccines ||= [] + let vaccineToEnable = currentOrganisation.vaccines.find((vaccine) => vaccine.name === vaccine) if (vaccineToEnable) { diff --git a/app/views/apply/check-chain.html b/app/views/apply/check-chain.html index 3fb1087a..2b097c48 100644 --- a/app/views/apply/check-chain.html +++ b/app/views/apply/check-chain.html @@ -16,7 +16,7 @@

{{ pageName }}

{% set pharmaciesHtml %} {% for pharmacy in pharmacies %} - {{ pharmacy.name | capitalize }} ({{ pharmacy.id }}) + {{ pharmacy.name }} ({{ pharmacy.id }})
{% endfor %} {% endset %} @@ -29,7 +29,7 @@

Your pharmacies

text: "Company" }, value: { - html: (pharmacyChain.name | capitalize) + html: pharmacyChain.name }, actions: { items: [ @@ -146,7 +146,7 @@

2nd lead administrator

}) }} {% endif %} -
+ {{ button({ text: "Confirm" }) }} diff --git a/app/views/apply/check-pharmacy-chain.html b/app/views/apply/check-pharmacy-chain.html index a745c2f4..4f686713 100644 --- a/app/views/apply/check-pharmacy-chain.html +++ b/app/views/apply/check-pharmacy-chain.html @@ -22,7 +22,7 @@

{{ pageName }}

text: "Name" }, value: { - text: organisation.name | capitalize + text: organisation.name } }, { @@ -46,7 +46,7 @@

{{ pageName }}

text: "Address" }, value: { - html: (organisation.address.line1 | capitalize) + "
" + (organisation.address.town | capitalize) + "
" + organisation.address.postcode + html: organisation.address.line1 + "
" + organisation.address.town + "
" + organisation.address.postcode } } ] diff --git a/app/views/apply/pharmacies.html b/app/views/apply/pharmacies.html index e8c2af9d..2ceb04cf 100644 --- a/app/views/apply/pharmacies.html +++ b/app/views/apply/pharmacies.html @@ -31,7 +31,7 @@ {% for pharmacy in pharmacies %} {% set items = (items.push({ - text: (pharmacy.name | capitalize) + ", " + pharmacy.address.postcode + " (" + pharmacy.id + ")", + text: pharmacy.name + ", " + pharmacy.address.postcode + " (" + pharmacy.id + ")", value: pharmacy.id }), items) %} {% endfor %} diff --git a/app/views/apply/pharmacy-chain-check.html b/app/views/apply/pharmacy-chain-check.html index ed817f61..6c0cdd4b 100644 --- a/app/views/apply/pharmacy-chain-check.html +++ b/app/views/apply/pharmacy-chain-check.html @@ -28,7 +28,7 @@

{{ pageName }}

{% for pharmacy in pharmacies %} - {{ pharmacy.name | capitalize }}, {{ pharmacy.address.postcode }} ({{ pharmacy.id }}) + {{ pharmacy.name }}, {{ pharmacy.address.postcode }} ({{ pharmacy.id }}) {{ button({ text: "Remove", diff --git a/app/views/apply/start.html b/app/views/apply/start.html index 7154561c..79ec5629 100644 --- a/app/views/apply/start.html +++ b/app/views/apply/start.html @@ -37,7 +37,7 @@ {% for pharmacyCompany in (allPharmacyCompanies | sort(false, false, "name")) %} {% set pharmacyCompanyItems = (pharmacyCompanyItems.push({ - text: (pharmacyCompany.name | capitalize) + " (" + (pharmacyCompany.id) + ")", + text: (pharmacyCompany.name) + " (" + (pharmacyCompany.id) + ")", value: pharmacyCompany.id }), pharmacyCompanyItems) %} {% endfor %} diff --git a/app/views/apply/welcome-email-chain.html b/app/views/apply/welcome-email-chain.html index 4b0acc01..338b7e82 100644 --- a/app/views/apply/welcome-email-chain.html +++ b/app/views/apply/welcome-email-chain.html @@ -26,18 +26,18 @@

1. Activate your Okta account

If you cannot find the email, check your spam or junk.

2. Log in to the Record a vaccination service

-

Once you’ve activated your Okta account, log in to www.ravs.england.nhs.uk using your Okta username and password.

+

Once you’ve activated your Okta account, log in to www.ravs.england.nhs.uk using your Okta username and password.

You can also access the service through your Okta account by clicking ‘RAVS (PROD) app’.

3. Add users to individual pharmacies

When you log in, you'll see a list of pharmacies where you now have access to the service.

-

To give more users access to the service at individual pharmacies:

- +

To give more users access to the service at individual pharmacies:

+ - +

Training and support

To help you get started, you can: