Compare commits

...

6 Commits

6 changed files with 101 additions and 35 deletions

View File

@@ -1,8 +1,8 @@
import './Download.styles.scss';
import { CloudDownloadOutlined } from '@ant-design/icons';
import { Button, Dropdown, MenuProps } from 'antd';
import { Button, Dropdown, MenuProps, Tooltip } from 'antd';
import { Excel } from 'antd-table-saveas-excel';
import { DownloadIcon } from 'lucide-react';
import { unparse } from 'papaparse';
import { DownloadProps } from './Download.types';
@@ -56,17 +56,18 @@ function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element {
};
return (
<Dropdown menu={menu} trigger={['click']}>
<Button
className="download-button"
loading={isLoading}
size="small"
type="link"
>
<CloudDownloadOutlined />
Download
</Button>
</Dropdown>
<Tooltip title="Download" placement="top">
<Dropdown menu={menu} trigger={['click']}>
<Button
className="download-button"
loading={isLoading}
size="small"
type="link"
>
<DownloadIcon size={20} />
</Button>
</Dropdown>
</Tooltip>
);
}

View File

@@ -33,6 +33,10 @@ function LogsExplorerTable({
loading={isLoading}
rootClassName="logs-table"
sticky
downloadOption={{
isDownloadEnabled: true,
fileName: 'logs-table-export',
}}
/>
);
}

View File

@@ -1,9 +1,12 @@
import './QueryTable.styles.scss';
import type { TablePaginationConfig } from 'antd/es/table';
import cx from 'classnames';
import { ResizeTable } from 'components/ResizeTable';
import Download from 'container/Download/Download';
import { IServiceName } from 'container/MetricsApplication/Tabs/types';
import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination';
import { getDefaultPaginationConfig } from 'hooks/queryPagination/utils';
import {
createTableColumnsFromQuery,
RowData,
@@ -14,7 +17,10 @@ import { useParams } from 'react-router-dom';
import useTableContextMenu from './Drilldown/useTableContextMenu';
import { QueryTableProps } from './QueryTable.intefaces';
import { createDownloadableData } from './utils';
import { createDownloadableData, getFormattedTimestamp } from './utils';
// I saw this done in other places
const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS];
export function QueryTable({
queryTableData,
@@ -130,10 +136,20 @@ export function QueryTable({
[tableColumns, isQueryTypeBuilder, enableDrillDown, handleColumnClick],
);
const [pageSize, setPageSize] = useState(
getDefaultPaginationConfig(PER_PAGE_OPTIONS).limit,
);
const paginationConfig = {
pageSize: 10,
showSizeChanger: false,
hideOnSinglePage: true,
pageSize,
showSizeChanger: true,
pageSizeOptions: PER_PAGE_OPTIONS,
hideOnSinglePage: false,
position: ['topRight'] as TablePaginationConfig['position'],
onChange: (_page: number, newPageSize: number): void => {
if (newPageSize !== pageSize) {
setPageSize(newPageSize);
}
},
};
const [filterTable, setFilterTable] = useState<RowData[] | null>(null);
@@ -159,16 +175,16 @@ export function QueryTable({
return (
<>
{isDownloadEnabled && (
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Download
data={downloadableData}
fileName={`${fileName}-${servicename}-${getFormattedTimestamp()}`}
isLoading={loading as boolean}
/>
</div>
)}
<div className="query-table">
{isDownloadEnabled && (
<div className="query-table--download">
<Download
data={downloadableData}
fileName={`${fileName}-${servicename}`}
isLoading={loading as boolean}
/>
</div>
)}
<ResizeTable
columns={columnsWithClickHandlers}
tableLayout="fixed"

View File

@@ -1,14 +1,55 @@
import { RowData } from 'lib/query/createTableColumnsFromQuery';
/**
* Strips Ant table key and converts all values to String for CSV/Excel export.
*/
export function createDownloadableData(
inputData: RowData[],
): Record<string, string>[] {
return inputData.map((row) => ({
Name: String(row.operation || ''),
'P50 (in ns)': String(row.A || ''),
'P90 (in ns)': String(row.B || ''),
'P99 (in ns)': String(row.C || ''),
'Number Of Calls': String(row.F || ''),
'Error Rate (%)': String(row.F1 && row.F1 !== 'N/A' ? row.F1 : '0'),
}));
if (!inputData || inputData.length === 0) {
return [];
}
// Get all keys from the first row since it's a table
const allKeys = new Set<string>();
Object.keys(inputData[0]).forEach((key) => {
// Exclude internal keys used by Ant table
if (key !== 'key') {
allKeys.add(key);
}
});
return inputData.map((row) => {
const downloadableRow: Record<string, string> = {};
allKeys.forEach((key) => {
const value = row[key];
// TODO : Possible change to format and normalize headers
const formattedKey = key;
// Handle null and undefined
if (value === null || value === undefined) {
downloadableRow[formattedKey] = '';
// Handle objects/arrays by stringifying
} else if (typeof value === 'object') {
downloadableRow[formattedKey] = JSON.stringify(value);
// Else make sure it's a string
} else {
downloadableRow[formattedKey] = String(value);
}
});
return downloadableRow;
});
}
export function getFormattedTimestamp(): string {
const now = new Date();
const pad = (n: number): string => n.toString().padStart(2, '0');
return `${now.getFullYear()}_${pad(now.getMonth() + 1)}_${pad(
now.getDate(),
)}_${pad(now.getHours())}_${pad(now.getMinutes())}_${pad(now.getSeconds())}`;
}

View File

@@ -83,6 +83,10 @@ function TableView({
queryTableData={queryTableData as QueryDataV3[]}
loading={isLoading}
sticky
downloadOption={{
isDownloadEnabled: true,
fileName: 'traces-table-export',
}}
/>
)}
</Space.Compact>

View File

@@ -1 +1 @@
export const DEFAULT_PER_PAGE_OPTIONS: number[] = [25, 50, 100, 200];
export const DEFAULT_PER_PAGE_OPTIONS: number[] = [25, 50, 100, 200, 500, 1000];