MIF_E31231302_WEBSITE/resources/views/skrining.blade.php

2396 lines
65 KiB
PHP

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Skrining 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 {
display: flex;
background: #f5f6fa;
min-height: 100vh;
}
/* SIDEBAR */
.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;
transition: .3s;
gap: 15px;
}
.menu li:hover a,
.menu li.active a {
background: white;
color: #c40000;
border-radius: 30px 0 0 30px;
margin-left: 10px;
}
/* CONTENT */
.main-content {
margin-left: 250px;
padding: 40px;
width: calc(100% - 250px);
}
.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;
}
/* TOP BAR */
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.search-wrapper {
position: relative;
width: 300px;
display: flex;
align-items: center;
}
.search-wrapper .icon-container {
position: absolute;
left: 15px;
display: flex;
align-items: center;
pointer-events: none;
color: #888;
z-index: 9;
}
.search-wrapper input {
width: 100%;
padding: 12px 15px 12px 45px !important;
border-radius: 12px;
border: 1px solid #ddd;
background: #fff;
outline: none;
font-size: 14px;
transition: .3s;
}
.search-wrapper input:focus {
border-color: #c40000;
box-shadow: 0 0 0 3px rgba(196, 0, 0, .1);
}
/* FILTER */
.filter-accordion {
background: white;
border-radius: 15px;
margin-bottom: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, .05);
overflow: hidden;
border: 1px solid #eee;
}
.filter-header {
padding: 15px 25px;
background: #fff;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: .3s;
}
.filter-header:hover {
background: #fefefe;
}
.filter-header h4 {
font-size: 15px;
color: #333;
display: flex;
align-items: center;
gap: 10px;
}
.filter-content {
padding: 0 25px;
max-height: 0;
overflow: hidden;
transition: max-height .3s ease-out, padding .3s ease;
background: #fff;
}
.filter-content.active {
padding: 20px 25px;
max-height: 600px;
border-top: 1px solid #f5f5f5;
}
.filter-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
align-items: flex-end;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.filter-group label {
font-size: 12px;
font-weight: 600;
color: #64748b;
}
.filter-group input,
.filter-group select {
padding: 12px;
border: 1px solid #ddd;
border-radius: 10px;
outline: none;
font-size: 13px;
transition: .3s;
}
.filter-group input:focus,
.filter-group select:focus {
border-color: #c40000;
box-shadow: 0 0 0 3px rgba(196, 0, 0, .08);
}
.filter-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 15px;
border-top: 1px dashed #eee;
padding-top: 15px;
flex-wrap: wrap;
}
.btn-cari,
.btn-reset,
.btn-export {
padding: 8px 16px;
border-radius: 8px;
border: none;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 12px;
transition: .3s;
text-decoration: none;
}
.btn-cari {
background: #c40000;
color: white;
}
.btn-reset {
background: #64748b;
color: white;
}
.btn-export {
background: #22c55e;
color: white;
}
/* TABLE */
.table-container {
background: white;
border-radius: 20px;
padding: 20px;
box-shadow: 0 5px 20px rgba(0, 0, 0, .05);
width: 100%;
}
.table-scroll {
width: 100%;
overflow: visible;
}
table {
width: 100%;
table-layout: fixed;
}
th,
td {
padding: 14px 12px;
text-align: left;
vertical-align: middle;
white-space: nowrap;
font-size: 13px;
}
thead th {
background: #fafafa;
border-bottom: 2px solid #f0f0f0;
color: #444;
font-weight: 600;
}
th.col-no,
td.col-no {
width: 60px;
}
th.col-nama,
td.col-nama {
width: 220px;
}
td,
th {
white-space: normal;
word-break: break-word;
}
tbody tr:hover td.col-no,
tbody tr:hover td.col-nama {
background-color: #f9fafb !important;
}
.badge {
padding: 6px 12px;
border-radius: 20px;
font-size: 10px;
font-weight: 700;
display: inline-block;
text-align: center;
}
.badge-danger {
background: #fee2e2;
color: #ef4444;
}
.badge-success {
background: #dcfce7;
color: #22c55e;
}
/* ACTION */
.action-group {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.btn-action {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
border: none;
cursor: pointer;
transition: .3s;
}
.btn-view-detail {
background: #f1f5f9;
color: #64748b;
}
.btn-make-patient {
background: #eff6ff;
color: #3b82f6;
}
.btn-delete-skrining {
background: #fee2e2;
color: #ef4444;
}
.btn-delete-skrining:hover {
background: #fecaca;
}
.disabled-btn {
background: #e5e7eb !important;
color: #9ca3af !important;
cursor: not-allowed !important;
}
.swal2-popup.modern-popup {
width: 360px !important;
border-radius: 26px !important;
padding: 1.5rem 1.3rem 1.3rem !important;
background: rgba(255, 255, 255, 0.98) !important;
backdrop-filter: blur(18px) !important;
-webkit-backdrop-filter: blur(18px) !important;
box-shadow: 0 25px 60px rgba(15, 23, 42, 0.18) !important;
border: 1px solid rgba(255, 255, 255, 0.7) !important;
}
.swal2-icon.swal2-warning {
border-color: rgba(196, 0, 0, 0.15) !important;
color: #c40000 !important;
width: 68px !important;
height: 68px !important;
margin-top: 5px !important;
}
.swal2-title.title-swal {
font-size: 20px !important;
font-weight: 700 !important;
color: #0f172a !important;
padding: 0 !important;
margin-top: 10px !important;
line-height: 1.4 !important;
}
.swal2-html-container {
font-size: 13px !important;
color: #64748b !important;
line-height: 1.7 !important;
margin: 10px 0 5px 0 !important;
padding: 0 10px !important;
}
.swal2-actions {
width: 100% !important;
display: flex !important;
gap: 10px !important;
margin-top: 20px !important;
padding: 0 5px !important;
}
.btn-confirm-swal {
flex: 1 !important;
background: linear-gradient(135deg, #c40000, #ff3b3b) !important;
color: white !important;
padding: 11px 14px !important;
border-radius: 14px !important;
font-weight: 600 !important;
font-size: 13px !important;
border: none !important;
margin: 0 !important;
box-shadow: 0 10px 25px rgba(196, 0, 0, 0.25) !important;
transition: all 0.25s ease !important;
}
.btn-confirm-swal:hover {
transform: translateY(-2px) scale(1.02) !important;
box-shadow: 0 14px 30px rgba(196, 0, 0, 0.35) !important;
}
.btn-cancel-swal {
flex: 1 !important;
background: #f8fafc !important;
color: #475569 !important;
padding: 11px 14px !important;
border-radius: 14px !important;
font-weight: 600 !important;
font-size: 13px !important;
border: 1px solid #e2e8f0 !important;
margin: 0 !important;
transition: all 0.25s ease !important;
}
.btn-cancel-swal:hover {
background: #eef2f7 !important;
transform: translateY(-2px) !important;
}
.swal2-success-ring {
border: 0.25em solid rgba(34, 197, 94, 0.25) !important;
}
/* MODAL */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(15, 23, 42, .45);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
backdrop-filter: blur(6px);
padding: 18px;
}
.modal-content {
background: #ffffff;
width: 100%;
max-width: 400px;
border-radius: 22px;
overflow: hidden;
box-shadow: 0 20px 45px rgba(0, 0, 0, .16);
}
.modal-header {
padding: 15px 18px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #f1f5f9;
}
.modal-header h3 {
font-size: 15px;
font-weight: 700;
color: #0f172a;
}
.btn-icon {
width: 30px;
height: 30px;
border: none;
background: #f8fafc;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #64748b;
}
.modal-body {
padding: 16px;
}
.patient-info {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 14px;
padding: 15px;
margin-bottom: 14px;
}
.patient-label {
font-size: 10px;
font-weight: 600;
color: #c40000;
margin-bottom: 4px;
}
.patient-name {
font-size: 14px;
font-weight: 700;
color: #1e293b;
}
.result-card.terduga {
background: #fef2f2;
border: 1px solid #fecaca;
}
.result-card.terduga .result-title,
.result-card.terduga .result-value {
color: #dc2626;
}
.result-card.tidak-terduga {
background: #f0fdf4;
border: 1px solid #bbf7d0;
}
.result-card.tidak-terduga .result-title,
.result-card.tidak-terduga .result-value {
color: #16a34a;
}
.status-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
}
.kuesioner-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.q-item {
background: #f8fafc;
border-radius: 12px;
padding: 10px 12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.q-text {
font-size: 12px;
font-weight: 500;
color: #334155;
}
.q-ans {
font-size: 11px;
font-weight: 700;
padding: 5px 10px;
border-radius: 20px;
}
.text-red {
background: #fee2e2;
color: #dc2626;
}
.text-green {
background: #dcfce7;
color: #16a34a;
}
.btn-close-modal {
width: 100%;
border: none;
background: #c40000;
color: white;
padding: 10px;
border-radius: 12px;
font-weight: 600;
font-size: 12px;
cursor: pointer;
}
.pagination-wrapper-modern {
margin-top: 25px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 18px;
background: #ffffff;
border-radius: 14px;
border: 1px solid #eef2f7;
box-shadow: 0 4px 12px rgba(0, 0, 0, .04);
flex-wrap: wrap;
gap: 10px;
}
.pagination-info {
font-size: 12px;
color: #64748b;
}
.pagination-links-modern nav {
display: flex;
gap: 6px;
}
@media(max-width:768px) {
.sidebar {
display: none;
}
.main-content {
margin-left: 0;
width: 100%;
padding: 20px;
}
table {
width: 1300px;
}
}
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
backdrop-filter: blur(4px);
}
.modal-content {
width: 900px;
max-width: 95%;
border-radius: 20px;
overflow: hidden;
border-radius: 20px;
padding: 30px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
}
.detail-layout {
display: grid;
grid-template-columns: 320px 1fr;
gap: 20px;
align-items: start;
}
.detail-left,
.detail-right {
display: flex;
flex-direction: column;
gap: 12px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.modal-header h3 {
font-size: 20px;
font-weight: 700;
color: #c40000;
}
.form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.form-group {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.form-group label {
font-size: 13px;
font-weight: 600;
color: #444;
}
.form-group input,
.form-group select {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 10px;
outline: none;
font-size: 14px;
font-family: 'Poppins', sans-serif;
background: white;
}
.field-error {
border: 1px solid #dc2626 !important;
}
.field-note {
color: #dc2626;
font-size: 11px;
font-weight: 500;
margin-top: 4px;
display: none;
}
.form-group input[readonly] {
background: #f9fafb;
}
.section-title {
font-size: 16px;
font-weight: 700;
margin-top: 20px;
margin-bottom: 10px;
color: #000;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 30px;
}
.btn {
padding: 12px 25px;
border-radius: 10px;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: 0.2s;
}
.btn-cancel {
background: #f3f4f6;
color: #4b5563;
}
.btn-submit {
background: #c40000;
color: white;
}
.btn-submit:hover {
background: #a80000;
}
.btn-submit:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.close-btn {
width: 30px;
height: 30px;
border: none;
border-radius: 10px;
background: #e8f5e9;
color: #2e7d32;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.identity-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin: 12px 0;
}
.identity-item,
.identity-item-full {
background: white;
border-radius: 10px;
padding: 10px;
border: 1px solid #ffffff;
min-height: 70px;
}
.identity-item span,
.identity-item-full span {
display: block;
font-size: 11px;
color: #47515f;
margin-bottom: 4px;
}
.identity-item strong,
.identity-item-full strong {
font-size: 13px;
color: #1e293b;
word-break: break-word;
white-space: normal;
}
.result-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 14px;
padding: 15px;
}
.result-title {
font-size: 12px;
color: #64748b;
margin-bottom: 6px;
}
.result-value {
font-size: 14px;
font-weight: 700;
}
.modal-footer-detail {
padding: 20px 30px;
border-top: 1px solid #f1f5f9;
display: flex;
justify-content: flex-end;
}
.modal-footer-detail .btn-close-modal {
width: auto;
min-width: 120px;
padding: 12px 24px;
border: none;
border-radius: 12px;
background: #c40000;
color: white;
font-weight: 600;
cursor: pointer;
transition: .2s;
}
.modal-footer-detail .btn-close-modal:hover {
background: #a80000;
}
</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>
<li>
@if(session('role') !== 'super_admin')<li><a href="{{ url('/data-pasien') }}"><i data-lucide="users"></i> Data Pasien</a></li>@endif
<li class="active">
<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>
</li>
</ul>
</div>
<div class="main-content">
<div class="header-wrapper">
<h1 class="header-title">Skrining Pasien</h1>
</div>
<div class="top-bar">
<div></div>
<div class="search-wrapper">
<div class="icon-container">
<i data-lucide="search" size="18"></i>
</div>
<input
type="text"
id="searchInput"
placeholder="Cari Nama atau NIK...">
</div>
</div>
<!-- FILTER -->
<div class="filter-accordion">
<div class="filter-header" onclick="toggleFilter()">
<h4>
<i data-lucide="filter" size="18"></i>
Pencarian Lanjutan & Export
</h4>
<i data-lucide="chevron-down" id="filterIcon"></i>
</div>
<div
class="filter-content
{{
request('hasil_skrining') ||
request('fasyankes') ||
request('tgl_mulai') ||
request('tgl_selesai')
? 'active'
: ''
}}"
id="filterContent">
<form action="{{ url('/skrining') }}" method="GET" id="filterForm">
<div class="filter-grid">
<div class="filter-group">
<label>Hasil Skrining</label>
<select name="hasil_skrining" id="filter_hasil">
<option value="">Semua Hasil</option>
<option
value="Terduga TBC"
{{ request('hasil_skrining') == 'Terduga TBC' ? 'selected' : '' }}>
Terduga TBC
</option>
<option
value="Tidak Terduga TBC"
{{ request('hasil_skrining') == 'Tidak Terduga TBC' ? 'selected' : '' }}>
Tidak Terduga TBC
</option>
</select>
</div>
<div class="filter-group">
<label>Fasyankes</label>
<select name="fasyankes" id="filter_fasyankes">
<option value="">Semua Fasyankes</option>
@forelse($fasyankes_list ?? [] as $f)
<option
value="{{ $f->nama_fasyankes }}"
{{ request('fasyankes') == $f->nama_fasyankes ? 'selected' : '' }}>
{{ $f->nama_fasyankes }}
</option>
@empty
@endforelse
</select>
</div>
<div class="filter-group">
<label>Mulai Tanggal</label>
<input
type="date"
name="tgl_mulai"
id="filter_tgl_mulai"
value="{{ request('tgl_mulai') }}">
</div>
<div class="filter-group">
<label>Sampai Tanggal</label>
<input
type="date"
name="tgl_selesai"
id="filter_tgl_selesai"
value="{{ request('tgl_selesai') }}">
</div>
</div>
<div class="filter-actions">
<a href="{{ url('/skrining') }}" class="btn-reset">
<i data-lucide="refresh-cw" size="14"></i>
Reset
</a>
<button
type="button"
class="btn-export"
onclick="submitExport()">
<i data-lucide="download" size="14"></i>
Export
</button>
<button type="submit" class="btn-cari">
<i data-lucide="search" size="14"></i>
Cari Data
</button>
</div>
</form>
</div>
</div>
<div class="table-container">
<div class="table-scroll">
<table>
<thead>
<tr>
<th class="col-no">No</th>
<th class="col-nama">Nama</th>
<th>NIK</th>
<th>Tgl Skrining</th>
<th>Hasil Skrining</th>
@if(session('role') === 'super_admin')
<th>Fasyankes</th>
@endif
<th style="text-align:center;">Aksi</th>
</tr>
</thead>
<tbody id="skriningTableBody">
@forelse($skrining as $s)
<tr>
<td class="col-no">
{{
($skrining->currentPage() - 1)
* $skrining->perPage()
+ $loop->iteration
}}
</td>
<td class="col-nama">
{{ $s->nama_lengkap ?? '-' }}
</td>
<td>
{{ $s->nik ?? '-' }}
</td>
<td>
{{ !empty($s->tgl_skrining) ? date('d/m/Y', strtotime($s->tgl_skrining)) : '-' }}
</td>
<td>
<span class="badge {{ ($s->hasil_skrining ?? '') == 'Terduga TBC' ? 'badge-danger' : 'badge-success' }}">
{{ $s->hasil_skrining ?? '-' }}
</span>
</td>
@if(session('role') === 'super_admin')
<td>
{{ $s->nama_fasyankes ?? '-' }}
</td>
@endif
{{-- GANTI DENGAN INI (100% TANPA JSON ERROR) --}}
<td style="text-align:center;">
<div class="action-group">
<button
type="button"
class="btn-action btn-view-detail"
title="Lihat Detail"
data-item='@json($s)'
onclick="openSkriningFromButton(this)">
<i data-lucide="eye" size="16"></i>
</button>
@if($s->hasil_skrining == 'Terduga TBC')
<button
type="button"
class="btn-action btn-make-patient"
title="Daftarkan Pasien"
data-item='@json($s)'
onclick="openModalJadikanPasien(this)">
<i data-lucide="user-plus" size="16"></i>
</button>
@else
<button
type="button"
class="btn-action btn-make-patient disabled-btn"
disabled>
<i data-lucide="user-plus" size="16"></i>
</button>
@endif
@if(session('role') == 'super_admin')
<form
id="delete-form-{{ $s->id_skrining ?? $s->id }}"
action="{{ url('/skrining/hapus/' . ($s->id_skrining ?? $s->id)) }}"
method="POST"
style="display:inline;">
@csrf
@method('DELETE')
<button
type="button"
class="btn-action btn-delete-skrining"
title="Hapus Skrining"
onclick="confirmDeleteSkrining(
'{{ $s->id_skrining ?? $s->id }}',
'{{ addslashes($s->nama_lengkap) }}'
)">
<i data-lucide="trash-2" size="16"></i>
</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="12" style="text-align:center;padding:30px;">
Data tidak ditemukan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if(isset($skrining) && count($skrining) > 0)
<div class="pagination-wrapper-modern">
<div class="pagination-info">
Menampilkan
<b>{{ $skrining->firstItem() }}</b>
-
<b>{{ $skrining->lastItem() }}</b>
dari
<b>{{ $skrining->total() }}</b>
data
</div>
<div class="pagination-links-modern">
{{ $skrining->links() }}
</div>
</div>
@endif
</div>
</div>
<form
id="logout-form"
action="{{ route('logout') }}"
method="POST"
style="display:none;">
@csrf
</form>
<!-- MODAL -->
<div class="modal-overlay" id="modalSkrining">
<div class="modal-content">
<div class="modal-header">
<h3>Detail Skrining</h3>
<button
type="button"
class="btn-icon"
onclick="closeSkrining()">
<i data-lucide="x" size="16"></i>
</button>
</div>
<div class="modal-body">
<div class="detail-layout">
<!-- KOLOM KIRI -->
<div class="detail-left">
<div class="patient-info">
<div class="patient-label">
Nama Pasien
</div>
<div class="patient-name" id="detail_nama"></div>
</div>
<div class="identity-grid">
<div class="identity-item">
<span>NIK</span>
<strong id="detail_nik">-</strong>
</div>
<div class="identity-item">
<span>Jenis Kelamin</span>
<strong id="detail_jk">-</strong>
</div>
<div class="identity-item">
<span>Tanggal Lahir</span>
<strong id="detail_tgl_lahir">-</strong>
</div>
<div class="identity-item">
<span>No. Telepon</span>
<strong id="detail_telp">-</strong>
</div>
<div class="identity-item-full" style="grid-column:1 / -1;">
<span>Alamat</span>
<strong id="detail_alamat">-</strong>
</div>
</div>
</div>
<!-- KOLOM KANAN -->
<div class="detail-right">
<div id="result_card" class="result-card">
<div class="result-title">
Hasil Skrining
</div>
<div class="result-value" id="detail_status_text">
-
</div>
</div>
<div class="kuesioner-list">
<div class="q-item">
<p class="q-text">Batuk ≥ 2 minggu</p>
<p class="q-ans" id="ans_batuk"></p>
</div>
<div class="q-item">
<p class="q-text">Batuk lainnya</p>
<p class="q-ans" id="ans_batuk_lain"></p>
</div>
<div class="q-item">
<p class="q-text">Berat badan turun</p>
<p class="q-ans" id="ans_bb"></p>
</div>
<div class="q-item">
<p class="q-text">Demam hilang timbul</p>
<p class="q-ans" id="ans_demam"></p>
</div>
<div class="q-item">
<p class="q-text">Berkeringat malam</p>
<p class="q-ans" id="ans_keringat"></p>
</div>
<div class="q-item">
<p class="q-text">Pembesaran kelenjar</p>
<p class="q-ans" id="ans_kelenjar"></p>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer modal-footer-detail">
<button
type="button"
class="btn-close-modal"
onclick="closeSkrining()">
Tutup
</button>
</div>
</div>
</div>
<!-- MODAL JADIKAN PASIEN -->
<div class="modal-overlay" id="modalJadikanPasien">
<div class="modal-content">
<div class="modal-header">
<h3>Jadikan Pasien TB</h3>
<button
type="button"
class="btn-icon info"
onclick="closeModalJadikanPasien()"
style="width:30px;height:30px;">
<i data-lucide="x" size="18"></i>
</button>
</div>
<form
id="formJadikanPasien"
method="POST"
enctype="multipart/form-data">
<input type="hidden" id="jp_id_skrining" name="id_skrining">
<input type="hidden" id="jp_id_user" name="id_user">
<!-- DATA MEDIS -->
<div class="form-row">
<div class="form-group">
<label>Diagnosa</label>
<select name="diagnosa" required onchange="validateFormJadikanPasien()">
<option value="TBC Paru">TBC Paru</option>
<option value="TBC Ekstra Paru">TBC Ekstra Paru</option>
</select>
<small class="field-note">
Diagnosa wajib diisi
</small>
</div>
<div class="form-group">
<label>Jam Minum Obat</label>
<input type="time" name="jam_minum" required onchange="validateFormJadikanPasien()">
<small class="field-note">
Jam minum obat wajib diisi
</small>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Tanggal Mulai Pengobatan</label>
<input type="date"
name="tgl_mulai"
id="tgl_mulai"
required
onchange="autoHitung168Hari()">
<small class="field-note">
Tanggal mulai wajib diisi
</small>
</div>
<div class="form-group">
<label>Tanggal Estimasi Selesai</label>
<input type="date" name="tgl_estimasi_selesai" id="tgl_estimasi_selesai" required readonly />
</div>
</div>
<!-- UPLOAD LAB -->
<div class="form-row">
<div class="form-group" style="width:100%;">
<label>Hasil Lab / Rontgen</label>
<input type="file"
name="file_lab[]"
multiple
required
accept=".pdf,.jpg,.jpeg,.png"
onchange="validateFormJadikanPasien()">
<small class="field-note">
File hasil lab wajib diupload
</small>
</div>
</div>
<!-- RIWAYAT PENGOBATAN (tetap kamu boleh pakai kalau masih dipakai) -->
<div id="containerRiwayatPengobatan"></div>
<div class="modal-footer">
<button type="button"
class="btn btn-cancel"
onclick="closeModalJadikanPasien()">
Batal
</button>
<button
type="submit"
class="btn btn-submit"
id="btnSubmitPasien">
Simpan Sebagai Pasien
</button>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
const today = new Date().toISOString().split('T')[0];
const tglMulai = document.getElementById('tgl_mulai');
if (tglMulai) {
tglMulai.setAttribute('max', today);
}
});
function toggleFilter() {
const content = document.getElementById('filterContent');
const icon = document.getElementById('filterIcon');
if (!content || !icon) return;
content.classList.toggle('active');
icon.style.transform =
content.classList.contains('active') ?
'rotate(180deg)' :
'rotate(0deg)';
}
function renderAns(id, val) {
const el = document.getElementById(id);
if (!el) return;
const isYes =
val == 1 ||
val === '1' ||
val === true ||
val === 'Ya';
el.innerText = isYes ? 'Ya' : 'Tidak';
el.className =
isYes ?
'q-ans text-red' :
'q-ans text-green';
}
function openSkrining(data, nama) {
document.getElementById('detail_nama').innerText =
nama || '-';
document.getElementById('detail_nik').innerText =
data.nik || '-';
document.getElementById('detail_jk').innerText =
data.jenis_kelamin || '-';
document.getElementById('detail_tgl_lahir').innerText =
data.tgl_lahir || '-';
document.getElementById('detail_telp').innerText =
data.no_telp || '-';
document.getElementById('detail_alamat').innerText =
data.alamat || '-';
const statusText =
document.getElementById('detail_status_text');
const resultCard =
document.getElementById('result_card');
statusText.innerText =
data.hasil_skrining || '-';
resultCard.classList.remove(
'terduga',
'tidak-terduga'
);
if (data.hasil_skrining === 'Terduga TBC') {
resultCard.classList.add('terduga');
} else {
resultCard.classList.add('tidak-terduga');
}
renderAns('ans_batuk', data.jwb_batuk);
renderAns('ans_batuk_lain', data.jwb_batuk_lain);
renderAns('ans_keringat', data.jwb_keringat_malam);
renderAns('ans_demam', data.jwb_demam);
renderAns('ans_kelenjar', data.jwb_pembesaran_kelenjar);
renderAns('ans_bb', data.jwb_berat_badan);
document.getElementById('modalSkrining').style.display =
'flex';
}
function closeSkrining() {
document.getElementById('modalSkrining').style.display =
'none';
}
function submitExport() {
const query = new URLSearchParams({
hasil_skrining: document.getElementById('filter_hasil').value,
fasyankes: document.getElementById('filter_fasyankes').value,
tgl_mulai: document.getElementById('filter_tgl_mulai').value,
tgl_selesai: document.getElementById('filter_tgl_selesai').value,
search: document.getElementById('searchInput').value
});
window.location.href =
"{{ route('skrining.export') }}?" +
query.toString();
}
const searchInput =
document.getElementById('searchInput');
if (searchInput) {
searchInput.addEventListener('keyup', function() {
const filter =
this.value.toLowerCase();
const rows =
document.querySelectorAll('#skriningTableBody tr');
rows.forEach(row => {
if (row.cells.length < 3) return;
const nama =
row.cells[1].textContent.toLowerCase();
const nik =
row.cells[2].textContent.toLowerCase();
row.style.display =
(nama.includes(filter) || nik.includes(filter)) ?
'' :
'none';
});
});
}
function confirmDeleteSkrining(id, nama) {
Swal.fire({
icon: 'warning',
title: 'Hapus Skrining?',
html: `
<div style="margin-top:5px;">
Data skrining milik <b>${nama}</b>
akan dihapus permanen dari sistem
dan tidak dapat dikembalikan lagi.
</div>
`,
width: '360px',
showCancelButton: true,
confirmButtonText: `
<div style="
display:flex;
align-items:center;
justify-content:center;
gap:6px;
">
<i data-lucide="trash-2"
style="width:16px;height:16px;">
</i>
<span>Hapus</span>
</div>
`,
cancelButtonText: `
<div style="
display:flex;
align-items:center;
justify-content:center;
gap:6px;
">
<i data-lucide="x"
style="width:16px;height:16px;">
</i>
<span>Batal</span>
</div>
`,
reverseButtons: true,
focusCancel: true,
allowOutsideClick: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal',
cancelButton: 'btn-cancel-swal'
},
didOpen: () => {
lucide.createIcons();
}
}).then((result) => {
if (result.isConfirmed) {
fetch(`/skrining/hapus/${id}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
}
})
.then(async response => {
const data = await response.json();
if (!response.ok) {
throw new Error(
data.message || 'Gagal menghapus'
);
}
return data;
})
.then(() => {
Swal.fire({
icon: 'success',
title: 'Berhasil Dihapus',
html: `
<div style="margin-top:5px;">
Data skrining berhasil dihapus dari sistem.
</div>
`,
width: '340px',
timer: 1800,
showConfirmButton: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal'
}
}).then(() => {
location.reload();
});
})
.catch((error) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
html: `
<div style="margin-top:5px;">
${error.message}
</div>
`,
width: '340px',
confirmButtonText: 'Tutup',
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal'
}
});
});
}
});
}
function openSkriningFromButton(button) {
const data = JSON.parse(
button.dataset.item
.replace(/&quot;/g, '"')
);
openSkrining(
data,
data.nama_lengkap
);
}
function openModalJadikanPasien(button) {
let data;
try {
const raw =
button.getAttribute('data-item');
data =
JSON.parse(raw);
} catch (error) {
console.error(error);
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Data skrining gagal dibuka'
});
return;
}
const userId =
data.id_user || data.id;
if (!userId) {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'ID User tidak ditemukan'
});
return;
}
fetch(`/skrining/cek-pasien/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error(
'Gagal menghubungi server'
);
}
return response.json();
})
.then(res => {
if (res.exists) {
Swal.fire({
icon: 'warning',
title: 'Sudah Menjadi Pasien',
html: `
<div>
<b>${data.nama_lengkap}</b>
sudah terdaftar sebagai pasien.
</div>
`,
customClass: {
popup: 'modern-popup',
title: 'title-swal',
confirmButton: 'btn-confirm-swal'
}
});
return;
}
document
.getElementById('formJadikanPasien')
.reset();
document.querySelectorAll(
'#formJadikanPasien .field-note'
).forEach(el => {
el.style.display = 'none';
});
document.querySelectorAll(
'#formJadikanPasien .field-error'
).forEach(el => {
el.classList.remove('field-error');
});
document
.getElementById('jp_id_skrining')
.value =
data.id_skrining || data.id || '';
document
.getElementById('jp_id_user')
.value =
userId;
document.getElementById(
'btnSubmitPasien'
).disabled = false;
document
.getElementById('modalJadikanPasien')
.style.display = 'flex';
})
.catch(error => {
console.error(error);
Swal.fire({
icon: 'error',
title: 'Error',
text: error.message
});
});
}
function closeModalJadikanPasien() {
document.getElementById('modalJadikanPasien').style.display = 'none';
}
function generateRiwayatPengobatanSkrining(index) {
return `
<div class="pengobatan-item"
style="
border:1px solid #eee;
border-radius:16px;
padding:20px;
margin-top:20px;
background:#fafafa;
">
<div style="
display:flex;
justify-content:space-between;
align-items:center;
margin-bottom:15px;
">
<h4 style="color:#111827;">
Pengobatan Ke ${index + 1}
</h4>
<button type="button"
class="btn-hapus-pengobatan"
onclick="hapusRiwayatPengobatanSkrining(this)"
style="
background:#fee2e2;
color:#c40000;
border:none;
padding:8px 12px;
border-radius:8px;
cursor:pointer;
font-weight:600;
">
Hapus
</button>
</div>
<div class="form-row">
<div class="form-group">
<label>Diagnosa</label>
<select
name="riwayat[${index}][diagnosa]"
required>
<option value="TBC Paru">
TBC Paru
</option>
<option value="TBC Ekstra Paru">
TBC Ekstra Paru
</option>
</select>
</div>
<div class="form-group">
<label>Jam Minum Obat</label>
<input
type="time"
name="riwayat[${index}][jam_minum]"
required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Tanggal Mulai Pengobatan</label>
<input
type="date"
name="riwayat[${index}][tgl_mulai]"
required
max="{{ date('Y-m-d') }}"
onchange="hitungTanggalRiwayatPengobatan(this)">
</div>
<div class="form-group">
<label>Tanggal Selesai</label>
<input
type="date"
name="riwayat[${index}][tgl_selesai]"
readonly
required
style="background:#f3f4f6;">
</div>
<div class="form-group">
<label>Status Pengobatan</label>
<select
name="riwayat[${index}][status_kesehatan]"
required>
<option value="Masih Proses" selected>
Masih Proses
</option>
<option value="Sembuh">
Sembuh
</option>
<option value="Gagal">
Gagal
</option>
<option value="Meninggal">
Meninggal
</option>
</select>
</div>
</div>
</div>
`;
}
function tambahRiwayatPengobatanSkrining() {
const container =
document.getElementById('containerRiwayatPengobatan');
const index =
container.querySelectorAll('.pengobatan-item').length;
container.insertAdjacentHTML(
'beforeend',
generateRiwayatPengobatanSkrining(index)
);
updateButtonHapusRiwayatPengobatan();
lucide.createIcons();
}
function hapusRiwayatPengobatanSkrining(btn) {
btn.closest('.pengobatan-item').remove();
resetNomorRiwayatPengobatan();
updateButtonHapusRiwayatPengobatan();
}
function updateButtonHapusRiwayatPengobatan() {
const items =
document.querySelectorAll(
'#containerRiwayatPengobatan .pengobatan-item'
);
items.forEach((item, index) => {
const btn =
item.querySelector('.btn-hapus-pengobatan');
if (!btn) return;
if (items.length > 1 && index === items.length - 1) {
btn.style.display = 'inline-block';
} else {
btn.style.display = 'none';
}
});
}
function resetNomorRiwayatPengobatan() {
const items =
document.querySelectorAll(
'#containerRiwayatPengobatan .pengobatan-item'
);
items.forEach((item, index) => {
item.querySelector('h4').innerText =
'Pengobatan Ke ' + (index + 1);
item.querySelectorAll('input, select')
.forEach(el => {
if (el.name.includes('[diagnosa]')) {
el.name = `riwayat[${index}][diagnosa]`;
}
if (el.name.includes('[jam_minum]')) {
el.name = `riwayat[${index}][jam_minum]`;
}
if (el.name.includes('[tgl_mulai]')) {
el.name = `riwayat[${index}][tgl_mulai]`;
}
if (el.name.includes('[tgl_selesai]')) {
el.name = `riwayat[${index}][tgl_selesai]`;
}
if (el.name.includes('[status_kesehatan]')) {
el.name = `riwayat[${index}][status_kesehatan]`;
}
});
});
}
function autoHitung168Hari() {
const tglMulai = document.getElementById('tgl_mulai').value;
const tglSelesai = document.getElementById('tgl_estimasi_selesai');
if (!tglMulai) {
tglSelesai.value = '';
validateFormJadikanPasien();
return;
}
const date = new Date(tglMulai);
date.setDate(date.getDate() + 168);
const result = date.toISOString().split('T')[0];
tglSelesai.value = result;
validateFormJadikanPasien();
}
function validateFormJadikanPasien() {
let valid = true;
const fields = document.querySelectorAll(
'#formJadikanPasien input[required], #formJadikanPasien select[required]'
);
fields.forEach(field => {
let isEmpty = false;
if (field.type === 'file') {
isEmpty = field.files.length === 0;
} else {
isEmpty = !field.value;
}
const note =
field.parentElement.querySelector('.field-note');
if (isEmpty) {
valid = false;
field.classList.add('field-error');
if (note) {
note.style.display = 'block';
}
} else {
field.classList.remove('field-error');
if (note) {
note.style.display = 'none';
}
}
});
const btn = document.getElementById('btnSubmitPasien');
if (btn) {
btn.disabled = !valid;
}
return valid;
}
document.getElementById('formJadikanPasien').addEventListener('submit', function(e) {
e.preventDefault();
if (!validateFormJadikanPasien()) {
return;
}
const formData = new FormData(this);
formData.append(
'id_user',
document.getElementById('jp_id_user').value
);
formData.append(
'id_skrining',
document.getElementById('jp_id_skrining').value
);
fetch(`/skrining/jadikan-pasien/${document.getElementById('jp_id_skrining').value}`, {
method: 'POST',
body: formData,
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Accept': 'application/json'
}
})
.then(async response => {
const data = await response.json();
if (response.status === 409) {
Swal.fire({
icon: 'warning',
title: 'Sudah Menjadi Pasien',
text: data.message
});
throw new Error('STOP'); // hentikan flow
}
if (!response.ok) {
throw new Error(data.message || 'Gagal');
}
return data;
})
.then(res => {
Swal.fire({
icon: 'success',
title: 'Pasien Berhasil Terdaftar',
html: `
<div style="margin-top:8px; font-size:13px; color:#64748b;">
${res.message || 'Pengguna telah resmi menjadi pasien TB.'}
</div>
`,
width: '340px',
timer: 1800,
showConfirmButton: false,
customClass: {
popup: 'modern-popup',
title: 'title-swal'
},
didOpen: () => {
lucide.createIcons();
}
});
// RESET FORM TOTAL
document.getElementById('formJadikanPasien').reset();
document.getElementById('modalJadikanPasien').style.display = 'none';
// OPTIONAL: refresh table tanpa reload full page
setTimeout(() => {
location.reload();
}, 800);
})
.catch(err => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message
});
});
});
document.querySelectorAll(
'#formJadikanPasien input, #formJadikanPasien select'
).forEach(el => {
el.addEventListener('input', validateFormJadikanPasien);
el.addEventListener('change', validateFormJadikanPasien);
});
</script>
</body>
</html>