MIF_E31231302_WEBSITE/resources/views/detail-pasien.blade.php

1001 lines
29 KiB
PHP

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Detail Pasien - Sistem TBC</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
<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 {
background: #f8fafc;
display: flex;
min-height: 100vh;
}
.sidebar {
width: 250px;
background: #c40000;
color: white;
padding-top: 30px;
position: fixed;
height: 100vh;
}
/* SIDEBAR TIDAK DIUBAH */
.sidebar h2 {
text-align: center;
margin-bottom: 40px;
font-size: 20px;
font-weight: 700;
}
.menu a {
display: flex;
align-items: center;
padding: 15px 25px;
color: white;
text-decoration: none;
gap: 15px;
transition: 0.3s;
}
.menu li.active a {
background: white;
color: #c40000;
border-radius: 30px 0 0 30px;
margin-left: 10px;
}
.main-content {
margin-left: 250px;
width: calc(100% - 250px);
padding: 28px;
}
.back-link {
display: flex;
align-items: center;
gap: 8px;
color: #64748b;
text-decoration: none;
font-weight: 500;
margin-bottom: 20px;
font-size: 13px;
}
.back-link:hover {
color: #c40000;
}
/* INFO PASIEN CARD */
.card-info {
background: white;
border-radius: 16px;
padding: 22px;
display: flex;
align-items: center;
gap: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.03);
margin-bottom: 24px;
}
.avatar-box {
width: 75px;
height: 75px;
background: #fff1f2;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
color: #c40000;
}
.info-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
flex: 1;
gap: 16px;
}
.info-item label {
display: block;
font-size: 10px;
color: #94a3b8;
font-weight: 600;
text-transform: uppercase;
margin-bottom: 4px;
}
.info-item span {
font-size: 13px;
font-weight: 600;
color: #1e293b;
}
.tgl-mulai {
color: #c40000 !important;
}
.tgl-selesai {
color: #166534 !important;
}
.text-danger {
color: #ef4444 !important;
}
/* TRACKING GRID */
.section-title {
font-size: 15px;
font-weight: 700;
color: #1e293b;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.grid-container {
background: white;
border-radius: 16px;
padding: 22px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.03);
width: 100%;
}
.phase-label {
font-size: 12px;
font-weight: 700;
color: #475569;
margin: 20px 0 12px;
display: block;
background: #f1f5f9;
padding: 5px 12px;
border-radius: 8px;
width: fit-content;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
gap: 6px;
}
.day-box {
height: 38px;
border: 1px solid #e2e8f0;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
color: #64748b;
transition: 0.2s;
}
/* TOOLTIP JAM KONFIRMASI */
.day-box {
position: relative;
cursor: pointer;
}
.day-box[data-jam]:hover::after {
content: attr(data-jam);
position: absolute;
bottom: 46px;
left: 50%;
transform: translateX(-50%);
background: #1e293b;
color: white;
padding: 6px 10px;
border-radius: 8px;
font-size: 10px;
font-weight: 600;
white-space: nowrap;
z-index: 99;
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.18);
}
.day-box[data-jam]:hover::before {
content: "";
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: #1e293b transparent transparent transparent;
z-index: 99;
}
.day-box.done {
background: #16a34a;
color: white;
border-color: #16a34a;
}
.day-box.missed {
background: #ef4444;
color: white;
border-color: #ef4444;
}
.day-box.pending {
background: #f8fafc;
color: #64748b;
border: 1px solid #e2e8f0;
}
.day-box i {
font-size: 7px;
margin-top: 1px;
color: white;
}
/* SWEETALERT */
.swal2-popup.modern-popup {
border-radius: 20px !important;
padding: 1.5rem !important;
border: 1px solid rgba(255, 255, 255, 0.5) !important;
background: rgba(255, 255, 255, 0.96) !important;
backdrop-filter: blur(10px) !important;
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.14) !important;
}
.swal2-title.title-swal {
font-size: 18px !important;
font-weight: 700 !important;
color: #1e293b !important;
margin-bottom: 8px !important;
}
.swal2-html-container.text-swal {
font-size: 13px !important;
font-weight: 500 !important;
color: #64748b !important;
line-height: 1.5 !important;
margin-top: 6px !important;
}
.swal2-icon.swal2-warning {
border-color: #c40000 !important;
color: #c40000 !important;
transform: scale(0.9);
}
.btn-confirm-swal {
background: linear-gradient(135deg, #c40000, #ff1e1e) !important;
color: white !important;
padding: 10px 24px !important;
border-radius: 14px !important;
font-weight: 600 !important;
font-size: 13px !important;
margin: 4px !important;
border: none !important;
box-shadow: 0 10px 20px rgba(196, 0, 0, 0.2) !important;
transition: 0.3s !important;
}
.btn-cancel-swal {
background: #f1f5f9 !important;
color: #475569 !important;
padding: 10px 24px !important;
border-radius: 14px !important;
font-weight: 600 !important;
font-size: 13px !important;
border: none !important;
margin: 4px !important;
transition: 0.3s !important;
}
.swal2-styled:focus {
box-shadow: none !important;
}
.swal2-backdrop-show {
background: rgba(15, 23, 42, 0.55) !important;
backdrop-filter: blur(5px);
}
.timeline-wrapper {
position: relative;
margin-bottom: 24px;
}
.timeline-line {
position: absolute;
left: 18px;
top: 50px;
bottom: 0;
width: 3px;
background: #e2e8f0;
border-radius: 10px;
}
.timeline-item {
position: relative;
padding-left: 55px;
margin-bottom: 16px;
}
.timeline-dot {
position: absolute;
left: 8px;
top: 10px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #c40000;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
font-weight: 700;
}
.timeline-card {
background: white;
border-radius: 14px;
padding: 15px 16px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.03);
border: 1px solid #f1f5f9;
}
.timeline-title {
font-size: 13px;
font-weight: 700;
color: #1e293b;
margin-bottom: 6px;
}
.timeline-meta {
font-size: 12px;
color: #64748b;
line-height: 1.5;
}
.badge-active {
display: inline-block;
padding: 4px 10px;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
background: #dcfce7;
color: #166534;
margin-left: 8px;
}
/* TOGGLE RIWAYAT */
.riwayat-wrapper {
margin-bottom: 28px;
}
.riwayat-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.toggle-btn {
width: 36px;
height: 36px;
border-radius: 10px;
background: #f1f5f9;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: 0.25s;
}
.toggle-btn:hover {
background: #e2e8f0;
}
.toggle-btn i {
width: 16px;
height: 16px;
color: #475569;
transition: 0.3s;
}
.riwayat-wrapper.closed .toggle-btn i {
transform: rotate(0deg);
}
.riwayat-wrapper.active .toggle-btn i {
transform: rotate(180deg);
}
.riwayat-content {
overflow: hidden;
transition: all 0.35s ease;
}
.riwayat-wrapper.closed .riwayat-content {
max-height: 0;
opacity: 0;
margin-top: 0;
}
.riwayat-wrapper.active .riwayat-content {
max-height: 5000px;
opacity: 1;
margin-top: 18px;
}
.btn-danger-swal {
background: #dc2626 !important;
color: white !important;
border: none !important;
border-radius: 10px !important;
padding: 10px 18px !important;
font-size: 13px !important;
font-weight: 600 !important;
}
.btn-danger-swal:hover {
background: #b91c1c !important;
}
.btn-cancel-swal {
background: #e5e7eb !important;
color: #111827 !important;
border: none !important;
border-radius: 10px !important;
padding: 10px 18px !important;
font-size: 13px !important;
font-weight: 600 !important;
}
.btn-cancel-swal:hover {
background: #d1d5db !important;
}
</style>
</head>
<body>
<div class="sidebar">
<h2>Sistem TBC</h2>
<ul class="menu">
<li><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">
<a href="{{ url('/data-pasien') }}" class="back-link">
<i data-lucide="arrow-left" size="18"></i> Kembali ke Daftar Pasien</a>
@foreach($pasien->riwayat_pengobatan->sortByDesc('id_medis') as $rp)
<!-- =========================
CARD INFO
========================= -->
<div class="card-info">
<div class="avatar-box">
<i data-lucide="user" size="45"></i>
</div>
<div class="info-grid">
<div class="info-item">
<label>Pengobatan Ke</label>
<span>{{ $rp->pengobatan_ke }}</span>
</div>
<div class="info-item">
<label>Diagnosa</label>
<span>{{ $rp->diagnosa }}</span>
</div>
<div class="info-item">
<label>Mulai Pengobatan</label>
<span class="tgl-mulai">
{{ $rp->tgl_mulai_pengobatan }}
</span>
</div>
<div class="info-item">
<label>Estimasi Selesai</label>
<span class="tgl-selesai">
{{ $rp->tgl_estimasi_selesai }}
</span>
</div>
<div class="info-item">
<label>Jam Minum Obat</label>
<span>{{ $rp->jam_minum ?? '-' }}</span>
</div>
<div class="info-item">
<label>Tidak Minum Obat</label>
<span class="text-danger">
{{ $rp->jumlah_missed ?? 0 }} Hari
</span>
</div>
<div class="info-item">
<label>Hasil Laboratorium</label>
<button type="button"
onclick="lihatFileLab({{ $rp->id_medis }})"
style="
margin-top:8px;
background:#c40000;
color:white;
border:none;
padding:6px 10px;
border-radius:999px;
font-size:13px;
font-weight:600;
cursor:pointer;
display:flex;
align-items:center;
gap:8px;
transition:0.2s;
">
<i data-lucide="file-text" size="16"></i>
Lihat File Lab
</button>
</div>
<div class="info-item">
<label>Status</label>
@php
$status = $rp->status_kesehatan ?? 'Masih Proses';
$bgColor = '#f1f5f9';
$textColor = '#475569';
if ($status == 'Sembuh') {
$bgColor = '#dcfce7';
$textColor = '#166534';
}
elseif ($status == 'Gagal') {
$bgColor = '#fee2e2';
$textColor = '#991b1b';
}
elseif ($status == 'Meninggal') {
$bgColor = '#e2e8f0';
$textColor = '#334155';
}
elseif ($status == 'Masih Proses') {
$bgColor = '#fef3c7';
$textColor = '#92400e';
}
@endphp
<div style="margin-top:8px;">
<span style="
display:inline-flex;
align-items:center;
padding:10px 16px;
border-radius:999px;
font-size:13px;
font-weight:700;
background:{{ $bgColor }};
color:{{ $textColor }};
">
{{ $status }}
</span>
</div>
</div>
</div>
</div>
@php
$totalRiwayat = count($pasien->riwayat_pengobatan);
/*
DATA BARU = pengobatan_ke terbesar
DATA LAMA = selain pengobatan terbaru
*/
$maxPengobatan = collect($pasien->riwayat_pengobatan)
->max('pengobatan_ke');
$isRiwayatLama = $rp->pengobatan_ke != $maxPengobatan;
@endphp
<div class="riwayat-wrapper
{{ ($totalRiwayat > 1 && $isRiwayatLama) ? 'closed' : 'active' }}">
<div class="grid-container">
<div class="riwayat-header"
@if($totalRiwayat> 1 && $isRiwayatLama)
onclick="toggleRiwayat(this)"
@endif>
<div class="section-title" style="margin-bottom:0;">
<i data-lucide="calendar-check" color="#ef4444"></i>
Progress Minum Obat - Pengobatan Ke {{ $rp->pengobatan_ke }}
</div>
@if($totalRiwayat > 1 && $isRiwayatLama)
<button type="button" class="toggle-btn">
<i data-lucide="chevron-down"></i>
</button>
@endif
</div>
<div class="riwayat-content">
<!-- TAHAP AWAL -->
<span class="phase-label">
TAHAP AWAL (56 HARI)
</span>
<div class="calendar-grid">
@foreach($rp->tahap_awal as $hari => $item)
<div
class="day-box {{ $item['status'] ?? 'missed' }}"
data-jam="{{ $item['jam'] ? 'Konfirmasi: '.$item['jam'] : '' }}">
{{ $hari }}
@if(($item['status'] ?? null) == 'done')
<i data-lucide="check" size="10"></i>
@elseif(($item['status'] ?? null) == 'missed')
<i data-lucide="x" size="10"></i>
@endif
</div>
@endforeach
</div>
<!-- TAHAP LANJUTAN -->
<span class="phase-label">
TAHAP LANJUTAN (112 HARI)
</span>
<div class="calendar-grid">
@foreach($rp->tahap_lanjutan as $hari => $item)
<div
class="day-box {{ $item['status'] ?? 'missed' }}"
data-jam="{{ $item['jam'] ? 'Konfirmasi: '.$item['jam'] : '' }}">
{{ $hari }}
@if(($item['status'] ?? null) == 'done')
<i data-lucide="check" size="10"></i>
@elseif(($item['status'] ?? null) == 'missed')
<i data-lucide="x" size="10"></i>
@endif
</div>
@endforeach
</div>
</div>
</div>
</div>
@endforeach
</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?",
icon: 'warning',
iconColor: '#c40000',
width: '350px',
showCancelButton: true,
confirmButtonText: 'Ya, Logout',
cancelButtonText: 'Batal',
reverseButtons: true,
buttonsStyling: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal',
htmlContainer: 'text-swal',
confirmButton: 'btn-confirm-swal',
cancelButton: 'btn-cancel-swal'
}
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Berhasil Logout',
text: 'Anda akan diarahkan ke halaman login...',
icon: 'success',
timer: 1200,
showConfirmButton: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal',
htmlContainer: 'text-swal'
}
});
setTimeout(() => {
document.getElementById('logout-form').submit();
}, 900);
}
});
}
// =====================================
// LIHAT FILE LAB BERDASARKAN ID MEDIS
// =====================================
function lihatFileLab(id_medis) {
fetch(`/pasien/medis/${id_medis}/file`)
.then(res => res.json())
.then(res => {
if (!res.success || !res.data || res.data.length === 0) {
Swal.fire({
icon: 'info',
title: 'Tidak Ada File',
text: 'Belum ada file hasil laboratorium.',
width: '360px',
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal'
}
});
return;
}
let html = `<div style="text-align:left;">`;
res.data.forEach((file, index) => {
let nama =
file.nama_file ??
('File Lab ' + (index + 1));
html += `
<div style="
display:flex;
justify-content:space-between;
align-items:center;
padding:12px;
border-bottom:1px solid #eee;
gap:10px;
">
<div style="flex:1;">
<b style="
font-size:13px;
color:#111827;
">
${nama}
</b>
<br>
<small style="color:#94a3b8;">
${file.created_at ?? ''}
</small>
</div>
<div style="
display:flex;
gap:8px;
align-items:center;
">
<a href="/storage/${file.path_file}"
target="_blank"
style="
background:#16a34a;
color:white;
padding:7px 13px;
border-radius:10px;
text-decoration:none;
font-size:12px;
font-weight:600;
">
Buka
</a>
</div>
</div>
`;
});
html += `</div>`;
Swal.fire({
title: 'File Hasil Laboratorium',
html: html,
width: 700,
showCloseButton: true,
showConfirmButton: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal'
}
});
})
.catch(() => {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Gagal mengambil file laboratorium.',
width: '360px',
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal'
}
});
});
}
// =========================================
// FUNCTION HAPUS FILE LAB
// =========================================
function hapusFileLab(id_file) {
Swal.fire({
title: 'Hapus File?',
text: 'File laboratorium akan dihapus permanen.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal',
reverseButtons: true,
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-danger-swal',
cancelButton: 'btn-cancel-swal'
}
}).then((result) => {
if (result.isConfirmed) {
fetch(`/file-lab/hapus/${id_file}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json'
}
})
.then(res => res.json())
.then(res => {
if (res.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: 'File berhasil dihapus.',
timer: 1200,
showConfirmButton: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal'
}
});
setTimeout(() => {
lihatFileLab(res.id_medis);
}, 1200);
}
})
.catch(() => {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Terjadi kesalahan server.',
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal'
}
});
});
}
});
}
function toggleRiwayat(el) {
const wrapper = el.closest('.riwayat-wrapper');
if (wrapper.classList.contains('closed')) {
wrapper.classList.remove('closed');
wrapper.classList.add('active');
} else {
wrapper.classList.remove('active');
wrapper.classList.add('closed');
}
lucide.createIcons();
}
</script>
</body>
</html>