UI Views
The app has four browser-facing views, all served by ImmunizationController. All templates extend from a consistent design using inline CSS (no external stylesheet required, no CDN dependency).
Dashboard (GET /dashboard)
The entry point after SMART launch. ImmunizationController.dashboard() populates:
| Model attribute | Type | Source |
|---|---|---|
summary | ImmunizationSummary | fhirService.buildSummary(ctx) |
recommendations | List<VaccineRecommendation> | fhirService.getRecommendations(ctx) |
recentHistory | List<VaccinationRecord> (max 5) | fhirService.getVaccinationHistory(ctx).stream().limit(5) |
ctx | SmartLaunchContext | HTTP session |
secondsRemaining | long | ctx.secondsRemaining() |
What renders:
- Patient banner — shown when
ctx.isNeedPatientBanner()is true; displays patient name - Overdue alert — a red warning banner when
summary.overdueCount() > 0 - Four stat cards — Total vaccinations, Overdue, Due this month, Total recommendations
- Recent vaccinations — last 5 records with vaccine name, dose, facility, date
- Upcoming & overdue — top recommendations (non-immune) with status badges
- Session timer — JavaScript countdown; warns at 5 minutes, redirects at 0
The session timer uses a th:inline="javascript" block:
const secs = [[${secondsRemaining}]];
let remaining = secs;
setInterval(() => {
remaining--;
if (remaining <= 300) { /* show warning */ }
if (remaining <= 0) { window.location.href='/launch?error=session_expired'; }
}, 1000);
History (GET /history)
Full vaccination timeline with server-side filtering. The filter parameters are applied post-cache — getVaccinationHistory() always returns the full list from Caffeine, and Java streams filter it.
Query parameters:
| Param | Type | Effect |
|---|---|---|
vaccineCode | String | Filter to one vaccine type |
from | LocalDate (ISO) | Earliest occurrence date |
to | LocalDate (ISO) | Latest occurrence date |
if (vaccineCode != null && !vaccineCode.isBlank()) {
history = history.stream()
.filter(r -> vaccineCode.equalsIgnoreCase(r.vaccineCode()))
.toList();
}
The filter dropdown is populated from the distinct vaccine codes in the full unfiltered history:
List<String> vaccineCodes = fhirService.getVaccinationHistory(ctx).stream()
.map(VaccinationRecord::vaccineCode)
.filter(c -> c != null && !c.isBlank())
.distinct().sorted().toList();
What renders: A filterable table with columns for date, vaccine name, code, dose, lot number, administering facility, route/site, and a "Self-reported" badge for patientReported=true records.
Recommendations (GET /recommendations)
Displays ImmunizationRecommendation forecast entries grouped by forecast status.
ImmunizationController.recommendations() splits the recommendations list into four sub-lists before passing to the template:
model.addAttribute("overdue", recs.stream().filter(r -> "overdue".equals(r.forecastStatus())).toList());
model.addAttribute("due", recs.stream().filter(r -> "due".equals(r.forecastStatus())).toList());
model.addAttribute("upToDate", recs.stream().filter(r -> "immune".equals(r.forecastStatus())).toList());
model.addAttribute("other", recs.stream().filter(r ->
r.forecastStatus() != null && !List.of("overdue","due","immune").contains(r.forecastStatus())).toList());
What renders: Four labelled sections — Overdue (red), Due (amber), Up to date / Immune (green), Other — each with a count badge. Rows show vaccine name, series, dose number, and due date. Empty sections are hidden via th:if.
Certificate (GET /certificate)
Printable vaccination card with VDS-NC QR code.
ImmunizationController.certificate():
- Loads
Patientfrom FHIR (cached) - Filters vaccination history to
isValid()records only - Calls
VdsNcService.generateQrBase64(name, dob, validRecords, 300)→ Base64 PNG - Passes all data to the template
Model attributes:
| Attribute | Type |
|---|---|
patientName | String |
patientDob | String (ISO date or null) |
vaccinations | List<VaccinationRecord> (valid only) |
qrBase64 | String (Base64 PNG) or null |
generatedDate | LocalDate |
What renders: A bordered certificate card with a navy header, patient name and DOB, a vaccination table (date, vaccine, dose, lot number, facility), a VDS-NC QR code in the top-right corner, and a footer with the generation date and source reference.
Printing: A window.print() button triggers the print dialog. @media print CSS hides the navbar and print button, leaving only the certificate card. The result is print-ready for a single A4 page or can be saved as PDF.
In Chrome, print to PDF using "Save as PDF" destination. The certificate fits on one A4 sheet with default margins.