add everything that i like to ad
This commit is contained in:
parent
8fc5257458
commit
d74be80085
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue