feat: implement download logs feature for logs explorer new design (#4728)

* feat: implement download logs feature for logs explorer new design

* feat: address review comments

* feat: added timestamp and body to the start

---------

Co-authored-by: Nityananda Gohain <nityanandagohain@gmail.com>
This commit is contained in:
Vikrant Gupta 2024-03-22 13:28:38 +05:30 committed by GitHub
parent 0df86454ce
commit 0df3c26f04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 207 additions and 4 deletions

View File

@ -27,7 +27,7 @@
line-height: 18px;
letter-spacing: 0.08em;
text-align: left;
color: var(--bg-slate-200, #52575c);
color: #52575c;
}
.menu-items {
@ -65,7 +65,7 @@
padding: 12px;
.title {
color: var(--bg-slate-200, #52575c);
color: #52575c;
font-family: Inter;
font-size: 11px;
font-style: normal;
@ -149,7 +149,7 @@
}
.title {
color: var(--bg-slate-200, #52575c);
color: #52575c;
font-family: Inter;
font-size: 11px;
font-style: normal;

View File

@ -0,0 +1,84 @@
.download-logs-popover {
.ant-popover-inner {
border-radius: 4px;
border: 1px solid var(--bg-slate-400);
background: linear-gradient(
139deg,
rgba(18, 19, 23, 0.8) 0%,
rgba(18, 19, 23, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
padding: 12px 18px 12px 14px;
.download-logs-content {
display: flex;
flex-direction: column;
gap: 8px;
align-items: flex-start;
.action-btns {
padding: 4px 0px !important;
width: 159px;
display: flex;
align-items: center;
color: var(--bg-vanilla-400);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.14px;
gap: 6px;
.ant-btn-icon {
margin-inline-end: 0px;
}
}
.action-btns:hover {
&.ant-btn-text {
background-color: rgba(171, 189, 255, 0.04) !important;
}
}
.export-heading {
color: #52575c;
font-size: 11px;
font-style: normal;
font-weight: 600;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
}
}
}
}
.lightMode {
.download-logs-popover {
.ant-popover-inner {
border: 1px solid var(--bg-vanilla-300);
background: linear-gradient(
139deg,
rgba(255, 255, 255, 0.8) 0%,
rgba(255, 255, 255, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2);
.download-logs-content {
.action-btns {
color: var(--bg-ink-400);
}
.action-btns:hover {
&.ant-btn-text {
background-color: var(--bg-vanilla-300) !important;
}
}
.export-heading {
color: var(--bg-ink-200);
}
}
}
}
}

View File

@ -0,0 +1,84 @@
import './DownloadV2.styles.scss';
import { Button, Popover, Typography } from 'antd';
import { Excel } from 'antd-table-saveas-excel';
import { FileDigit, FileDown, Sheet } from 'lucide-react';
import { unparse } from 'papaparse';
import { DownloadProps } from './DownloadV2.types';
function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element {
const downloadExcelFile = (): void => {
const headers = Object.keys(Object.assign({}, ...data)).map((item) => {
const updatedTitle = item
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
return {
title: updatedTitle,
dataIndex: item,
};
});
const excel = new Excel();
excel
.addSheet(fileName)
.addColumns(headers)
.addDataSource(data, {
str2Percent: true,
})
.saveAs(`${fileName}.xlsx`);
};
const downloadCsvFile = (): void => {
const csv = unparse(data);
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const csvUrl = URL.createObjectURL(csvBlob);
const downloadLink = document.createElement('a');
downloadLink.href = csvUrl;
downloadLink.download = `${fileName}.csv`;
downloadLink.click();
downloadLink.remove();
};
return (
<Popover
trigger={['click']}
placement="bottomRight"
rootClassName="download-logs-popover"
arrow={false}
content={
<div className="download-logs-content">
<Typography.Text className="export-heading">Export As</Typography.Text>
<Button
icon={<Sheet size={14} />}
type="text"
onClick={downloadExcelFile}
className="action-btns"
>
Excel (.xlsx)
</Button>
<Button
icon={<FileDigit size={14} />}
type="text"
onClick={downloadCsvFile}
className="action-btns"
>
CSV
</Button>
</div>
}
>
<Button
className="periscope-btn"
loading={isLoading}
icon={<FileDown size={14} />}
/>
</Popover>
);
}
Download.defaultProps = {
isLoading: undefined,
};
export default Download;

View File

@ -0,0 +1,10 @@
export type DownloadOptions = {
isDownloadEnabled: boolean;
fileName: string;
};
export type DownloadProps = {
data: Record<string, string>[];
isLoading?: boolean;
fileName: string;
};

View File

@ -14,6 +14,7 @@ import {
PANEL_TYPES,
} from 'constants/queryBuilder';
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import Download from 'container/DownloadV2/DownloadV2';
import ExplorerOptions from 'container/ExplorerOptions/ExplorerOptions';
import GoToTop from 'container/GoToTop';
import LogsExplorerChart from 'container/LogsExplorerChart';
@ -21,6 +22,7 @@ import LogsExplorerList from 'container/LogsExplorerList';
import LogsExplorerTable from 'container/LogsExplorerTable';
import { useOptionsMenu } from 'container/OptionsMenu';
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
import dayjs from 'dayjs';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
import { LogTimeRange } from 'hooks/logs/types';
@ -33,8 +35,9 @@ import useClickOutside from 'hooks/useClickOutside';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { FlatLogData } from 'lib/logs/flatLogData';
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
import { defaultTo, isEmpty } from 'lodash-es';
import { defaultTo, isEmpty, omit } from 'lodash-es';
import { Sliders } from 'lucide-react';
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
@ -523,6 +526,23 @@ function LogsExplorerViews({
},
});
const flattenLogData = useMemo(
() =>
logs.map((log) => {
const timestamp =
typeof log.timestamp === 'string'
? dayjs(log.timestamp).format()
: dayjs(log.timestamp / 1e6).format();
return FlatLogData({
timestamp,
body: log.body,
...omit(log, 'timestamp', 'body'),
});
}),
[logs],
);
return (
<div className="logs-explorer-views-container">
{showHistogram && (
@ -578,6 +598,11 @@ function LogsExplorerViews({
<div className="logs-actions-container">
{selectedPanelType === PANEL_TYPES.LIST && (
<div className="tab-options">
<Download
data={flattenLogData}
isLoading={isFetching}
fileName="log_data"
/>
<div className="format-options-container" ref={menuRef}>
<Button
className="periscope-btn"