si-klinik/sk-klinik.my.id/resources/views/livewire/admin/patient-manager.blade.php

385 lines
19 KiB
PHP

<div class="mx-auto">
<div class="flex justify-between items-center mb-8">
<div>
<flux:heading size="xl">Database Pasien</flux:heading>
<flux:subheading>Daftar seluruh pasien yang telah melengkapi profil medis.</flux:subheading>
</div>
<flux:button wire:click="create" variant="primary" icon="plus">Tambah Pasien</flux:button>
</div>
<div class="space-y-4">
{{-- Filter Area --}}
<div class="flex md:flex-row flex-col gap-4 w-full">
<div class="flex flex-1 gap-2">
<div class="flex-1">
<flux:input wire:model.live.debounce.300ms="search" placeholder="Cari Nama, NIK atau No. RM..."
icon="magnifying-glass" class="w-full" />
</div>
<flux:button icon="camera" x-on:click="startScanner()">Scan Kartu</flux:button>
</div>
<div class="w-full md:w-64">
<flux:select wire:model.live="filterInsurance" icon="funnel">
<option value="">Semua Penjamin</option>
<option value="Umum">Umum (Mandiri)</option>
<option value="BPJS">BPJS Kesehatan</option>
<option value="Asuransi Swasta">Asuransi Swasta</option>
</flux:select>
</div>
</div>
{{-- Table --}}
<flux:table>
<flux:table.columns>
<flux:table.column>Pasien</flux:table.column>
<flux:table.column>No. RM</flux:table.column>
<flux:table.column>NIK</flux:table.column>
<flux:table.column>Kontak</flux:table.column>
<flux:table.column>Penjamin</flux:table.column>
<flux:table.column align="end">Aksi</flux:table.column>
</flux:table.columns>
<flux:table.rows>
@forelse ($patients as $patient)
<flux:table.row :key="$patient->id">
<flux:table.cell>
<div class="flex items-center gap-3">
<flux:avatar initials="{{ $patient->user->initials() }}" size="sm" />
<div>
<div class="font-medium text-zinc-800 dark:text-white">{{ $patient->user->name }}
</div>
<div class="text-zinc-500 text-xs">
{{ $patient->gender == 'L' ? 'Laki-laki' : 'Perempuan' }}</div>
</div>
</div>
</flux:table.cell>
<flux:table.cell class="font-mono text-xs">{{ $patient->medical_record_number }}</flux:table.cell>
<flux:table.cell class="font-mono text-xs">{{ $patient->nik }}</flux:table.cell>
<flux:table.cell>
<div class="text-sm">{{ $patient->phone }}</div>
<div class="max-w-[150px] text-zinc-500 text-xs truncate">{{ $patient->address }}</div>
</flux:table.cell>
<flux:table.cell>
<flux:badge size="sm"
:color="match($patient->insurance_type) { 'BPJS' => 'green', 'Asuransi Swasta' => 'indigo', default => 'zinc' }">
{{ $patient->insurance_type }}
</flux:badge>
</flux:table.cell>
<flux:table.cell align="end">
<div class="flex justify-end gap-2">
<flux:button href="{{ route('admin.patients.history', $patient->id) }}" icon:trailing="arrow-up-right">
Lihat Riwayat
</flux:button>
<flux:button wire:click="showDetail({{ $patient->id }})" variant="ghost" icon="eye"
size="sm" />
<flux:button wire:click="edit({{ $patient->id }})" variant="ghost" icon="pencil-square"
size="sm" />
<flux:button
x-on:click="confirmDelete({{ $patient->id }}, '{{ $patient->user->name }}')"
variant="ghost" icon="trash" size="sm" class="text-red-500" />
</div>
</flux:table.cell>
</flux:table.row>
@empty
<flux:table.row>
<flux:table.cell colspan="6" align="center">
<div class="py-6 text-zinc-500">Tidak ada data pasien.</div>
</flux:table.cell>
</flux:table.row>
@endforelse
</flux:table.rows>
</flux:table>
<div class="mt-4">{{ $patients->links() }}</div>
</div>
{{-- MODAL DETAIL --}}
<flux:modal name="patient-detail" class="p-0 md:w-[650px] overflow-hidden">
@if ($selectedPatient)
<div class="bg-zinc-50 dark:bg-zinc-900 px-6 py-8 border-zinc-200 dark:border-zinc-800 border-b">
<div class="flex items-center gap-5">
<flux:avatar initials="{{ $selectedPatient->user->initials() }}" class="size-20" />
<div>
<flux:heading size="xl">{{ $selectedPatient->user->name }}</flux:heading>
<div class="flex gap-2 mt-2">
<flux:badge size="sm" color="zinc">{{ $selectedPatient->nik }}</flux:badge>
<flux:badge size="sm"
:color="$selectedPatient->gender === 'L' ? 'blue' : 'secondary'">
{{ $selectedPatient->gender === 'L' ? 'Laki-laki' : 'Perempuan' }}
</flux:badge>
</div>
</div>
</div>
</div>
<div class="space-y-6 p-6">
<div class="gap-4 grid grid-cols-3">
<div class="space-y-1">
<p class="font-semibold text-zinc-500 text-xs uppercase">Gol. Darah</p>
<flux:badge size="sm" color="zinc">{{ $selectedPatient->blood_type ?? '-' }}
</flux:badge>
</div>
<div class="space-y-1">
<p class="font-semibold text-zinc-500 text-xs uppercase">Tgl Lahir</p>
<p class="font-medium text-sm">
{{ \Carbon\Carbon::parse($selectedPatient->date_of_birth)->format('d/m/Y') }}</p>
</div>
<div class="space-y-1">
<p class="font-semibold text-zinc-500 text-xs uppercase">Penjamin</p>
<flux:badge size="sm" color="indigo">{{ $selectedPatient->insurance_type }}</flux:badge>
</div>
</div>
<div class="space-y-1">
<p class="font-semibold text-zinc-500 text-xs uppercase">Alamat</p>
<p class="bg-zinc-50 dark:bg-zinc-800 p-3 border rounded-lg text-sm leading-relaxed">
{{ $selectedPatient->address }}
</p>
</div>
</div>
<div class="flex justify-end gap-2 bg-zinc-50/50 p-6 border-t">
<flux:modal.close>
<flux:button variant="ghost">Tutup</flux:button>
</flux:modal.close>
{{-- Tombol Cetak diarahkan ke route cetak yang kamu miliki --}}
<flux:button variant="primary" icon="printer" tag="a" target="_blank"
href="{{ route('admin.patients.print', $selectedPatient->id) }}">
Cetak Kartu
</flux:button>
</div>
@endif
</flux:modal>
{{-- MODAL EDIT --}}
<flux:modal name="edit-patient" class="space-y-6 md:w-[700px]">
<div>
<flux:heading size="lg">Edit Profil Pasien Lengkap</flux:heading>
<flux:subheading>Pastikan informasi medis dan kontak darurat sudah sesuai.</flux:subheading>
</div>
<form wire:submit="update" class="space-y-6">
{{-- Section 1: Identitas Dasar --}}
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:input label="Nama Lengkap" wire:model="editForm.name" class="md:col-span-2" />
<flux:input label="NIK" wire:model="editForm.nik" />
<flux:select label="Jenis Kelamin" wire:model="editForm.gender">
<option value="L">Laki-laki</option>
<option value="P">Perempuan</option>
</flux:select>
<flux:input label="Tempat Lahir" wire:model="editForm.place_of_birth" />
<flux:input type="date" label="Tanggal Lahir" wire:model="editForm.date_of_birth" />
</div>
<hr class="border-zinc-200 dark:border-zinc-800" />
{{-- Section 2: Kontak & Alamat --}}
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:input label="Nomor Telepon" wire:model="editForm.phone" />
<flux:select label="Golongan Darah" wire:model="editForm.blood_type">
<option value="-">-</option>
<option value="A">A</option>
<option value="B">B</option>
<option value="AB">AB</option>
<option value="O">O</option>
</flux:select>
<flux:textarea label="Alamat Domisili" wire:model="editForm.address" class="md:col-span-2"
rows="2" />
</div>
<div class="space-y-4 bg-orange-50 dark:bg-orange-900/20 p-4 rounded-xl">
<p class="font-bold text-orange-700 dark:text-orange-400 text-sm">Kontak Darurat</p>
<div class="gap-4 grid grid-cols-1 md:grid-cols-3">
<flux:input label="Nama" wire:model="editForm.emergency_contact_name" />
<flux:input label="Hubungan" wire:model="editForm.emergency_contact_relation" />
<flux:input label="Nomor HP" wire:model="editForm.emergency_contact_phone" />
</div>
</div>
{{-- Section 3: Asuransi --}}
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:select label="Metode Penjamin" wire:model="editForm.insurance_type">
<option value="Umum">Umum</option>
<option value="BPJS">BPJS</option>
<option value="Asuransi Swasta">Asuransi Swasta</option>
</flux:select>
<flux:input label="Nomor Kartu Asuransi" wire:model="editForm.insurance_number"
:disabled="$editForm['insurance_type'] === 'Umum'" />
</div>
<div class="flex justify-end gap-2 pt-4">
<flux:modal.close>
<flux:button variant="ghost">Batal</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="primary">Simpan Perubahan Data</flux:button>
</div>
</form>
</flux:modal>
{{-- MODAL TAMBAH PASIEN --}}
<flux:modal name="add-patient" class="space-y-8 p-6 md:w-[800px] max-h-[90vh] overflow-y-auto">
<div>
<flux:heading size="xl">Registrasi Pasien Baru</flux:heading>
<flux:subheading>Input data medis lengkap untuk pendaftaran pasien baru.</flux:subheading>
</div>
<form wire:submit="store" class="space-y-8">
{{-- Seksi 1: Akun & Identitas Dasar --}}
<section class="space-y-4">
<flux:heading variant="subtle" class="font-bold text-xs uppercase tracking-wider">Akun & Identitas
Dasar</flux:heading>
<flux:separator variant="subtle" />
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:input wire:model="createForm.name" label="Nama Lengkap (Sesuai KTP)"
placeholder="Nama lengkap..." />
<flux:input wire:model="createForm.email" label="Alamat Email" type="email"
placeholder="email@pasien.com" />
<flux:input wire:model="createForm.nik" label="NIK" mask="9999999999999999"
placeholder="16 digit angka" />
<div class="gap-2 grid grid-cols-2">
<flux:select wire:model="createForm.gender" label="Jenis Kelamin">
<option value="">Pilih...</option>
<option value="L">Laki-laki</option>
<option value="P">Perempuan</option>
</flux:select>
<flux:select wire:model="createForm.blood_type" label="Gol. Darah">
<option value="">Pilih...</option>
<option value="A">A</option>
<option value="B">B</option>
<option value="AB">AB</option>
<option value="O">O</option>
<option value="-">-</option>
</flux:select>
</div>
</div>
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:input wire:model="createForm.place_of_birth" label="Tempat Lahir" />
<flux:input wire:model="createForm.date_of_birth" type="date" label="Tanggal Lahir" />
</div>
</section>
{{-- Seksi 2: Kontak --}}
<section class="space-y-4">
<flux:heading variant="subtle" class="font-bold text-xs uppercase tracking-wider">Informasi Kontak
</flux:heading>
<flux:separator variant="subtle" />
<flux:input wire:model="createForm.phone" label="Nomor Telepon/WhatsApp" icon="phone" />
<flux:textarea wire:model="createForm.address" label="Alamat Domisili" rows="2" />
</section>
{{-- Seksi 3: Kontak Darurat --}}
<section
class="space-y-4 bg-zinc-50 dark:bg-white/5 p-4 border border-zinc-200 dark:border-white/10 rounded-xl">
<flux:heading variant="subtle"
class="font-bold text-orange-600 dark:text-orange-400 text-xs uppercase tracking-wider">Kontak
Darurat</flux:heading>
<div class="gap-4 grid grid-cols-1 md:grid-cols-3">
<flux:input wire:model="createForm.emergency_contact_name" label="Nama Kontak" />
<flux:input wire:model="createForm.emergency_contact_relation" label="Hubungan"
placeholder="Misal: Istri" />
<flux:input wire:model="createForm.emergency_contact_phone" label="No. Telepon" />
</div>
</section>
{{-- Seksi 4: Penjamin --}}
<section class="space-y-4">
<flux:heading variant="subtle" class="font-bold text-xs uppercase tracking-wider">Metode Pembayaran /
Penjamin</flux:heading>
<flux:separator variant="subtle" />
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<flux:select wire:model.live="createForm.insurance_type" label="Jenis Penjaminan">
<option value="Umum">Umum (Mandiri)</option>
<option value="BPJS">BPJS Kesehatan</option>
<option value="Asuransi Swasta">Asuransi Swasta</option>
</flux:select>
<div x-show="$wire.createForm.insurance_type !== 'Umum'">
<flux:input wire:model="createForm.insurance_number" label="Nomor Kartu Asuransi" />
</div>
</div>
</section>
<div class="flex justify-end gap-3 pt-6 border-t">
<flux:modal.close>
<flux:button variant="ghost">Batal</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="primary" icon="check">
Daftarkan Pasien
</flux:button>
</div>
</form>
</flux:modal>
{{-- MODAL SCANNER --}}
<flux:modal name="scanner-modal" class="space-y-4 md:w-[500px]">
<flux:heading size="lg">Scan Kartu</flux:heading>
<div id="reader" class="bg-black border rounded-xl aspect-square overflow-hidden"></div>
<flux:modal.close>
<flux:button variant="ghost" class="w-full" x-on:click="stopScanner()">Batal</flux:button>
</flux:modal.close>
</flux:modal>
</div>
@script
<script>
// SweetAlert Hapus
window.confirmDelete = (id, name) => {
Swal.fire({
title: 'Hapus Pasien?',
text: `Data ${name} akan dihapus permanen!`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ef4444',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((res) => {
if (res.isConfirmed) $wire.delete(id)
});
}
// Scanner Logic
let html5QrCode;
window.startScanner = () => {
Flux.modal('scanner-modal').show();
html5QrCode = new window.Html5Qrcode("reader");
html5QrCode.start({
facingMode: "environment"
}, {
fps: 10,
qrbox: {
width: 250,
height: 250
}
},
(text) => {
// Logika pembersihan string jika QR berisi URL (contoh: https://rs.com/patient/123456)
const nik = text.includes('/') ? text.split('/').pop() : text;
$wire.set('search', nik);
stopScanner();
Flux.modal('scanner-modal').close();
}
).catch(err => {
console.error("Gagal menjalankan kamera", err);
alert("Kamera tidak dapat diakses.");
});
}
window.stopScanner = () => {
if (html5QrCode && html5QrCode.isScanning) {
html5QrCode.stop().then(() => html5QrCode.clear());
}
}
</script>
@endscript