'use client' import { useState, useMemo } from 'react' import { Activity, ChevronDown, Edit, Trash2, X, Loader2 } from 'lucide-react' import { CetakPDFButton } from './CetakPDFButton' import { deleteHasilStunting, updateHasilStunting } from '@/app/actions' import { showSwal } from '@/lib/swal' interface HasilStunting { id: number tinggi_badan: number | null berat_badan: number | null z_score: number | null status_stunting: boolean | null pesan_ai: string | null tanggal_upload: string | null nama_posyandu: string | null } interface Pengguna { nama_orang_tua: string alamat: string | null nama_anak: string jenis_kelamin: string | null tanggal_lahir: string | null } interface Props { data: HasilStunting[] pengguna: Pengguna posyanduList: { nama_posyandu: string }[] } const START_YEAR = 2026 export function HasilStuntingTable({ data, pengguna, posyanduList }: Props) { const [editRecord, setEditRecord] = useState(null) const handleDelete = async (id: number) => { const confirmed = await showSwal.confirm( 'Hapus Data Pengukuran?', 'Data pengukuran ini akan dihapus permanen dari riwayat balita!' ) if (confirmed.isConfirmed) { try { const res = await deleteHasilStunting(id) if (res.success) { await showSwal.success('Berhasil!', res.message) } else { showSwal.error('Gagal!', res.message) } } catch (err: any) { showSwal.error('Gagal!', err.message || 'Terjadi kesalahan.') } } } const availableYears = useMemo(() => { const currentYear = new Date().getFullYear() const dataYears = Array.from( new Set(data.map(d => d.tanggal_upload ? new Date(d.tanggal_upload).getFullYear() : null).filter(Boolean)) ) as number[] const maxYear = Math.max(currentYear, ...dataYears, START_YEAR) return Array.from( new Set([ ...Array.from({ length: maxYear - START_YEAR + 1 }, (_, i) => START_YEAR + i), ...dataYears, ]) ).filter(y => y >= START_YEAR).sort((a, b) => a - b) }, [data]) const [selectedYear, setSelectedYear] = useState(availableYears[0] ?? START_YEAR) const filtered = useMemo(() => { return data.filter(d => { if (!d.tanggal_upload) return false return new Date(d.tanggal_upload).getFullYear() === selectedYear }) }, [data, selectedYear]) const formatDate = (d: string | null) => { if (!d) return '-' return new Date(d).toLocaleDateString('id-ID', { day: 'numeric', month: 'short', year: 'numeric' }) } return (
{/* Section Header + Filter */}

Riwayat Hasil Pengukuran

Periode:
{filtered.length} data
{/* Table */}
{/* Header */}
# Panjang Berat Z-Score Status Pesan AI Posyandu Tgl Upload Aksi
{filtered.length === 0 ? (

Tidak ada data untuk tahun {selectedYear}

) : ( filtered.map((row, idx) => { const isStunting = row.status_stunting === true return (
{/* Index */}
{idx + 1}
{/* Panjang Badan */}
{row.tinggi_badan ?? '-'} {row.tinggi_badan && cm}
{/* Berat Badan */}
{row.berat_badan ?? '-'} {row.berat_badan && kg}
{/* Z-Score */}
{row.z_score ?? '-'} {row.z_score !== null && SD}
{/* Status Stunting */}
{row.status_stunting === null ? ( ) : ( {isStunting ? '⚠ Stunting' : '✓ Normal'} )}
{/* Pesan AI — truncated */}
{row.pesan_ai ? (

{row.pesan_ai.length > 80 ? row.pesan_ai.slice(0, 80) + '..........' : row.pesan_ai}

) : ( Tidak ada pesan )}
{/* Nama Posyandu */}
{row.nama_posyandu ?? '-'}
{/* Tanggal Upload */}
{formatDate(row.tanggal_upload)}
{/* Aksi: Cetak, Edit, Hapus */}
) }) )}
{/* Edit Modal */} {editRecord && ( setEditRecord(null)} record={editRecord} posyanduList={posyanduList} /> )}
) } interface EditModalProps { isOpen: boolean onClose: () => void record: HasilStunting posyanduList: { nama_posyandu: string }[] } function EditHasilStuntingModal({ isOpen, onClose, record, posyanduList }: EditModalProps) { const [tinggi, setTinggi] = useState(record.tinggi_badan?.toString() || '') const [berat, setBerat] = useState(record.berat_badan?.toString() || '') const [zScore, setZScore] = useState(record.z_score?.toString() || '') const [statusStunting, setStatusStunting] = useState(record.status_stunting === true ? 'true' : 'false') const [pesanAi, setPesanAi] = useState(record.pesan_ai || '') const [tanggalUpload, setTanggalUpload] = useState(record.tanggal_upload ? record.tanggal_upload.split('T')[0] : '') const [namaPosyandu, setNamaPosyandu] = useState(record.nama_posyandu || '') const [loading, setLoading] = useState(false) if (!isOpen) return null const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setLoading(true) const formData = new FormData() formData.append('id', record.id.toString()) formData.append('tinggi_badan', tinggi) formData.append('berat_badan', berat) formData.append('z_score', zScore) formData.append('status_stunting', statusStunting) formData.append('pesan_ai', pesanAi) formData.append('tanggal_upload', tanggalUpload) formData.append('nama_posyandu', namaPosyandu) try { const res = await updateHasilStunting(null, formData) if (res.success) { await showSwal.success('Berhasil!', res.message) onClose() } else { showSwal.error('Gagal!', res.message) } } catch (err: any) { showSwal.error('Gagal!', err.message || 'Terjadi kesalahan.') } finally { setLoading(false) } } return (
{/* Header */}

Edit Data Pengukuran

{/* Form */}
setTinggi(e.target.value)} className="w-full border-2 border-gray-200 focus:border-black rounded-lg p-2.5 text-sm font-semibold focus:outline-none transition-colors" required />
setBerat(e.target.value)} className="w-full border-2 border-gray-200 focus:border-black rounded-lg p-2.5 text-sm font-semibold focus:outline-none transition-colors" required />
setZScore(e.target.value)} className="w-full border-2 border-gray-200 focus:border-black rounded-lg p-2.5 text-sm font-semibold focus:outline-none transition-colors" required />
setTanggalUpload(e.target.value)} className="w-full border-2 border-gray-200 focus:border-black rounded-lg p-2.5 text-sm font-semibold focus:outline-none transition-colors" required />