Skip to content
Open
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b008fa9
Merge pull request #1 from kafbat/main
alexanderlz Dec 29, 2024
c55f8c0
add an option to save all messages as csv/json, Issue #688
alexanderlz Dec 30, 2024
252c8a9
linter issues fix
alexanderlz Dec 30, 2024
1aab310
lint fixes
alexanderlz Dec 30, 2024
9817ee5
linter fixes + comments
alexanderlz Jan 5, 2025
628ecf1
forgot missing date-fns in package.json
alexanderlz Jan 5, 2025
b393c2d
forgot missing date-fns in pnpm
alexanderlz Jan 5, 2025
d841a95
linter
alexanderlz Jan 5, 2025
eef6107
linter warnings fix
alexanderlz Jan 5, 2025
cc31f80
linter warnings fix ->> single-quote
alexanderlz Jan 5, 2025
578982d
linter warnings fix (hopefully last)
alexanderlz Jan 5, 2025
0615fc5
Merge branch 'main' into issues/688
alexanderlz Jan 5, 2025
f0f3508
Merge branch 'main' into issues/688
alexanderlz Jan 22, 2025
8ef27ad
Merge branch 'main' into issues/688
alexanderlz Feb 12, 2025
a5f925b
Update MessagesTable.tsx
alexanderlz Feb 12, 2025
bff1aa0
Update MessagesTable.tsx
alexanderlz Feb 12, 2025
f8aa582
redesign the export messages button look and feel
alexanderlz Jul 13, 2025
38abcd0
redesign the export messages button look and feel, linter errors
alexanderlz Jul 13, 2025
66c67ee
redesign the export messages button look and feel, linter errors #2
alexanderlz Jul 13, 2025
297f754
Merge branch 'main' into issues/688
alexanderlz Jul 18, 2025
7819ca6
Update Filters.tsx
alexanderlz Jul 18, 2025
416c57a
Merge branch 'main' into issues/688
germanosin Jul 23, 2025
55a8fa3
replace the icon according to the discussion
alexanderlz Jul 31, 2025
b0c419c
Merge branch 'issues/688' of https://github.com/alexanderlz/kafka-ui …
alexanderlz Jul 31, 2025
72cc645
linter warning
alexanderlz Aug 2, 2025
9ccacb8
svg to separate file, size according to figma
alexanderlz Sep 21, 2025
e32d02e
Update useMessagesFilters to be aligned with merge
alexanderlz Sep 21, 2025
4c00bb7
linter
alexanderlz Sep 21, 2025
1346b6d
linter
alexanderlz Sep 21, 2025
14ff067
Fix spacing in ExportIcon component usage
alexanderlz Sep 21, 2025
8c13134
Merge branch 'main' into issues/688
germanosin Sep 24, 2025
7479f01
Merge branch 'main' into issues/688
Haarolean Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import * as S from 'components/common/NewTable/Table.styled';
import { usePaginateTopics, useIsLiveMode } from 'lib/hooks/useMessagesFilters';
import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore';
import useDataSaver from 'lib/hooks/useDataSaver';
import Select from 'components/common/Select/Select';

Check failure on line 11 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

'components/common/Select/Select' imported multiple times
import { SelectOption } from 'components/common/Select/Select';

Check failure on line 12 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

'components/common/Select/Select' imported multiple times

import PreviewModal from './PreviewModal';
import Message, { PreviewFilter } from './Message';
Expand All @@ -16,6 +19,33 @@
isFetching: boolean;
}

interface MessageData {
Value: string | undefined;
Offset: number;
Key: string | undefined;
Partition: number;
Headers: { [key: string]: string | undefined; } | undefined;

Check warning on line 27 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Delete `;`
Timestamp: Date;
}

type DownloadFormat = 'json' | 'csv';

function padCurrentDateTimeString(): string {
const now: Date = new Date();

const year: string = now.getFullYear().toString();
const month: string = (now.getMonth() + 1).toString().padStart(2, '0');
const day: string = now.getDate().toString().padStart(2, '0');
const hours: string = now.getHours().toString().padStart(2, '0');
const minutes: string = now.getMinutes().toString().padStart(2, '0');
const seconds: string = now.getSeconds().toString().padStart(2, '0');

const dateTimeString: string = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;

return `_${dateTimeString}`;
}


Check warning on line 48 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Delete `⏎`
const MessagesTable: React.FC<MessagesTableProps> = ({
messages,
isFetching,
Expand All @@ -28,8 +58,80 @@
const nextCursor = useMessageFiltersStore((state) => state.nextCursor);
const isLive = useIsLiveMode();

const [selectedFormat, setSelectedFormat] = useState<DownloadFormat>('json');

const formatOptions: SelectOption<DownloadFormat>[] = [
{ label: 'JSON', value: 'json' },
{ label: 'CSV', value: 'csv' }

Check warning on line 65 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Insert `,`
];

const handleFormatSelect = (format: DownloadFormat) => {
setSelectedFormat(format);
};

const handleDownload = () => {

Check warning on line 73 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Replace `⏎··const·savedMessagesJson:·MessageData[]·=·messages.map(message` with `····const·savedMessagesJson:·MessageData[]·=·messages.map((message)`
const savedMessagesJson: MessageData[] = messages.map(message => ({
Value: message.content,

Check warning on line 75 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Replace `····` with `······`
Offset: message.offset,

Check warning on line 76 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Insert `··`
Key: message.key,

Check warning on line 77 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Replace `····` with `······`
Partition: message.partition,

Check warning on line 78 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Insert `··`
Headers: message.headers,

Check warning on line 79 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Replace `····` with `······`
Timestamp: message.timestamp,

Check warning on line 80 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Insert `··`
}));

const convertToCSV = (messages: MessageData[]) => {

Check failure on line 83 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

'messages' is already declared in the upper scope on line 50 column 3
const headers = ['Value', 'Offset', 'Key', 'Partition', 'Headers', 'Timestamp'] as const;
type Header = typeof headers[number];

Check failure on line 85 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

'Header' is defined but never used

Check failure on line 85 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

'Header' is defined but never used

const rows = messages.map(msg =>
headers.map(header => {
const value = msg[header];
if (header === 'Headers') {
return JSON.stringify(value || {});
}
return String(value ?? '');
}).join(',')
);

return [headers.join(','), ...rows].join('\n');
};

const baseFileName = 'topic-messages'+padCurrentDateTimeString();

Check failure on line 100 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Unexpected string concatenation
const jsonSaver = useDataSaver(baseFileName+'.json', JSON.stringify(savedMessagesJson, null, '\t'));

Check failure on line 101 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

React Hook "useDataSaver" is called in function "handleDownload" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

Check failure on line 101 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Unexpected string concatenation
const csvSaver = useDataSaver(baseFileName+'.csv', convertToCSV(savedMessagesJson));

Check failure on line 102 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

React Hook "useDataSaver" is called in function "handleDownload" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

Check failure on line 102 in frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx

View workflow job for this annotation

GitHub Actions / build / build-and-test

Unexpected string concatenation

if (selectedFormat === 'json') {
jsonSaver.saveFile();
} else {
csvSaver.saveFile();
}
};

return (
<div style={{ position: 'relative' }}>
<div style={{ display: 'flex', gap: '8px', marginLeft: '1rem', marginBottom: '1rem' }}>
<Select<DownloadFormat>
id="download-format"
name="download-format"
onChange={handleFormatSelect}
options={formatOptions}
value={selectedFormat}
minWidth="70px"
selectSize="M"
placeholder="Select format to download"
disabled={isFetching || messages.length === 0}
/>
<Button
disabled={isFetching || messages.length === 0}
buttonType="secondary"
buttonSize="M"
onClick={handleDownload}
>
Download All Messages
</Button>
</div>

{previewFor !== null && (
<PreviewModal
values={previewFor === 'key' ? keyFilters : contentFilters}
Expand Down
Loading