update menu report

This commit is contained in:
Athif
2025-12-18 15:03:39 +07:00
parent dc78add71d
commit 6b75f6f4b9

View File

@@ -16,9 +16,10 @@ const ListReport = memo(function ListReport(props) {
const dateNow = dayjs();
const dateNowFormated = dateNow.format('YYYY-MM-DD');
const [isLoadingModal, setIsLoadingModal] = useState(false); // Modal loading
const [isLoadingTable, setIsLoadingTable] = useState(false); // Table loading
const [isLoadingModal, setIsLoadingModal] = useState(false);
const [isLoadingTable, setIsLoadingTable] = useState(false);
const [tableData, setTableData] = useState([]);
const [columns, setColumns] = useState([]);
const [pivotData, setPivotData] = useState([]);
const [valueReportData, setValueReportData] = useState([]);
const [pagination, setPagination] = useState({
@@ -33,46 +34,6 @@ const ListReport = memo(function ListReport(props) {
const [endDate, setEndDate] = useState(dateNow);
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 times = [];
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'));
currentTime = currentTime.add(intervalMinutes, 'minute');
// Jika waktu berikutnya melebihi akhir hari, break
if (currentTime.isAfter(endOfDay)) {
break;
}
@@ -99,9 +59,9 @@ const ListReport = memo(function ListReport(props) {
}
if (showModal) {
setIsLoadingModal(true); // Modal untuk fetch pertama
setIsLoadingModal(true);
} else {
setIsLoadingTable(true); // Spin untuk pagination
setIsLoadingTable(true);
}
try {
const formattedDateStart = startDate.format('YYYY-MM-DD');
@@ -121,8 +81,6 @@ const ListReport = memo(function ListReport(props) {
if (pivotResponse && pivotResponse.data) {
console.log('API Pivot Response:', pivotResponse);
console.log('First row pivot data:', pivotResponse.data[0]);
setPivotData(pivotResponse.data);
if (valueReportResponse && valueReportResponse.data) {
@@ -130,42 +88,90 @@ const ListReport = memo(function ListReport(props) {
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) => {
const tagName = row.id;
const dataPoints = row.data || [];
tagSet.add(tagName);
const dataPoints = row.data || [];
dataPoints.forEach((item) => {
if (item && typeof item === 'object' && 'x' in item && 'y' in item) {
unpivotedData.push({
datetime: item.x,
tagName: tagName,
value: item.y,
});
const datetime = item.x;
if (!timeMap.has(datetime)) {
timeMap.set(datetime, {});
}
timeMap.get(datetime)[tagName] = item.y;
}
});
});
console.log('Unpivoted data sample:', unpivotedData.slice(0, 10));
console.log('Total unpivoted rows:', unpivotedData.length);
// Konversi ke array dan sort berdasarkan waktu
const sortedTimes = Array.from(timeMap.keys()).sort();
const sortedTags = Array.from(tagSet).sort();
unpivotedData.sort((a, b) => {
if (a.tagName !== b.tagName) {
return a.tagName.localeCompare(b.tagName);
}
return new Date(a.datetime) - new Date(b.datetime);
// Buat data untuk table
const pivotTableData = sortedTimes.map((datetime, index) => {
const rowData = {
key: index,
datetime: datetime,
};
sortedTags.forEach((tagName) => {
rowData[tagName] = timeMap.get(datetime)[tagName];
});
return rowData;
});
const transformedData = unpivotedData.map((item, index) => ({
key: index,
...item,
}));
console.log('Pivot table data sample:', pivotTableData.slice(0, 5));
console.log('Total pivot rows:', pivotTableData.length);
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 endIndex = startIndex + pageSize;
const paginatedData = transformedData.slice(startIndex, endIndex);
const paginatedData = pivotTableData.slice(startIndex, endIndex);
setTableData(paginatedData);
setPagination({
@@ -199,6 +205,7 @@ const ListReport = memo(function ListReport(props) {
setEndDate(dateNow);
setPeriode(30);
setTableData([]);
setColumns([]);
setPivotData([]);
setValueReportData([]);
setPagination({
@@ -256,47 +263,43 @@ const ListReport = memo(function ListReport(props) {
const selectedSection = plantSubSectionList.find(item => item.plant_sub_section_id === plantSubSection);
const sectionName = selectedSection ? selectedSection.plant_sub_section_name : 'Unknown';
// Generate data lengkap untuk setiap tanggal dengan semua interval waktu
const dataByDate = {};
// Buat struktur pivot yang sama seperti di tabel
const timeMap = new Map();
const tagSet = new Set();
// Pertama, kumpulkan semua tanggal yang ada
const allDates = new Set();
pivotData.forEach(series => {
series.data.forEach(item => {
const date = dayjs(item.x).format('YYYY-MM-DD');
allDates.add(date);
});
});
pivotData.forEach((row) => {
const tagName = row.id;
tagSet.add(tagName);
// Untuk setiap tanggal, generate semua waktu dan isi dengan data yang ada
Array.from(allDates).forEach(date => {
const fullDayTimes = generateFullDayTimes(date, periode);
dataByDate[date] = {};
// Initialize dengan semua waktu
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;
}
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 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) => {
return new Promise((resolve, reject) => {
@@ -322,21 +325,17 @@ const ListReport = memo(function ListReport(props) {
const marginRight = 10;
const tableWidth = pageWidth - marginLeft - marginRight;
// Konstanta untuk mengatur lebar kolom
const IO_TAG_COLUMN_WIDTH = 17; // Lebar kolom IO Soft Tag di TABEL (mm)
const HEADER_LEFT_COLUMN_WIDTH = 40; // Lebar kolom KIRI di HEADER (untuk logo1) (mm)
const MAX_TIME_COLUMNS_PER_PAGE = 24;
const DATETIME_COLUMN_WIDTH = 25;
const HEADER_LEFT_COLUMN_WIDTH = 40;
const MAX_TAG_COLUMNS_PER_PAGE = 15;
let globalPageNumber = 1;
// FUNGSI HEADER LENGKAP - Untuk Halaman Pertama
const drawFullHeader = (doc, date) => {
const drawFullHeader = (doc) => {
doc.setLineWidth(0.5);
doc.line(marginLeft, 10, marginLeft + tableWidth, 10);
doc.line(marginLeft, 10, marginLeft, 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 col2Width = tableWidth - col1Width - col3Width;
@@ -391,194 +390,199 @@ const ListReport = memo(function ListReport(props) {
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.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.setFontSize(10);
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 41, { align: 'center' });
};
// FUNGSI HEADER SEDERHANA - Untuk Halaman Selanjutnya
const drawSimpleHeader = (doc, date) => {
const formattedDate = dayjs(date).format('DD-MM-YYYY');
// Hitung total kolom tag chunks
const totalTagColumns = sortedTags.length;
const totalTagChunks = Math.ceil(totalTagColumns / MAX_TAG_COLUMNS_PER_PAGE);
// Gunakan struktur kolom yang SAMA seperti header lengkap
const col1Width = HEADER_LEFT_COLUMN_WIDTH; // Gunakan konstanta HEADER
const col3Width = tableWidth * 0.20;
const col2Width = tableWidth - col1Width - col3Width;
// PERBAIKAN: Variabel untuk tracking total halaman yang sebenarnya
let actualTotalPages = 0;
const pageInfoArray = []; // Array untuk menyimpan info setiap page
// Border luar
doc.setLineWidth(0.5);
doc.line(marginLeft, 10, marginLeft + tableWidth, 10);
doc.line(marginLeft, 10, marginLeft, 30);
doc.line(marginLeft + tableWidth, 10, marginLeft + tableWidth, 30);
doc.line(marginLeft, 30, marginLeft + tableWidth, 30);
// Loop pertama: hitung dulu total halaman yang akan dibuat
for (let pageChunk = 0; pageChunk < totalTagChunks; pageChunk++) {
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);
// Garis vertikal pemisah kolom
doc.line(marginLeft + tableWidth - col3Width, 10, marginLeft + tableWidth - col3Width, 30);
// Simulasi autoTable untuk menghitung jumlah halaman
const tempDoc = new jsPDF({ orientation: 'landscape' });
const headerRow = ['Datetime', ...pageTagColumns.map(tag => tagMapping[tag] || tag)];
// Kolom Kiri (KOSONG - tidak ada isi)
// Kolom Tengah - Plant Section
doc.setFontSize(10);
doc.setFont('helvetica', 'bold');
doc.text(`Plant Section : ${sectionName}`, marginLeft + col1Width + col2Width / 2, 20, { align: 'center' });
// 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;
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;
});
for (let pageChunk = 0; pageChunk < totalPages; pageChunk++) {
if (dateIndex > 0 || pageChunk > 0) {
doc.addPage();
}
const availableWidthForTags = tableWidth - DATETIME_COLUMN_WIDTH;
const TAG_COLUMN_WIDTH = availableWidthForTags / pageTagColumns.length;
const startColIndex = pageChunk * MAX_TIME_COLUMNS_PER_PAGE;
const endColIndex = Math.min(startColIndex + MAX_TIME_COLUMNS_PER_PAGE, totalTimeColumns);
const pageTimeColumns = formattedTimes.slice(startColIndex, endColIndex);
const pageRawTimes = allTimes.slice(startColIndex, endColIndex);
const tagColumnStyles = {};
for (let i = 0; i < pageTagColumns.length; i++) {
tagColumnStyles[i + 1] = {
cellWidth: TAG_COLUMN_WIDTH,
halign: 'center'
};
}
const isFirstPage = (dateIndex === 0 && pageChunk === 0);
let pagesForThisChunk = 0;
// Gunakan header yang sesuai
if (isFirstPage) {
drawFullHeader(doc, date);
} else {
drawSimpleHeader(doc, date);
}
const availableWidthForTime = tableWidth - IO_TAG_COLUMN_WIDTH;
const TIME_COLUMN_WIDTH = availableWidthForTime / pageTimeColumns.length;
const headerRow = ['IO Soft Tag', ...pageTimeColumns];
const pdfRows = sortedTags.map(tagName => {
const tagIdentifier = tagMapping[tagName] || tagName;
const row = [tagIdentifier];
const tagData = dateData[tagName];
for (let i = startColIndex; i < endColIndex; i++) {
const dataPoint = tagData[i];
const val = dataPoint && dataPoint.y;
row.push(val !== undefined && val !== null ? Number(val).toFixed(2) : '-');
}
return row;
});
const timeColumnStyles = {};
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],
autoTable(tempDoc, {
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',
lineColor: [0, 0, 0],
lineWidth: 0.3,
valign: 'middle'
},
columnStyles: {
0: {
cellWidth: IO_TAG_COLUMN_WIDTH,
fontStyle: 'bold',
halign: 'center',
valign: 'middle'
},
...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++;
},
});
}
});
...tagColumnStyles
},
margin: { left: marginLeft, right: marginRight, top: 15 },
tableWidth: tableWidth,
pageBreak: 'auto',
didDrawPage: () => {
pagesForThisChunk++;
}
});
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 (
@@ -707,20 +711,23 @@ const ListReport = memo(function ListReport(props) {
</Col>
<Col xs={24} style={{ marginTop: '16px' }}>
<Spin spinning={isLoadingTable}>
<Table
columns={columns}
dataSource={tableData}
pagination={{
...pagination,
showSizeChanger: true,
showTotal: (total) => `Total ${total} data`,
pageSizeOptions: ['10', '20', '50', '100'],
}}
onChange={handleTableChange}
scroll={{ x: 'max-content', y: 500 }}
bordered
size="small"
/>
<div style={{ overflowX: 'auto', width: '100%' }}>
<Table
columns={columns}
dataSource={tableData}
pagination={{
...pagination,
showSizeChanger: true,
showTotal: (total) => `Total ${total} data`,
pageSizeOptions: ['10', '20', '50', '100'],
}}
onChange={handleTableChange}
scroll={{ x: 'max-content', y: 500 }}
bordered
size="small"
sticky
/>
</div>
</Spin>
</Col>
</Row>