update menu report
This commit is contained in:
@@ -16,9 +16,10 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
const dateNow = dayjs();
|
const dateNow = dayjs();
|
||||||
const dateNowFormated = dateNow.format('YYYY-MM-DD');
|
const dateNowFormated = dateNow.format('YYYY-MM-DD');
|
||||||
|
|
||||||
const [isLoadingModal, setIsLoadingModal] = useState(false); // Modal loading
|
const [isLoadingModal, setIsLoadingModal] = useState(false);
|
||||||
const [isLoadingTable, setIsLoadingTable] = useState(false); // Table loading
|
const [isLoadingTable, setIsLoadingTable] = useState(false);
|
||||||
const [tableData, setTableData] = useState([]);
|
const [tableData, setTableData] = useState([]);
|
||||||
|
const [columns, setColumns] = useState([]);
|
||||||
const [pivotData, setPivotData] = useState([]);
|
const [pivotData, setPivotData] = useState([]);
|
||||||
const [valueReportData, setValueReportData] = useState([]);
|
const [valueReportData, setValueReportData] = useState([]);
|
||||||
const [pagination, setPagination] = useState({
|
const [pagination, setPagination] = useState({
|
||||||
@@ -33,46 +34,6 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
const [endDate, setEndDate] = useState(dateNow);
|
const [endDate, setEndDate] = useState(dateNow);
|
||||||
const [periode, setPeriode] = useState(30);
|
const [periode, setPeriode] = useState(30);
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'No',
|
|
||||||
key: 'no',
|
|
||||||
width: 60,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'left',
|
|
||||||
render: (_, __, index) => {
|
|
||||||
return (pagination.current - 1) * pagination.pageSize + index + 1;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Datetime',
|
|
||||||
dataIndex: 'datetime',
|
|
||||||
key: 'datetime',
|
|
||||||
width: 180,
|
|
||||||
sorter: (a, b) => new Date(a.datetime) - new Date(b.datetime),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Tag Name',
|
|
||||||
dataIndex: 'tagName',
|
|
||||||
key: 'tagName',
|
|
||||||
width: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Value',
|
|
||||||
dataIndex: 'value',
|
|
||||||
key: 'value',
|
|
||||||
width: 120,
|
|
||||||
align: 'left', // Aligned kanan (seperti angka pada umumnya)
|
|
||||||
render: (value) => {
|
|
||||||
if (value === null || value === undefined) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
return Number(value).toFixed(2); // Format 2 desimal
|
|
||||||
},
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Fungsi helper untuk generate semua waktu dalam sehari berdasarkan periode
|
|
||||||
const generateFullDayTimes = (dateString, intervalMinutes) => {
|
const generateFullDayTimes = (dateString, intervalMinutes) => {
|
||||||
const times = [];
|
const times = [];
|
||||||
const startOfDay = dayjs(dateString).startOf('day');
|
const startOfDay = dayjs(dateString).startOf('day');
|
||||||
@@ -84,7 +45,6 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
times.push(currentTime.format('YYYY-MM-DD HH:mm:ss'));
|
times.push(currentTime.format('YYYY-MM-DD HH:mm:ss'));
|
||||||
currentTime = currentTime.add(intervalMinutes, 'minute');
|
currentTime = currentTime.add(intervalMinutes, 'minute');
|
||||||
|
|
||||||
// Jika waktu berikutnya melebihi akhir hari, break
|
|
||||||
if (currentTime.isAfter(endOfDay)) {
|
if (currentTime.isAfter(endOfDay)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -99,9 +59,9 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showModal) {
|
if (showModal) {
|
||||||
setIsLoadingModal(true); // Modal untuk fetch pertama
|
setIsLoadingModal(true);
|
||||||
} else {
|
} else {
|
||||||
setIsLoadingTable(true); // Spin untuk pagination
|
setIsLoadingTable(true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const formattedDateStart = startDate.format('YYYY-MM-DD');
|
const formattedDateStart = startDate.format('YYYY-MM-DD');
|
||||||
@@ -121,8 +81,6 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
|
|
||||||
if (pivotResponse && pivotResponse.data) {
|
if (pivotResponse && pivotResponse.data) {
|
||||||
console.log('API Pivot Response:', pivotResponse);
|
console.log('API Pivot Response:', pivotResponse);
|
||||||
console.log('First row pivot data:', pivotResponse.data[0]);
|
|
||||||
|
|
||||||
setPivotData(pivotResponse.data);
|
setPivotData(pivotResponse.data);
|
||||||
|
|
||||||
if (valueReportResponse && valueReportResponse.data) {
|
if (valueReportResponse && valueReportResponse.data) {
|
||||||
@@ -130,42 +88,90 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
setValueReportData(valueReportResponse.data);
|
setValueReportData(valueReportResponse.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const unpivotedData = [];
|
// Buat struktur pivot: waktu sebagai baris, tag sebagai kolom
|
||||||
|
const timeMap = new Map();
|
||||||
|
const tagSet = new Set();
|
||||||
|
|
||||||
|
// Kumpulkan semua waktu unik dan tag unik
|
||||||
pivotResponse.data.forEach((row) => {
|
pivotResponse.data.forEach((row) => {
|
||||||
const tagName = row.id;
|
const tagName = row.id;
|
||||||
const dataPoints = row.data || [];
|
tagSet.add(tagName);
|
||||||
|
|
||||||
|
const dataPoints = row.data || [];
|
||||||
dataPoints.forEach((item) => {
|
dataPoints.forEach((item) => {
|
||||||
if (item && typeof item === 'object' && 'x' in item && 'y' in item) {
|
if (item && typeof item === 'object' && 'x' in item && 'y' in item) {
|
||||||
unpivotedData.push({
|
const datetime = item.x;
|
||||||
datetime: item.x,
|
if (!timeMap.has(datetime)) {
|
||||||
tagName: tagName,
|
timeMap.set(datetime, {});
|
||||||
value: item.y,
|
}
|
||||||
});
|
timeMap.get(datetime)[tagName] = item.y;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Unpivoted data sample:', unpivotedData.slice(0, 10));
|
// Konversi ke array dan sort berdasarkan waktu
|
||||||
console.log('Total unpivoted rows:', unpivotedData.length);
|
const sortedTimes = Array.from(timeMap.keys()).sort();
|
||||||
|
const sortedTags = Array.from(tagSet).sort();
|
||||||
|
|
||||||
unpivotedData.sort((a, b) => {
|
// Buat data untuk table
|
||||||
if (a.tagName !== b.tagName) {
|
const pivotTableData = sortedTimes.map((datetime, index) => {
|
||||||
return a.tagName.localeCompare(b.tagName);
|
const rowData = {
|
||||||
}
|
key: index,
|
||||||
return new Date(a.datetime) - new Date(b.datetime);
|
datetime: datetime,
|
||||||
|
};
|
||||||
|
|
||||||
|
sortedTags.forEach((tagName) => {
|
||||||
|
rowData[tagName] = timeMap.get(datetime)[tagName];
|
||||||
|
});
|
||||||
|
|
||||||
|
return rowData;
|
||||||
});
|
});
|
||||||
|
|
||||||
const transformedData = unpivotedData.map((item, index) => ({
|
console.log('Pivot table data sample:', pivotTableData.slice(0, 5));
|
||||||
key: index,
|
console.log('Total pivot rows:', pivotTableData.length);
|
||||||
...item,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const total = transformedData.length;
|
// Buat kolom dinamis
|
||||||
|
const dynamicColumns = [
|
||||||
|
{
|
||||||
|
title: 'No',
|
||||||
|
key: 'no',
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'left',
|
||||||
|
render: (_, __, index) => {
|
||||||
|
return (page - 1) * pageSize + index + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Datetime',
|
||||||
|
dataIndex: 'datetime',
|
||||||
|
key: 'datetime',
|
||||||
|
width: 180,
|
||||||
|
fixed: 'left',
|
||||||
|
sorter: (a, b) => new Date(a.datetime) - new Date(b.datetime),
|
||||||
|
},
|
||||||
|
...sortedTags.map((tagName) => ({
|
||||||
|
title: tagName,
|
||||||
|
dataIndex: tagName,
|
||||||
|
key: tagName,
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
render: (value) => {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return Number(value).toFixed(2);
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
setColumns(dynamicColumns);
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
const total = pivotTableData.length;
|
||||||
const startIndex = (page - 1) * pageSize;
|
const startIndex = (page - 1) * pageSize;
|
||||||
const endIndex = startIndex + pageSize;
|
const endIndex = startIndex + pageSize;
|
||||||
const paginatedData = transformedData.slice(startIndex, endIndex);
|
const paginatedData = pivotTableData.slice(startIndex, endIndex);
|
||||||
|
|
||||||
setTableData(paginatedData);
|
setTableData(paginatedData);
|
||||||
setPagination({
|
setPagination({
|
||||||
@@ -199,6 +205,7 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
setEndDate(dateNow);
|
setEndDate(dateNow);
|
||||||
setPeriode(30);
|
setPeriode(30);
|
||||||
setTableData([]);
|
setTableData([]);
|
||||||
|
setColumns([]);
|
||||||
setPivotData([]);
|
setPivotData([]);
|
||||||
setValueReportData([]);
|
setValueReportData([]);
|
||||||
setPagination({
|
setPagination({
|
||||||
@@ -256,47 +263,43 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
const selectedSection = plantSubSectionList.find(item => item.plant_sub_section_id === plantSubSection);
|
const selectedSection = plantSubSectionList.find(item => item.plant_sub_section_id === plantSubSection);
|
||||||
const sectionName = selectedSection ? selectedSection.plant_sub_section_name : 'Unknown';
|
const sectionName = selectedSection ? selectedSection.plant_sub_section_name : 'Unknown';
|
||||||
|
|
||||||
// Generate data lengkap untuk setiap tanggal dengan semua interval waktu
|
// Buat struktur pivot yang sama seperti di tabel
|
||||||
const dataByDate = {};
|
const timeMap = new Map();
|
||||||
|
const tagSet = new Set();
|
||||||
|
|
||||||
// Pertama, kumpulkan semua tanggal yang ada
|
pivotData.forEach((row) => {
|
||||||
const allDates = new Set();
|
const tagName = row.id;
|
||||||
pivotData.forEach(series => {
|
tagSet.add(tagName);
|
||||||
series.data.forEach(item => {
|
|
||||||
const date = dayjs(item.x).format('YYYY-MM-DD');
|
|
||||||
allDates.add(date);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Untuk setiap tanggal, generate semua waktu dan isi dengan data yang ada
|
const dataPoints = row.data || [];
|
||||||
Array.from(allDates).forEach(date => {
|
dataPoints.forEach((item) => {
|
||||||
const fullDayTimes = generateFullDayTimes(date, periode);
|
if (item && typeof item === 'object' && 'x' in item && 'y' in item) {
|
||||||
dataByDate[date] = {};
|
const datetime = item.x;
|
||||||
|
if (!timeMap.has(datetime)) {
|
||||||
// Initialize dengan semua waktu
|
timeMap.set(datetime, {});
|
||||||
pivotData.forEach(series => {
|
|
||||||
dataByDate[date][series.id] = fullDayTimes.map(time => ({
|
|
||||||
x: time,
|
|
||||||
y: null // Default null, akan diisi jika ada data
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Isi dengan data aktual yang ada
|
|
||||||
pivotData.forEach(series => {
|
|
||||||
series.data.forEach(item => {
|
|
||||||
const itemDate = dayjs(item.x).format('YYYY-MM-DD');
|
|
||||||
if (itemDate === date) {
|
|
||||||
const itemTime = dayjs(item.x).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
const timeIndex = fullDayTimes.indexOf(itemTime);
|
|
||||||
if (timeIndex !== -1 && dataByDate[date][series.id][timeIndex]) {
|
|
||||||
dataByDate[date][series.id][timeIndex].y = item.y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
timeMap.get(datetime)[tagName] = item.y;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedDates = Object.keys(dataByDate).sort();
|
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('PDF Pivot data:', pivotTableData.slice(0, 5));
|
||||||
|
console.log('Total rows for PDF:', pivotTableData.length);
|
||||||
|
|
||||||
const loadImage = (src) => {
|
const loadImage = (src) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -322,21 +325,17 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
const marginRight = 10;
|
const marginRight = 10;
|
||||||
const tableWidth = pageWidth - marginLeft - marginRight;
|
const tableWidth = pageWidth - marginLeft - marginRight;
|
||||||
|
|
||||||
// Konstanta untuk mengatur lebar kolom
|
const DATETIME_COLUMN_WIDTH = 25;
|
||||||
const IO_TAG_COLUMN_WIDTH = 17; // Lebar kolom IO Soft Tag di TABEL (mm)
|
const HEADER_LEFT_COLUMN_WIDTH = 40;
|
||||||
const HEADER_LEFT_COLUMN_WIDTH = 40; // Lebar kolom KIRI di HEADER (untuk logo1) (mm)
|
const MAX_TAG_COLUMNS_PER_PAGE = 15;
|
||||||
const MAX_TIME_COLUMNS_PER_PAGE = 24;
|
|
||||||
|
|
||||||
let globalPageNumber = 1;
|
const drawFullHeader = (doc) => {
|
||||||
|
|
||||||
// FUNGSI HEADER LENGKAP - Untuk Halaman Pertama
|
|
||||||
const drawFullHeader = (doc, date) => {
|
|
||||||
doc.setLineWidth(0.5);
|
doc.setLineWidth(0.5);
|
||||||
doc.line(marginLeft, 10, marginLeft + tableWidth, 10);
|
doc.line(marginLeft, 10, marginLeft + tableWidth, 10);
|
||||||
doc.line(marginLeft, 10, marginLeft, 50);
|
doc.line(marginLeft, 10, marginLeft, 50);
|
||||||
doc.line(marginLeft + tableWidth, 10, marginLeft + tableWidth, 50);
|
doc.line(marginLeft + tableWidth, 10, marginLeft + tableWidth, 50);
|
||||||
|
|
||||||
const col1Width = HEADER_LEFT_COLUMN_WIDTH; // Gunakan konstanta HEADER
|
const col1Width = HEADER_LEFT_COLUMN_WIDTH;
|
||||||
const col3Width = tableWidth * 0.20;
|
const col3Width = tableWidth * 0.20;
|
||||||
const col2Width = tableWidth - col1Width - col3Width;
|
const col2Width = tableWidth - col1Width - col3Width;
|
||||||
|
|
||||||
@@ -391,194 +390,199 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
doc.addImage(logo2, 'PNG', logoX, logoY, logoWidth, logoHeight);
|
doc.addImage(logo2, 'PNG', logoX, logoY, logoWidth, logoHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.line(marginLeft + tableWidth - col3Width, 30, marginLeft + tableWidth - col3Width, 50);
|
|
||||||
|
|
||||||
const formattedDate = dayjs(date).format('DD-MM-YYYY');
|
|
||||||
|
|
||||||
doc.setFontSize(9);
|
doc.setFontSize(9);
|
||||||
doc.setFont('helvetica', 'bold');
|
|
||||||
doc.text('Laporan Periode :', marginLeft + tableWidth - col3Width / 2, 35, { align: 'center' });
|
|
||||||
doc.setFont('helvetica', 'normal');
|
|
||||||
doc.text(formattedDate, marginLeft + tableWidth - col3Width / 2, 40, { align: 'center' });
|
|
||||||
|
|
||||||
// Hitung waktu akhir berdasarkan periode
|
|
||||||
const minutesInDay = 24 * 60;
|
|
||||||
const lastTimeMinutes = Math.floor((minutesInDay - periode) / periode) * periode;
|
|
||||||
const lastHour = Math.floor(lastTimeMinutes / 60);
|
|
||||||
const lastMinute = lastTimeMinutes % 60;
|
|
||||||
const endTime = `${String(lastHour).padStart(2, '0')}:${String(lastMinute).padStart(2, '0')}`;
|
|
||||||
|
|
||||||
doc.setFontSize(9);
|
|
||||||
doc.text(`00:00 - ${endTime}`, marginLeft + tableWidth - col3Width / 2, 45, { align: 'center' });
|
|
||||||
|
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 41, { align: 'center' });
|
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 41, { align: 'center' });
|
||||||
};
|
};
|
||||||
|
|
||||||
// FUNGSI HEADER SEDERHANA - Untuk Halaman Selanjutnya
|
// Hitung total kolom tag chunks
|
||||||
const drawSimpleHeader = (doc, date) => {
|
const totalTagColumns = sortedTags.length;
|
||||||
const formattedDate = dayjs(date).format('DD-MM-YYYY');
|
const totalTagChunks = Math.ceil(totalTagColumns / MAX_TAG_COLUMNS_PER_PAGE);
|
||||||
|
|
||||||
// Gunakan struktur kolom yang SAMA seperti header lengkap
|
// PERBAIKAN: Variabel untuk tracking total halaman yang sebenarnya
|
||||||
const col1Width = HEADER_LEFT_COLUMN_WIDTH; // Gunakan konstanta HEADER
|
let actualTotalPages = 0;
|
||||||
const col3Width = tableWidth * 0.20;
|
const pageInfoArray = []; // Array untuk menyimpan info setiap page
|
||||||
const col2Width = tableWidth - col1Width - col3Width;
|
|
||||||
|
|
||||||
// Border luar
|
// Loop pertama: hitung dulu total halaman yang akan dibuat
|
||||||
doc.setLineWidth(0.5);
|
for (let pageChunk = 0; pageChunk < totalTagChunks; pageChunk++) {
|
||||||
doc.line(marginLeft, 10, marginLeft + tableWidth, 10);
|
const startTagIndex = pageChunk * MAX_TAG_COLUMNS_PER_PAGE;
|
||||||
doc.line(marginLeft, 10, marginLeft, 30);
|
const endTagIndex = Math.min(startTagIndex + MAX_TAG_COLUMNS_PER_PAGE, totalTagColumns);
|
||||||
doc.line(marginLeft + tableWidth, 10, marginLeft + tableWidth, 30);
|
const pageTagColumns = sortedTags.slice(startTagIndex, endTagIndex);
|
||||||
doc.line(marginLeft, 30, marginLeft + tableWidth, 30);
|
const isFirstPage = (pageChunk === 0);
|
||||||
|
|
||||||
// Garis vertikal pemisah kolom
|
// Simulasi autoTable untuk menghitung jumlah halaman
|
||||||
doc.line(marginLeft + tableWidth - col3Width, 10, marginLeft + tableWidth - col3Width, 30);
|
const tempDoc = new jsPDF({ orientation: 'landscape' });
|
||||||
|
const headerRow = ['Datetime', ...pageTagColumns.map(tag => tagMapping[tag] || tag)];
|
||||||
|
|
||||||
// Kolom Kiri (KOSONG - tidak ada isi)
|
const pdfRows = pivotTableData.map((rowData) => {
|
||||||
|
const row = [dayjs(rowData.datetime).format('DD-MM-YYYY HH:mm')];
|
||||||
// Kolom Tengah - Plant Section
|
pageTagColumns.forEach((tagName) => {
|
||||||
doc.setFontSize(10);
|
const value = rowData[tagName];
|
||||||
doc.setFont('helvetica', 'bold');
|
row.push(value !== undefined && value !== null ? Number(value).toFixed(2) : '-');
|
||||||
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 20, { align: 'center' });
|
});
|
||||||
|
return row;
|
||||||
// Kolom Kanan - Laporan Periode + Time Range
|
|
||||||
doc.setFontSize(9);
|
|
||||||
doc.setFont('helvetica', 'bold');
|
|
||||||
doc.text('Laporan Periode :', marginLeft + tableWidth - col3Width / 2, 14, { align: 'center' });
|
|
||||||
doc.setFont('helvetica', 'normal');
|
|
||||||
doc.text(formattedDate, marginLeft + tableWidth - col3Width / 2, 19, { align: 'center' });
|
|
||||||
|
|
||||||
// Hitung waktu akhir berdasarkan periode
|
|
||||||
const minutesInDay = 24 * 60;
|
|
||||||
const lastTimeMinutes = Math.floor((minutesInDay - periode) / periode) * periode;
|
|
||||||
const lastHour = Math.floor(lastTimeMinutes / 60);
|
|
||||||
const lastMinute = lastTimeMinutes % 60;
|
|
||||||
const endTime = `${String(lastHour).padStart(2, '0')}:${String(lastMinute).padStart(2, '0')}`;
|
|
||||||
|
|
||||||
doc.text(`00:00 - ${endTime}`, marginLeft + tableWidth - col3Width / 2, 24, { align: 'center' });
|
|
||||||
};
|
|
||||||
|
|
||||||
sortedDates.forEach((date, dateIndex) => {
|
|
||||||
const dateData = dataByDate[date];
|
|
||||||
|
|
||||||
// Ambil semua waktu yang sudah di-generate lengkap
|
|
||||||
const allTimes = dateData[Object.keys(dateData)[0]].map(item => item.x);
|
|
||||||
const formattedTimes = allTimes.map(time => dayjs(time).format('HH:mm'));
|
|
||||||
|
|
||||||
const sortedTags = Object.keys(dateData).sort();
|
|
||||||
|
|
||||||
const totalTimeColumns = formattedTimes.length;
|
|
||||||
const totalPages = Math.ceil(totalTimeColumns / MAX_TIME_COLUMNS_PER_PAGE);
|
|
||||||
|
|
||||||
let totalPagesCount = 0;
|
|
||||||
sortedDates.forEach((date) => {
|
|
||||||
const dateData = dataByDate[date];
|
|
||||||
const totalTimeColumns = dateData[Object.keys(dateData)[0]].length;
|
|
||||||
const pagesForThisDate = Math.ceil(totalTimeColumns / MAX_TIME_COLUMNS_PER_PAGE);
|
|
||||||
totalPagesCount += pagesForThisDate;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let pageChunk = 0; pageChunk < totalPages; pageChunk++) {
|
const availableWidthForTags = tableWidth - DATETIME_COLUMN_WIDTH;
|
||||||
if (dateIndex > 0 || pageChunk > 0) {
|
const TAG_COLUMN_WIDTH = availableWidthForTags / pageTagColumns.length;
|
||||||
doc.addPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
const startColIndex = pageChunk * MAX_TIME_COLUMNS_PER_PAGE;
|
const tagColumnStyles = {};
|
||||||
const endColIndex = Math.min(startColIndex + MAX_TIME_COLUMNS_PER_PAGE, totalTimeColumns);
|
for (let i = 0; i < pageTagColumns.length; i++) {
|
||||||
const pageTimeColumns = formattedTimes.slice(startColIndex, endColIndex);
|
tagColumnStyles[i + 1] = {
|
||||||
const pageRawTimes = allTimes.slice(startColIndex, endColIndex);
|
cellWidth: TAG_COLUMN_WIDTH,
|
||||||
|
halign: 'center'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const isFirstPage = (dateIndex === 0 && pageChunk === 0);
|
let pagesForThisChunk = 0;
|
||||||
|
|
||||||
// Gunakan header yang sesuai
|
autoTable(tempDoc, {
|
||||||
if (isFirstPage) {
|
head: [headerRow],
|
||||||
drawFullHeader(doc, date);
|
body: pdfRows,
|
||||||
} else {
|
startY: isFirstPage ? 50 : 15,
|
||||||
drawSimpleHeader(doc, date);
|
theme: 'grid',
|
||||||
}
|
rowPageBreak: 'avoid',
|
||||||
|
styles: {
|
||||||
const availableWidthForTime = tableWidth - IO_TAG_COLUMN_WIDTH;
|
fontSize: 7,
|
||||||
const TIME_COLUMN_WIDTH = availableWidthForTime / pageTimeColumns.length;
|
cellPadding: 1.5,
|
||||||
|
minCellHeight: 8,
|
||||||
const headerRow = ['IO Soft Tag', ...pageTimeColumns];
|
lineColor: [0, 0, 0],
|
||||||
|
lineWidth: 0.1,
|
||||||
const pdfRows = sortedTags.map(tagName => {
|
halign: 'center',
|
||||||
const tagIdentifier = tagMapping[tagName] || tagName;
|
valign: 'middle',
|
||||||
const row = [tagIdentifier];
|
overflow: 'linebreak',
|
||||||
|
},
|
||||||
const tagData = dateData[tagName];
|
headStyles: {
|
||||||
|
fillColor: [220, 220, 220],
|
||||||
for (let i = startColIndex; i < endColIndex; i++) {
|
textColor: [0, 0, 0],
|
||||||
const dataPoint = tagData[i];
|
fontStyle: 'bold',
|
||||||
const val = dataPoint && dataPoint.y;
|
halign: 'center',
|
||||||
row.push(val !== undefined && val !== null ? Number(val).toFixed(2) : '-');
|
valign: 'middle',
|
||||||
}
|
lineColor: [0, 0, 0],
|
||||||
|
lineWidth: 0.3,
|
||||||
return row;
|
},
|
||||||
});
|
columnStyles: {
|
||||||
|
0: {
|
||||||
const timeColumnStyles = {};
|
cellWidth: DATETIME_COLUMN_WIDTH,
|
||||||
for (let i = 0; i < pageTimeColumns.length; i++) {
|
|
||||||
timeColumnStyles[i + 1] = {
|
|
||||||
cellWidth: TIME_COLUMN_WIDTH,
|
|
||||||
minCellWidht: 10,
|
|
||||||
halign: 'center'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
autoTable(doc, {
|
|
||||||
head: [headerRow],
|
|
||||||
body: pdfRows,
|
|
||||||
startY: isFirstPage ? 50 : 30, // Sesuaikan startY untuk header sederhana (sama tinggi dengan header lengkap)
|
|
||||||
theme: 'grid',
|
|
||||||
styles: {
|
|
||||||
fontSize: 5,
|
|
||||||
cellPadding: 1,
|
|
||||||
minCellHeight: 10,
|
|
||||||
lineColor: [0, 0, 0],
|
|
||||||
lineWidth: 0.1,
|
|
||||||
halign: 'center',
|
|
||||||
valign: 'middle',
|
|
||||||
overflow: 'linebreak',
|
|
||||||
},
|
|
||||||
headStyles: {
|
|
||||||
fillColor: [255, 255, 255],
|
|
||||||
textColor: [0, 0, 0],
|
|
||||||
fontStyle: 'bold',
|
fontStyle: 'bold',
|
||||||
halign: 'center',
|
halign: 'center',
|
||||||
valign: 'middle',
|
valign: 'middle'
|
||||||
lineColor: [0, 0, 0],
|
|
||||||
lineWidth: 0.3,
|
|
||||||
},
|
},
|
||||||
columnStyles: {
|
...tagColumnStyles
|
||||||
0: {
|
},
|
||||||
cellWidth: IO_TAG_COLUMN_WIDTH,
|
margin: { left: marginLeft, right: marginRight, top: 15 },
|
||||||
fontStyle: 'bold',
|
tableWidth: tableWidth,
|
||||||
halign: 'center',
|
pageBreak: 'auto',
|
||||||
valign: 'middle'
|
didDrawPage: () => {
|
||||||
},
|
pagesForThisChunk++;
|
||||||
...timeColumnStyles
|
}
|
||||||
},
|
});
|
||||||
margin: { left: marginLeft, right: marginRight },
|
|
||||||
tableWidth: tableWidth,
|
|
||||||
pageBreak: 'auto',
|
|
||||||
didDrawPage: (data) => {
|
|
||||||
doc.setFontSize(8);
|
|
||||||
doc.setFont('helvetica', 'normal');
|
|
||||||
doc.text(
|
|
||||||
`Page ${globalPageNumber} of ${totalPagesCount}`,
|
|
||||||
doc.internal.pageSize.width / 2,
|
|
||||||
doc.internal.pageSize.height - 10,
|
|
||||||
{ align: 'center' }
|
|
||||||
);
|
|
||||||
globalPageNumber++;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.save(`Report_${startDate.format('DD-MM-YYYY')}_to_${endDate.format('DD-MM-YYYY')}_ByDate.pdf`);
|
pageInfoArray.push({
|
||||||
|
chunkIndex: pageChunk,
|
||||||
|
pagesCount: pagesForThisChunk,
|
||||||
|
startPage: actualTotalPages + 1
|
||||||
|
});
|
||||||
|
|
||||||
|
actualTotalPages += pagesForThisChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Total pages akan dibuat:', actualTotalPages);
|
||||||
|
|
||||||
|
// Loop kedua: buat PDF yang sebenarnya dengan nomor halaman yang benar
|
||||||
|
let globalPageNumber = 1;
|
||||||
|
|
||||||
|
for (let pageChunk = 0; pageChunk < totalTagChunks; pageChunk++) {
|
||||||
|
if (pageChunk > 0) {
|
||||||
|
doc.addPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTagIndex = pageChunk * MAX_TAG_COLUMNS_PER_PAGE;
|
||||||
|
const endTagIndex = Math.min(startTagIndex + MAX_TAG_COLUMNS_PER_PAGE, totalTagColumns);
|
||||||
|
const pageTagColumns = sortedTags.slice(startTagIndex, endTagIndex);
|
||||||
|
const isFirstPage = (pageChunk === 0);
|
||||||
|
|
||||||
|
if (isFirstPage) {
|
||||||
|
drawFullHeader(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerRow = ['Datetime', ...pageTagColumns.map(tag => tagMapping[tag] || tag)];
|
||||||
|
|
||||||
|
const pdfRows = pivotTableData.map((rowData) => {
|
||||||
|
const row = [dayjs(rowData.datetime).format('DD-MM-YYYY HH:mm')];
|
||||||
|
|
||||||
|
pageTagColumns.forEach((tagName) => {
|
||||||
|
const value = rowData[tagName];
|
||||||
|
row.push(value !== undefined && value !== null ? Number(value).toFixed(2) : '-');
|
||||||
|
});
|
||||||
|
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableWidthForTags = tableWidth - DATETIME_COLUMN_WIDTH;
|
||||||
|
const TAG_COLUMN_WIDTH = availableWidthForTags / pageTagColumns.length;
|
||||||
|
|
||||||
|
const tagColumnStyles = {};
|
||||||
|
for (let i = 0; i < pageTagColumns.length; i++) {
|
||||||
|
tagColumnStyles[i + 1] = {
|
||||||
|
cellWidth: TAG_COLUMN_WIDTH,
|
||||||
|
halign: 'center'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
autoTable(doc, {
|
||||||
|
head: [headerRow],
|
||||||
|
body: pdfRows,
|
||||||
|
startY: isFirstPage ? 50 : 15,
|
||||||
|
theme: 'grid',
|
||||||
|
rowPageBreak: 'avoid',
|
||||||
|
styles: {
|
||||||
|
fontSize: 7,
|
||||||
|
cellPadding: 1.5,
|
||||||
|
minCellHeight: 8,
|
||||||
|
lineColor: [0, 0, 0],
|
||||||
|
lineWidth: 0.1,
|
||||||
|
halign: 'center',
|
||||||
|
valign: 'middle',
|
||||||
|
overflow: 'linebreak',
|
||||||
|
},
|
||||||
|
headStyles: {
|
||||||
|
fillColor: [220, 220, 220],
|
||||||
|
textColor: [0, 0, 0],
|
||||||
|
fontStyle: 'bold',
|
||||||
|
halign: 'center',
|
||||||
|
valign: 'middle',
|
||||||
|
lineColor: [0, 0, 0],
|
||||||
|
lineWidth: 0.3,
|
||||||
|
},
|
||||||
|
columnStyles: {
|
||||||
|
0: {
|
||||||
|
cellWidth: DATETIME_COLUMN_WIDTH,
|
||||||
|
fontStyle: 'bold',
|
||||||
|
halign: 'center',
|
||||||
|
valign: 'middle'
|
||||||
|
},
|
||||||
|
...tagColumnStyles
|
||||||
|
},
|
||||||
|
margin: { left: marginLeft, right: marginRight, top: 15 },
|
||||||
|
tableWidth: tableWidth,
|
||||||
|
pageBreak: 'auto',
|
||||||
|
didDrawPage: (data) => {
|
||||||
|
doc.setFontSize(8);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(
|
||||||
|
`Page ${globalPageNumber} of ${actualTotalPages}`,
|
||||||
|
doc.internal.pageSize.width / 2,
|
||||||
|
doc.internal.pageSize.height - 10,
|
||||||
|
{ align: 'center' }
|
||||||
|
);
|
||||||
|
globalPageNumber++;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.save(`Report_Pivot_${startDate.format('DD-MM-YYYY')}_to_${endDate.format('DD-MM-YYYY')}.pdf`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -707,20 +711,23 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} style={{ marginTop: '16px' }}>
|
<Col xs={24} style={{ marginTop: '16px' }}>
|
||||||
<Spin spinning={isLoadingTable}>
|
<Spin spinning={isLoadingTable}>
|
||||||
<Table
|
<div style={{ overflowX: 'auto', width: '100%' }}>
|
||||||
columns={columns}
|
<Table
|
||||||
dataSource={tableData}
|
columns={columns}
|
||||||
pagination={{
|
dataSource={tableData}
|
||||||
...pagination,
|
pagination={{
|
||||||
showSizeChanger: true,
|
...pagination,
|
||||||
showTotal: (total) => `Total ${total} data`,
|
showSizeChanger: true,
|
||||||
pageSizeOptions: ['10', '20', '50', '100'],
|
showTotal: (total) => `Total ${total} data`,
|
||||||
}}
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
onChange={handleTableChange}
|
}}
|
||||||
scroll={{ x: 'max-content', y: 500 }}
|
onChange={handleTableChange}
|
||||||
bordered
|
scroll={{ x: 'max-content', y: 500 }}
|
||||||
size="small"
|
bordered
|
||||||
/>
|
size="small"
|
||||||
|
sticky
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Spin>
|
</Spin>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
Reference in New Issue
Block a user