simple-lis/Views/Home/Observations.cshtml
2025-10-29 17:15:51 +05:30

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>
}