442 lines
16 KiB
PHP
442 lines
16 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Dashboard - Sistem TBC</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<style>
|
|
* {margin:0;padding:0;box-sizing:border-box;font-family:'Poppins',sans-serif;}
|
|
body {display:flex;background:#f5f6fa;}
|
|
.sidebar {width:250px;height:100vh;background:#c40000;padding-top:30px;position:fixed;z-index: 100;}
|
|
.sidebar h2 {color:white;text-align:center;margin-bottom:40px;font-size:20px;font-weight:700;}
|
|
.menu {list-style:none;}
|
|
.menu a {text-decoration:none;display:flex;align-items:center;padding:15px 25px;color:white;gap:15px;transition:0.3s;}
|
|
.menu li:hover a,.menu li.active a {background:white;color:#c40000;border-radius:30px 0 0 30px;margin-left:10px;}
|
|
.main-content {margin-left:250px;padding:40px;width:100%;}
|
|
.header-wrapper {border-bottom:1px solid #e0e0e0;margin-bottom:30px;padding-bottom:10px;}
|
|
.header-title {font-size:28px;font-weight:700;color:#c40000;display:inline-block;padding-bottom:10px;}
|
|
|
|
/* Statistik Cards */
|
|
.card-container {display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-bottom:40px;}
|
|
.card {background:white;padding:20px;border-radius:15px;box-shadow:0 5px 15px rgba(0,0,0,0.05);display:flex;align-items:center;gap:15px;}
|
|
.card-icon {background:rgba(196,0,0,0.1);color:#c40000;padding:10px;border-radius:12px;}
|
|
.card-info h4 {font-size:13px;color:#333;margin-bottom:5px;}
|
|
.card-info p {font-size:22px;font-weight:700;color:#111;}
|
|
|
|
/* Grid Grafik: 2 Atas, 2 Bawah */
|
|
.chart-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 25px;
|
|
}
|
|
.chart-box {
|
|
background: white;
|
|
padding: 25px;
|
|
border-radius: 20px;
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 400px;
|
|
}
|
|
.chart-title {font-size:15px;margin-bottom:20px;font-weight:600;color:#333;display:flex;align-items:center;gap:10px;}
|
|
.canvas-wrapper {position:relative; flex-grow:1; width:100%;}
|
|
|
|
#filterTanggal {margin-bottom:15px; padding:10px; border-radius:8px; border:1px solid #ddd; width: 100%; font-size:14px;}
|
|
#listPasien {list-style:none; overflow-y: auto; max-height: 250px;}
|
|
#listPasien li {padding:12px; border-bottom:1px solid #f0f0f0; display:flex; align-items:center; gap:10px; font-size:14px; color:#444;}
|
|
|
|
/* MODERN SWEETALERT POPUP - CONSISTENT STYLE */
|
|
.modern-popup { border-radius: 24px !important; padding: 2rem !important; box-shadow: 0 20px 50px rgba(0,0,0,0.15) !important; }
|
|
.title-swal { font-size: 22px !important; font-weight: 700 !important; color: #1e293b !important; padding-top: 10px !important; }
|
|
.content-swal { font-size: 15px !important; color: #64748b !important; }
|
|
.btn-confirm-swal { background: #c40000 !important; color: white !important; padding: 10px 20px !important; border-radius: 12px !important; font-weight: 600 !important; border: none !important; margin: 5px !important; cursor: pointer; transition: 0.3s; }
|
|
.btn-confirm-swal:hover { background: #a00000 !important; transform: translateY(-2px); }
|
|
.btn-cancel-swal { background: #f1f5f9 !important; color: #475569 !important; padding: 10px 20px !important; border-radius: 12px !important; font-weight: 600 !important; border: none !important; margin: 5px !important; cursor: pointer; transition: 0.3s; }
|
|
.btn-cancel-swal:hover { background: #e2e8f0 !important; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="sidebar">
|
|
<h2>Sistem TBC</h2>
|
|
<ul class="menu">
|
|
<li class="active"><a href="{{ url('/dashboard') }}"><i data-lucide="layout-dashboard"></i> Dashboard</a></li>
|
|
@if(session('role') !== 'super_admin')<li><a href="{{ url('/data-pasien') }}"><i data-lucide="users"></i> Data Pasien</a></li>@endif
|
|
<li><a href="{{ url('/skrining') }}"><i data-lucide="stethoscope"></i> Skrining</a></li>
|
|
@if(session('role') == 'super_admin')
|
|
<li>
|
|
<a href="{{ url('/kelola-admin') }}">
|
|
<i data-lucide="shield"></i> Kelola Admin
|
|
</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="{{ url('/riwayat-aktivitas') }}">
|
|
<i data-lucide="activity"></i> Riwayat Aktivitas
|
|
</a>
|
|
</li>
|
|
@endif
|
|
<li>
|
|
<a href="{{ url('/pengaturan-akun') }}"><i data-lucide="user"></i> Akun</a>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="header-wrapper">
|
|
<h1 class="header-title">Dashboard</h1>
|
|
</div>
|
|
|
|
<!-- Statistik Utama -->
|
|
<div class="card-container">
|
|
<div class="card">
|
|
<div class="card-icon"><i data-lucide="users-round"></i></div>
|
|
<div class="card-info">
|
|
<h4>Total Pasien Pengobatan</h4>
|
|
<p>{{ $totalPasien }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-icon"><i data-lucide="user-minus"></i></div>
|
|
<div class="card-info">
|
|
<h4>Pasien Belum Minum Obat</h4>
|
|
<p>{{ $belumMinumObat }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-icon"><i data-lucide="alert-triangle"></i></div>
|
|
<div class="card-info">
|
|
<h4>Hasil Skrining Berisiko</h4>
|
|
<p>{{ $skriningResiko }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grid Grafik: 2 Atas, 2 Bawah -->
|
|
<div class="chart-grid">
|
|
<!-- BARIS ATAS: Tren & List Pasien -->
|
|
<div class="chart-box">
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
|
|
|
<div class="chart-title" style="margin-bottom:0;">
|
|
<i data-lucide="bar-chart-3" style="color:#c40000"></i>
|
|
Kasus Bulanan
|
|
</div>
|
|
|
|
<form method="GET" action="{{ url('/dashboard') }}">
|
|
<select
|
|
name="tahun"
|
|
onchange="this.form.submit()"
|
|
style="
|
|
padding:8px 14px;
|
|
border-radius:10px;
|
|
border:1px solid #ddd;
|
|
outline:none;
|
|
font-size:13px;
|
|
background:white;
|
|
cursor:pointer;
|
|
"
|
|
>
|
|
@foreach($tahunList as $y)
|
|
<option value="{{ $y }}" {{ $tahunDipilih == $y ? 'selected' : '' }}>
|
|
Tahun {{ $y }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div class="canvas-wrapper">
|
|
<canvas id="chartBulanan"></canvas>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="chart-box">
|
|
<div class="chart-title"><i data-lucide="clipboard-list" style="color:#c40000"></i>Pasien Belum Minum Obat</div>
|
|
<input type="date" id="filterTanggal" value="{{ date('Y-m-d') }}" max="{{ date('Y-m-d') }}">
|
|
<ul id="listPasien"></ul>
|
|
</div>
|
|
|
|
<!-- BARIS BAWAH: Skrining & Status -->
|
|
<div class="chart-box">
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; flex-wrap:wrap; gap:10px;">
|
|
|
|
<div class="chart-title" style="margin-bottom:0;">
|
|
<i data-lucide="activity" style="color:#c40000"></i>
|
|
Aktivitas Skrining Bulanan
|
|
</div>
|
|
|
|
<form method="GET" action="{{ url('/dashboard') }}" style="display:flex; gap:10px;">
|
|
|
|
<!-- BULAN -->
|
|
<select
|
|
name="bulan_skrining"
|
|
onchange="this.form.submit()"
|
|
style="
|
|
padding:8px 14px;
|
|
border-radius:10px;
|
|
border:1px solid #ddd;
|
|
outline:none;
|
|
font-size:13px;
|
|
background:white;
|
|
cursor:pointer;
|
|
"
|
|
>
|
|
|
|
@php
|
|
$namaBulan = [
|
|
1 => 'Januari',
|
|
2 => 'Februari',
|
|
3 => 'Maret',
|
|
4 => 'April',
|
|
5 => 'Mei',
|
|
6 => 'Juni',
|
|
7 => 'Juli',
|
|
8 => 'Agustus',
|
|
9 => 'September',
|
|
10 => 'Oktober',
|
|
11 => 'November',
|
|
12 => 'Desember'
|
|
];
|
|
@endphp
|
|
|
|
@foreach($namaBulan as $key => $bulan)
|
|
<option value="{{ sprintf('%02d', $key) }}"
|
|
{{ $bulanSkriningDipilih == sprintf('%02d', $key) ? 'selected' : '' }}>
|
|
{{ $bulan }}
|
|
</option>
|
|
@endforeach
|
|
|
|
</select>
|
|
|
|
<!-- TAHUN -->
|
|
<select
|
|
name="tahun_skrining"
|
|
onchange="this.form.submit()"
|
|
style="
|
|
padding:8px 14px;
|
|
border-radius:10px;
|
|
border:1px solid #ddd;
|
|
outline:none;
|
|
font-size:13px;
|
|
background:white;
|
|
cursor:pointer;
|
|
"
|
|
>
|
|
|
|
@foreach($tahunList as $y)
|
|
<option value="{{ $y }}"
|
|
{{ $tahunSkrining == $y ? 'selected' : '' }}>
|
|
{{ $y }}
|
|
</option>
|
|
@endforeach
|
|
|
|
</select>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div class="canvas-wrapper">
|
|
<canvas id="chartSkrining"></canvas>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="chart-box">
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
|
|
|
<div class="chart-title" style="margin-bottom:0;">
|
|
<i data-lucide="pie-chart" style="color:#c40000"></i>
|
|
Status Medis
|
|
</div>
|
|
|
|
<form method="GET" action="{{ url('/dashboard') }}">
|
|
|
|
{{-- mempertahankan filter lain --}}
|
|
<input type="hidden" name="tahun" value="{{ $tahunDipilih }}">
|
|
<input type="hidden" name="bulan_skrining" value="{{ $bulanSkriningDipilih }}">
|
|
<input type="hidden" name="tahun_skrining" value="{{ $tahunSkrining }}">
|
|
|
|
<select
|
|
name="tahun_status"
|
|
onchange="this.form.submit()"
|
|
style="
|
|
padding:8px 14px;
|
|
border-radius:10px;
|
|
border:1px solid #ddd;
|
|
outline:none;
|
|
font-size:13px;
|
|
background:white;
|
|
cursor:pointer;
|
|
"
|
|
>
|
|
@foreach($tahunList as $y)
|
|
<option value="{{ $y }}" {{ $tahunStatusDipilih == $y ? 'selected' : '' }}>
|
|
Tahun {{ $y }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div class="canvas-wrapper">
|
|
<canvas id="chartStatus"></canvas>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display:none;">@csrf</form>
|
|
|
|
<script>
|
|
lucide.createIcons();
|
|
|
|
function confirmLogout() {
|
|
Swal.fire({
|
|
title: 'Logout Akun?',
|
|
text: "Anda yakin ingin keluar dari sistem?",
|
|
iconColor: '#c40000',
|
|
width: '400px',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ya, Keluar',
|
|
cancelButtonText: 'Batal',
|
|
reverseButtons: true,
|
|
buttonsStyling: false,
|
|
customClass: {
|
|
popup: 'modern-popup',
|
|
title: 'title-swal',
|
|
htmlContainer: 'content-swal',
|
|
confirmButton: 'btn-confirm-swal',
|
|
cancelButton: 'btn-cancel-swal'
|
|
}
|
|
}).then((result) => {
|
|
if (result.isConfirmed) document.getElementById('logout-form').submit();
|
|
});
|
|
}
|
|
|
|
// 1. Grafik Tren Bulanan (Bar)
|
|
new Chart(document.getElementById('chartBulanan'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: @json($dataBulanan['labels']),
|
|
datasets: [{ label: 'Pasien Baru', data: @json($dataBulanan['values']), backgroundColor: '#c40000' }]
|
|
},
|
|
options: { responsive: true, maintainAspectRatio: false }
|
|
});
|
|
|
|
// 2. Grafik Skrining (Line)
|
|
new Chart(document.getElementById('chartSkrining'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: @json($dataSkrining['labels']),
|
|
datasets: [{
|
|
label: 'Total Skrining',
|
|
data: @json($dataSkrining['values']),
|
|
borderColor: '#c40000',
|
|
fill: true,
|
|
backgroundColor: 'rgba(196,0,0,0.1)',
|
|
tension: 0.3,
|
|
pointBackgroundColor: '#c40000',
|
|
pointBorderColor: '#fff',
|
|
pointRadius: 5,
|
|
pointHoverRadius: 8,
|
|
pointHoverBackgroundColor: '#c40000'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
|
|
// TOOLTIP HANYA MUNCUL SAAT KURSOR KE TITIK
|
|
interaction: {
|
|
mode: 'nearest',
|
|
intersect: true
|
|
},
|
|
|
|
plugins: {
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function(context) {
|
|
|
|
const total = context.raw;
|
|
|
|
const terduga = @json($dataSkrining['terduga']);
|
|
const tidak = @json($dataSkrining['tidak_terduga']);
|
|
|
|
const index = context.dataIndex;
|
|
|
|
return [
|
|
'Total Skrining : ' + total,
|
|
'Terduga TBC : ' + terduga[index],
|
|
'Tidak Terduga : ' + tidak[index]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
ticks: {
|
|
precision: 0,
|
|
stepSize: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 3. Grafik Status (Doughnut - 4 Status)
|
|
new Chart(document.getElementById('chartStatus'), {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: @json($dataStatus['labels']),
|
|
datasets: [{ data: @json($dataStatus['values']), backgroundColor: ['#ffc107', '#28a745', '#dc3545', '#6c757d'] }]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 4. AJAX untuk List Pasien Belum Minum Obat (FIXED: Nama Saja)
|
|
async function updateBelumMinum() {
|
|
const tgl = document.getElementById('filterTanggal').value;
|
|
const res = await fetch(`{{ url('/dashboard-advanced') }}?tanggal=${tgl}`);
|
|
const data = await res.json();
|
|
let html = '';
|
|
if(data.top_pasien.length > 0) {
|
|
data.top_pasien.forEach(p => {
|
|
// Menghilangkan jam_minum, hanya menampilkan nama_lengkap
|
|
html += `<li><i data-lucide="user" style="width:16px; color:#c40000;"></i> <span>${p.nama_lengkap}</span></li>`;
|
|
});
|
|
} else {
|
|
html = '<li style="justify-content:center; color:#28a745; font-weight:600;">Semua pasien sudah absen pada tanggal ini.</li>';
|
|
}
|
|
document.getElementById('listPasien').innerHTML = html;
|
|
lucide.createIcons(); // Re-render icons
|
|
}
|
|
|
|
document.getElementById('filterTanggal').addEventListener('change', updateBelumMinum);
|
|
updateBelumMinum(); // Load saat pertama kali buka halaman
|
|
</script>
|
|
|
|
</body>
|
|
</html> |