Skip to content

Commit c55f8c0

Browse files
committed
add an option to save all messages as csv/json, Issue kafbat#688
1 parent b008fa9 commit c55f8c0

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

+102
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { Button } from 'components/common/Button/Button';
77
import * as S from 'components/common/NewTable/Table.styled';
88
import { usePaginateTopics, useIsLiveMode } from 'lib/hooks/useMessagesFilters';
99
import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore';
10+
import useDataSaver from 'lib/hooks/useDataSaver';
11+
import Select from 'components/common/Select/Select';
12+
import { SelectOption } from 'components/common/Select/Select';
1013

1114
import PreviewModal from './PreviewModal';
1215
import Message, { PreviewFilter } from './Message';
@@ -16,6 +19,33 @@ export interface MessagesTableProps {
1619
isFetching: boolean;
1720
}
1821

22+
interface MessageData {
23+
Value: string | undefined;
24+
Offset: number;
25+
Key: string | undefined;
26+
Partition: number;
27+
Headers: { [key: string]: string | undefined; } | undefined;
28+
Timestamp: Date;
29+
}
30+
31+
type DownloadFormat = 'json' | 'csv';
32+
33+
function padCurrentDateTimeString(): string {
34+
const now: Date = new Date();
35+
36+
const year: string = now.getFullYear().toString();
37+
const month: string = (now.getMonth() + 1).toString().padStart(2, '0');
38+
const day: string = now.getDate().toString().padStart(2, '0');
39+
const hours: string = now.getHours().toString().padStart(2, '0');
40+
const minutes: string = now.getMinutes().toString().padStart(2, '0');
41+
const seconds: string = now.getSeconds().toString().padStart(2, '0');
42+
43+
const dateTimeString: string = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
44+
45+
return `_${dateTimeString}`;
46+
}
47+
48+
1949
const MessagesTable: React.FC<MessagesTableProps> = ({
2050
messages,
2151
isFetching,
@@ -28,8 +58,80 @@ const MessagesTable: React.FC<MessagesTableProps> = ({
2858
const nextCursor = useMessageFiltersStore((state) => state.nextCursor);
2959
const isLive = useIsLiveMode();
3060

61+
const [selectedFormat, setSelectedFormat] = useState<DownloadFormat>('json');
62+
63+
const formatOptions: SelectOption<DownloadFormat>[] = [
64+
{ label: 'JSON', value: 'json' },
65+
{ label: 'CSV', value: 'csv' }
66+
];
67+
68+
const handleFormatSelect = (format: DownloadFormat) => {
69+
setSelectedFormat(format);
70+
};
71+
72+
const handleDownload = () => {
73+
74+
const savedMessagesJson: MessageData[] = messages.map(message => ({
75+
Value: message.content,
76+
Offset: message.offset,
77+
Key: message.key,
78+
Partition: message.partition,
79+
Headers: message.headers,
80+
Timestamp: message.timestamp,
81+
}));
82+
83+
const convertToCSV = (messages: MessageData[]) => {
84+
const headers = ['Value', 'Offset', 'Key', 'Partition', 'Headers', 'Timestamp'] as const;
85+
type Header = typeof headers[number];
86+
87+
const rows = messages.map(msg =>
88+
headers.map(header => {
89+
const value = msg[header];
90+
if (header === 'Headers') {
91+
return JSON.stringify(value || {});
92+
}
93+
return String(value ?? '');
94+
}).join(',')
95+
);
96+
97+
return [headers.join(','), ...rows].join('\n');
98+
};
99+
100+
const baseFileName = 'topic-messages'+padCurrentDateTimeString();
101+
const jsonSaver = useDataSaver(baseFileName+'.json', JSON.stringify(savedMessagesJson, null, '\t'));
102+
const csvSaver = useDataSaver(baseFileName+'.csv', convertToCSV(savedMessagesJson));
103+
104+
if (selectedFormat === 'json') {
105+
jsonSaver.saveFile();
106+
} else {
107+
csvSaver.saveFile();
108+
}
109+
};
110+
31111
return (
32112
<div style={{ position: 'relative' }}>
113+
<div style={{ display: 'flex', gap: '8px', marginLeft: '1rem', marginBottom: '1rem' }}>
114+
<Select<DownloadFormat>
115+
id="download-format"
116+
name="download-format"
117+
onChange={handleFormatSelect}
118+
options={formatOptions}
119+
value={selectedFormat}
120+
minWidth="70px"
121+
selectSize="M"
122+
placeholder="Select format to download"
123+
disabled={isFetching || messages.length === 0}
124+
/>
125+
<Button
126+
disabled={isFetching || messages.length === 0}
127+
buttonType="secondary"
128+
buttonSize="M"
129+
onClick={handleDownload}
130+
>
131+
Download All Messages
132+
</Button>
133+
</div>
134+
33135
{previewFor !== null && (
34136
<PreviewModal
35137
values={previewFor === 'key' ? keyFilters : contentFilters}

0 commit comments

Comments
 (0)