MIF_E31231302_WEBSITE/resources/views/dashboard.blade.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>