diff --git a/src/components/pages/report/production-result/ProductionResultReportPDF.tsx b/src/components/pages/report/production-result/ProductionResultReportPDF.tsx
new file mode 100644
index 00000000..9bc27c4b
--- /dev/null
+++ b/src/components/pages/report/production-result/ProductionResultReportPDF.tsx
@@ -0,0 +1,388 @@
+'use client';
+
+import React from 'react';
+import {
+ Document,
+ Page,
+ StyleSheet,
+ Text,
+ View,
+ Image,
+} from '@react-pdf/renderer';
+
+import { formatDate, formatNumber } from '@/lib/helper';
+import { BaseProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
+import { ProductionResult } from '@/types/api/report/production-result';
+
+type MappedProductionResultsItem = {
+ projectFlockKandang: BaseProjectFlockKandang;
+ productionResult: ProductionResult[] | null;
+};
+
+interface ProductionResultReportPDFProps {
+ mappedProductionResults?: MappedProductionResultsItem[];
+}
+
+const styles = StyleSheet.create({
+ page: {
+ paddingTop: 24,
+ paddingBottom: 52,
+ paddingHorizontal: 16,
+ },
+
+ companyInfoHeader: {
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ marginBottom: 8,
+ },
+ companyLogo: {
+ width: 64,
+ height: 'auto',
+ },
+ companyInfoHeaderDate: {
+ paddingTop: 8,
+ fontSize: 10,
+ },
+ companyName: {
+ fontSize: 12,
+ fontWeight: 'bold',
+ marginBottom: 4,
+ },
+ companyAddress: {
+ fontSize: 8,
+ maxWidth: 420,
+ marginBottom: 10,
+ },
+ doubleDivider: {
+ width: '100%',
+ height: 6,
+ borderTopWidth: 2,
+ borderTopColor: '#000',
+ borderBottomWidth: 2,
+ borderBottomColor: '#000',
+ },
+
+ title: {
+ marginTop: 14,
+ fontSize: 14,
+ lineHeight: '150%',
+ textAlign: 'center',
+ fontFamily: 'Times-Roman',
+ fontWeight: 'bold',
+ },
+
+ footer: {
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingHorizontal: 16,
+ position: 'absolute',
+ fontSize: 8,
+ bottom: 22,
+ left: 0,
+ right: 0,
+ textAlign: 'center',
+ color: 'grey',
+ },
+
+ section: {
+ marginTop: 12,
+ borderWidth: 1,
+ borderColor: '#000',
+ padding: 8,
+ },
+
+ sectionHeader: {
+ marginBottom: 6,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'baseline',
+ },
+ sectionTitle: {
+ fontSize: 10,
+ fontWeight: 'bold',
+ },
+ sectionSubtitle: {
+ fontSize: 8,
+ color: '#444',
+ },
+
+ // Simple grid table (label/value pairs)
+ grid: {
+ width: '100%',
+ borderWidth: 1,
+ borderColor: '#000',
+ },
+ gridRow: {
+ flexDirection: 'row',
+ borderBottomWidth: 1,
+ borderBottomColor: '#000',
+ },
+ gridRowLast: {
+ borderBottomWidth: 0,
+ },
+ gridCellLabel: {
+ width: '40%',
+ paddingVertical: 3,
+ paddingHorizontal: 6,
+ fontSize: 8,
+ borderRightWidth: 1,
+ borderRightColor: '#000',
+ fontWeight: 'bold',
+ },
+ gridCellValue: {
+ width: '60%',
+ paddingVertical: 3,
+ paddingHorizontal: 6,
+ fontSize: 8,
+ textAlign: 'right',
+ },
+
+ // Subsection headings
+ groupTitle: {
+ marginTop: 8,
+ marginBottom: 4,
+ fontSize: 9,
+ fontWeight: 'bold',
+ },
+
+ emptyText: {
+ fontSize: 8,
+ color: '#666',
+ fontStyle: 'italic',
+ },
+});
+
+function safeNum(v: unknown): number {
+ const n = typeof v === 'number' ? v : Number(v);
+ return Number.isFinite(n) ? n : 0;
+}
+
+function valueText(v: unknown) {
+ if (v === null || v === undefined) return '-';
+ if (typeof v === 'number') return formatNumber(v);
+ return String(v);
+}
+
+/**
+ * Render label/value table for one ProductionResult.
+ * Uses a compact grid to keep page readable.
+ */
+function ProductionResultGrid({ pr }: { pr: ProductionResult }) {
+ const rows: Array<[string, string]> = [
+ ['WOA', valueText(pr.woa)],
+
+ // BW
+ ['BW', valueText(pr.bw)],
+ ['Std BW', valueText(pr.std_bw)],
+ ['Uniformity', valueText(pr.uniformity)],
+ ['Std Uniformity', valueText(pr.std_uniformity)],
+
+ // Dep
+ ['Dep Kum', valueText(pr.dep_kum)],
+ ['Dep Std', valueText(pr.dep_std)],
+
+ // Butiran
+ ['Butiran Utuh', valueText(pr.butiran_utuh)],
+ ['Butiran Putih', valueText(pr.butiran_putih)],
+ ['Butiran Retak', valueText(pr.butiran_retak)],
+ ['Butiran Pecah', valueText(pr.butiran_pecah)],
+ ['Butiran Jumlah', valueText(pr.butiran_jumlah)],
+ ['Total Butir', valueText(pr.total_butir)],
+
+ // Kg
+ ['Kg Utuh', valueText(pr.kg_utuh)],
+ ['Kg Putih', valueText(pr.kg_putih)],
+ ['Kg Retak', valueText(pr.kg_retak)],
+ ['Kg Pecah', valueText(pr.kg_pecah)],
+ ['Kg Jumlah', valueText(pr.kg_jumlah)],
+ ['Total Kg', valueText(pr.total_kg)],
+
+ // %
+ ['% Utuh', valueText(pr.persen_utuh)],
+ ['% Putih', valueText(pr.persen_putih)],
+ ['% Retak', valueText(pr.persen_retak)],
+ ['% Pecah', valueText(pr.persen_pecah)],
+
+ // Produksi
+ ['HD', valueText(pr.hd)],
+ ['HD Std', valueText(pr.hd_std)],
+ ['FI', valueText(pr.fi)],
+ ['FI Std', valueText(pr.fi_std)],
+ ['EM', valueText(pr.em)],
+ ['EM Std', valueText(pr.em_std)],
+ ['EW', valueText(pr.ew)],
+ ['EW Std', valueText(pr.ew_std)],
+ ['FCR', valueText(pr.fcr)],
+ ['FCR Std', valueText(pr.fcr_std)],
+ ['HH', valueText(pr.hh)],
+ ['HH Std', valueText(pr.hh_std)],
+ ];
+
+ return (
+
+ {rows.map(([label, value], idx) => {
+ const isLast = idx === rows.length - 1;
+ return (
+
+ {label}
+ {value}
+
+ );
+ })}
+
+ );
+}
+
+/**
+ * If there are multiple ProductionResult entries for a kandang,
+ * we show them sequentially with a small header per result.
+ *
+ * You can later change this to render only the latest WOA, or group by week.
+ */
+function ProductionResultList({
+ productionResults,
+}: {
+ productionResults: ProductionResult[];
+}) {
+ return (
+
+ {productionResults.map((pr, idx) => {
+ const kandangName =
+ pr.project_flock?.kandang?.name ||
+ pr.project_flock?.kandang?.id?.toString() ||
+ '';
+
+ // Optional: show a compact subheader
+ const headerLeft = `Data #${idx + 1}`;
+ const headerRight =
+ kandangName && pr.woa !== undefined
+ ? `${kandangName} • WOA ${safeNum(pr.woa)}`
+ : pr.woa !== undefined
+ ? `WOA ${safeNum(pr.woa)}`
+ : '';
+
+ return (
+
+
+ {headerLeft}
+ {headerRight}
+
+
+
+
+ );
+ })}
+
+ );
+}
+
+/**
+ * ✅ Main PDF Component
+ */
+const ProductionResultReportPDF = ({
+ mappedProductionResults = [],
+}: ProductionResultReportPDFProps) => {
+ return (
+
+
+ {/* Header */}
+
+
+
+
+ {formatDate(Date.now(), 'DD MMMM YYYY')}
+
+
+
+
+ PT LUMBUNG TELUR INDONESIA
+
+ SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
+ Cipedes, Kec. Sukajadi, Kota Bandung 40162
+
+
+
+
+
+
+ Laporan Production Result
+
+ {/* Sections per ProjectFlockKandang */}
+ {mappedProductionResults.length === 0 ? (
+
+ Tidak ada data.
+
+ ) : (
+ mappedProductionResults.map((item, idx) => {
+ const pfk = item.projectFlockKandang;
+
+ // Try to display meaningful identifiers.
+ // Adjust these fields based on your real BaseProjectFlockKandang structure.
+ const kandangName =
+ pfk?.kandang?.name ?? `Kandang #${pfk?.kandang_id ?? idx + 1}`;
+
+ const projectName = pfk?.project_flock?.name ?? '';
+
+ const locationName = pfk?.project_flock?.location?.name ?? '';
+
+ const areaName = pfk?.project_flock?.area?.name ?? '';
+
+ return (
+ 0} // each kandang starts on a new page for clarity
+ >
+
+
+ {projectName
+ ? `${projectName} • ${kandangName}`
+ : kandangName}
+
+
+ {[areaName, locationName].filter(Boolean).join(' • ')}
+
+
+
+ {item.productionResult && item.productionResult.length > 0 ? (
+
+ ) : (
+
+ Tidak ada production result untuk kandang ini.
+
+ )}
+
+ );
+ })
+ )}
+
+ {/* Footer */}
+
+
+ `${pageNumber} / ${totalPages}`
+ }
+ fixed
+ />
+
+
+
+ );
+};
+
+export default ProductionResultReportPDF;