diff --git a/app/routes/home.js b/app/routes/home.js index a70f4c6d4..f36398a5 100644 --- a/app/routes/home.js +++ b/app/routes/home.js @@ -67,8 +67,93 @@ module.exports = router => { const data = req.session.data const allVaccinationsRecorded = data.vaccinationsRecorded - const dateToday = new Date() + const simulatedDateParam = req.query.simulatedDate + const simulatedDateMatch = (typeof simulatedDateParam === 'string') + ? simulatedDateParam.match(/^(\d{4})-(\d{2})-(\d{2})$/) + : null + + const dateToday = simulatedDateMatch + ? new Date(Date.UTC( + parseInt(simulatedDateMatch[1]), + parseInt(simulatedDateMatch[2]) - 1, + parseInt(simulatedDateMatch[3]), + 0, + 0, + 0 + )) + : new Date() + + const dateYesterday = new Date(dateToday) + dateYesterday.setDate(dateToday.getDate() - 1) + + const startOfThisWeek = new Date(Date.UTC( + dateToday.getUTCFullYear(), + dateToday.getUTCMonth(), + dateToday.getUTCDate() - ((dateToday.getUTCDay() + 6) % 7), + 0, + 0, + 0 + )) + const startOfLastCalendarWeek = new Date(startOfThisWeek) + startOfLastCalendarWeek.setUTCDate(startOfThisWeek.getUTCDate() - 7) + const endOfLastCalendarWeek = new Date(startOfThisWeek) + endOfLastCalendarWeek.setUTCDate(startOfThisWeek.getUTCDate() - 1) + endOfLastCalendarWeek.setUTCHours(23, 59, 59, 999) + + const monthNameFormatter = new Intl.DateTimeFormat('en-GB', { + month: 'long', + timeZone: 'UTC' + }) + const shortMonthNameFormatter = new Intl.DateTimeFormat('en-GB', { + month: 'short', + timeZone: 'UTC' + }) + const lastCalendarWeekStartDay = startOfLastCalendarWeek.getUTCDate() + const lastCalendarWeekEndDay = endOfLastCalendarWeek.getUTCDate() + const lastCalendarWeekEndMonth = monthNameFormatter.format(endOfLastCalendarWeek) + const lastCalendarWeekStartMonthShort = shortMonthNameFormatter.format(startOfLastCalendarWeek) + const lastCalendarWeekEndMonthShort = shortMonthNameFormatter.format(endOfLastCalendarWeek) + const isLastCalendarWeekInSameMonth = ( + startOfLastCalendarWeek.getUTCMonth() === endOfLastCalendarWeek.getUTCMonth() && + startOfLastCalendarWeek.getUTCFullYear() === endOfLastCalendarWeek.getUTCFullYear() + ) + + const lastCalendarWeekRangeLabel = isLastCalendarWeekInSameMonth + ? `${lastCalendarWeekStartDay} to ${lastCalendarWeekEndDay} ${lastCalendarWeekEndMonth}` + : `${lastCalendarWeekStartDay} ${lastCalendarWeekStartMonthShort} to ${lastCalendarWeekEndDay} ${lastCalendarWeekEndMonthShort}` + + const lastCalendarWeekHeaderLineOne = isLastCalendarWeekInSameMonth + ? `${lastCalendarWeekStartDay} to ${lastCalendarWeekEndDay}` + : `${lastCalendarWeekStartDay} ${lastCalendarWeekStartMonthShort} to` + + const lastCalendarWeekHeaderLineTwo = isLastCalendarWeekInSameMonth + ? lastCalendarWeekEndMonth + : `${lastCalendarWeekEndDay} ${lastCalendarWeekEndMonthShort}` + + const startOfThisMonth = new Date(Date.UTC( + dateToday.getUTCFullYear(), + dateToday.getUTCMonth(), + 1, + 0, + 0, + 0 + )) + const startOfLastCalendarMonth = new Date(Date.UTC( + startOfThisMonth.getUTCFullYear(), + startOfThisMonth.getUTCMonth() - 1, + 1, + 0, + 0, + 0 + )) + const endOfLastCalendarMonth = new Date(startOfThisMonth) + endOfLastCalendarMonth.setUTCDate(0) + endOfLastCalendarMonth.setUTCHours(23, 59, 59, 999) + const monthToday = (dateToday.getMonth() + 1) // JavaScript dates are 0-indexed + const currentYear = dateToday.getFullYear() + const lastCalendarMonth = (startOfLastCalendarMonth.getMonth() + 1) + const lastCalendarMonthYear = startOfLastCalendarMonth.getFullYear() // Vaccinations to count let vaccinationsRecorded = [] @@ -105,6 +190,20 @@ module.exports = router => { let totalsByVaccine = [] let totalsByDay = [] + const selectedSiteIdFromQuery = req.query.siteId + const hasMultipleVisibleSites = sites.length > 1 + const selectedByVaccinationSiteId = ( + hasMultipleVisibleSites && + selectedSiteIdFromQuery && + sites.some((site) => site.id === selectedSiteIdFromQuery) + ) + ? selectedSiteIdFromQuery + : 'all' + + const vaccinationsRecordedForByVaccine = (selectedByVaccinationSiteId === 'all') + ? vaccinationsRecorded + : vaccinationsRecorded.filter((vaccination) => vaccination.siteId === selectedByVaccinationSiteId) + const totalVaccinationsRecorded = countVaccinations(vaccinationsRecorded) const totalVaccinationsRecordedToday = countVaccinations( @@ -112,6 +211,11 @@ module.exports = router => { {date: dateToday} ) + const totalVaccinationsRecordedYesterday = countVaccinations( + vaccinationsRecorded, + {date: dateYesterday} + ) + const sevenDaysAgo = new Date(Date.UTC(dateToday.getFullYear(), dateToday.getMonth(), dateToday.getDate() - 7, 0, 0, 0)); const totalVaccinationsRecordedPast7Days = countVaccinations( @@ -123,7 +227,24 @@ module.exports = router => { month: dateToday }) - const uniqueVaccinesRecorded = [...new Set(vaccinationsRecorded.map((vaccination) => vaccination.vaccine))] + const totalVaccinationsRecordedLastCalendarWeek = countVaccinations( + vaccinationsRecorded, + { + // Range counting uses > minDate and <= maxDate, so move minDate back one day + // to include all vaccinations on the start date. + minDate: new Date(Date.UTC( + startOfLastCalendarWeek.getUTCFullYear(), + startOfLastCalendarWeek.getUTCMonth(), + startOfLastCalendarWeek.getUTCDate() - 1, + 0, + 0, + 0 + )), + maxDate: endOfLastCalendarWeek + } + ) + + const uniqueVaccinesRecorded = [...new Set(vaccinationsRecordedForByVaccine.map((vaccination) => vaccination.vaccine))] for (let i = 0; i < 7; i++) { @@ -142,11 +263,39 @@ module.exports = router => { totalsByVaccine.push({ vaccine: vaccine, - today: countVaccinations(vaccinationsRecorded, { + today: countVaccinations(vaccinationsRecordedForByVaccine, { date: dateToday, vaccine: vaccine }), - month: countVaccinations(vaccinationsRecorded, { + yesterday: countVaccinations(vaccinationsRecordedForByVaccine, { + date: dateYesterday, + vaccine: vaccine + }), + lastCalendarWeek: countVaccinations(vaccinationsRecordedForByVaccine, { + minDate: new Date(Date.UTC( + startOfLastCalendarWeek.getUTCFullYear(), + startOfLastCalendarWeek.getUTCMonth(), + startOfLastCalendarWeek.getUTCDate() - 1, + 0, + 0, + 0 + )), + maxDate: endOfLastCalendarWeek, + vaccine: vaccine + }), + lastCalendarMonth: countVaccinations(vaccinationsRecordedForByVaccine, { + minDate: new Date(Date.UTC( + startOfLastCalendarMonth.getUTCFullYear(), + startOfLastCalendarMonth.getUTCMonth(), + startOfLastCalendarMonth.getUTCDate() - 1, + 0, + 0, + 0 + )), + maxDate: endOfLastCalendarMonth, + vaccine: vaccine + }), + thisMonth: countVaccinations(vaccinationsRecordedForByVaccine, { month: dateToday, vaccine: vaccine }), @@ -175,13 +324,36 @@ module.exports = router => { date: dateToday, siteId: site.id }), - month:countVaccinations(vaccinationsRecorded, { - month: dateToday, + yesterday: countVaccinations(vaccinationsRecorded, { + date: dateYesterday, siteId: site.id }), - past7Days: countVaccinations(vaccinationsRecorded, { - minDate: sevenDaysAgo, - maxDate: dateToday, + lastCalendarWeek: countVaccinations(vaccinationsRecorded, { + minDate: new Date(Date.UTC( + startOfLastCalendarWeek.getUTCFullYear(), + startOfLastCalendarWeek.getUTCMonth(), + startOfLastCalendarWeek.getUTCDate() - 1, + 0, + 0, + 0 + )), + maxDate: endOfLastCalendarWeek, + siteId: site.id + }), + lastCalendarMonth: countVaccinations(vaccinationsRecorded, { + minDate: new Date(Date.UTC( + startOfLastCalendarMonth.getUTCFullYear(), + startOfLastCalendarMonth.getUTCMonth(), + startOfLastCalendarMonth.getUTCDate() - 1, + 0, + 0, + 0 + )), + maxDate: endOfLastCalendarMonth, + siteId: site.id + }), + thisMonth: countVaccinations(vaccinationsRecorded, { + month: dateToday, siteId: site.id }), total: total @@ -220,11 +392,20 @@ module.exports = router => { res.render('dashboard/index', { sites, pharmacies, + selectedByVaccinationSiteId, totalVaccinationsRecorded, totalVaccinationsRecordedToday, + totalVaccinationsRecordedYesterday, totalVaccinationsRecordedThisMonth, + totalVaccinationsRecordedLastCalendarWeek, totalVaccinationsRecordedPast7Days, monthToday, + currentYear, + lastCalendarWeekRangeLabel, + lastCalendarWeekHeaderLineOne, + lastCalendarWeekHeaderLineTwo, + lastCalendarMonth, + lastCalendarMonthYear, totalsBySite, totalsByVaccine, totalsByDay, diff --git a/app/views/dashboard/_by-site.html b/app/views/dashboard/_by-site.html index af72f9f8..14353c1f 100644 --- a/app/views/dashboard/_by-site.html +++ b/app/views/dashboard/_by-site.html @@ -9,13 +9,17 @@ Today - Past 7 days + Yesterday - {{ (monthToday | monthName) }} to date + {{ lastCalendarWeekHeaderLineOne }} + {{ lastCalendarWeekHeaderLineTwo }} - Total + {{ (lastCalendarMonth | monthName) }} {{ lastCalendarMonthYear }} + + + {{ (monthToday | monthName) }} {{ currentYear }} @@ -30,13 +34,16 @@ {{ totalBySite.today }} - {{ totalBySite.past7Days }} + {{ totalBySite.yesterday }} - {{ totalBySite.month }} + {{ totalBySite.lastCalendarWeek }} + + + {{ totalBySite.lastCalendarMonth }} - {{ totalBySite.total }} + {{ totalBySite.thisMonth }} {% endfor %} diff --git a/app/views/dashboard/_by-vaccine.html b/app/views/dashboard/_by-vaccine.html index 56277088..fef9cdd1 100644 --- a/app/views/dashboard/_by-vaccine.html +++ b/app/views/dashboard/_by-vaccine.html @@ -1,46 +1,99 @@ - - - - - - - - - - - - +

By vaccination

+ +{% if (sites | length) > 1 %} + {% set siteFilterItems = [{ + text: "All sites", + value: "all", + selected: (selectedByVaccinationSiteId == "all") + }] %} - {% for totalByVaccine in (totalsByVaccine | sort(false, true, "vaccine") ) %} - - - - - - - + {% for site in (sites | sort(false, false, "name")) %} + {% set siteFilterItems = (siteFilterItems.push({ + text: site.name, + value: site.id, + selected: (selectedByVaccinationSiteId == site.id) + }), siteFilterItems) %} {% endfor %} + + {{ select({ + id: "dashboard-by-vaccination-site-filter", + name: "siteId", + label: { + text: "Filter by site", + size: "s" + }, + items: siteFilterItems, + attributes: { + onchange: "this.form.submit()" + } + }) }} + +{% endif %} + +{% if selectedByVaccinationSiteId != "all" and (totalsByVaccine | length) == 0 %} + {% set selectedSite = sites | findById(selectedByVaccinationSiteId) %} +

{{ selectedSite.name if selectedSite else "This site" }} has not yet recorded any vaccinations.

+{% else %} +
By vaccination
- Vaccination - - Today - - Past 7 days - - {{ (monthToday | monthName) }} to date - - Total -
- {{ (totalByVaccine.vaccine ) | capitaliseFirstLetter }} - - {{ totalByVaccine.today }} - - {{ totalByVaccine.past7Days }} - - {{ totalByVaccine.month }} - - {{ totalByVaccine.total }} -
+ + + + + + + + + + + + + {% for totalByVaccine in (totalsByVaccine | sort(false, true, "vaccine") ) %} + + + + + + + + + {% endfor %} + - -
+ Vaccine + + Today + + Yesterday + + {{ lastCalendarWeekHeaderLineOne }} + {{ lastCalendarWeekHeaderLineTwo }} + + {{ (lastCalendarMonth | monthName) }} {{ lastCalendarMonthYear }} + + {{ (monthToday | monthName) }} {{ currentYear }} +
+ Vaccine + {{ (totalByVaccine.vaccine ) | capitaliseFirstLetter }} + + Today + {{ totalByVaccine.today }} + + Yesterday + {{ totalByVaccine.yesterday }} + + + {{ lastCalendarWeekHeaderLineOne }} + {{ lastCalendarWeekHeaderLineTwo }} + + {{ totalByVaccine.lastCalendarWeek }} + + {{ (lastCalendarMonth | monthName) }} {{ lastCalendarMonthYear }} + {{ totalByVaccine.lastCalendarMonth }} + + {{ (monthToday | monthName) }} {{ currentYear }} + {{ totalByVaccine.thisMonth }} +
+ + +{% endif %} diff --git a/app/views/dashboard/_vaccination-totals.html b/app/views/dashboard/_vaccination-totals.html index 64c03011..1b377cd5 100644 --- a/app/views/dashboard/_vaccination-totals.html +++ b/app/views/dashboard/_vaccination-totals.html @@ -8,18 +8,18 @@ }), topStats) %} {% set topStats = (topStats.push({ - label: "Past 7 days", - value: totalVaccinationsRecordedPast7Days + label: "Yesterday", + value: totalVaccinationsRecordedYesterday }), topStats) %} {% set topStats = (topStats.push({ - label: (monthToday | monthName) + " to date", - value: totalVaccinationsRecordedThisMonth + label: lastCalendarWeekRangeLabel, + value: totalVaccinationsRecordedLastCalendarWeek }), topStats) %} {% set topStats = (topStats.push({ - label: "Total", - value: totalVaccinationsRecorded + label: (monthToday | monthName) + " to date", + value: totalVaccinationsRecordedThisMonth }), topStats) %} {% for stat in topStats %} @@ -27,6 +27,10 @@ {% set cardHtml %}

{{ stat.value }}

{{ stat.label }} + {% if stat.hint %} +
+ {{ stat.hint }} + {% endif %} {% endset %} {{ card({ diff --git a/app/views/dashboard/index.html b/app/views/dashboard/index.html index 5d90a5e1..bd2c1dfc 100644 --- a/app/views/dashboard/index.html +++ b/app/views/dashboard/index.html @@ -15,6 +15,10 @@

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

+ {{ insetText({ + text: "The data shown here is based on the date the vaccination was given." + }) }} + {% if currentOrganisation and totalVaccinationsRecorded == 0 %} {% include "dashboard/_no-vaccinations-recorded.html" %} @@ -54,19 +58,20 @@

All vaccinations

{{ tabs({ items: [ { - label: "By day", - id: "past-7-days", + label: "By vaccination", + id: "by-vaccination", + selected: true, panel: { - html: byDayHtml + html: byVaccinationHtml } }, { - label: "By vaccination", - id: "by-vaccination", + label: "By day", + id: "past-7-days", panel: { - html: byVaccinationHtml + html: byDayHtml } - } if (totalsByVaccine | length) > 1, + }, { label: "By site", id: "by-site",