"use client"; import React, { useState, useEffect, useRef } from 'react'; import { FaTrash, FaPlus, FaChevronDown } from 'react-icons/fa'; import { getRuangan, getTarifRumahSakit, getICD9, getICD10, getApiBaseUrl, type Ruangan, type TarifData, type ICD9, type ICD10 } from '@/lib/api-helper'; interface EditPasienModalProps { isOpen: boolean; billingId: number; currentData: { nama_pasien: string; usia: number; jenis_kelamin: string; ruangan: string; kelas: string; tindakan_rs?: string[]; icd9?: string[]; icd10?: string[]; }; onClose: () => void; onSuccess: () => void; } const EditPasienModal: React.FC = ({ isOpen, billingId, currentData, onClose, onSuccess, }) => { const [nama, setNama] = useState(''); const [usia, setUsia] = useState(''); const [gender, setGender] = useState(''); const [ruangan, setRuangan] = useState(''); // Store nama_ruangan const [ruanganId, setRuanganId] = useState(''); // Store ID_Ruangan const [ruanganSearch, setRuanganSearch] = useState(''); const [ruanganList, setRuanganList] = useState([]); const [ruanganDropdownOpen, setRuanganDropdownOpen] = useState(false); const ruanganInputRef = useRef(null); const ruanganDropdownRef = useRef(null); const [kelas, setKelas] = useState(''); const [tindakan, setTindakan] = useState([]); const [tindakanInput, setTindakanInput] = useState(''); const [tindakanList, setTindakanList] = useState([]); const [tindakanSearch, setTindakanSearch] = useState(''); const [tindakanDropdownOpen, setTindakanDropdownOpen] = useState(false); const tindakanInputRef = useRef(null); const tindakanDropdownRef = useRef(null); const [icd9, setIcd9] = useState([]); const [icd9Input, setIcd9Input] = useState(''); const [icd9List, setIcd9List] = useState([]); const [icd9Search, setIcd9Search] = useState(''); const [icd9DropdownOpen, setIcd9DropdownOpen] = useState(false); const icd9InputRef = useRef(null); const icd9DropdownRef = useRef(null); const [icd10, setIcd10] = useState([]); const [icd10Input, setIcd10Input] = useState(''); const [icd10List, setIcd10List] = useState([]); const [icd10Search, setIcd10Search] = useState(''); const [icd10DropdownOpen, setIcd10DropdownOpen] = useState(false); const icd10InputRef = useRef(null); const icd10DropdownRef = useRef(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [totalTarifRS, setTotalTarifRS] = useState(0); const [billingSign, setBillingSign] = useState(''); // Hitung total tarif pas tindakan berubah (mirip kayak billing pasien) useEffect(() => { // Hitung tarif dari SEMUA tindakan yang ada di state sekarang console.log('🔄 Calculation useEffect triggered'); console.log(' tindakan length:', tindakan.length); console.log(' tindakan array:', tindakan); console.log(' tindakanList length:', tindakanList.length); const total = tindakan.reduce((sum, deskripsi) => { const tarif = tindakanList.find(t => (t as any).Deskripsi === deskripsi); const harga = (tarif as any)?.Harga || 0; console.log(` Tindakan: ${deskripsi} → Harga: ${harga}`); return sum + harga; }, 0); console.log(`📊 Edit Modal Calculation: Total Tarif RS = ${total}`); setTotalTarifRS(total); // Hitung billing sign const totalKlaimBPJS = (currentData as any).total_klaim_bpjs || 0; const sign = computeBillingSign(total, totalKlaimBPJS); console.log(`🎨 Edit Modal Billing Sign: ${sign} (total: ${total}, klaim: ${totalKlaimBPJS})`); setBillingSign(sign); }, [tindakan, tindakanList, currentData]); // Compute billing sign berdasarkan tarif dan klaim const computeBillingSign = (tarif: number, totalKlaim: number): string => { const totalTarif = tarif || 0; const klaim = totalKlaim || 0; if (!klaim || klaim === 0) return ''; const percentage = (totalTarif / klaim) * 100; if (percentage <= 25) { return "Hijau"; } else if (percentage <= 50) { return "Kuning"; } else { return "Merah"; } }; const getBillingSignColor = (sign: string) => { switch(sign) { case 'Hijau': return { bg: 'bg-green-100', border: 'border-green-300', text: 'text-green-700', dot: 'bg-green-500' }; case 'Kuning': return { bg: 'bg-yellow-100', border: 'border-yellow-300', text: 'text-yellow-700', dot: 'bg-yellow-500' }; case 'Merah': return { bg: 'bg-red-100', border: 'border-red-300', text: 'text-red-700', dot: 'bg-red-500' }; default: return { bg: 'bg-gray-100', border: 'border-gray-300', text: 'text-gray-700', dot: 'bg-gray-400' }; } }; useEffect(() => { if (isOpen && currentData) { console.log('📋 Modal opened with currentData:', currentData); console.log('📋 currentData.tindakan_rs:', currentData.tindakan_rs); console.log('📋 currentData.tindakan_rs type:', typeof currentData.tindakan_rs); console.log('📋 currentData.tindakan_rs is Array?:', Array.isArray(currentData.tindakan_rs)); console.log('📋 currentData.tindakan_rs length:', (currentData.tindakan_rs as any)?.length); console.log('📋 Full currentData object keys:', Object.keys(currentData)); setNama(currentData.nama_pasien); setUsia(currentData.usia.toString()); setGender(currentData.jenis_kelamin); setRuangan(currentData.ruangan); setRuanganId(currentData.ruangan); setRuanganSearch(currentData.ruangan); setKelas(currentData.kelas); const tindakanToSet = Array.isArray(currentData.tindakan_rs) ? currentData.tindakan_rs : []; setTindakan(tindakanToSet); console.log('📋 setTindakan called with:', tindakanToSet); setIcd9(currentData.icd9 || []); setIcd10(currentData.icd10 || []); setError(''); setSuccess(''); // Fetch dropdown data const fetchDropdownData = async () => { try { const [ruanganRes, tarifRes, icd9Res, icd10Res] = await Promise.all([ getRuangan(), getTarifRumahSakit(), getICD9(), getICD10(), ]); if (ruanganRes.data) { setRuanganList(ruanganRes.data); // Find ruangan name by ID or name match const foundRuangan = ruanganRes.data.find(r => (r as any).ID_Ruangan?.toString() === currentData.ruangan || (r as any).Nama_Ruangan === currentData.ruangan ); if (foundRuangan) { setRuangan((foundRuangan as any).Nama_Ruangan); setRuanganId((foundRuangan as any).ID_Ruangan?.toString()); setRuanganSearch((foundRuangan as any).Nama_Ruangan); } } if (tarifRes.data) { console.log('📋 Setting tindakanList with', tarifRes.data.length, 'items'); setTindakanList(tarifRes.data); } if (icd9Res.data) setIcd9List(icd9Res.data); if (icd10Res.data) setIcd10List(icd10Res.data); } catch (err) { console.error('Error fetching dropdown data:', err); } }; fetchDropdownData(); } }, [isOpen, currentData]); // Close dropdowns when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as Node; if (ruanganDropdownOpen) { const isClickInsideInput = ruanganInputRef.current?.contains(target); const isClickInsideDropdown = ruanganDropdownRef.current?.contains(target); if (!isClickInsideInput && !isClickInsideDropdown) { setRuanganDropdownOpen(false); } } if (tindakanDropdownOpen) { const isClickInsideInput = tindakanInputRef.current?.contains(target); const isClickInsideDropdown = tindakanDropdownRef.current?.contains(target); if (!isClickInsideInput && !isClickInsideDropdown) { setTindakanDropdownOpen(false); } } if (icd9DropdownOpen) { const isClickInsideInput = icd9InputRef.current?.contains(target); const isClickInsideDropdown = icd9DropdownRef.current?.contains(target); if (!isClickInsideInput && !isClickInsideDropdown) { setIcd9DropdownOpen(false); } } if (icd10DropdownOpen) { const isClickInsideInput = icd10InputRef.current?.contains(target); const isClickInsideDropdown = icd10DropdownRef.current?.contains(target); if (!isClickInsideInput && !isClickInsideDropdown) { setIcd10DropdownOpen(false); } } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [ruanganDropdownOpen, tindakanDropdownOpen, icd9DropdownOpen, icd10DropdownOpen]); const addTindakan = (kode?: string) => { const tarifData = kode ? tindakanList.find(t => (t as any).KodeRS === kode) : tindakanList.find(t => (t as any).Deskripsi === tindakanInput); if (tarifData) { const deskripsi = (tarifData as any).Deskripsi; if (!tindakan.includes(deskripsi)) { setTindakan([...tindakan, deskripsi]); setTindakanInput(''); setTindakanSearch(''); setTindakanDropdownOpen(false); } } }; const removeTindakan = (index: number) => { setTindakan(tindakan.filter((_, i) => i !== index)); }; const addIcd9 = (kode?: string) => { const icdData = kode ? icd9List.find(i => (i as any).Kode_ICD9 === kode) : icd9List.find(i => (i as any).Prosedur === icd9Input); if (icdData) { const prosedur = (icdData as any).Prosedur; if (!icd9.includes(prosedur)) { setIcd9([...icd9, prosedur]); setIcd9Input(''); setIcd9Search(''); setIcd9DropdownOpen(false); } } }; const removeIcd9 = (index: number) => { setIcd9(icd9.filter((_, i) => i !== index)); }; const addIcd10 = (kode?: string) => { const icdData = kode ? icd10List.find(i => (i as any).Kode_ICD10 === kode) : icd10List.find(i => (i as any).Diagnosa === icd10Input); if (icdData) { const diagnosa = (icdData as any).Diagnosa; if (!icd10.includes(diagnosa)) { setIcd10([...icd10, diagnosa]); setIcd10Input(''); setIcd10Search(''); setIcd10DropdownOpen(false); } } }; const removeIcd10 = (index: number) => { setIcd10(icd10.filter((_, i) => i !== index)); }; const handleSelectRuangan = (ruanganId: string, ruanganName: string) => { setRuangan(ruanganName); // Store nama ruangan setRuanganId(ruanganId); setRuanganSearch(ruanganName); setRuanganDropdownOpen(false); }; // Tampilkan semua ruangan kalo search kosong atau sama pake selected, else filter const filteredRuangan = ruanganSearch === '' || ruanganSearch === ruangan ? ruanganList : ruanganList.filter(r => (r as any).Nama_Ruangan?.toLowerCase().includes(ruanganSearch.toLowerCase()) ); const filteredTindakan = tindakanList.filter(t => (t as any).Deskripsi?.toLowerCase().includes(tindakanSearch.toLowerCase()) || (t as any).KodeRS?.toLowerCase().includes(tindakanSearch.toLowerCase()) ); const filteredIcd9 = icd9List.filter(i => (i as any).Prosedur?.toLowerCase().includes(icd9Search.toLowerCase()) || (i as any).Kode_ICD9?.toLowerCase().includes(icd9Search.toLowerCase()) ); const filteredIcd10 = icd10List.filter(i => (i as any).Diagnosa?.toLowerCase().includes(icd10Search.toLowerCase()) || (i as any).Kode_ICD10?.toLowerCase().includes(icd10Search.toLowerCase()) );; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); console.log('🟢 handleSubmit CALLED - Form submit triggered'); console.log(' nama:', nama); console.log(' usia:', usia); console.log(' gender:', gender); console.log(' ruangan:', ruangan); console.log(' kelas:', kelas); // Validasi required fields if (!nama || !usia || !gender || !ruangan || !kelas) { const errorMsg = 'Nama, Usia, Jenis Kelamin, Ruangan, dan Kelas harus diisi'; console.error('❌ Validation failed:', errorMsg); setError(errorMsg); return; } // Validasi billingId if (!billingId || billingId <= 0) { const errorMsg = 'Billing ID tidak valid. Silakan close modal dan coba lagi.'; console.error('❌ Invalid billingId:', billingId); setError(errorMsg); return; } console.log('🟢 All validations passed, starting submission...'); setLoading(true); setError(''); setSuccess(''); try { // Deteksi apakah ada perubahan pada tindakan, icd9, atau icd10 const tindakanChanged = JSON.stringify(tindakan) !== JSON.stringify(currentData.tindakan_rs || []); const icd9Changed = JSON.stringify(icd9) !== JSON.stringify(currentData.icd9 || []); const icd10Changed = JSON.stringify(icd10) !== JSON.stringify(currentData.icd10 || []); const hasDataChanged = tindakanChanged || icd9Changed || icd10Changed; // Base request body - selalu kirim identitas pasien + billing sign const requestBody: any = { nama_pasien: nama, usia: parseInt(usia), jenis_kelamin: gender, ruangan: ruangan, kelas: kelas, tindakan_rs: tindakan || [], icd9: icd9 || [], icd10: icd10 || [], total_tarif_rs: Number(totalTarifRS) || 0, // ← Ensure it's a number, not string }; // ALWAYS send billing_sign (tidak peduli ada perubahan atau tidak) if (billingSign) { requestBody.billing_sign = billingSign; } // Jika ada perubahan tindakan/icd9/icd10, log details if (hasDataChanged) { console.log('📤 Update dengan perubahan tindakan/ICD:', { tindakanChanged, icd9Changed, icd10Changed, totalTarifRS, totalTarifRS_type: typeof totalTarifRS, totalTarifRS_asNumber: Number(totalTarifRS), billing_sign: billingSign, requestBody, }); } else { console.log('📤 Update hanya identitas pasien (tanpa perubahan tindakan/ICD)', requestBody); } const fullUrl = `${getApiBaseUrl()}/billing/${billingId}`; const requestBodyJson = JSON.stringify(requestBody); console.log('� Request Details:'); console.log(' URL:', fullUrl); console.log(' Method: PUT'); console.log(' billingId:', billingId, 'type:', typeof billingId); console.log(' Request Body:', requestBody); console.log(' JSON String:', requestBodyJson); console.log(' Parsed back:', JSON.parse(requestBodyJson)); const response = await fetch(fullUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: requestBodyJson, }); console.log('✅ Response received. Status:', response.status); console.log('📥 Response:', { status: response.status, statusText: response.statusText, ok: response.ok, headers: { contentType: response.headers.get('content-type'), } }); // DISPATCH EVENT REGARDLESS OF RESPONSE STATUS (sebelum throw error) console.log('🔍 Preparing to dispatch event...'); if (typeof window !== 'undefined') { try { console.log('🔍 Starting event dispatch preparation...'); // Hitung tarif value buat pass ke event - HITUNG DARI SEMUA TINDAKAN YANG ADA const calculatedTotalTarif = tindakan.reduce((sum, deskripsi) => { const tarif = tindakanList.find(t => (t as any).Deskripsi === deskripsi); const harga = (tarif as any)?.Harga || 0; console.log(` Dispatch calc - Tindakan: ${deskripsi} → Harga: ${harga}`); return sum + harga; }, 0); console.log('💰 Final calculated tarif for dispatch:', calculatedTotalTarif); const totalKlaimBPJS = (currentData as any).total_klaim_bpjs || 0; const calculatedBillingSign = computeBillingSign(calculatedTotalTarif, totalKlaimBPJS); console.log('📤 ABOUT TO DISPATCH EVENT with calculated values:', { calculatedTotalTarif, calculatedBillingSign, tindakan, billingId }); // Dispatch custom event dengan data yang sudah di-calculate (jangan perlu fetch ulang) window.dispatchEvent(new CustomEvent('billingDataUpdated', { detail: { billingId, timestamp: new Date().getTime(), totalTarifRS: calculatedTotalTarif, // ← Pass calculated value billingSign: calculatedBillingSign, // ← Pass calculated sign tindakan: tindakan, // ← Pass updated tindakan list icd9: icd9, // ← Pass updated ICD9 icd10: icd10 // ← Pass updated ICD10 } })); console.log('✅ EVENT DISPATCHED SUCCESSFULLY!'); // Clear localStorage cache jika ada sessionStorage.removeItem('billingHistory'); sessionStorage.removeItem('billingAktif'); } catch (eventError) { console.error('❌ Error saat dispatch event:', eventError); } } if (!response.ok) { let errData: any = {}; let errMessage = `HTTP ${response.status}: ${response.statusText}`; let rawText = ''; try { rawText = await response.text(); console.log('📥 Raw response body:', rawText); // Log full response console.log('📥 Raw response body length:', rawText.length); if (rawText && rawText.trim()) { try { errData = JSON.parse(rawText); console.log('📥 Parsed JSON error:', errData); errMessage = errData?.message || errData?.error || `HTTP ${response.status}`; } catch (jsonError) { console.warn('Response is not valid JSON:', jsonError); // Response mungkin HTML error page atau plain text if (rawText.includes(' { onSuccess(); onClose(); }, 1500); } catch (err) { console.error('❌ ERROR in handleSubmit:', err); console.error(' Error type:', err instanceof Error ? 'Error object' : typeof err); if (err instanceof Error) { console.error(' Error message:', err.message); console.error(' Error stack:', err.stack); } setError(err instanceof Error ? err.message : 'Terjadi kesalahan'); } finally { console.log('🟢 handleSubmit finally block - setting loading to false'); setLoading(false); } }; if (!isOpen) return null; return ( <> {/* Blur background - halaman tetap terlihat tapi sedikit blur dan gelap */}
{/* Modal */}
{/* Header dengan gradient */}

Edit Data Pasien

Perbarui informasi identitas dan pemeriksaan pasien

{/* Content */}
{error && (
{error}
)} {success && (
{success}
)} {/* Section 1: Data Dasar */}

👤 Data Dasar Pasien

setNama(e.target.value)} className="w-full border-2 border-blue-200 rounded-lg px-4 py-2.5 text-sm text-[#2591D0] bg-white focus:ring-2 focus:ring-blue-400 focus:border-blue-400 focus:outline-none transition-all" placeholder="Nama lengkap pasien" required />
setUsia(e.target.value)} className="w-full border-2 border-blue-200 rounded-lg px-4 py-2.5 text-sm text-[#2591D0] bg-white focus:ring-2 focus:ring-blue-400 focus:border-blue-400 focus:outline-none transition-all" placeholder="Contoh: 45" required />
{ setRuanganSearch(e.target.value); setRuanganDropdownOpen(true); }} onFocus={() => setRuanganDropdownOpen(true)} className="w-full border-2 border-blue-200 rounded-lg px-4 py-2.5 text-sm text-[#2591D0] bg-white focus:ring-2 focus:ring-blue-400 focus:border-blue-400 focus:outline-none transition-all" placeholder="Cari ruangan..." required /> { setRuanganDropdownOpen(!ruanganDropdownOpen); if (!ruanganDropdownOpen) { setRuanganSearch(''); } }} className="absolute right-3 top-1/2 -translate-y-1/2 text-blue-400 cursor-pointer text-sm hover:text-blue-600" /> {ruanganDropdownOpen && (
e.stopPropagation()} > {ruanganSearch === '' ? ruanganList.map((r) => (
{ handleSelectRuangan((r as any).ID_Ruangan.toString(), (r as any).Nama_Ruangan); setRuanganDropdownOpen(false); }} className="px-4 py-2 hover:bg-blue-50 cursor-pointer text-sm text-[#2591D0] border-b border-blue-100 last:border-b-0" >
{(r as any).Nama_Ruangan}
)) : ruanganList .filter(r => (r as any).Nama_Ruangan?.toLowerCase().includes(ruanganSearch.toLowerCase()) ) .map((r) => (
{ handleSelectRuangan((r as any).ID_Ruangan.toString(), (r as any).Nama_Ruangan); setRuanganDropdownOpen(false); }} className="px-4 py-2 hover:bg-blue-50 cursor-pointer text-sm text-[#2591D0] border-b border-blue-100 last:border-b-0" >
{(r as any).Nama_Ruangan}
)) || (ruanganSearch && (
Tidak ada ruangan ditemukan
))}
)}
{/* Selected Ruangan Display */} {ruangan && (
{ruangan}
)}
{/* Section 1.5: Billing Summary */}
{/* Total Tarif RS */}

Total Tarif RS

Rp {totalTarifRS.toLocaleString('id-ID')}

💰
{/* Billing Sign Warning */} {billingSign && (

Status Billing Sign

{billingSign}

⚠️
)}
{/* Section 2: Tindakan */}

🔬 Tindakan & Pemeriksaan Penunjang

{ setTindakanSearch(e.target.value); setTindakanDropdownOpen(true); }} onFocus={() => setTindakanDropdownOpen(true)} className="w-full border-2 border-emerald-300 rounded-lg px-4 py-2.5 text-sm text-emerald-700 bg-white focus:ring-2 focus:ring-emerald-400 focus:border-emerald-400 focus:outline-none transition-all placeholder-emerald-400" placeholder="Cari tindakan..." /> setTindakanDropdownOpen(!tindakanDropdownOpen)} className="absolute right-3 top-1/2 -translate-y-1/2 text-emerald-400 cursor-pointer text-xs" /> {tindakanDropdownOpen && (
e.stopPropagation()} > {(tindakanSearch ? filteredTindakan : tindakanList).map((t) => (
addTindakan((t as any).KodeRS)} className="px-4 py-2 hover:bg-emerald-50 cursor-pointer text-sm text-emerald-700 border-b border-emerald-100 last:border-b-0" >
{(t as any).Deskripsi}
{(t as any).KodeRS}
))} {tindakanSearch && filteredTindakan.length === 0 && (
Tidak ada tindakan ditemukan
)}
)}
{tindakan.length > 0 && (
{tindakan.map((t, idx) => (
🏥 {t}
))}
)}
{/* Section 3: ICD9 */}

📋 ICD 9 (Prosedur)

{ setIcd9Search(e.target.value); setIcd9DropdownOpen(true); }} onFocus={() => setIcd9DropdownOpen(true)} className="w-full border-2 border-amber-300 rounded-lg px-4 py-2.5 text-sm text-amber-700 bg-white focus:ring-2 focus:ring-amber-400 focus:border-amber-400 focus:outline-none transition-all placeholder-amber-400" placeholder="Cari ICD 9..." /> setIcd9DropdownOpen(!icd9DropdownOpen)} className="absolute right-3 top-1/2 -translate-y-1/2 text-amber-400 cursor-pointer text-xs" /> {icd9DropdownOpen && (
e.stopPropagation()} > {(icd9Search ? filteredIcd9 : icd9List).map((i) => (
addIcd9((i as any).Kode_ICD9)} className="px-4 py-2 hover:bg-amber-50 cursor-pointer text-sm text-amber-700 border-b border-amber-100 last:border-b-0" >
{(i as any).Prosedur}
{(i as any).Kode_ICD9}
))} {icd9Search && filteredIcd9.length === 0 && (
Tidak ada ICD 9 ditemukan
)}
)}
{icd9.length > 0 && (
{icd9.map((i, idx) => (
🔖 {i}
))}
)}
{/* Section 4: ICD10 */}

🏷️ ICD 10 (Diagnosis)

{ setIcd10Search(e.target.value); setIcd10DropdownOpen(true); }} onFocus={() => setIcd10DropdownOpen(true)} className="w-full border-2 border-violet-300 rounded-lg px-4 py-2.5 text-sm text-violet-700 bg-white focus:ring-2 focus:ring-violet-400 focus:border-violet-400 focus:outline-none transition-all placeholder-violet-400" placeholder="Cari ICD 10..." /> setIcd10DropdownOpen(!icd10DropdownOpen)} className="absolute right-3 top-1/2 -translate-y-1/2 text-violet-400 cursor-pointer text-xs" /> {icd10DropdownOpen && (
e.stopPropagation()} > {(icd10Search ? filteredIcd10 : icd10List).map((i) => (
addIcd10((i as any).Kode_ICD10)} className="px-4 py-2 hover:bg-violet-50 cursor-pointer text-sm text-violet-700 border-b border-violet-100 last:border-b-0" >
{(i as any).Diagnosa}
{(i as any).Kode_ICD10}
))} {icd10Search && filteredIcd10.length === 0 && (
Tidak ada ICD 10 ditemukan
)}
)}
{icd10.length > 0 && (
{icd10.map((i, idx) => (
🩺 {i}
))}
)}
{/* Action Buttons */}
); }; export default EditPasienModal;