'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 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 username?: string | null password?: 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, zscore: match?.z_score ?? 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)
{/* Z-Score PDF Chart */}
📈 Z-Score (SD)
{/* ── Data Pemeriksaan ── */}
Data Pemeriksaan
{['Tinggi', 'Berat', 'Z-Score', 'Status Stunting', 'Posyandu', 'Tgl Pemeriksaan'].map(h => ( ))}
{h}
{row.tinggi_badan ?? '-'} cm {row.berat_badan ?? '-'} kg {row.z_score ?? '-'} SD {isStunting ? '⚠ Stunting' : '✓ Normal'} {row.nama_posyandu ?? '-'} {tanggalUpload}
{/* Pesan AI */} {row.pesan_ai && (
Rekomendasi / Pesan AI
{row.pesan_ai}
)}
{/* ── WhatsApp Info Box ── */}
📱
Layanan Informasi WhatsApp
Untuk orang tua yang tidak memiliki akun WhatsApp, yuk segera buat akun karena kami melayani layanan penyampaian informasi hasil stunting dengan menggunakan WhatsApp agar mendapatkan informasi lebih cepat.
{/* ── Portal Access Info Box ── */}
🌐 Akses Portal Online Orang Tua
* Yuk kunjungi website Bapak/Ibu agar mendapatkan informasi perkembangan buah hati Anda.
Alamat Website (URL)
https://website-cloud-stunting.vercel.app/
Username
{pengguna.username || '-'}
Password
{pengguna.password || '-'}
{/* ── Doc footer ── */}
Dicetak oleh Sistem Informasi Posyandu Dokumen ini sah tanpa tanda tangan basah apabila dicetak dari sistem
{/* ─── Visible Button ─── */} ) }