update Menu Report
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
import { getAllPlantSection } from '../../../../api/master-plant-section';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
import ExcelJS from 'exceljs';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
@@ -54,9 +56,9 @@ const ListReport = memo(function ListReport(props) {
|
||||
};
|
||||
|
||||
const fetchData = async (page = 1, pageSize = 10, showModal = false) => {
|
||||
if (!plantSubSection) {
|
||||
return;
|
||||
}
|
||||
// if (!plantSubSection) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (showModal) {
|
||||
setIsLoadingModal(true);
|
||||
@@ -195,8 +197,34 @@ const ListReport = memo(function ListReport(props) {
|
||||
fetchData(pagination.current, pagination.pageSize, false);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
fetchData(1, pagination.pageSize, true);
|
||||
const handleSearch = async () => {
|
||||
setIsLoadingModal(true);
|
||||
|
||||
try {
|
||||
const formattedDateStart = startDate.format('YYYY-MM-DD');
|
||||
const formattedDateEnd = endDate.format('YYYY-MM-DD');
|
||||
|
||||
const params = new URLSearchParams({
|
||||
plant_sub_section_id: plantSubSection,
|
||||
from: formattedDateStart,
|
||||
to: formattedDateEnd,
|
||||
interval: periode,
|
||||
page: 1,
|
||||
limit: 1000,
|
||||
});
|
||||
|
||||
const pivotResponse = await getAllHistoryValueReportPivot(params);
|
||||
|
||||
// Jika response sukses, proses data
|
||||
if (pivotResponse && pivotResponse.data) {
|
||||
await fetchData(1, pagination.pageSize, false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
// Error akan ditangkap oleh api-request.js dan muncul Swal otomatis
|
||||
} finally {
|
||||
setIsLoadingModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
@@ -247,6 +275,168 @@ const ListReport = memo(function ListReport(props) {
|
||||
{ value: 120, label: '2 Hour', disabled: false },
|
||||
];
|
||||
|
||||
const exportToExcel = async () => {
|
||||
if (pivotData.length === 0) {
|
||||
alert('No data to export');
|
||||
return;
|
||||
}
|
||||
|
||||
const tagMapping = {};
|
||||
valueReportData.forEach(item => {
|
||||
if (item.tag_name && item.tag_number) {
|
||||
tagMapping[item.tag_name] = item.tag_number;
|
||||
}
|
||||
});
|
||||
|
||||
const selectedSection = plantSubSectionList.find(
|
||||
item => item.plant_sub_section_id === plantSubSection
|
||||
);
|
||||
const sectionName = selectedSection ? selectedSection.plant_sub_section_name : 'Unknown';
|
||||
|
||||
// Buat struktur pivot yang sama seperti di tabel
|
||||
const timeMap = new Map();
|
||||
const tagSet = new Set();
|
||||
|
||||
pivotData.forEach((row) => {
|
||||
const tagName = row.id;
|
||||
tagSet.add(tagName);
|
||||
|
||||
const dataPoints = row.data || [];
|
||||
dataPoints.forEach((item) => {
|
||||
if (item && typeof item === 'object' && 'x' in item && 'y' in item) {
|
||||
const datetime = item.x;
|
||||
if (!timeMap.has(datetime)) {
|
||||
timeMap.set(datetime, {});
|
||||
}
|
||||
timeMap.get(datetime)[tagName] = item.y;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sortedTimes = Array.from(timeMap.keys()).sort();
|
||||
const sortedTags = Array.from(tagSet).sort();
|
||||
|
||||
const pivotTableData = sortedTimes.map((datetime) => {
|
||||
const rowData = {
|
||||
datetime: datetime,
|
||||
};
|
||||
|
||||
sortedTags.forEach((tagName) => {
|
||||
rowData[tagName] = timeMap.get(datetime)[tagName];
|
||||
});
|
||||
|
||||
return rowData;
|
||||
});
|
||||
|
||||
console.log('Excel Pivot data:', pivotTableData.slice(0, 5));
|
||||
console.log('Total rows for Excel:', pivotTableData.length);
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const ws = workbook.addWorksheet('Pivot Report');
|
||||
|
||||
// Buat header info (3 baris pertama)
|
||||
ws.addRow(['PT. PUPUK INDONESIA UTILITAS']);
|
||||
ws.addRow(['GRESIK GAS COGENERATION PLANT']);
|
||||
ws.addRow([`${sectionName}`]);
|
||||
ws.addRow([]); // Baris kosong sebagai pemisah
|
||||
|
||||
// Buat header kolom dengan tag number
|
||||
const headerRow = [
|
||||
'Datetime',
|
||||
...sortedTags.map(tag => tagMapping[tag] || tag)
|
||||
];
|
||||
ws.addRow(headerRow);
|
||||
|
||||
// Buat data rows - PERBAIKAN: Simpan sebagai number murni
|
||||
pivotTableData.forEach((rowData) => {
|
||||
const row = [dayjs(rowData.datetime).format('DD-MM-YYYY HH:mm')];
|
||||
sortedTags.forEach((tagName) => {
|
||||
const value = rowData[tagName];
|
||||
// Simpan sebagai number, bukan string
|
||||
if (value !== undefined && value !== null) {
|
||||
row.push(Number(value));
|
||||
} else {
|
||||
row.push('-');
|
||||
}
|
||||
});
|
||||
ws.addRow(row);
|
||||
});
|
||||
|
||||
// Set column widths
|
||||
ws.getColumn(1).width = 18; // Datetime column
|
||||
for (let i = 2; i <= sortedTags.length + 1; i++) {
|
||||
ws.getColumn(i).width = 12; // Tag columns
|
||||
}
|
||||
|
||||
// Merge cells untuk header info
|
||||
const totalCols = sortedTags.length + 1;
|
||||
ws.mergeCells(1, 1, 1, totalCols); // Baris 1
|
||||
ws.mergeCells(2, 1, 2, totalCols); // Baris 2
|
||||
ws.mergeCells(3, 1, 3, totalCols); // Baris 3
|
||||
|
||||
// Style untuk header info (3 baris pertama - bold dan center)
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const cell = ws.getCell(i, 1);
|
||||
cell.font = { bold: true, size: 12 };
|
||||
cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||
}
|
||||
|
||||
// Style untuk header kolom (bold, background color, center, border)
|
||||
const headerRowIndex = 5; // Baris header
|
||||
for (let col = 1; col <= totalCols; col++) {
|
||||
const cell = ws.getCell(headerRowIndex, col);
|
||||
cell.font = { bold: true, size: 11 };
|
||||
cell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFDCDCDC' }
|
||||
};
|
||||
cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||
cell.border = {
|
||||
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
right: { style: 'thin', color: { argb: 'FF000000' } }
|
||||
};
|
||||
}
|
||||
|
||||
// Style untuk data cells (border dan alignment) - PERBAIKAN: Format number dengan 2 desimal
|
||||
for (let row = headerRowIndex + 1; row <= ws.rowCount; row++) {
|
||||
for (let col = 1; col <= totalCols; col++) {
|
||||
const cell = ws.getCell(row, col);
|
||||
|
||||
cell.alignment = {
|
||||
horizontal: 'center',
|
||||
vertical: 'middle',
|
||||
wrapText: true
|
||||
};
|
||||
cell.border = {
|
||||
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
right: { style: 'thin', color: { argb: 'FF000000' } }
|
||||
};
|
||||
|
||||
// Format number dengan 2 desimal untuk kolom value (kolom 2 dst)
|
||||
if (col > 1) {
|
||||
const cellValue = cell.value;
|
||||
// Hanya set format number jika cell berisi angka
|
||||
if (typeof cellValue === 'number') {
|
||||
cell.numFmt = '0.00';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate file name
|
||||
const fileName = `Report_Pivot_${startDate.format('DD-MM-YYYY')}_to_${endDate.format('DD-MM-YYYY')}.xlsx`;
|
||||
|
||||
// Save file
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||||
saveAs(blob, fileName);
|
||||
};
|
||||
|
||||
const exportToPDF = async () => {
|
||||
if (pivotData.length === 0) {
|
||||
alert('No data to export');
|
||||
@@ -393,7 +583,7 @@ const ListReport = memo(function ListReport(props) {
|
||||
doc.setFontSize(9);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(10);
|
||||
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 41, { align: 'center' });
|
||||
doc.text(`${sectionName}`, marginLeft + col1Width + col2Width / 2, 38, { align: 'center' });
|
||||
};
|
||||
|
||||
// Hitung total kolom tag chunks
|
||||
@@ -534,7 +724,7 @@ const ListReport = memo(function ListReport(props) {
|
||||
autoTable(doc, {
|
||||
head: [headerRow],
|
||||
body: pdfRows,
|
||||
startY: isFirstPage ? 50 : 15,
|
||||
startY: isFirstPage ? 43 : 15,
|
||||
theme: 'grid',
|
||||
rowPageBreak: 'avoid',
|
||||
styles: {
|
||||
@@ -542,7 +732,7 @@ const ListReport = memo(function ListReport(props) {
|
||||
cellPadding: 1.5,
|
||||
minCellHeight: 8,
|
||||
lineColor: [0, 0, 0],
|
||||
lineWidth: 0.1,
|
||||
lineWidth: 0.5,
|
||||
halign: 'center',
|
||||
valign: 'middle',
|
||||
overflow: 'linebreak',
|
||||
@@ -554,7 +744,7 @@ const ListReport = memo(function ListReport(props) {
|
||||
halign: 'center',
|
||||
valign: 'middle',
|
||||
lineColor: [0, 0, 0],
|
||||
lineWidth: 0.3,
|
||||
lineWidth: 0.5,
|
||||
},
|
||||
columnStyles: {
|
||||
0: {
|
||||
@@ -694,11 +884,23 @@ const ListReport = memo(function ListReport(props) {
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={exportToPDF}
|
||||
disabled={false}
|
||||
disabled={pivotData.length === 0}
|
||||
style={{ backgroundColor: '#1890ff', borderColor: '#1890ff' }}
|
||||
>
|
||||
Export PDF
|
||||
</Button>
|
||||
</Col>
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={exportToExcel}
|
||||
disabled={pivotData.length === 0}
|
||||
style={{ backgroundColor: '#28a745', borderColor: '#28a745' }}
|
||||
>
|
||||
Export Excel
|
||||
</Button>
|
||||
</Col>
|
||||
<Col>
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
|
||||
@@ -130,9 +130,26 @@ const ReportTrending = memo(function ReportTrending(props) {
|
||||
}
|
||||
};
|
||||
|
||||
// Fungsi untuk menentukan apakah rentang tanggal lebih dari 1 hari
|
||||
const isMultipleDays = () => {
|
||||
return !startDate.isSame(endDate, 'day');
|
||||
};
|
||||
|
||||
// Format sumbu X yang otomatis menyesuaikan
|
||||
const formatXAxis = (tickItem) => {
|
||||
const date = new Date(tickItem);
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
// Jika rentang lebih dari 1 hari, tampilkan tanggal + waktu
|
||||
if (isMultipleDays()) {
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
return `${day}/${month} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
// Jika hanya 1 hari, tampilkan waktu saja
|
||||
return `${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
const CustomTooltip = ({ active, payload, label }) => {
|
||||
|
||||
Reference in New Issue
Block a user