mirror of
https://github.com/AmanTahiliani/FHIR-Sandbox.git
synced 2026-08-08 04:06:16 -04:00
Patient Match
This commit is contained in:
@@ -63,6 +63,10 @@
|
||||
Refresh Chart
|
||||
</button>
|
||||
</form>
|
||||
<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>
|
||||
<span class="text-xs text-muted">
|
||||
Last Synced: {{if .LatestSync}}{{formatDateTime .LatestSync.SyncedAt}}{{else}}Never{{end}}
|
||||
</span>
|
||||
@@ -469,5 +473,137 @@ function onSyncClick() {
|
||||
|
||||
// Helper for splitting scopes string
|
||||
function split(s, sep) { return s.split(sep); }
|
||||
|
||||
/* ── "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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 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>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user