'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', }) } 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 ) }) 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 ExportPDFButton({ 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 { 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_Perkembangan_${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 Perkembangan Anak
Tanggal Cetak
{tanggalCetak}
Sesi Pemeriksaan
{tanggalUpload}
{/* ── Identitas ── */}
Identitas Balita
{[ ['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 Pertumbuhan (5 Bulan Terakhir)
{/* Tinggi */}
📏 Tinggi Badan (cm)
{/* Berat */}
⚖️ Berat Badan (kg)
{/* Z-Score PDF Chart */}
📈 Z-Score (SD)
{/* ── Data Pemeriksaan ── */}
Hasil Pengukuran Sesi Ini
{['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 || '-'}
Dicetak oleh Dashboard Pengguna StuntiScan Dokumen ini diterbitkan secara otomatis oleh sistem
) }