'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' import { supabase } from '@/lib/supabase' 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 [idBerkas, setIdBerkas] = useState(null) 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 currentIdBerkas = Date.now() setIdBerkas(currentIdBerkas) await supabase.from('cetak_balita').insert({ id_berkas: currentIdBerkas, nama_balita: pengguna.nama_anak, }) // Tunggu render React selesai (untuk menampilkan ID Berkas) await new Promise(r => setTimeout(r, 500)) 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 ?? '-'], ['ID Berkas', idBerkas ? String(idBerkas) : '-'], ['Tanggal Lahir', tanggalLahir], ].map(([label, value], i) => ( label ? (
{label}
{value}
) :
))}
{/* ── Charts ── */}
Grafik Perkembangan Balita (5 Bulan Terakhir)
{/* Panjang */}
📏 Panjang Badan (cm)
{/* Berat */}
⚖️ Berat Badan (kg)
{/* Z-Score PDF Chart */}
📈 Z-Score (SD)
{/* ── Data Pemeriksaan ── */}
Data Pemeriksaan
{['Panjang', '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}
)}
{/* ── 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 ─── */} ) }