@@ -7,6 +7,9 @@ import { Button } from 'components/common/Button/Button';
7
7
import * as S from 'components/common/NewTable/Table.styled' ;
8
8
import { usePaginateTopics , useIsLiveMode } from 'lib/hooks/useMessagesFilters' ;
9
9
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' ;
10
13
11
14
import PreviewModal from './PreviewModal' ;
12
15
import Message , { PreviewFilter } from './Message' ;
@@ -16,6 +19,33 @@ export interface MessagesTableProps {
16
19
isFetching : boolean ;
17
20
}
18
21
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
+
19
49
const MessagesTable : React . FC < MessagesTableProps > = ( {
20
50
messages,
21
51
isFetching,
@@ -28,8 +58,80 @@ const MessagesTable: React.FC<MessagesTableProps> = ({
28
58
const nextCursor = useMessageFiltersStore ( ( state ) => state . nextCursor ) ;
29
59
const isLive = useIsLiveMode ( ) ;
30
60
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
+
31
111
return (
32
112
< 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
+
33
135
{ previewFor !== null && (
34
136
< PreviewModal
35
137
values = { previewFor === 'key' ? keyFilters : contentFilters }
0 commit comments