TKK_E32231405/app/dashboard/kelola-jadwal/CetakPDFJadwal.tsx

234 lines
12 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useRef, useState } from 'react'
import { FileText, Loader2, Printer } from 'lucide-react'
import { getPetugasLokalByPosyandu } from './action-jadwal'
import { showSwal } from '@/lib/swal'
interface PetugasLokal {
nama_petugas: string
nomor_hp: string
jabatan: string
}
interface JadwalData {
id: string
tanggal: string
jam_mulai: string
jam_selesai: string
diedit_oleh: string
posyandu_id: string
detail_posyandu: {
nama_posyandu: string
alamat: string
}
}
interface Props {
jadwal: JadwalData
currentAdmin: string
}
export function CetakPDFJadwal({ jadwal, currentAdmin }: Props) {
const [loading, setLoading] = useState(false)
const templateRef = useRef<HTMLDivElement>(null)
const [petugas, setPetugas] = useState<PetugasLokal[]>([])
const now = new Date()
const tglCetak = now.toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' })
const jamCetak = now.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' })
const tglJadwal = new Date(jadwal.tanggal).toLocaleDateString('id-ID', {
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric'
})
const handlePrint = async () => {
setLoading(true)
try {
// 1. Fetch Local Officers first
const res = await getPetugasLokalByPosyandu(jadwal.posyandu_id)
if (res.success) {
setPetugas(res.petugas || [])
}
// Small delay to ensure state update renders in the hidden div
await new Promise(r => setTimeout(r, 300))
const { default: html2canvas } = await import('html2canvas')
const { default: jsPDF } = await import('jspdf')
if (!templateRef.current) throw new Error('Template not found')
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 imgH = (canvas.height * pageW) / canvas.width
pdf.addImage(imgData, 'PNG', 0, 0, pageW, imgH)
pdf.save(`Jadwal_${jadwal.detail_posyandu.nama_posyandu.replace(/ /g, '_')}_${jadwal.tanggal}.pdf`)
} catch (err: any) {
showSwal.error('Gagal!', `Gagal mencetak PDF: ${err.message}`)
} finally {
setLoading(false)
}
}
return (
<>
{/* ─── Hidden PDF Template ─── */}
<div style={{ position: 'fixed', top: '-9999px', left: '-9999px', zIndex: -1 }}>
<div
ref={templateRef}
style={{
width: '794px', // A4 width at 96 DPI
backgroundColor: '#ffffff',
fontFamily: "'Inter', 'Arial', sans-serif",
color: '#111111',
padding: '60px 70px',
boxSizing: 'border-box',
}}
>
{/* Header: Kop Surat Style */}
<div style={{ borderBottom: '4px solid #000', paddingBottom: '20px', marginBottom: '30px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: '12px', fontWeight: 800, letterSpacing: '4px', textTransform: 'uppercase', color: '#666', marginBottom: '8px' }}>
Pemerintah Kabupaten / Kota
</div>
<div style={{ fontSize: '26px', fontWeight: 900, letterSpacing: '-1px', color: '#000' }}>
DINAS KESEHATAN & POSYANDU
</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{ fontSize: '10px', fontWeight: 700, color: '#888', textTransform: 'uppercase' }}>Nomor Dokumen</div>
<div style={{ fontSize: '14px', fontWeight: 800 }}>JDWL-{jadwal.id.slice(0, 8).toUpperCase()}</div>
</div>
</div>
</div>
<div style={{ textAlign: 'center', marginBottom: '40px' }}>
<h1 style={{ fontSize: '20px', fontWeight: 900, textTransform: 'uppercase', margin: 0, letterSpacing: '2px' }}>
SURAT PEMBERITAHUAN JADWAL PELAKSANAAN
</h1>
<div style={{ width: '60px', height: '3px', background: '#000', margin: '15px auto' }}></div>
</div>
{/* Informasi Utama */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '40px', marginBottom: '40px' }}>
<div>
<div style={{ fontSize: '9px', fontWeight: 800, textTransform: 'uppercase', color: '#888', letterSpacing: '1px', marginBottom: '15px' }}>
A. DETAIL POSYANDU
</div>
<div style={{ marginBottom: '15px' }}>
<div style={{ fontSize: '10px', color: '#888', fontWeight: 600, marginBottom: '4px' }}>Nama Posyandu</div>
<div style={{ fontSize: '14px', fontWeight: 800, color: '#000', textTransform: 'uppercase' }}>{jadwal.detail_posyandu.nama_posyandu}</div>
</div>
<div>
<div style={{ fontSize: '10px', color: '#888', fontWeight: 600, marginBottom: '4px' }}>Alamat Lokasi</div>
<div style={{ fontSize: '12px', fontWeight: 600, lineHeight: '1.5', color: '#333' }}>{jadwal.detail_posyandu.alamat}</div>
</div>
</div>
<div>
<div style={{ fontSize: '9px', fontWeight: 800, textTransform: 'uppercase', color: '#888', letterSpacing: '1px', marginBottom: '15px' }}>
B. WAKTU PELAKSANAAN
</div>
<div style={{ marginBottom: '15px' }}>
<div style={{ fontSize: '10px', color: '#888', fontWeight: 600, marginBottom: '4px' }}>Hari & Tanggal</div>
<div style={{ fontSize: '14px', fontWeight: 800, color: '#000' }}>{tglJadwal}</div>
</div>
<div>
<div style={{ fontSize: '10px', color: '#888', fontWeight: 600, marginBottom: '4px' }}>Sesi Operasional</div>
<div style={{ fontSize: '18px', fontWeight: 900, color: '#e11d48' }}>{jadwal.jam_mulai.slice(0, 5)} - {jadwal.jam_selesai.slice(0, 5)} WIB</div>
</div>
</div>
</div>
{/* Tabel Petugas */}
<div style={{ marginBottom: '40px' }}>
<div style={{ fontSize: '9px', fontWeight: 800, textTransform: 'uppercase', color: '#888', letterSpacing: '1px', marginBottom: '15px' }}>
C. DAFTAR PETUGAS LOKAL BERTUGAS
</div>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '2px solid #000' }}>
<th style={{ textAlign: 'left', padding: '12px 8px', fontSize: '11px', fontWeight: 800, textTransform: 'uppercase' }}>Nama Petugas</th>
<th style={{ textAlign: 'left', padding: '12px 8px', fontSize: '11px', fontWeight: 800, textTransform: 'uppercase' }}>Nomor Telepon</th>
<th style={{ textAlign: 'left', padding: '12px 8px', fontSize: '11px', fontWeight: 800, textTransform: 'uppercase' }}>Jabatan</th>
</tr>
</thead>
<tbody>
{petugas.length > 0 ? petugas.map((p, i) => (
<tr key={i} style={{ borderBottom: '1px solid #eee' }}>
<td style={{ padding: '12px 8px', fontSize: '12px', fontWeight: 700 }}>{p.nama_petugas}</td>
<td style={{ padding: '12px 8px', fontSize: '12px', fontWeight: 600, color: '#666' }}>{p.nomor_hp}</td>
<td style={{ padding: '12px 8px', fontSize: '11px', fontWeight: 700, color: '#e11d48', textTransform: 'uppercase' }}>{p.jabatan}</td>
</tr>
)) : (
<tr>
<td colSpan={3} style={{ padding: '30px', textAlign: 'center', fontSize: '12px', color: '#888', fontStyle: 'italic', background: '#f9f9f9' }}>
Belum ada petugas lokal yang terdaftar untuk Posyandu ini.
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* Catatan Penting */}
<div style={{ background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: '12px', padding: '20px', marginBottom: '50px' }}>
<div style={{ fontSize: '10px', fontWeight: 800, color: '#334155', textTransform: 'uppercase', marginBottom: '8px', display: 'flex', alignItems: 'center', gap: '6px' }}>
CATATAN SISTEM
</div>
<p style={{ fontSize: '11px', lineHeight: '1.7', color: '#475569', margin: 0, fontWeight: 500 }}>
"Dimohon agar aktivitas program pengecekan stunting di setiap Posyandu dijalankan sesuai dengan waktu dan sesi yang tertera dalam jadwal, guna mendukung pemeliharaan sistem yang baik."
</p>
</div>
{/* Footer / Metadata */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '40px', borderTop: '1px solid #eee', paddingTop: '30px' }}>
<div style={{ fontSize: '10px', color: '#888', lineHeight: '1.6' }}>
<div style={{ fontWeight: 700, color: '#555', marginBottom: '4px' }}>Metode Penjadwalan:</div>
Sistem Penjadwalan Otomatis (Algoritma Random Batch)<br />
Ditentukan oleh Admin: <span style={{ fontWeight: 700, color: '#000' }}>{jadwal.diedit_oleh.replace('[HISTORY] ', '')}</span>
</div>
<div style={{ textAlign: 'right', fontSize: '10px', color: '#888', lineHeight: '1.6' }}>
<div style={{ fontWeight: 700, color: '#555', marginBottom: '4px' }}>Detail Pengunduhan:</div>
Dicetak oleh Admin: <span style={{ fontWeight: 700, color: '#000' }}>{currentAdmin}</span><br />
Waktu Cetak: <span style={{ fontWeight: 700, color: '#000' }}>{tglCetak}, {jamCetak} WIB</span>
</div>
</div>
<div style={{ marginTop: '40px', textAlign: 'center', fontSize: '9px', color: '#ccc', letterSpacing: '1px' }}>
DOKUMEN INI DIHASILKAN SECARA OTOMATIS OLEH SISTEM CLOUD STUNTING
</div>
</div>
</div>
{/* ─── Visible Button ─── */}
<button
onClick={handlePrint}
disabled={loading}
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-all group relative"
title="Cetak PDF Detail Jadwal"
>
{loading ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<FileText className="w-4 h-4" />
)}
</button>
</>
)
}