'use client' import { useRef, useMemo, useState } from 'react' import { Printer } from 'lucide-react' import { AreaChart, Area, XAxis, YAxis, CartesianGrid, ResponsiveContainer, } from 'recharts' import { showSwal } from '@/lib/swal' interface HasilItem { id: number tinggi_badan: number | null berat_badan: 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 { row: HasilItem allData: HasilItem[] pengguna: Pengguna } function formatTgl(d: string | null, style: 'long' | 'short' = 'long') { if (!d) return '-' return new Date(d).toLocaleDateString('id-ID', { day: 'numeric', month: style === 'long' ? 'long' : 'short', year: 'numeric', }) } /** Build 5-month window ending at rowDate (inclusive), no future data */ function build5MonthData(allData: HasilItem[], rowDate: Date) { const slots = [] for (let i = 4; i >= 0; i--) { const d = new Date(rowDate.getFullYear(), rowDate.getMonth() - i, 1) slots.push({ year: d.getFullYear(), month: d.getMonth() + 1 }) } return slots.map(slot => { const match = allData.find(item => { if (!item.tanggal_upload) return false const id = new Date(item.tanggal_upload) return ( id.getFullYear() === slot.year && id.getMonth() + 1 === slot.month && id <= rowDate // no future data ) }) const label = new Date(slot.year, slot.month - 1, 1) .toLocaleDateString('id-ID', { month: 'short', year: '2-digit' }) return { label, tinggi: match?.tinggi_badan ?? null, berat: match?.berat_badan ?? null, } }) } export function CetakPDFButton({ row, allData, pengguna }: Props) { const templateRef = useRef(null) const [loading, setLoading] = useState(false) const rowDate = row.tanggal_upload ? new Date(row.tanggal_upload) : new Date() const chartData = useMemo(() => build5MonthData(allData, rowDate), [allData, row.tanggal_upload]) const isStunting = row.status_stunting === true const tanggalCetak = new Date().toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' }) const tanggalUpload = formatTgl(row.tanggal_upload, 'long') const tanggalLahir = formatTgl(pengguna.tanggal_lahir, 'long') const handlePrint = async () => { if (!templateRef.current || loading) return setLoading(true) try { const { default: html2canvas } = await import('html2canvas') const { default: jsPDF } = await import('jspdf') const canvas = await html2canvas(templateRef.current, { scale: 2, useCORS: true, backgroundColor: '#ffffff', logging: false, }) const imgData = canvas.toDataURL('image/png') const pdf = new jsPDF('p', 'mm', 'a4') const pageW = pdf.internal.pageSize.getWidth() const pageH = pdf.internal.pageSize.getHeight() const imgH = (canvas.height * pageW) / canvas.width if (imgH <= pageH) { pdf.addImage(imgData, 'PNG', 0, 0, pageW, imgH) } else { // Multi-page let yPos = 0 const sliceH = canvas.width * (pageH / pageW) while (yPos < canvas.height) { const sliceCanvas = document.createElement('canvas') sliceCanvas.width = canvas.width sliceCanvas.height = Math.min(sliceH, canvas.height - yPos) const ctx = sliceCanvas.getContext('2d')! ctx.drawImage(canvas, 0, -yPos) if (yPos > 0) pdf.addPage() pdf.addImage(sliceCanvas.toDataURL('image/png'), 'PNG', 0, 0, pageW, pageH) yPos += sliceH } } pdf.save(`Laporan_${pengguna.nama_anak}_${tanggalUpload.replace(/ /g, '_')}.pdf`) } catch (err: any) { showSwal.error('Gagal!', `Gagal mencetak PDF: ${err.message}`) } finally { setLoading(false) } } return ( <> {/* ─── Hidden PDF Template ─── */}
{/* ── Header ── */}
Sistem Informasi Posyandu
Laporan Pemeriksaan Balita
Tanggal Cetak
{tanggalCetak}
Tgl Pemeriksaan
{tanggalUpload}
{/* ── Identitas ── */}
Identitas
{[ ['Nama Ibu / Orang Tua', pengguna.nama_orang_tua], ['Nama Anak', pengguna.nama_anak], ['Alamat', pengguna.alamat ?? '-'], ['Jenis Kelamin', pengguna.jenis_kelamin ?? '-'], ['', ''], ['Tanggal Lahir', tanggalLahir], ].map(([label, value], i) => ( label ? (
{label}
{value}
) :
))}
{/* ── Charts ── */}
Grafik Perkembangan Balita (5 Bulan Terakhir)
{/* Tinggi */}
📏 Tinggi Badan (cm)
{/* Berat */}
⚖️ Berat Badan (kg)
{/* ── Data Pemeriksaan ── */}
Data Pemeriksaan
{['Tinggi Badan', 'Berat Badan', 'Status Stunting', 'Posyandu', 'Tgl Pemeriksaan'].map(h => ( ))}
{h}
{row.tinggi_badan ?? '-'} {row.tinggi_badan ? 'cm' : ''} {row.berat_badan ?? '-'} {row.berat_badan ? 'kg' : ''} {isStunting ? '⚠ Stunting' : '✓ Normal'} {row.nama_posyandu ?? '-'} {tanggalUpload}
{/* Pesan AI */} {row.pesan_ai && (
Pesan Kecerdasan Buatan (AI)
{row.pesan_ai}
)}
{/* ── Footer / Tanda Tangan ── */}
Mengetahui,
Kepala Puskesmas / Supervisor
Petugas Posyandu,
Nama & Tanda Tangan
{/* ── Doc footer ── */}
Dicetak oleh Sistem Informasi Posyandu Dokumen ini sah tanpa tanda tangan basah apabila dicetak dari sistem
{/* ─── Visible Button ─── */} ) }