mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
feat(FE-438): Add Excel and PDF export for Uniformity table
This commit is contained in:
@@ -39,6 +39,11 @@ import {
|
|||||||
getStatusIndicatorColor,
|
getStatusIndicatorColor,
|
||||||
getStatusText,
|
getStatusText,
|
||||||
} from '@/components/pages/uniformity/uniformity-utils';
|
} from '@/components/pages/uniformity/uniformity-utils';
|
||||||
|
import { generateUniformityPDF } from '@/components/pages/uniformity/export/UniformityExportPDF';
|
||||||
|
import { generateUniformityExcel } from '@/components/pages/uniformity/export/UniformityExportExcel';
|
||||||
|
import Dropdown from '@/components/Dropdown';
|
||||||
|
import Menu from '@/components/menu/Menu';
|
||||||
|
import MenuItem from '@/components/menu/MenuItem';
|
||||||
|
|
||||||
const isUniformityLocked = (uniformity: Uniformity): boolean => {
|
const isUniformityLocked = (uniformity: Uniformity): boolean => {
|
||||||
return uniformity.status === 'APPROVED' || uniformity.status === 'REJECTED';
|
return uniformity.status === 'APPROVED' || uniformity.status === 'REJECTED';
|
||||||
@@ -174,6 +179,9 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
>(undefined);
|
>(undefined);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
|
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
|
||||||
|
const [isPdfExportLoading, setIsPdfExportLoading] = useState(false);
|
||||||
|
const [isExcelExportLoading, setIsExcelExportLoading] = useState(false);
|
||||||
|
const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading;
|
||||||
|
|
||||||
const singleDeleteModal = useModal();
|
const singleDeleteModal = useModal();
|
||||||
const successModal = useModal();
|
const successModal = useModal();
|
||||||
@@ -532,6 +540,111 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
}
|
}
|
||||||
}, [selectedRowIds, refreshUniformities, bulkRejectModal]);
|
}, [selectedRowIds, refreshUniformities, bulkRejectModal]);
|
||||||
|
|
||||||
|
// ===== EXPORT HANDLERS =====
|
||||||
|
const uniformityExport = useCallback(async (): Promise<
|
||||||
|
Uniformity[] | null
|
||||||
|
> => {
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (filterProjectFlockKandangId) {
|
||||||
|
queryParams.append(
|
||||||
|
'project_flock_kandang_id',
|
||||||
|
filterProjectFlockKandangId.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (filterStartDate) {
|
||||||
|
queryParams.append('start_date', filterStartDate);
|
||||||
|
}
|
||||||
|
if (filterEndDate) {
|
||||||
|
queryParams.append('end_date', filterEndDate);
|
||||||
|
}
|
||||||
|
queryParams.append('limit', '10000');
|
||||||
|
queryParams.append('page', '1');
|
||||||
|
|
||||||
|
const queryString = queryParams.toString();
|
||||||
|
const url = `${UniformityApi.basePath}${queryString ? `?${queryString}` : ''}`;
|
||||||
|
|
||||||
|
const response = await UniformityApi.getAllFetcher(url);
|
||||||
|
|
||||||
|
return isResponseSuccess(response) ? response.data : null;
|
||||||
|
}, [filterProjectFlockKandangId, filterStartDate, filterEndDate]);
|
||||||
|
|
||||||
|
const handleExportExcel = useCallback(async () => {
|
||||||
|
setIsExcelExportLoading(true);
|
||||||
|
try {
|
||||||
|
const allDataForExport = await uniformityExport();
|
||||||
|
|
||||||
|
if (!allDataForExport || allDataForExport.length === 0) {
|
||||||
|
toast.error('Tidak ada data untuk diekspor.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locationName = filterLocation?.label || 'Semua Lokasi';
|
||||||
|
const projectFlockName =
|
||||||
|
filterProjectFlock?.label || 'Semua Project Flock';
|
||||||
|
const kandangName = filterKandang?.label || 'Semua Kandang';
|
||||||
|
|
||||||
|
generateUniformityExcel(allDataForExport, {
|
||||||
|
location_name: locationName,
|
||||||
|
project_flock_name: projectFlockName,
|
||||||
|
kandang_name: kandangName,
|
||||||
|
start_date: filterStartDate,
|
||||||
|
end_date: filterEndDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.success('Excel berhasil dibuat dan diunduh.');
|
||||||
|
} catch {
|
||||||
|
toast.error('Gagal membuat Excel. Silakan coba lagi.');
|
||||||
|
} finally {
|
||||||
|
setIsExcelExportLoading(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
uniformityExport,
|
||||||
|
filterLocation,
|
||||||
|
filterProjectFlock,
|
||||||
|
filterKandang,
|
||||||
|
filterStartDate,
|
||||||
|
filterEndDate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleExportPDF = useCallback(async () => {
|
||||||
|
setIsPdfExportLoading(true);
|
||||||
|
try {
|
||||||
|
const allDataForExport = await uniformityExport();
|
||||||
|
|
||||||
|
if (!allDataForExport || allDataForExport.length === 0) {
|
||||||
|
toast.error('Tidak ada data untuk diekspor.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locationName = filterLocation?.label || 'Semua Lokasi';
|
||||||
|
const projectFlockName =
|
||||||
|
filterProjectFlock?.label || 'Semua Project Flock';
|
||||||
|
const kandangName = filterKandang?.label || 'Semua Kandang';
|
||||||
|
|
||||||
|
await generateUniformityPDF(allDataForExport, {
|
||||||
|
location_name: locationName,
|
||||||
|
project_flock_name: projectFlockName,
|
||||||
|
kandang_name: kandangName,
|
||||||
|
start_date: filterStartDate,
|
||||||
|
end_date: filterEndDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.success('PDF berhasil dibuat dan diunduh.');
|
||||||
|
} catch {
|
||||||
|
toast.error('Gagal membuat PDF. Silakan coba lagi.');
|
||||||
|
} finally {
|
||||||
|
setIsPdfExportLoading(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
uniformityExport,
|
||||||
|
filterLocation,
|
||||||
|
filterProjectFlock,
|
||||||
|
filterKandang,
|
||||||
|
filterStartDate,
|
||||||
|
filterEndDate,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isResponseSuccess(uniformities) && uniformities.data) {
|
if (isResponseSuccess(uniformities) && uniformities.data) {
|
||||||
const newSelection: Record<string, boolean> = {};
|
const newSelection: Record<string, boolean> = {};
|
||||||
@@ -688,10 +801,24 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
Filter
|
Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button variant='outline'>
|
<Dropdown
|
||||||
<Icon icon='heroicons:cloud-arrow-down' width={18} height={18} />
|
trigger={
|
||||||
|
<Button variant='outline' isLoading={isAnyExportLoading}>
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:cloud-arrow-down'
|
||||||
|
width={18}
|
||||||
|
height={18}
|
||||||
|
/>
|
||||||
Export
|
Export
|
||||||
</Button>
|
</Button>
|
||||||
|
}
|
||||||
|
align='end'
|
||||||
|
>
|
||||||
|
<Menu>
|
||||||
|
<MenuItem title='Excel' onClick={handleExportExcel} />
|
||||||
|
<MenuItem title='PDF' onClick={handleExportPDF} />
|
||||||
|
</Menu>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
import type { Uniformity } from '@/types/api/uniformity/uniformity';
|
||||||
|
import { formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
|
||||||
|
interface UniformityExportExcelParams {
|
||||||
|
data: Uniformity[];
|
||||||
|
params: {
|
||||||
|
location_name?: string;
|
||||||
|
project_flock_name?: string;
|
||||||
|
kandang_name?: string;
|
||||||
|
start_date?: string;
|
||||||
|
end_date?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'APPROVED':
|
||||||
|
return 'Disetujui';
|
||||||
|
case 'REJECTED':
|
||||||
|
return 'Ditolak';
|
||||||
|
case 'CREATED':
|
||||||
|
return 'Pengajuan';
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateUniformityExcel = (
|
||||||
|
data: UniformityExportExcelParams['data'],
|
||||||
|
params: UniformityExportExcelParams['params']
|
||||||
|
): void => {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const excelData: { [key: string]: string | number }[] = data.map(
|
||||||
|
(item: Uniformity, index: number) => ({
|
||||||
|
No: index + 1,
|
||||||
|
Lokasi: item.location_name || '',
|
||||||
|
'Project Flock': item.flock_name || '',
|
||||||
|
Kandang: item.kandang_name || '',
|
||||||
|
Tanggal: formatDate(item.applied_at, 'DD MMM YYYY'),
|
||||||
|
Minggu: item.week || 0,
|
||||||
|
Status: getStatusText(item.status),
|
||||||
|
'Uniformity (%)': formatNumber(item.uniformity),
|
||||||
|
'CV (%)': formatNumber(item.cv),
|
||||||
|
'Chick Qty': formatNumber(item.chick_qty_of_weight),
|
||||||
|
'Uniform Qty': formatNumber(item.uniform_qty),
|
||||||
|
'Mean Up': formatNumber(item.mean_up),
|
||||||
|
'Mean Down': formatNumber(item.mean_down),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||||
|
|
||||||
|
const colWidths = [
|
||||||
|
{ wch: 6 }, // No
|
||||||
|
{ wch: 25 }, // Lokasi
|
||||||
|
{ wch: 20 }, // Project Flock
|
||||||
|
{ wch: 15 }, // Kandang
|
||||||
|
{ wch: 15 }, // Tanggal
|
||||||
|
{ wch: 10 }, // Minggu
|
||||||
|
{ wch: 12 }, // Status
|
||||||
|
{ wch: 15 }, // Uniformity (%)
|
||||||
|
{ wch: 10 }, // CV (%)
|
||||||
|
{ wch: 12 }, // Chick Qty
|
||||||
|
{ wch: 12 }, // Uniform Qty
|
||||||
|
{ wch: 12 }, // Mean Up
|
||||||
|
{ wch: 12 }, // Mean Down
|
||||||
|
];
|
||||||
|
worksheet['!cols'] = colWidths;
|
||||||
|
|
||||||
|
const workbook = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Uniformity');
|
||||||
|
|
||||||
|
const period =
|
||||||
|
params.start_date && params.end_date
|
||||||
|
? `${params.start_date}-${params.end_date}`
|
||||||
|
: formatDate(new Date(), 'YYYY-MM-DD');
|
||||||
|
const filename = `laporan-uniformity-${period}.xlsx`;
|
||||||
|
|
||||||
|
XLSX.writeFile(workbook, filename);
|
||||||
|
};
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Page,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
Document,
|
||||||
|
StyleSheet,
|
||||||
|
Font,
|
||||||
|
pdf,
|
||||||
|
} from '@react-pdf/renderer';
|
||||||
|
|
||||||
|
import { formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
import type { Uniformity } from '@/types/api/uniformity/uniformity';
|
||||||
|
|
||||||
|
Font.register({
|
||||||
|
family: 'Helvetica',
|
||||||
|
src: 'helvetica',
|
||||||
|
});
|
||||||
|
|
||||||
|
const pdfStyles = StyleSheet.create({
|
||||||
|
page: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontFamily: 'Helvetica',
|
||||||
|
padding: 20,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
},
|
||||||
|
titleSection: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
mainTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 5,
|
||||||
|
color: '#1f74bf',
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#000000',
|
||||||
|
marginBottom: 15,
|
||||||
|
},
|
||||||
|
tableRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
tableHeader: {
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
textAlign: 'left',
|
||||||
|
},
|
||||||
|
tableCellHeader: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000000',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
paddingVertical: 12,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
tableCellHeaderRight: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
textAlign: 'right',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000000',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
paddingVertical: 12,
|
||||||
|
},
|
||||||
|
tableCellRight: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
tableCellCenter: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
tableBorderBottom: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000000',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
},
|
||||||
|
badge: {
|
||||||
|
backgroundColor: '#1f74bf',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
padding: 2,
|
||||||
|
borderRadius: 2,
|
||||||
|
fontSize: 7,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
alignSelf: 'center',
|
||||||
|
},
|
||||||
|
parameterBadge: {
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
color: '#333333',
|
||||||
|
padding: 4,
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
parameterContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface UniformityExportPDFParams {
|
||||||
|
data: Uniformity[];
|
||||||
|
params: {
|
||||||
|
location_name?: string;
|
||||||
|
project_flock_name?: string;
|
||||||
|
kandang_name?: string;
|
||||||
|
start_date?: string;
|
||||||
|
end_date?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getParameterText = (params: UniformityExportPDFParams['params']) => {
|
||||||
|
const paramsText = [];
|
||||||
|
|
||||||
|
if (params.location_name && params.location_name !== 'Semua Lokasi') {
|
||||||
|
paramsText.push(`Lokasi: ${params.location_name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
params.project_flock_name &&
|
||||||
|
params.project_flock_name !== 'Semua Project Flock'
|
||||||
|
) {
|
||||||
|
paramsText.push(`Project Flock: ${params.project_flock_name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.kandang_name && params.kandang_name !== 'Semua Kandang') {
|
||||||
|
paramsText.push(`Kandang: ${params.kandang_name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.start_date && params.end_date) {
|
||||||
|
const formattedStartDate = formatDate(params.start_date, 'DD MMM YYYY');
|
||||||
|
const formattedEndDate = formatDate(params.end_date, 'DD MMM YYYY');
|
||||||
|
paramsText.push(`Periode: ${formattedStartDate} - ${formattedEndDate}`);
|
||||||
|
} else if (params.start_date) {
|
||||||
|
const formattedStartDate = formatDate(params.start_date, 'DD MMM YYYY');
|
||||||
|
paramsText.push(`Tanggal Mulai: ${formattedStartDate}`);
|
||||||
|
} else if (params.end_date) {
|
||||||
|
const formattedEndDate = formatDate(params.end_date, 'DD MMM YYYY');
|
||||||
|
paramsText.push(`Tanggal Akhir: ${formattedEndDate}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDate = formatDate(new Date().toISOString(), 'DD MMM YYYY HH:mm');
|
||||||
|
paramsText.push(`Dicetak: ${currentDate}`);
|
||||||
|
|
||||||
|
return paramsText;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'APPROVED':
|
||||||
|
return 'Disetujui';
|
||||||
|
case 'REJECTED':
|
||||||
|
return 'Ditolak';
|
||||||
|
case 'CREATED':
|
||||||
|
return 'Pengajuan';
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPDFDocument = (
|
||||||
|
data: UniformityExportPDFParams['data'],
|
||||||
|
params: UniformityExportPDFParams['params']
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Document>
|
||||||
|
<Page size='A4' orientation='landscape' style={pdfStyles.page}>
|
||||||
|
{/* Title and Parameters */}
|
||||||
|
<View style={pdfStyles.titleSection}>
|
||||||
|
<Text style={pdfStyles.mainTitle}>Production > Uniformity</Text>
|
||||||
|
<View style={pdfStyles.parameterContainer}>
|
||||||
|
{getParameterText(params).map((param, index) => (
|
||||||
|
<View key={index} style={pdfStyles.parameterBadge}>
|
||||||
|
<Text>{param}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Table */}
|
||||||
|
<View style={pdfStyles.table}>
|
||||||
|
{/* Table Header */}
|
||||||
|
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 0.5 }]}>
|
||||||
|
<Text>No</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
||||||
|
<Text>Lokasi</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
||||||
|
<Text>Project Flock</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
||||||
|
<Text>Kandang</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
||||||
|
<Text>Tanggal (Week)</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
|
||||||
|
<Text>Status</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
|
<Text>Uniformity (%)</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
|
<Text>CV (%)</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
|
<Text>Chick Qty</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
|
<Text>Uniform Qty</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
|
<Text>Mean Up</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
|
<Text>Mean Down</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Table Body */}
|
||||||
|
{data.map((item: Uniformity, index: number) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
style={[
|
||||||
|
pdfStyles.tableRow,
|
||||||
|
index < data.length - 1 ? pdfStyles.tableBorderBottom : {},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={[pdfStyles.tableCellCenter, { flex: 0.5 }]}>
|
||||||
|
<Text>{index + 1}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
|
<Text>{item.location_name || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
|
<Text>{item.flock_name || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
||||||
|
<Text>{item.kandang_name || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
|
<Text>
|
||||||
|
{formatDate(item.applied_at, 'DD MMM YYYY')} (Week {item.week}
|
||||||
|
)
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellCenter, { flex: 1 }]}>
|
||||||
|
<View style={pdfStyles.badge}>
|
||||||
|
<Text>{getStatusText(item.status)}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
|
<Text>{formatNumber(item.uniformity)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
|
<Text>{formatNumber(item.cv)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
|
<Text>{formatNumber(item.chick_qty_of_weight)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
|
<Text>{formatNumber(item.uniform_qty)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
|
<Text>{formatNumber(item.mean_up)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
|
<Text>{formatNumber(item.mean_down)}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</Page>
|
||||||
|
</Document>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateUniformityPDF = async (
|
||||||
|
data: UniformityExportPDFParams['data'],
|
||||||
|
params: UniformityExportPDFParams['params']
|
||||||
|
): Promise<void> => {
|
||||||
|
const PDFDocument = createPDFDocument(data, params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const blob = await pdf(PDFDocument).toBlob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
|
||||||
|
const period =
|
||||||
|
params.start_date && params.end_date
|
||||||
|
? `${params.start_date}-${params.end_date}`
|
||||||
|
: formatDate(new Date(), 'YYYY-MM-DD');
|
||||||
|
link.download = `laporan-uniformity-${period}.pdf`;
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user