Skip to main content

UI Layer

UiControllerโ€‹

UiController handles all browser-facing routes. It builds a SessionModel for every request โ€” a typed view model passed to Thymeleaf.

@GetMapping("/dashboard")
public String dashboard(HttpSession session, Model model) {
SmartLaunchContext ctx = getContext(session);

model.addAttribute("session", buildSessionModel(ctx));
model.addAttribute("patient", fhirService.loadPatient(ctx));
model.addAttribute("stats", fhirService.loadStats(ctx));

return "dashboard";
}

SessionModelโ€‹

The SessionModel record flattens SmartLaunchContext into template-friendly fields:

FieldTypeDescription
patientIdStringFHIR Patient ID
patientNameStringPatient display name
clinicianNameStringFrom UserProfile.name
fhirUserStringPractitioner reference
needBannerbooleanFrom needPatientBanner
secondsRemaininglongexpiresAt - now()
scopeListList<String>Granted scopes
fhirBaseUrlStringFor client-side display

Patient bannerโ€‹

The patient banner is rendered on every clinical page when needBanner=true:

<div class="patient-banner" th:if="${session.needBanner}">
<div class="patient-id">
<span class="mrn-label">MRN</span>
<code th:text="${session.patientId}"
onclick="navigator.clipboard.writeText(this.innerText)">โ€”</code>
</div>
<div class="patient-name" th:text="${session.patientName}">โ€”</div>
</div>

Epic's SMART App Launch specification requires the patient banner when need_patient_banner=true. Omitting it fails App Orchard review.

Session timerโ€‹

app.js shows a countdown and warns the clinician before session expiry:

const secondsRemaining = /*[[${session.secondsRemaining}]]*/ 3600;
let remaining = secondsRemaining;

setInterval(() => {
remaining--;
if (remaining <= 120) {
document.getElementById('session-warning').style.display = 'block';
document.getElementById('session-countdown').textContent =
Math.floor(remaining / 60) + ':' + String(remaining % 60).padStart(2, '0');
}
if (remaining <= 0) {
window.location.href = '/launch?error=session_expired';
}
}, 1000);

Template structureโ€‹

src/main/resources/templates/
โ”œโ”€โ”€ layout.html Base layout โ€” nav, patient banner, footer
โ”œโ”€โ”€ dashboard.html Stat cards, recent activity
โ”œโ”€โ”€ patient/
โ”‚ โ”œโ”€โ”€ conditions.html Conditions table with ICD-10 codes
โ”‚ โ””โ”€โ”€ medications.html Medications table with status badges
โ””โ”€โ”€ error.html Error page

All templates extend layout.html via Thymeleaf fragment:

<html th:replace="~{layout :: page(~{::title}, ~{::main})}">

โ† Token Refresh ยท Developer Guide Home