2026-02-20 15:28:41 -05:00
{{template "base.html" .}}
{{define "nav"}}
2026-02-20 23:59:46 -05:00
< div class = "flex items-center gap-4" >
< a href = "/patients" class = "nav-link" > All Patients< / a >
< a href = "/" class = "nav-link" > Home< / a >
{{if .Practitioner}}
< div class = "practitioner-mini-card" >
< div class = "avatar avatar-sm" >
{{if .Practitioner.FirstName}}{{slice .Practitioner.FirstName 0 1}}{{end}}{{if .Practitioner.LastName}}{{slice .Practitioner.LastName 0 1}}{{end}}
< / div >
< span class = "text-xs font-medium" > Dr. {{.Practitioner.LastName}}< / span >
< / div >
{{end}}
< form action = "/logout" method = "POST" class = "flex items-center" >
< button class = "btn btn-danger btn-sm" type = "submit" > Logout< / button >
< / form >
< / div >
2026-02-20 15:28:41 -05:00
{{end}}
{{define "content"}}
2026-02-20 17:46:34 -05:00
{{/* ---- Flash Messages ---- */}}
2026-02-20 17:17:26 -05:00
{{if .Synced}}
2026-02-20 23:59:46 -05:00
< div class = "flash-message mb-6" >
2026-02-20 17:46:34 -05:00
< div class = "flex items-center gap-2" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "20" height = "20" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" > < path d = "M22 11.08V12a10 10 0 1 1-5.93-9.14" / > < polyline points = "22 4 12 14.01 9 11.01" / > < / svg >
2026-02-20 17:17:26 -05:00
< div >
2026-02-20 23:59:46 -05:00
< strong class = "font-medium text-main" > Sync complete< / strong >
< span class = "text-sm text-muted" > Data has been refreshed from the EHR.< / span >
2026-02-20 17:17:26 -05:00
< / div >
< / div >
2026-02-20 23:59:46 -05:00
< button onclick = "this.parentElement.style.display='none';" class = "btn btn-icon text-muted" > × < / button >
2026-02-20 17:17:26 -05:00
< / div >
{{end}}
2026-02-20 23:59:46 -05:00
{{if .Patient}}
< div class = "flex flex-col gap-6 pt-4" >
{{/* ---- Top Header: Patient Banner ---- */}}
< div class = "panel panel-padded flex items-center justify-between border-l-brand" >
< div class = "flex items-center gap-6 w-full" >
< div class = "avatar avatar-lg" >
{{if .Patient.FirstName}}{{slice .Patient.FirstName 0 1}}{{end}}{{if .Patient.LastName}}{{slice .Patient.LastName 0 1}}{{end}}
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< div class = "flex flex-col gap-1 w-full" >
< div class = "flex items-center gap-3" >
< h1 class = "page-title m-0" >
{{.Patient.LastName}}, {{.Patient.FirstName}} {{.Patient.MiddleName}}
< / h1 >
< span class = "mrn-badge" > MRN: {{orDash .Patient.MRN}}< / span >
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< div class = "patient-meta-strip" >
< span class = "meta-item" > < strong > DOB:< / strong > {{formatDate .Patient.DOB}} ({{calculateAge .Patient.DOB}} yrs)< / span >
< span class = "meta-item" > < strong > Gender:< / strong > {{titleCase .Patient.Gender}}< / span >
{{with .Patient.Email}}< span class = "meta-item" > < strong > Email:< / strong > {{.}}< / span > {{end}}
{{with primaryPhone .RawPatient}}< span class = "meta-item" > < strong > Phone:< / strong > {{.}}< / span > {{end}}
2026-02-20 17:46:34 -05:00
< / div >
< / div >
2026-02-20 23:59:46 -05:00
< div class = "flex flex-col items-end gap-2 shrink-0" >
< form action = "/dashboard/sync?patient_id={{.Patient.FHIRID}}" method = "POST" class = "flex items-center" >
< button class = "btn btn-primary" type = "submit" id = "syncBtn" onclick = "onSyncClick()" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" > < path d = "M23 4v6h-6" / > < path d = "M1 20v-6h6" / > < path d = "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" / > < / svg >
Refresh Chart
< / button >
< / form >
2026-02-24 00:26:55 -05:00
< button class = "btn btn-outline" type = "button" onclick = "findInRimidi('{{.Patient.FHIRID}}')" title = "Search for this patient in the Rimidi system" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" > < circle cx = "11" cy = "11" r = "8" / > < line x1 = "21" y1 = "21" x2 = "16.65" y2 = "16.65" / > < / svg >
Find in Rimidi
< / button >
2026-02-20 23:59:46 -05:00
< span class = "text-xs text-muted" >
Last Synced: {{if .LatestSync}}{{formatDateTime .LatestSync.SyncedAt}}{{else}}Never{{end}}
< / span >
< / div >
2026-02-20 15:28:41 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< / div >
2026-02-20 15:28:41 -05:00
2026-02-20 23:59:46 -05:00
< div class = "dashboard-layout" >
{{/* ---- Main Content Column ---- */}}
< div class = "dashboard-main" >
2026-02-20 17:46:34 -05:00
2026-02-20 23:59:46 -05:00
{{/* Clinical Summary Stats */}}
< div class = "grid grid-cols-3 gap-4 lg:grid-cols-1" >
< div class = "panel panel-padded stat-card" >
< div class = "stat-value text-blue" > {{.Summary.ActiveCondCount}}< / div >
< div class = "stat-label" > Active Conditions< / div >
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< div class = "panel panel-padded stat-card" >
< div class = "stat-value text-success" > {{.Summary.ActiveMedCount}}< / div >
< div class = "stat-label" > Active Medications< / div >
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< div class = "panel panel-padded stat-card {{if gt .Summary.AbnormalLabCount 0}}danger{{end}}" >
< div class = "stat-value {{if gt .Summary.AbnormalLabCount 0}}text-danger{{end}}" > {{.Summary.AbnormalLabCount}}< / div >
< div class = "stat-label" > Abnormal Labs (30d)< / div >
2026-02-20 17:46:34 -05:00
< / div >
< / div >
2026-02-20 17:17:26 -05:00
2026-02-20 23:59:46 -05:00
{{/* Clinical Data Tabs */}}
< div class = "panel p-0 overflow-hidden" >
< div class = "bg-panel px-4 pt-4 border-b border-hard" >
< div class = "tabs" >
< button class = "tab-btn active" onclick = "openTab(event, 'summary')" > Summary< / button >
< button class = "tab-btn" onclick = "openTab(event, 'vitals')" > Vitals ({{len (latestObPerCode (filterObsByCategory .Observations "vital-signs"))}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'labs')" > Labs ({{len (filterObsByCategory .Observations "laboratory")}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'conditions')" > Conditions ({{len .Conditions}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'medications')" > Meds ({{len .Medications}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'allergies')" > Allergies ({{len .Allergies}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'notes')" > Notes ({{len .DocumentReferences}})< / button >
< button class = "tab-btn" onclick = "openTab(event, 'more')" title = "More Data" > ...< / button >
2026-02-20 17:46:34 -05:00
< / div >
< / div >
2026-02-20 15:28:41 -05:00
2026-02-20 23:59:46 -05:00
< div class = "panel-padded min-h-[400px]" >
{{/* ---- Summary Tab ---- */}}
< div id = "summary" class = "tab-content active" >
< div class = "flex flex-col gap-8" >
< div class = "flex flex-col gap-4" >
< h3 class = "text-xs text-muted font-medium uppercase tracking-wider" > Latest Vital Signs< / h3 >
{{if .Summary.LatestVitals}}
< div class = "vitals-strip" >
{{range .Summary.LatestVitals}}
< div class = "vital-item {{if isAbnormal .Interpretation}}vital-abnormal{{end}}" >
< span class = "vital-label" title = "{{.CodeText}}" > {{orDash .CodeText}}< / span >
< span class = "vital-value" >
{{if .ValueQuantity}}{{printf "%.4g" (derefFloat64 .ValueQuantity)}} < small > {{.ValueUnit}}< / small >
{{else if .ValueString}}{{.ValueString}}
{{else}}—{{end}}
< / span >
< span class = "vital-date" > {{formatDate .EffectiveDate}}< / span >
< / div >
{{end}}
< / div >
{{else}}
< p class = "empty-state" > No vital signs recorded.< / p >
{{end}}
< / div >
2026-02-20 17:17:26 -05:00
2026-02-20 23:59:46 -05:00
< div class = "flex flex-col gap-4" >
< h3 class = "text-xs text-muted font-medium uppercase tracking-wider" > Recent Abnormal Labs< / h3 >
{{if .Summary.AbnormalLabsRecent}}
< div class = "table-wrapper" >
< table >
< thead >
< tr > < th > Date< / th > < th > Test< / th > < th > Result< / th > < th > Flag< / th > < / tr >
< / thead >
< tbody >
{{range .Summary.AbnormalLabsRecent}}
< tr class = "row-abnormal" >
< td > {{formatDate .EffectiveDate}}< / td >
< td class = "font-medium" > {{.CodeText}}< / td >
< td > {{if .ValueQuantity}}{{printf "%.4g" (derefFloat64 .ValueQuantity)}} {{.ValueUnit}}{{else}}{{.ValueString}}{{end}}< / td >
< td > < span class = "badge badge-danger" > {{.Interpretation}}< / span > < / td >
< / tr >
{{end}}
< / tbody >
< / table >
< / div >
{{else}}
< p class = "empty-state" > No abnormal labs in the last 30 days.< / p >
{{end}}
< / div >
< / div >
< / div >
2026-02-20 17:17:26 -05:00
2026-02-20 23:59:46 -05:00
{{/* ---- Vitals Tab ---- */}}
< div id = "vitals" class = "tab-content" >
{{$vitals := latestObPerCode (filterObsByCategory .Observations "vital-signs")}}
{{if $vitals}}
< div class = "vitals-grid" >
{{range $vitals}}
< div class = "panel panel-padded p-5 {{if isAbnormal .Interpretation}}vital-abnormal{{end}}" >
< div class = "flex justify-between items-start mb-2" >
< span class = "text-xs font-semibold text-muted" > {{orDash .CodeText}}< / span >
{{if isAbnormal .Interpretation}}< span class = "badge badge-danger" > {{.Interpretation}}< / span > {{end}}
< / div >
< div class = "text-2xl font-semibold mb-4" >
{{if .ValueQuantity}}{{printf "%.4g" (derefFloat64 .ValueQuantity)}} < span class = "text-sm font-medium text-muted" > {{.ValueUnit}}< / span >
{{else if .ValueString}}{{.ValueString}}
{{else}}—{{end}}
< / div >
< div class = "flex flex-col gap-1 text-xs text-muted" >
< span > {{formatDate .EffectiveDate}}< / span >
{{if or .ReferenceRangeLow .ReferenceRangeHigh}}
< span > Ref: {{if .ReferenceRangeLow}}{{printf "%.4g" (derefFloat64 .ReferenceRangeLow)}}{{else}}—{{end}}-{{if .ReferenceRangeHigh}}{{printf "%.4g" (derefFloat64 .ReferenceRangeHigh)}}{{else}}—{{end}}< / span >
{{end}}
< / div >
< / div >
{{end}}
< / div >
{{else}}
< p class = "empty-state" > No vital signs found.< / p >
{{end}}
< / div >
2026-02-20 15:28:41 -05:00
2026-02-20 23:59:46 -05:00
{{/* ---- Labs Tab ---- */}}
< div id = "labs" class = "tab-content" >
{{$labs := filterObsByCategory .Observations "laboratory"}}
{{if $labs}}
< div class = "table-wrapper" >
2026-02-20 17:46:34 -05:00
< table >
< thead >
2026-02-20 23:59:46 -05:00
< tr > < th > Date< / th > < th > Test< / th > < th > Result< / th > < th > Ref Range< / th > < th > Flag< / th > < / tr >
2026-02-20 17:46:34 -05:00
< / thead >
< tbody >
2026-02-20 23:59:46 -05:00
{{range $labs}}
< tr { { if isAbnormal . Interpretation } } class = "row-abnormal" { { end } } >
< td > {{formatDate .EffectiveDate}}< / td >
2026-02-20 17:46:34 -05:00
< td class = "font-medium" > {{orDash .CodeText}}< / td >
< td >
2026-02-20 23:59:46 -05:00
{{if .ValueQuantity}}< strong > {{printf "%.4g" (derefFloat64 .ValueQuantity)}}< / strong > {{.ValueUnit}}
{{else if .ValueString}}{{.ValueString}}
2026-02-20 17:46:34 -05:00
{{else}}—{{end}}
< / td >
2026-02-20 23:59:46 -05:00
< td class = "text-xs text-muted" >
{{if or .ReferenceRangeLow .ReferenceRangeHigh}}
[{{if .ReferenceRangeLow}}{{printf "%.4g" (derefFloat64 .ReferenceRangeLow)}}{{else}}—{{end}} – {{if .ReferenceRangeHigh}}{{printf "%.4g" (derefFloat64 .ReferenceRangeHigh)}}{{else}}—{{end}}]
2026-02-20 17:46:34 -05:00
{{else}}—{{end}}
< / td >
2026-02-20 23:59:46 -05:00
< td > {{if isAbnormal .Interpretation}}< span class = "badge badge-danger" > {{.Interpretation}}< / span > {{else if .Interpretation}}< span class = "badge badge-neutral" > {{.Interpretation}}< / span > {{end}}< / td >
2026-02-20 17:46:34 -05:00
< / tr >
{{end}}
< / tbody >
< / table >
< / div >
2026-02-20 23:59:46 -05:00
{{else}}
< p class = "empty-state" > No lab results found.< / p >
{{end}}
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 15:28:41 -05:00
2026-02-20 23:59:46 -05:00
{{/* ---- Conditions Tab ---- */}}
< div id = "conditions" class = "tab-content" >
{{if .Conditions}}
< div class = "table-wrapper" >
< table >
< thead >
< tr > < th > Onset< / th > < th > Condition< / th > < th > Status< / th > < th > Verification< / th > < / tr >
< / thead >
< tbody >
{{range .Conditions}}
< tr >
< td > {{formatDate (or .OnsetDate .RecordedDate)}}< / td >
< td class = "font-medium" > {{orDash .CodeText}}< / td >
< td > {{if eq .ClinicalStatus "active"}}< span class = "badge badge-success" > Active< / span > {{else}}< span class = "badge badge-neutral" > {{.ClinicalStatus}}< / span > {{end}}< / td >
< td > {{titleCase .VerificationStatus}}< / td >
< / tr >
{{end}}
< / tbody >
< / table >
< / div >
{{else}}
< p class = "empty-state" > No conditions found.< / p >
{{end}}
< / div >
{{/* ---- Medications Tab ---- */}}
< div id = "medications" class = "tab-content" >
{{if .Medications}}
< div class = "table-wrapper" >
< table >
< thead >
< tr > < th > Authored< / th > < th > Medication< / th > < th > Dosage< / th > < th > Status< / th > < / tr >
< / thead >
< tbody >
{{range .Medications}}
< tr >
< td > {{formatDate .AuthoredOn}}< / td >
< td class = "font-medium" > {{orDash .MedCodeText}}< / td >
< td > {{orDash .DosageText}}< / td >
< td > {{if eq .Status "active"}}< span class = "badge badge-success" > Active< / span > {{else}}< span class = "badge badge-neutral" > {{.Status}}< / span > {{end}}< / td >
< / tr >
{{end}}
< / tbody >
< / table >
< / div >
{{else}}
< p class = "empty-state" > No medications found.< / p >
{{end}}
< / div >
{{/* ---- Allergies Tab ---- */}}
< div id = "allergies" class = "tab-content" >
{{if .Allergies}}
< div class = "table-wrapper" >
< table >
< thead >
< tr > < th > Allergen< / th > < th > Reaction< / th > < th > Severity< / th > < th > Status< / th > < / tr >
< / thead >
< tbody >
{{range .Allergies}}
< tr >
< td class = "font-medium" > {{orDash .CodeText}}< / td >
< td > {{orDash .ReactionManifestation}}< / td >
< td > {{if eq .ReactionSeverity "severe"}}< span class = "badge badge-danger" > Severe< / span > {{else if .ReactionSeverity}}< span class = "badge badge-warning" > {{.ReactionSeverity}}< / span > {{else}}—{{end}}< / td >
< td > {{titleCase .ClinicalStatus}}< / td >
< / tr >
2026-02-20 17:46:34 -05:00
{{end}}
2026-02-20 23:59:46 -05:00
< / tbody >
< / table >
< / div >
{{else}}
< p class = "empty-state" > No allergies found.< / p >
{{end}}
< / div >
{{/* ---- Notes Tab ---- */}}
< div id = "notes" class = "tab-content" >
{{if .DocumentReferences}}
< div class = "notes-list" >
{{range .DocumentReferences}}
< div class = "panel panel-padded note-item" >
< div class = "note-header" >
< span class = "note-date" > {{formatDate .Date}}< / span >
< span class = "note-type" > {{orDash .TypeText}}< / span >
{{if .ContentURL}}< a href = "{{.ContentURL}}" target = "_blank" class = "btn btn-outline btn-sm" > View Document< / a > {{end}}
< / div >
< p class = "note-desc" > {{orDash .Description}}< / p >
< / div >
2026-02-20 17:46:34 -05:00
{{end}}
2026-02-20 23:59:46 -05:00
< / div >
{{else}}
< p class = "empty-state" > No clinical notes found.< / p >
{{end}}
< / div >
{{/* ---- More Tab (Procedures, Immunizations, Encounters) ---- */}}
< div id = "more" class = "tab-content" >
< div class = "flex flex-col gap-6" >
< div class = "flex flex-col gap-2" >
< h4 class = "font-semibold text-sm text-main" > Recent Procedures< / h4 >
{{if .Procedures}}
< ul class = "flex flex-col gap-1 text-sm text-muted" >
{{range (slice .Procedures 0 (min (len .Procedures) 5))}}
< li > < strong class = "text-main" > {{formatDate .PerformedDate}}:< / strong > {{.CodeText}}< / li >
{{end}}
< / ul >
{{else}}< p class = "text-muted text-xs" > None recorded< / p > {{end}}
< / div >
< div class = "flex flex-col gap-2" >
< h4 class = "font-semibold text-sm text-main" > Recent Immunizations< / h4 >
{{if .Immunizations}}
< ul class = "flex flex-col gap-1 text-sm text-muted" >
{{range (slice .Immunizations 0 (min (len .Immunizations) 5))}}
< li > < strong class = "text-main" > {{formatDate .OccurrenceDate}}:< / strong > {{.VaccineText}}< / li >
{{end}}
< / ul >
{{else}}< p class = "text-muted text-xs" > None recorded< / p > {{end}}
< / div >
< div class = "flex flex-col gap-2" >
< h4 class = "font-semibold text-sm text-main" > Recent Encounters< / h4 >
{{if .Encounters}}
< ul class = "flex flex-col gap-1 text-sm text-muted" >
{{range (slice .Encounters 0 (min (len .Encounters) 5))}}
< li > < strong class = "text-main" > {{formatDate .PeriodStart}}:< / strong > {{or .TypeText "Visit"}}< / li >
{{end}}
< / ul >
{{else}}< p class = "text-muted text-xs" > None recorded< / p > {{end}}
< / div >
< / div >
< / div >
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 15:28:41 -05:00
< / div >
2026-02-20 23:59:46 -05:00
< / div >
2026-02-20 17:46:34 -05:00
2026-02-20 23:59:46 -05:00
{{/* ---- Sidebar Column ---- */}}
< div class = "dashboard-sidebar" >
{{/* High Priority: Alerts */}}
{{if hasCriticalAllergies .Allergies}}
< div class = "panel panel-padded alert-card" >
< div class = "flex items-center gap-2 mb-4" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "20" height = "20" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" class = "text-danger" > < path d = "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" / > < path d = "M12 9v4" / > < path d = "M12 17h.01" / > < / svg >
< h3 class = "font-semibold text-danger" > Critical Alerts< / h3 >
< / div >
< div class = "flex flex-col gap-3" >
{{range .Allergies}}
{{if eq .Criticality "high"}}
< div class = "alert-item" >
< span class = "alert-code" > {{.CodeText}} Allergy< / span >
{{with .ReactionManifestation}}< span class = "alert-detail" > {{.}}{{with $.ReactionSeverity}} ({{.}}){{end}}< / span > {{end}}
< / div >
{{end}}
{{end}}
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 15:28:41 -05:00
< / div >
2026-02-20 23:59:46 -05:00
{{end}}
2026-02-20 17:46:34 -05:00
2026-02-20 23:59:46 -05:00
{{/* Patient Details (Expanded) */}}
< div class = "panel panel-padded" >
< div class = "mb-4" >
< h3 class = "font-semibold text-main" > Patient Information< / h3 >
< / div >
< div class = "kv-list" >
< div class = "kv-row" >
< span class = "kv-label" > Full Name< / span >
< span class = "kv-value font-medium" > {{.Patient.FirstName}} {{.Patient.MiddleName}} {{.Patient.LastName}}< / span >
< / div >
< div class = "kv-row" >
< span class = "kv-label" > FHIR ID< / span >
< span class = "kv-value text-xs font-mono" > {{.Patient.FHIRID}}< / span >
< / div >
{{with primaryAddress .RawPatient}}
< div class = "kv-row" >
< span class = "kv-label" > Address< / span >
< span class = "kv-value" > {{.}}< / span >
< / div >
{{end}}
{{with usRace .RawPatient}}
< div class = "kv-row" >
< span class = "kv-label" > Race< / span >
< span class = "kv-value" > {{.}}< / span >
< / div >
{{end}}
{{with usEthnicity .RawPatient}}
< div class = "kv-row" >
< span class = "kv-label" > Ethnicity< / span >
< span class = "kv-value" > {{.}}< / span >
< / div >
{{end}}
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 15:28:41 -05:00
< / div >
2026-02-20 17:46:34 -05:00
2026-02-20 23:59:46 -05:00
{{/* Low Priority: SMART Inspector */}}
< div class = "panel panel-padded inspector-card" >
< details >
< summary class = "text-xs text-muted font-medium uppercase tracking-wider cursor-pointer outline-none" > System Inspector< / summary >
< div class = "inspector-content mt-4" >
< div class = "inspect-item" >
< span class = "label" > EHR Server< / span >
< code class = "text-xs text-muted break-all" title = "{{.Session.EHRURL}}" > {{.Session.EHRURL}}< / code >
< / div >
< div class = "inspect-item" >
< span class = "label" > Granted Scopes< / span >
< div class = "scopes-cloud" >
{{range (split .Session.Scope " ")}}
< span class = "scope-tag" > {{.}}< / span >
{{end}}
< / div >
< / div >
< / div >
< / details >
2026-02-20 15:28:41 -05:00
< / div >
2026-02-20 17:46:34 -05:00
2026-02-20 15:28:41 -05:00
< / div >
< / div >
2026-02-20 17:46:34 -05:00
< / div >
2026-02-20 23:59:46 -05:00
{{end}}
2026-02-20 15:28:41 -05:00
{{end}}
{{define "scripts"}}
< script >
function openTab(evt, tabName) {
2026-02-20 23:59:46 -05:00
var tabcontent = document.getElementsByClassName("tab-content");
for (var i = 0; i < tabcontent.length ; i + + ) {
2026-02-20 17:46:34 -05:00
tabcontent[i].classList.remove("active");
2026-02-20 15:28:41 -05:00
}
2026-02-20 23:59:46 -05:00
var tablinks = document.getElementsByClassName("tab-btn");
for (var i = 0; i < tablinks.length ; i + + ) {
2026-02-20 17:46:34 -05:00
tablinks[i].classList.remove("active");
2026-02-20 15:28:41 -05:00
}
2026-02-20 17:46:34 -05:00
var target = document.getElementById(tabName);
setTimeout(() => target.classList.add("active"), 10);
evt.currentTarget.classList.add("active");
2026-02-20 15:28:41 -05:00
}
2026-02-20 17:17:26 -05:00
function onSyncClick() {
var btn = document.getElementById("syncBtn");
2026-02-20 17:46:34 -05:00
setTimeout(function() {
btn.disabled = true;
btn.innerHTML = `< svg class = "animate-spin" xmlns = "http://www.w3.org/2000/svg" width = "16" height = "16" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" > < path d = "M21 12a9 9 0 1 1-6.219-8.56" / > < / svg > Syncing...`;
}, 50);
2026-02-20 17:17:26 -05:00
}
2026-02-20 23:59:46 -05:00
// Helper for splitting scopes string
function split(s, sep) { return s.split(sep); }
2026-02-24 00:26:55 -05:00
/* ── "Find in Rimidi" match modal ────────────────────────────────── */
function findInRimidi(patientFHIRID) {
var overlay = document.getElementById('rimidiMatchOverlay');
var body = document.getElementById('rimidiMatchBody');
overlay.style.display = 'flex';
body.innerHTML =
'< div style = "text-align:center;padding:40px;" > ' +
'< div class = "animate-spin" style = "width:24px;height:24px;border:3px solid #ddd;border-top-color:#0d6efd;border-radius:50%;display:inline-block;" > < / div > ' +
'< p style = "margin-top:12px;color:var(--color-text-muted);" > Searching Rimidi…< / p > < / div > ';
var url = '/api/patient-match-proxy';
if (patientFHIRID) {
url += '?patient_id=' + encodeURIComponent(patientFHIRID);
}
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.then(function(resp) {
if (!resp.ok) {
return resp.json().then(function(d) { throw new Error(d.error || 'HTTP ' + resp.status); });
}
return resp.json();
})
.then(function(data) {
renderRimidiMatchResults(body, data);
})
.catch(function(err) {
body.innerHTML =
'< div style = "padding:20px;color:var(--color-danger);background:var(--color-danger-bg);border-radius:8px;margin:20px;" > ' +
'< strong > Error:< / strong > ' + escapeHtml(err.message) + '< / div > ';
});
}
function closeRimidiMatch() {
document.getElementById('rimidiMatchOverlay').style.display = 'none';
}
function escapeHtml(s) {
var d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
function renderRimidiMatchResults(container, data) {
var matches = data.matches || [];
var local = data.local_patient || {};
if (matches.length === 0) {
container.innerHTML =
'< div style = "padding:20px;text-align:center;color:var(--color-text-muted);" > ' +
'No matching patients found in the Rimidi system.< / div > ';
return;
}
var html = '< div style = "padding:16px;" > ';
html += '< p style = "margin-bottom:16px;color:var(--color-text-muted);" > ' +
'Found < strong > ' + matches.length + '< / strong > potential match' +
(matches.length > 1 ? 'es' : '') + ' in Rimidi.< / p > ';
var fieldLabels = {
first_name: 'First Name', last_name: 'Last Name',
email: 'Email', dob: 'Date of Birth', sex: 'Sex'
};
var fieldOrder = ['first_name', 'last_name', 'email', 'dob', 'sex'];
for (var i = 0; i < matches.length ; i + + ) {
var m = matches[i];
var f = m.fields || {};
var borderColor = m.score >= 4 ? 'var(--color-success)' : (m.score >= 3 ? 'var(--color-warning, #f0ad4e)' : 'var(--color-brand)');
var badgeClass = m.score >= 4 ? 'badge-success' : (m.score >= 3 ? 'badge-warning' : 'badge-blue');
html += '< div class = "panel" style = "margin-bottom:16px;border-left:4px solid ' + borderColor + ';padding:0;" > ';
html += '< div style = "display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--color-border-soft);" > ';
html += '< strong > Match #' + (i + 1) + '< / strong > ';
html += '< span class = "badge ' + badgeClass + '" > ' + m.score + '/5 fields match< / span > ';
html += '< / div > ';
html += '< table style = "width:100%;border-collapse:collapse;" > ';
html += '< thead > < tr style = "background:var(--color-bg-panel);" > ';
html += '< th style = "padding:8px 12px;text-align:left;width:25%;font-size:12px;text-transform:uppercase;color:var(--color-text-muted);" > Field< / th > ';
html += '< th style = "padding:8px 12px;text-align:left;width:30%;font-size:12px;text-transform:uppercase;color:var(--color-text-muted);" > HRS (Local)< / th > ';
html += '< th style = "padding:8px 12px;text-align:left;width:30%;font-size:12px;text-transform:uppercase;color:var(--color-text-muted);" > Rimidi (Remote)< / th > ';
html += '< th style = "padding:8px 12px;text-align:center;width:15%;font-size:12px;text-transform:uppercase;color:var(--color-text-muted);" > Match< / th > ';
html += '< / tr > < / thead > < tbody > ';
for (var j = 0; j < fieldOrder.length ; j + + ) {
var key = fieldOrder[j];
var fld = f[key] || {};
var localVal = local[key] || '—';
var remoteVal = fld.value || '—';
var isMatch = fld.match === true;
var icon = isMatch
? '< span style = "color:var(--color-success);font-size:16px;" > ✓< / span > '
: '< span style = "color:var(--color-danger);font-size:16px;" > ✗< / span > ';
var rowBg = isMatch ? '' : 'background:rgba(240,173,78,0.1);';
html += '< tr style = "border-top:1px solid var(--color-border-soft);' + rowBg + '" > ';
html += '< td style = "padding:8px 12px;font-weight:600;" > ' + fieldLabels[key] + '< / td > ';
html += '< td style = "padding:8px 12px;" > ' + escapeHtml(localVal) + '< / td > ';
html += '< td style = "padding:8px 12px;" > ' + escapeHtml(remoteVal) + '< / td > ';
html += '< td style = "padding:8px 12px;text-align:center;" > ' + icon + '< / td > ';
html += '< / tr > ';
}
html += '< / tbody > < / table > < / div > ';
}
html += '< / div > ';
container.innerHTML = html;
}
2026-02-20 15:28:41 -05:00
< / script >
2026-02-24 00:26:55 -05:00
<!-- Rimidi Patient Match Modal Overlay -->
< div id = "rimidiMatchOverlay" style = "display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);align-items:center;justify-content:center;" >
< div style = "background:var(--color-bg-main);border-radius:12px;width:90%;max-width:700px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.3);" >
< div style = "display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--color-border-soft);" >
< h3 style = "margin:0;font-size:18px;" >
< svg xmlns = "http://www.w3.org/2000/svg" width = "18" height = "18" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" stroke-linejoin = "round" style = "vertical-align:text-bottom;margin-right:6px;" > < circle cx = "11" cy = "11" r = "8" / > < line x1 = "21" y1 = "21" x2 = "16.65" y2 = "16.65" / > < / svg >
Find Patient in Rimidi
< / h3 >
< button onclick = "closeRimidiMatch()" style = "background:none;border:none;font-size:24px;cursor:pointer;color:var(--color-text-muted);padding:0 4px;" > × < / button >
< / div >
< div id = "rimidiMatchBody" style = "overflow-y:auto;flex:1;" >
< / div >
< div style = "padding:12px 20px;border-top:1px solid var(--color-border-soft);text-align:right;" >
< button class = "btn btn-secondary" onclick = "closeRimidiMatch()" > Close< / button >
< / div >
< / div >
< / div >
2026-02-20 15:28:41 -05:00
{{end}}