add everything that i like to ad

This commit is contained in:
oxel 2026-04-24 21:48:54 +07:00
parent 8fc5257458
commit d74be80085
1 changed files with 117 additions and 59 deletions

View File

@ -30,6 +30,8 @@ interface PenggunaData {
nama_anak: string
jenis_kelamin: string | null
tanggal_lahir: string | null
username?: string
password?: string
}
const MONTHS = [
@ -203,7 +205,7 @@ export function CetakInstanModal() {
logging: false,
})
const imgData = canvas.toDataURL('image/jpeg', 0.95)
const imgData = canvas.toDataURL('image/png')
// Safety check: ensure imgData is a valid Data URI
if (!imgData || !imgData.startsWith('data:image/')) {
@ -217,7 +219,7 @@ export function CetakInstanModal() {
const imgH = (canvas.height * pageW) / canvas.width
if (imgH <= pageH) {
pdf.addImage(imgData, 'JPEG', 0, 0, pageW, imgH)
pdf.addImage(imgData, 'PNG', 0, 0, pageW, imgH)
} else {
let yPos = 0
const sliceH = canvas.width * (pageH / pageW)
@ -228,7 +230,7 @@ export function CetakInstanModal() {
const ctx = sliceCanvas.getContext('2d')!
ctx.drawImage(canvas, 0, -yPos)
if (yPos > 0) pdf.addPage()
pdf.addImage(sliceCanvas.toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, pageW, pageH)
pdf.addImage(sliceCanvas.toDataURL('image/png'), 'PNG', 0, 0, pageW, pageH)
yPos += sliceH
}
}
@ -281,6 +283,8 @@ export function CetakInstanModal() {
const isStunting = activePrintData?.row.status_stunting === true
const tanggalCetak = new Date().toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' })
const tanggalUpload = activePrintData ? formatTgl(activePrintData.row.tanggal_upload) : '-'
const tanggalLahir = activePrintData ? formatTgl(activePrintData.pengguna.tanggal_lahir) : '-'
return (
<>
@ -431,128 +435,182 @@ export function CetakInstanModal() {
backgroundColor: '#ffffff',
fontFamily: 'Arial, Helvetica, sans-serif',
color: '#111111',
padding: '48px 56px',
padding: '32px 48px',
boxSizing: 'border-box',
}}
>
{/* Header */}
<div style={{ borderBottom: '3px solid #111', paddingBottom: 20, marginBottom: 24, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div style={{ borderBottom: '3px solid #111', paddingBottom: 16, marginBottom: 20, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 3, textTransform: 'uppercase', color: '#555', marginBottom: 6 }}>Sistem Informasi Posyandu</div>
<div style={{ fontSize: 22, fontWeight: 900, letterSpacing: -0.5 }}>Laporan Pemeriksaan Balita</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{ fontSize: 10, color: '#888' }}>Tanggal Cetak</div>
<div style={{ fontSize: 13, fontWeight: 700 }}>{tanggalCetak}</div>
<div style={{ fontSize: 10, color: '#888', marginBottom: 3 }}>Tanggal Cetak</div>
<div style={{ fontSize: 12, fontWeight: 700 }}>{tanggalCetak}</div>
<div style={{ marginTop: 6, fontSize: 10, color: '#888' }}>Tgl Pemeriksaan</div>
<div style={{ fontSize: 13, fontWeight: 700 }}>{formatTgl(activePrintData.row.tanggal_upload)}</div>
<div style={{ fontSize: 12, fontWeight: 700 }}>{tanggalUpload}</div>
</div>
</div>
{/* Identitas */}
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', color: '#888', marginBottom: 10 }}>Identitas</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px 32px' }}>
<div style={{ marginBottom: 20 }}>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 2, textTransform: 'uppercase', color: '#888', marginBottom: 6 }}>Identitas</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px 32px' }}>
{[
['Nama Ibu / Orang Tua', activePrintData.pengguna.nama_orang_tua],
['Nama Anak', activePrintData.pengguna.nama_anak],
['Alamat', activePrintData.pengguna.alamat ?? '-'],
['Jenis Kelamin', activePrintData.pengguna.jenis_kelamin ?? '-'],
['Tanggal Lahir', formatTgl(activePrintData.pengguna.tanggal_lahir)],
['', ''],
['Tanggal Lahir', tanggalLahir],
].map(([label, value], i) => (
<div key={i} style={{ borderBottom: '1px solid #eee', paddingBottom: 8 }}>
<div style={{ fontSize: 9, color: '#888', fontWeight: 700, textTransform: 'uppercase' }}>{label}</div>
<div style={{ fontSize: 13, fontWeight: 600 }}>{value}</div>
</div>
label ? (
<div key={i} style={{ borderBottom: '1px solid #eee', paddingBottom: 6 }}>
<div style={{ fontSize: 8, color: '#888', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 1, marginBottom: 2 }}>{label}</div>
<div style={{ fontSize: 12, fontWeight: 600 }}>{value}</div>
</div>
) : <div key={i} />
))}
</div>
</div>
{/* Charts */}
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', color: '#888', marginBottom: 10 }}>Grafik Perkembangan (5 Bulan)</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
<div style={{ border: '1.5px solid #dbeafe', borderRadius: 12, padding: '14px 14px 4px', background: '#eff6ff' }}>
<div style={{ fontSize: 11, fontWeight: 700, color: '#1d4ed8', marginBottom: 8 }}>📏 Tinggi Badan (cm)</div>
<ResponsiveContainer width="100%" height={140}>
<div style={{ marginBottom: 20 }}>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 2, textTransform: 'uppercase', color: '#888', marginBottom: 6 }}>Grafik Perkembangan Balita (5 Bulan Terakhir)</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
{/* Tinggi */}
<div style={{ border: '1.5px solid #dbeafe', borderRadius: 10, padding: '12px 12px 2px', background: '#eff6ff' }}>
<div style={{ fontSize: 10, fontWeight: 700, color: '#1d4ed8', marginBottom: 6 }}>📏 Tinggi Badan (cm)</div>
<ResponsiveContainer width="100%" height={120}>
<AreaChart data={chartData} margin={{ top: 4, right: 8, left: -20, bottom: 0 }}>
<defs>
<linearGradient id="pdfTinggiGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#bfdbfe" />
<XAxis dataKey="label" fontSize={9} axisLine={false} tickLine={false} />
<YAxis fontSize={9} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="tinggi" stroke="#3b82f6" strokeWidth={2} fill="none" dot={{ r: 4, fill: '#3b82f6', stroke: 'white' }} isAnimationActive={false} />
<XAxis dataKey="label" tick={{ fontSize: 8, fontWeight: 600 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fontSize: 8 }} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="tinggi" stroke="#3b82f6" strokeWidth={2} fill="url(#pdfTinggiGrad)" dot={{ r: 3, fill: '#3b82f6', stroke: 'white' }} connectNulls={false} isAnimationActive={false} />
</AreaChart>
</ResponsiveContainer>
</div>
<div style={{ border: '1.5px solid #d1fae5', borderRadius: 12, padding: '14px 14px 4px', background: '#f0fdf4' }}>
<div style={{ fontSize: 11, fontWeight: 700, color: '#059669', marginBottom: 8 }}> Berat Badan (kg)</div>
<ResponsiveContainer width="100%" height={140}>
{/* Berat */}
<div style={{ border: '1.5px solid #d1fae5', borderRadius: 10, padding: '12px 12px 2px', background: '#f0fdf4' }}>
<div style={{ fontSize: 10, fontWeight: 700, color: '#059669', marginBottom: 6 }}> Berat Badan (kg)</div>
<ResponsiveContainer width="100%" height={120}>
<AreaChart data={chartData} margin={{ top: 4, right: 8, left: -20, bottom: 0 }}>
<defs>
<linearGradient id="pdfBeratGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#10b981" stopOpacity={0.3} />
<stop offset="95%" stopColor="#10b981" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#a7f3d0" />
<XAxis dataKey="label" fontSize={9} axisLine={false} tickLine={false} />
<YAxis fontSize={9} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="berat" stroke="#10b981" strokeWidth={2} fill="none" dot={{ r: 4, fill: '#10b981', stroke: 'white' }} isAnimationActive={false} />
<XAxis dataKey="label" tick={{ fontSize: 8, fontWeight: 600 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fontSize: 8 }} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="berat" stroke="#10b981" strokeWidth={2} fill="url(#pdfBeratGrad)" dot={{ r: 3, fill: '#10b981', stroke: 'white' }} connectNulls={false} isAnimationActive={false} />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
{/* Z-Score PDF Chart */}
<div style={{ marginTop: 20, border: '1.5px solid #f3e8ff', borderRadius: 12, padding: '14px 14px 4px', background: '#faf5ff' }}>
<div style={{ fontSize: 11, fontWeight: 700, color: '#9333ea', marginBottom: 8 }}>📈 Z-Score (SD)</div>
<div style={{ marginTop: 16, border: '1.5px solid #f3e8ff', borderRadius: 10, padding: '12px 12px 2px', background: '#faf5ff' }}>
<div style={{ fontSize: 10, fontWeight: 700, color: '#9333ea', marginBottom: 6 }}>📈 Z-Score (SD)</div>
<ResponsiveContainer width="100%" height={100}>
<AreaChart data={chartData} margin={{ top: 4, right: 16, left: -20, bottom: 0 }}>
<defs>
<linearGradient id="pdfZscoreGradAdmin" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#a855f7" stopOpacity={0.3} />
<stop offset="95%" stopColor="#a855f7" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#e9d5ff" />
<XAxis dataKey="label" fontSize={9} axisLine={false} tickLine={false} />
<YAxis fontSize={9} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="zscore" stroke="#9333ea" strokeWidth={2} fill="none" dot={{ r: 4, fill: '#9333ea', stroke: 'white' }} isAnimationActive={false} />
<XAxis dataKey="label" tick={{ fontSize: 8, fontWeight: 600 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fontSize: 8 }} axisLine={false} tickLine={false} />
<Area type="monotone" dataKey="zscore" stroke="#9333ea" strokeWidth={2} fill="url(#pdfZscoreGradAdmin)" dot={{ r: 3, fill: '#9333ea', stroke: 'white' }} connectNulls={false} isAnimationActive={false} />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
{/* Table */}
<div style={{ marginBottom: 32 }}>
<div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', color: '#888', marginBottom: 10 }}>Data Pemeriksaan</div>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
<div style={{ marginBottom: 20 }}>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 2, textTransform: 'uppercase', color: '#888', marginBottom: 6 }}>Data Pemeriksaan</div>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 11 }}>
<thead>
<tr style={{ background: '#111', color: '#fff' }}>
{['Tinggi', 'Berat', 'Z-Score', 'Status', 'Posyandu', 'Tgl Upload'].map(h => (
<th key={h} style={{ padding: '8px 12px', textAlign: 'left', fontSize: 10 }}>{h}</th>
{['Tinggi', 'Berat', 'Z-Score', 'Status Stunting', 'Posyandu', 'Tgl Pemeriksaan'].map(h => (
<th key={h} style={{ padding: '8px 10px', fontWeight: 700, fontSize: 9, letterSpacing: 1, textTransform: 'uppercase', textAlign: 'left' }}>{h}</th>
))}
</tr>
</thead>
<tbody>
<tr style={{ background: '#f9fafb' }}>
<td style={{ padding: '10px 12px', fontWeight: 700 }}>{activePrintData.row.tinggi_badan} cm</td>
<td style={{ padding: '10px 12px', fontWeight: 700 }}>{activePrintData.row.berat_badan} kg</td>
<td style={{ padding: '10px 12px', fontWeight: 700 }}>{activePrintData.row.z_score} SD</td>
<td style={{ padding: '10px 12px' }}>
<span style={{ padding: '2px 8px', borderRadius: 10, fontSize: 10, fontWeight: 700, background: isStunting ? '#fee2e2' : '#dcfce7', color: isStunting ? '#991b1b' : '#166534' }}>
{isStunting ? 'Stunting' : 'Normal'}
<td style={{ padding: '10px', fontWeight: 700 }}>{activePrintData.row.tinggi_badan ?? '-'} cm</td>
<td style={{ padding: '10px', fontWeight: 700 }}>{activePrintData.row.berat_badan ?? '-'} kg</td>
<td style={{ padding: '10px', fontWeight: 700 }}>{activePrintData.row.z_score ?? '-'} SD</td>
<td style={{ padding: '10px' }}>
<span style={{ display: 'inline-block', padding: '2px 8px', borderRadius: 20, fontSize: 10, fontWeight: 700, background: isStunting ? '#fef2f2' : '#f0fdf4', color: isStunting ? '#b91c1c' : '#15803d', border: `1px solid ${isStunting ? '#fecaca' : '#bbf7d0'}` }}>
{isStunting ? 'Stunting' : 'Normal'}
</span>
</td>
<td style={{ padding: '10px 12px' }}>{activePrintData.row.nama_posyandu}</td>
<td style={{ padding: '10px 12px' }}>{formatTgl(activePrintData.row.tanggal_upload)}</td>
<td style={{ padding: '10px' }}>{activePrintData.row.nama_posyandu ?? '-'}</td>
<td style={{ padding: '10px' }}>{tanggalUpload}</td>
</tr>
</tbody>
</table>
{activePrintData.row.pesan_ai && (
<div style={{ marginTop: 15, border: '1px solid #fde68a', borderRadius: 8, padding: '12px', background: '#fffbeb' }}>
<div style={{ fontSize: 9, fontWeight: 700, color: '#92400e', marginBottom: 4 }}>PESAN AI</div>
<div style={{ fontSize: 11, color: '#78350f' }}>{activePrintData.row.pesan_ai}</div>
<div style={{ marginTop: 8, border: '1.5px solid #fde68a', borderRadius: 8, padding: '10px 14px', background: '#fffbeb' }}>
<div style={{ fontSize: 8, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 1.5, color: '#92400e', marginBottom: 3 }}>Rekomendasi / Pesan AI</div>
<div style={{ fontSize: 11, lineHeight: 1.5, color: '#78350f' }}>{activePrintData.row.pesan_ai}</div>
</div>
)}
</div>
{/* Signatures */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 40, marginTop: 40, paddingTop: 20, borderTop: '1px solid #eee' }}>
{/* ── WhatsApp Info Box ── */}
<div style={{ marginTop: 16, padding: '10px 14px', borderRadius: 10, backgroundColor: '#f0fdf4', border: '1px solid #bbf7d0', display: 'flex', alignItems: 'flex-start', gap: 10 }}>
<div style={{ fontSize: 16 }}>📱</div>
<div>
<div style={{ fontSize: 10, color: '#888', marginBottom: 40 }}>Mengetahui,</div>
<div style={{ borderTop: '1px solid #333', fontSize: 10, paddingTop: 4 }}>Supervisor</div>
<div style={{ fontSize: 9, fontWeight: 800, color: '#166534', textTransform: 'uppercase', letterSpacing: 1, marginBottom: 2 }}>Layanan Informasi WhatsApp</div>
<div style={{ fontSize: 10, lineHeight: 1.5, color: '#14532d' }}>
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.
</div>
</div>
<div>
<div style={{ fontSize: 10, color: '#888', marginBottom: 40 }}>Petugas Posyandu,</div>
<div style={{ borderTop: '1px solid #333', fontSize: 10, paddingTop: 4 }}>Nama & Tanda Tangan</div>
</div>
{/* ── Portal Access Info Box ── */}
<div style={{ marginTop: 16, padding: '14px', border: '2px dashed #000', borderRadius: 12, backgroundColor: '#fdfcf0' }}>
<div style={{ fontSize: 10, fontWeight: 900, textTransform: 'uppercase', letterSpacing: 2, color: '#854d0e', display: 'flex', alignItems: 'center', gap: 6 }}>
🌐 Akses Portal Online Orang Tua
</div>
<div style={{ fontSize: 9, color: '#a16207', fontWeight: 500, fontStyle: 'italic', marginBottom: 10 }}>
* Yuk kunjungi website Bapak/Ibu agar mendapatkan informasi perkembangan buah hati Anda.
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 16 }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 8, color: '#a16207', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Alamat Website (URL)</div>
<div style={{ fontSize: 11, fontWeight: 800, color: '#000' }}>https://website-cloud-stunting.vercel.app/</div>
</div>
<div style={{ display: 'flex', gap: 16 }}>
<div>
<div style={{ fontSize: 8, color: '#a16207', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Username</div>
<div style={{ fontSize: 11, fontWeight: 800, color: '#000' }}>{activePrintData.pengguna.username || '-'}</div>
</div>
<div>
<div style={{ fontSize: 8, color: '#a16207', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Password</div>
<div style={{ fontSize: 11, fontWeight: 800, color: '#000' }}>{activePrintData.pengguna.password || '-'}</div>
</div>
</div>
</div>
</div>
{/* Footer */}
<div style={{ marginTop: 16, paddingTop: 8, borderTop: '1px solid #f0f0f0', display: 'flex', justifyContent: 'space-between', fontSize: 8, color: '#bbb' }}>
<span>Dicetak oleh Sistem Informasi Posyandu</span>
<span>Dokumen ini sah tanpa tanda tangan basah apabila dicetak dari sistem</span>
</div>
</div>
</div>