mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 21:41:57 +00:00
refactor(FE): Refactor HppPerKandang export logic to use ExcelJS
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
'use client';
|
||||
|
||||
import ExcelJS from 'exceljs';
|
||||
import { formatCurrency, formatNumber } from '@/lib/helper';
|
||||
import {
|
||||
HppPerKandangReport,
|
||||
HppPerKandangRow,
|
||||
HppPerKandangPerWeightRange,
|
||||
} from '@/types/api/report/hpp-per-kandang';
|
||||
|
||||
interface HppPerKandangExportExcelParams {
|
||||
data: HppPerKandangReport;
|
||||
allFeedSuppliers: string;
|
||||
allDocSuppliers: string;
|
||||
}
|
||||
|
||||
const formatSuppliers = (
|
||||
suppliers: { alias?: string; name: string }[] | null
|
||||
): string => {
|
||||
if (!suppliers || suppliers.length === 0) return '';
|
||||
return suppliers.map((s) => s.alias || s.name).join(' | ');
|
||||
};
|
||||
|
||||
export const generateHppPerKandangExcel = async (
|
||||
params: HppPerKandangExportExcelParams
|
||||
): Promise<void> => {
|
||||
if (!params.data || !params.data.rows || params.data.rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
|
||||
// ===== REKAPITULASI WORKSHEET =====
|
||||
const rekapitulasiColumns = [
|
||||
{ header: 'No', key: 'no', width: 5 },
|
||||
{ header: 'Rentang BW', key: 'weightRange', width: 15 },
|
||||
{ header: 'Sisa Butir', key: 'eggPieces', width: 15 },
|
||||
{ header: 'Sisa Kg', key: 'eggKg', width: 12 },
|
||||
{ header: 'Rata-Rata Bobot (Kg)', key: 'avgWeight', width: 18 },
|
||||
{ header: 'Feed (Supplier)', key: 'feedSuppliers', width: 20 },
|
||||
{ header: 'DOC (Supplier)', key: 'docSuppliers', width: 20 },
|
||||
{ header: 'Rata-Rata Harga DOC', key: 'avgDocPrice', width: 20 },
|
||||
{ header: 'HPP Telur (Rp/Kg)', key: 'eggHpp', width: 18 },
|
||||
{ header: 'Nominal Sisa', key: 'eggValue', width: 25 },
|
||||
];
|
||||
|
||||
const rekapitulasiWorksheet = workbook.addWorksheet('Rekapitulasi');
|
||||
rekapitulasiWorksheet.columns = rekapitulasiColumns;
|
||||
|
||||
const perWeightRangeSummary = params.data.summary.per_weight_range || [];
|
||||
|
||||
perWeightRangeSummary.forEach(
|
||||
(item: HppPerKandangPerWeightRange, index: number) => {
|
||||
rekapitulasiWorksheet.addRow({
|
||||
no: index + 1,
|
||||
weightRange: item.label || '',
|
||||
eggPieces: formatNumber(item.egg_production_pieces || 0),
|
||||
eggKg: formatNumber(item.egg_production_kg || 0),
|
||||
avgWeight: formatNumber(item.avg_weight_kg || 0),
|
||||
feedSuppliers: formatSuppliers(item.feed_suppliers),
|
||||
docSuppliers: formatSuppliers(item.doc_suppliers),
|
||||
avgDocPrice: formatCurrency(item.average_doc_price_rp || 0),
|
||||
eggHpp: formatCurrency(item.egg_hpp_rp_per_kg || 0),
|
||||
eggValue: formatCurrency(item.egg_value_rp || 0),
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// ===== DETAIL PER KANDANG WORKSHEET =====
|
||||
const detailColumns = [
|
||||
{ header: 'No', key: 'no', width: 5 },
|
||||
{ header: 'Kandang', key: 'kandang', width: 30 },
|
||||
{ header: 'Rentang Bobot', key: 'weightRange', width: 15 },
|
||||
{ header: 'Rata-Rata Bobot (KG)', key: 'avgWeightKg', width: 18 },
|
||||
{ header: 'Sisa Telur (Butir)', key: 'eggPieces', width: 15 },
|
||||
{ header: 'Sisa Telur (KG)', key: 'eggKg', width: 15 },
|
||||
{ header: 'Feed (Supplier)', key: 'feedSuppliers', width: 20 },
|
||||
{ header: 'DOC (Supplier)', key: 'docSuppliers', width: 20 },
|
||||
{ header: 'Rata-Rata Harga DOC (Rp)', key: 'avgDocPrice', width: 20 },
|
||||
{ header: 'HPP Telur (Rp/Kg)', key: 'eggHpp', width: 18 },
|
||||
{ header: 'Nilai Nominal Sisa Telur (Rp)', key: 'eggValue', width: 25 },
|
||||
];
|
||||
|
||||
const detailWorksheet = workbook.addWorksheet('Detail Per Kandang');
|
||||
detailWorksheet.columns = detailColumns;
|
||||
|
||||
const allExportData = params.data.rows;
|
||||
|
||||
allExportData.forEach((item: HppPerKandangRow, index: number) => {
|
||||
detailWorksheet.addRow({
|
||||
no: index + 1,
|
||||
kandang: item.kandang?.name || '',
|
||||
weightRange: item.weight_range
|
||||
? `${formatNumber(item.weight_range.weight_min)} - ${formatNumber(item.weight_range.weight_max)}`
|
||||
: '',
|
||||
avgWeightKg: formatNumber(item.avg_weight_kg || 0),
|
||||
eggPieces: formatNumber(item.egg_production_pieces || 0),
|
||||
eggKg: formatNumber(item.egg_production_kg || 0),
|
||||
feedSuppliers: formatSuppliers(item.feed_suppliers),
|
||||
docSuppliers: formatSuppliers(item.doc_suppliers),
|
||||
avgDocPrice: formatCurrency(item.average_doc_price_rp || 0),
|
||||
eggHpp: formatCurrency(item.egg_hpp_rp_per_kg || 0),
|
||||
eggValue: formatCurrency(item.egg_value_rp || 0),
|
||||
});
|
||||
});
|
||||
|
||||
// Add TOTAL row
|
||||
const summaryTotal = params.data.summary.total;
|
||||
detailWorksheet.addRow({
|
||||
no: 'TOTAL',
|
||||
kandang: 'ALL',
|
||||
weightRange: '-',
|
||||
avgWeightKg: formatNumber(summaryTotal?.average_weight_kg || 0),
|
||||
eggPieces: formatNumber(summaryTotal?.total_egg_production_pieces || 0),
|
||||
eggKg: formatNumber(summaryTotal?.total_egg_production_kg || 0),
|
||||
feedSuppliers: params.allFeedSuppliers,
|
||||
docSuppliers: params.allDocSuppliers,
|
||||
avgDocPrice: formatCurrency(summaryTotal?.total_average_doc_price_rp || 0),
|
||||
eggHpp: formatCurrency(summaryTotal?.average_egg_hpp_rp_per_kg || 0),
|
||||
eggValue: formatCurrency(summaryTotal?.total_egg_value_rp || 0),
|
||||
});
|
||||
|
||||
const filename = `laporan-hpp-harian-kandang-periode-${params.data.period}.xlsx`;
|
||||
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
};
|
||||
@@ -26,9 +26,9 @@ import Button from '@/components/Button';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import MenuItem from '@/components/menu/MenuItem';
|
||||
import Menu from '@/components/menu/Menu';
|
||||
import { generateHppPerKandangPDF } from '../export/HppPerkandangExport';
|
||||
import { generateHppPerKandangPDF } from '../export/HppPerkandangExportPDF';
|
||||
import { generateHppPerKandangExcel } from '../export/HppPerkandangExportXLSX';
|
||||
import toast from 'react-hot-toast';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
const HppPerKandangTab = () => {
|
||||
@@ -346,136 +346,18 @@ const HppPerKandangTab = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const allExportData =
|
||||
allDataForExport.rows as HppPerKandangReport['rows'];
|
||||
|
||||
const perWeightRangeSummary =
|
||||
allDataForExport.summary.per_weight_range || [];
|
||||
|
||||
const summaryTotal = allDataForExport.summary.total;
|
||||
|
||||
const rekapitulasiData: { [key: string]: string | number }[] =
|
||||
perWeightRangeSummary.map(
|
||||
(item: HppPerKandangPerWeightRange, index: number) => ({
|
||||
No: index + 1,
|
||||
'Rentang BW': item.label || '',
|
||||
'Sisa Butir': item.egg_production_pieces || 0,
|
||||
'Sisa Kg': item.egg_production_kg || 0,
|
||||
'Rata-Rata Bobot (Kg)': item.avg_weight_kg || 0,
|
||||
'Feed (Supplier)':
|
||||
item.feed_suppliers
|
||||
?.map(
|
||||
(s: { alias?: string; name: string }) => s.alias || s.name
|
||||
)
|
||||
.join(' | ') || '',
|
||||
'DOC (Supplier)':
|
||||
item.doc_suppliers
|
||||
?.map(
|
||||
(s: { alias?: string; name: string }) => s.alias || s.name
|
||||
)
|
||||
.join(' | ') || '',
|
||||
'Rata-Rata Harga DOC': item.average_doc_price_rp || 0,
|
||||
'HPP Telur (RP/KG)': item.egg_hpp_rp_per_kg || 0,
|
||||
'Nominal Sisa': item.egg_value_rp || 0,
|
||||
})
|
||||
);
|
||||
|
||||
const rekapitulasiWorksheet = XLSX.utils.json_to_sheet(rekapitulasiData);
|
||||
|
||||
const rekapitulasiColWidths = [
|
||||
{ wch: 5 }, // No
|
||||
{ wch: 15 }, // Rentang BW
|
||||
{ wch: 15 }, // Sisa Butir
|
||||
{ wch: 12 }, // Sisa Kg
|
||||
{ wch: 18 }, // Rata-Rata Bobot (Kg)
|
||||
{ wch: 20 }, // Feed (Supplier)
|
||||
{ wch: 20 }, // DOC (Supplier)
|
||||
{ wch: 20 }, // Rata-Rata Harga DOC
|
||||
{ wch: 18 }, // HPP Telur (RP/KG)
|
||||
{ wch: 25 }, // Nominal Sisa
|
||||
];
|
||||
rekapitulasiWorksheet['!cols'] = rekapitulasiColWidths;
|
||||
|
||||
const excelData: { [key: string]: string | number }[] = allExportData.map(
|
||||
(item: HppPerKandangRow, index: number) => ({
|
||||
No: index + 1,
|
||||
Kandang: item.kandang?.name || '',
|
||||
'Rentang Bobot': item.weight_range
|
||||
? `${formatNumber(item.weight_range.weight_min)} - ${formatNumber(item.weight_range.weight_max)}`
|
||||
: '',
|
||||
'Rata-Rata Bobot (KG)': item.avg_weight_kg || 0,
|
||||
'Sisa Telur (Butir)': item.egg_production_pieces || 0,
|
||||
'Sisa Telur (KG)': item.egg_production_kg || 0,
|
||||
'Feed (Supplier)':
|
||||
item.feed_suppliers
|
||||
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
||||
.join(' | ') || '',
|
||||
'DOC (Supplier)':
|
||||
item.doc_suppliers
|
||||
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
||||
.join(' | ') || '',
|
||||
'Rata-Rata Harga DOC (RP)': item.average_doc_price_rp || 0,
|
||||
'HPP Telur (RP/KG)': item.egg_hpp_rp_per_kg || 0,
|
||||
'Nilai Nominal Sisa Telur (RP)': item.egg_value_rp || 0,
|
||||
})
|
||||
);
|
||||
|
||||
excelData.push({
|
||||
No: 'TOTAL',
|
||||
Kandang: 'ALL',
|
||||
'Rentang Bobot': '-',
|
||||
'Rata-Rata Bobot (KG)': summaryTotal?.average_weight_kg || 0,
|
||||
'Sisa Telur (Butir)': summaryTotal?.total_egg_production_pieces || 0,
|
||||
'Sisa Telur (KG)': summaryTotal?.total_egg_production_kg || 0,
|
||||
'Feed (Supplier)': allFeedSuppliers,
|
||||
'DOC (Supplier)': allDocSuppliers,
|
||||
'Rata-Rata Harga DOC (RP)':
|
||||
summaryTotal?.total_average_doc_price_rp || 0,
|
||||
'HPP Telur (RP/KG)': summaryTotal?.average_egg_hpp_rp_per_kg || 0,
|
||||
'Nilai Nominal Sisa Telur (RP)': summaryTotal?.total_egg_value_rp || 0,
|
||||
await generateHppPerKandangExcel({
|
||||
data: allDataForExport,
|
||||
allFeedSuppliers,
|
||||
allDocSuppliers,
|
||||
});
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||
|
||||
const colWidths = [
|
||||
{ wch: 5 }, // No
|
||||
{ wch: 30 }, // Kandang
|
||||
{ wch: 15 }, // Rentang Bobot
|
||||
{ wch: 18 }, // Rata-Rata Bobot (KG)
|
||||
{ wch: 15 }, // Sisa Telur (Butir)
|
||||
{ wch: 15 }, // Sisa Telur (KG)
|
||||
{ wch: 20 }, // Feed (Supplier)
|
||||
{ wch: 20 }, // DOC (Supplier)
|
||||
{ wch: 20 }, // Rata-Rata Harga DOC (RP)
|
||||
{ wch: 18 }, // HPP Telur (RP/KG)
|
||||
{ wch: 25 }, // Nilai Nominal Sisa Telur (RP)
|
||||
];
|
||||
worksheet['!cols'] = colWidths;
|
||||
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(
|
||||
workbook,
|
||||
rekapitulasiWorksheet,
|
||||
'Rekapitulasi'
|
||||
);
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Detail Per Kandang');
|
||||
|
||||
const filename = `laporan-hpp-harian-kandang-periode-${tableFilterState.period}.xlsx`;
|
||||
|
||||
XLSX.writeFile(workbook, filename);
|
||||
toast.success('Excel berhasil dibuat dan diunduh.');
|
||||
} catch {
|
||||
toast.error('Gagal membuat Excel. Silakan coba lagi.');
|
||||
} finally {
|
||||
setIsExcelExportLoading(false);
|
||||
}
|
||||
}, [
|
||||
hppPerKandangExport,
|
||||
tableFilterState,
|
||||
areaOptions,
|
||||
locationOptions,
|
||||
kandangOptions,
|
||||
]);
|
||||
}, [hppPerKandangExport, allFeedSuppliers, allDocSuppliers]);
|
||||
|
||||
const handleExportPDF = useCallback(async () => {
|
||||
setIsPdfExportLoading(true);
|
||||
|
||||
Reference in New Issue
Block a user