Skip to main content

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 attributeTypeSource
summaryImmunizationSummaryfhirService.buildSummary(ctx)
recommendationsList<VaccineRecommendation>fhirService.getRecommendations(ctx)
recentHistoryList<VaccinationRecord> (max 5)fhirService.getVaccinationHistory(ctx).stream().limit(5)
ctxSmartLaunchContextHTTP session
secondsRemaininglongctx.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:

ParamTypeEffect
vaccineCodeStringFilter to one vaccine type
fromLocalDate (ISO)Earliest occurrence date
toLocalDate (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():

  1. Loads Patient from FHIR (cached)
  2. Filters vaccination history to isValid() records only
  3. Calls VdsNcService.generateQrBase64(name, dob, validRecords, 300) → Base64 PNG
  4. Passes all data to the template

Model attributes:

AttributeType
patientNameString
patientDobString (ISO date or null)
vaccinationsList<VaccinationRecord> (valid only)
qrBase64String (Base64 PNG) or null
generatedDateLocalDate

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.

PDF export

In Chrome, print to PDF using "Save as PDF" destination. The certificate fits on one A4 sheet with default margins.


Session & Security · API Reference →