add sequencing tab

This commit is contained in:
2026-03-30 11:04:03 +07:00
parent 34358e3ee7
commit f0a5079b15
12 changed files with 125979 additions and 271 deletions

View File

@@ -21,29 +21,14 @@
</select>
<div id="custom_range_container" style="display:none;" class="align-items-center gap-1">
<select id="start_year" class="form-select"></select>
<select id="start_week" class="form-select"></select>
<span class="mx-1">to</span>
<select id="end_year" class="form-select"></select>
<select id="end_week" class="form-select"></select>
</div>
</div>
</div>
<!-- STATUS -->
<div class="alert alert-info mb-4">
<b>Current {{ $selected->code }} Status:</b>
<span id="activityStatus">Loading...</span>
</div>
<!-- SUMMARY CARDS -->
<div class="row g-3 mb-4">

View File

@@ -1,223 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>NRML Surveillance Dashboard</title>
<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="bg-light">
<div class="container-fluid py-4">
<h3 class="mb-4">NRML Surveillance Dashboard</h3>
<!-- Filters -->
<div class="card mb-4">
<div class="card-body row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label">Surveillance Program</label>
<select id="surveillance_id" class="form-select">
@foreach($programs as $program)
<option value="{{ $program->id }}">
{{ $program->code }} - {{ $program->name_en }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<label class="form-label">Period</label>
<select id="period_type" class="form-select">
<option value="week">Epiweek</option>
<option value="month">Month</option>
<option value="year">Year</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Date From</label>
<input type="date" id="date_from" class="form-control">
</div>
<div class="col-md-2">
<label class="form-label">Date To</label>
<input type="date" id="date_to" class="form-control">
</div>
<div class="col-md-2">
<button onclick="loadDashboard()" class="btn btn-primary w-100">
Apply
</button>
</div>
</div>
</div>
<!-- Summary Cards -->
<div class="row" id="summary_cards"></div>
<!-- Trend Chart -->
<div class="card mt-4">
<div class="card-body">
<h5>Epidemic Trend</h5>
<canvas id="trendChart"></canvas>
</div>
</div>
<!-- Province Table -->
<div class="card mt-4">
<div class="card-body">
<h5>Cases by Province</h5>
<table class="table table-bordered" id="provinceTable">
<thead>
<tr>
<th>Province</th>
<th>Total Cases</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<script>
let trendChart;
function loadDashboard() {
const surveillanceId = document.getElementById('surveillance_id').value;
const periodType = document.getElementById('period_type').value;
const dateFrom = document.getElementById('date_from').value;
const dateTo = document.getElementById('date_to').value;
loadSummary(dateFrom, dateTo);
loadTrend(periodType, dateFrom, dateTo);
loadProvince(surveillanceId, dateFrom, dateTo);
}
function loadSummary(dateFrom, dateTo) {
fetch(`/api/dashboard/summary?date_from=${dateFrom}&date_to=${dateTo}`)
.then(res => res.json())
.then(data => {
let html = '';
data.forEach(item => {
html += `
<div class="col-md-3 mb-3">
<div class="card shadow-sm">
<div class="card-body">
<h6>${item.code}</h6>
<h4>${item.current_total}</h4>
<small class="${item.percent_change >= 0 ? 'text-success' : 'text-danger'}">
${item.percent_change}%
</small>
</div>
</div>
</div>
`;
});
document.getElementById('summary_cards').innerHTML = html;
});
}
function loadTrend(periodType, dateFrom, dateTo) {
fetch(`/api/dashboard/trend?period_type=${periodType}&date_from=${dateFrom}&date_to=${dateTo}`)
.then(res => res.json())
.then(data => {
if (trendChart) trendChart.destroy();
const labelsSet = new Set();
Object.values(data).forEach(program => {
program.forEach(row => {
labelsSet.add(`${row.year ?? ''}-${row.period}`);
});
});
const labels = Array.from(labelsSet).sort();
const datasets = [];
const colors = {
SARI: 'red',
ILI: 'blue',
LBM: 'green'
};
Object.keys(data).forEach(code => {
const values = labels.map(label => {
const found = data[code].find(row =>
`${row.year ?? ''}-${row.period}` === label
);
return found ? found.total : 0;
});
datasets.push({
label: code,
data: values,
borderColor: colors[code] || 'black',
fill: false
});
});
trendChart = new Chart(document.getElementById('trendChart'), {
type: 'line',
data: {
labels: labels,
datasets: datasets
}
});
});
}
function loadProvince(surveillanceId, dateFrom, dateTo) {
fetch(`/api/dashboard/province?surveillance_id=${surveillanceId}&date_from=${dateFrom}&date_to=${dateTo}`)
.then(res => res.json())
.then(data => {
let html = '';
data.forEach(item => {
html += `
<tr>
<td>${item.site_province_name}</td>
<td>${item.total}</td>
</tr>
`;
});
document.querySelector('#provinceTable tbody').innerHTML = html;
});
}
document.addEventListener('DOMContentLoaded', function() {
const today = new Date().toISOString().split('T')[0];
const past = new Date();
past.setDate(past.getDate() - 30);
document.getElementById('date_from').value = past.toISOString().split('T')[0];
document.getElementById('date_to').value = today;
loadDashboard();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<div class="d-flex justify-content-between mb-3">
<h4 class="fw-bold">Sequencing Analysis</h4>
<div class="d-flex align-items-center gap-2">
<select id="trend_range" class="form-select w-auto">
<option value="8" selected>Last 8 weeks</option>
<option value="12">Last 12 weeks</option>
<option value="26">Last 26 weeks</option>
<option value="custom">Custom range</option>
</select>
<div id="custom_range_container" style="display:none;" class="align-items-center gap-1">
<select id="start_year" class="form-select"></select>
<select id="start_week" class="form-select"></select>
<span class="mx-1">to</span>
<select id="end_year" class="form-select"></select>
<select id="end_week" class="form-select"></select>
</div>
</div>
</div>
<div class="card">
<div class="card-body" style="height:600px;">
<canvas id="sequencingChart"></canvas>
</div>
</div>
<div class="row mt-3">
<!-- Counts -->
<div class="col-md-6">
<div class="card">
<div class="card-body" style="height:600px;">
<h6 class="fw-bold">Lineage Counts Over Time</h6>
<canvas id="sequencingCountChart"></canvas>
</div>
</div>
</div>
<!-- Distribution -->
<div class="col-md-6">
<div class="card">
<div class="card-body" style="height:600px;">
<h6 class="fw-bold">Total Sequenced Samples Over Time</h6>
<canvas id="sequencingTotalChart" style="height: 550px;"></canvas>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
window.SURVEILLANCE_ID = 6;
</script>
<script src="/js/sequencing.js"></script>
@endsection

View File

@@ -40,7 +40,7 @@
display: flex;
background: white;
border-bottom: 1px solid #dcdcdc;
padding-left: 15px;
padding: 0 20px;
position: sticky;
top: 0;
z-index: 1000;
@@ -49,6 +49,17 @@
}
/* NAV ITEMS */
.btn-theme-outline {
background-color: #fff;
color: #0B8F3C;
border: 1px solid #0B8F3C;
}
.btn-theme-outline:hover {
background-color: #cce0d4;
color: #0B8F3C;
}
.nav-item {
padding: 12px 18px;
text-decoration: none;
@@ -92,7 +103,7 @@
}
.card {
border-radius: 10px;
border-radius: 0px !important;
border: 1px solid #E5E7EB;
}
@@ -112,9 +123,7 @@
</div>
<div class="ms-auto small">
Last update: 12:05 |
Data latency: 510 min |
User: National - Read Only
Last update: 12:05 | 2026-03-15
</div>
</div>
@@ -126,16 +135,34 @@
Overview
</a>
@foreach($programs as $program)
<!-- @foreach($programs as $program)
<a href="/dashboard/{{ strtolower($program->code) }}"
class="nav-item {{ request()->is('dashboard/' . strtolower($program->code)) ? 'active-tab' : '' }}">
{{ $program->code }}
</a>
@endforeach -->
@foreach($programs->where('code', '!=', 'NDS') as $program)
@if($program->code === 'SEQ')
<a href="/dashboard/seq"
class="nav-item {{ request()->is('dashboard/seq') ? 'active-tab' : '' }}">
SEQ
</a>
@else
<a href="/dashboard/{{ strtolower($program->code) }}"
class="nav-item {{ request()->is('dashboard/' . strtolower($program->code)) ? 'active-tab' : '' }}">
{{ $program->code }}
</a>
@endif
@endforeach
<button type="button" onclick="reloadDataSource()" class="btn btn-sm btn-warning" style="height: 27px; position: absolute; right: 40px; top: 70px;">
Refresh Data
</button>
<div class="ms-auto d-flex align-items-center gap-4 pe-3">
<button type="button" onclick="reloadDataSource()" class="btn btn-sm btn-theme-outline">
Refresh Data
</button>
</div>
</div>
@@ -163,4 +190,4 @@
</body>
</html>
</html>