262 lines
12 KiB
Plaintext
262 lines
12 KiB
Plaintext
@model IEnumerable<SimpleLIS.Models.Observation>
|
|
@{
|
|
ViewData["Title"] = "Lab Observations";
|
|
}
|
|
|
|
<div class="container-fluid">
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h1 class="display-4 mb-0">Laboratory Observations</h1>
|
|
<p class="text-muted">All lab test results and observations</p>
|
|
</div>
|
|
</div>
|
|
@if (Model.Any())
|
|
{
|
|
var abnormalCount = Model.Count(o => !string.IsNullOrEmpty(o.AbnormalFlag) && o.AbnormalFlag != "N");
|
|
var finalCount = Model.Count(o => o.ResultStatus == "F");
|
|
var uniqueTests = Model.Select(o => o.ObservationCode).Distinct().Count();
|
|
|
|
<div class="row mt-4">
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-success">
|
|
<div class="card-body text-center">
|
|
<h3>@Model.Count()</h3>
|
|
<p class="mb-0">Total Observations</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-warning">
|
|
<div class="card-body text-center">
|
|
<h3>@abnormalCount</h3>
|
|
<p class="mb-0">Abnormal Results</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-info">
|
|
<div class="card-body text-center">
|
|
<h3>@uniqueTests</h3>
|
|
<p class="mb-0">Unique Test Types</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">Test Results</h5>
|
|
<span class="badge bg-info">@Model.Count() Total</span>
|
|
</div>
|
|
<div class="card-body">
|
|
@if (Model.Any())
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover" id="observationsTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Observation ID</th>
|
|
<th>Patient</th>
|
|
<th>Test Code</th>
|
|
<th>Result Value</th>
|
|
<th>Units</th>
|
|
<th>Abnormal Flag</th>
|
|
<th>Status</th>
|
|
<th>Message Date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var obs in Model)
|
|
{
|
|
<tr>
|
|
<td><code>@obs.ObservationId</code></td>
|
|
<td>
|
|
<strong>@obs.Message.Patient.LastName, @obs.Message.Patient.FirstName</strong>
|
|
<br />
|
|
<small class="text-muted">@obs.Message.Patient.HL7PatientId</small>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-secondary">@obs.ObservationCode</span>
|
|
</td>
|
|
<td>
|
|
<strong class="@(obs.AbnormalFlag != null && obs.AbnormalFlag != "N" && obs.AbnormalFlag != "" ? "text-danger" : "")">
|
|
@obs.ObservationValue
|
|
</strong>
|
|
</td>
|
|
<td>@obs.Units</td>
|
|
<td>
|
|
@if (string.IsNullOrEmpty(obs.AbnormalFlag) || obs.AbnormalFlag == "N")
|
|
{
|
|
<span class="badge bg-success">Normal</span>
|
|
}
|
|
else if (obs.AbnormalFlag == "H")
|
|
{
|
|
<span class="badge bg-danger">High</span>
|
|
}
|
|
else if (obs.AbnormalFlag == "L")
|
|
{
|
|
<span class="badge bg-warning">Low</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="badge bg-dark">@obs.AbnormalFlag</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (obs.ResultStatus == "F")
|
|
{
|
|
<span class="badge bg-success">Final</span>
|
|
}
|
|
else if (obs.ResultStatus == "P")
|
|
{
|
|
<span class="badge bg-warning">Preliminary</span>
|
|
}
|
|
else if (obs.ResultStatus == "C")
|
|
{
|
|
<span class="badge bg-info">Corrected</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="badge bg-secondary">@obs.ResultStatus</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
@obs.Message.Timestamp.ToString("yyyy-MM-dd")
|
|
<br />
|
|
<small class="text-muted">@obs.Message.Timestamp.ToString("HH:mm:ss")</small>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="alert alert-info mb-0">
|
|
<strong>No observations found.</strong> Lab observations will appear here once HL7 messages with OBX segments are processed.
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics -->
|
|
@if (Model.Any())
|
|
{
|
|
var abnormalCount = Model.Count(o => !string.IsNullOrEmpty(o.AbnormalFlag) && o.AbnormalFlag != "N");
|
|
var finalCount = Model.Count(o => o.ResultStatus == "F");
|
|
var uniqueTests = Model.Select(o => o.ObservationCode).Distinct().Count();
|
|
|
|
@* <div class="row mt-4">
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-success">
|
|
<div class="card-body text-center">
|
|
<h3>@Model.Count()</h3>
|
|
<p class="mb-0">Total Observations</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-warning">
|
|
<div class="card-body text-center">
|
|
<h3>@abnormalCount</h3>
|
|
<p class="mb-0">Abnormal Results</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card text-white bg-info">
|
|
<div class="card-body text-center">
|
|
<h3>@uniqueTests</h3>
|
|
<p class="mb-0">Unique Test Types</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> *@
|
|
|
|
<!-- Common Test Types -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header bg-white">
|
|
<h5 class="mb-0">Test Type Distribution</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
@foreach (var testGroup in Model.GroupBy(o => o.ObservationCode).OrderByDescending(g => g.Count()).Take(6))
|
|
{
|
|
<div class="col-md-4 mb-3">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="badge bg-secondary">@testGroup.Key</span>
|
|
<span class="badge bg-primary">@testGroup.Count() results</span>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<a asp-controller="Home" asp-action="Index" class="btn btn-secondary">
|
|
<i class="bi bi-arrow-left"></i> Back to Dashboard
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<script>
|
|
@if (Model.Any())
|
|
{
|
|
<text>
|
|
$(document).ready(function() {
|
|
var filterHtml = '<div class="row mb-3">' +
|
|
'<div class="col-md-8"><input type="text" id="searchInput" class="form-control" placeholder="Search observations..."></div>' +
|
|
'<div class="col-md-4">' +
|
|
'<select id="statusFilter" class="form-select">' +
|
|
'<option value="">All Statuses</option>' +
|
|
'<option value="Normal">Normal Only</option>' +
|
|
'<option value="Abnormal">Abnormal Only</option>' +
|
|
'</select>' +
|
|
'</div>' +
|
|
'</div>';
|
|
|
|
$('#observationsTable').before(filterHtml);
|
|
|
|
// Search functionality
|
|
$('#searchInput').on('keyup', function() {
|
|
var value = $(this).val().toLowerCase();
|
|
$('#observationsTable tbody tr').filter(function() {
|
|
var matchesSearch = $(this).text().toLowerCase().indexOf(value) > -1;
|
|
var statusFilter = $('#statusFilter').val();
|
|
var matchesStatus = true;
|
|
|
|
if (statusFilter === 'Normal') {
|
|
matchesStatus = $(this).find('.badge.bg-success').length > 0;
|
|
} else if (statusFilter === 'Abnormal') {
|
|
matchesStatus = $(this).find('.badge.bg-danger, .badge.bg-warning').length > 0;
|
|
}
|
|
|
|
$(this).toggle(matchesSearch && matchesStatus);
|
|
});
|
|
});
|
|
|
|
// Status filter
|
|
$('#statusFilter').on('change', function() {
|
|
$('#searchInput').trigger('keyup');
|
|
});
|
|
});
|
|
</text>
|
|
}
|
|
</script>
|
|
}
|