From cb0c53daea1143636f50a4051257d93ea56f0ed5 Mon Sep 17 00:00:00 2001 From: Athif Date: Tue, 23 Dec 2025 02:20:16 +0700 Subject: [PATCH] update Menu Report --- .../report/report/component/ListReport.jsx | 222 +++++++++++++++++- src/pages/report/trending/ReportTrending.jsx | 19 +- 2 files changed, 230 insertions(+), 11 deletions(-) diff --git a/src/pages/report/report/component/ListReport.jsx b/src/pages/report/report/component/ListReport.jsx index 693a18e..f48f51e 100644 --- a/src/pages/report/report/component/ListReport.jsx +++ b/src/pages/report/report/component/ListReport.jsx @@ -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={} onClick={exportToPDF} - disabled={false} + disabled={pivotData.length === 0} + style={{ backgroundColor: '#1890ff', borderColor: '#1890ff' }} > Export PDF + + +