Messages component refactoring (#174)
* ISSUE-169 Sort topics (#172) * Messages Table component created * Messages component refactored * MessageItem component fixed * MessageItem component updated * MessageItem component refactored Co-authored-by: German Osin <german.osin@gmail.com> Co-authored-by: Oleg Shuralev <workshur@gmail.com>
This commit is contained in:
parent
cc097e9327
commit
10a27ba60e
4 changed files with 138 additions and 93 deletions
|
@ -0,0 +1,52 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import JSONTree from 'react-json-tree';
|
||||||
|
import { TopicMessage } from 'generated-sources';
|
||||||
|
|
||||||
|
interface MessageItemProp {
|
||||||
|
partition: TopicMessage['partition'];
|
||||||
|
offset: TopicMessage['offset'];
|
||||||
|
timestamp: TopicMessage['timestamp'];
|
||||||
|
content: TopicMessage['content'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageItem: React.FC<MessageItemProp> = ({
|
||||||
|
partition,
|
||||||
|
offset,
|
||||||
|
timestamp,
|
||||||
|
content,
|
||||||
|
}) => (
|
||||||
|
<tr key="{timestamp}">
|
||||||
|
<td style={{ width: 200 }}>
|
||||||
|
{timestamp ? format(timestamp, 'yyyy-MM-dd HH:mm:ss') : null}
|
||||||
|
</td>
|
||||||
|
<td style={{ width: 150 }}>{offset}</td>
|
||||||
|
<td style={{ width: 100 }}>{partition}</td>
|
||||||
|
<td key="{content}" style={{ wordBreak: 'break-word' }}>
|
||||||
|
{content && (
|
||||||
|
<JSONTree
|
||||||
|
data={content}
|
||||||
|
hideRoot
|
||||||
|
invertTheme={false}
|
||||||
|
theme={{
|
||||||
|
tree: ({ style }) => ({
|
||||||
|
style: {
|
||||||
|
...style,
|
||||||
|
backgroundColor: undefined,
|
||||||
|
marginLeft: 0,
|
||||||
|
marginTop: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
value: ({ style }) => ({
|
||||||
|
style: { ...style, marginLeft: 0 },
|
||||||
|
}),
|
||||||
|
base0D: '#3273dc',
|
||||||
|
base0B: '#363636',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MessageItem;
|
|
@ -6,19 +6,15 @@ import {
|
||||||
} from 'redux/interfaces';
|
} from 'redux/interfaces';
|
||||||
import { TopicMessage, Partition, SeekType } from 'generated-sources';
|
import { TopicMessage, Partition, SeekType } from 'generated-sources';
|
||||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||||
import { format } from 'date-fns';
|
|
||||||
import DatePicker from 'react-datepicker';
|
import DatePicker from 'react-datepicker';
|
||||||
import JSONTree from 'react-json-tree';
|
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import CustomParamButton, {
|
|
||||||
CustomParamButtonType,
|
|
||||||
} from 'components/Topics/shared/Form/CustomParams/CustomParamButton';
|
|
||||||
|
|
||||||
import MultiSelect from 'react-multi-select-component';
|
import MultiSelect from 'react-multi-select-component';
|
||||||
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
import { Option } from 'react-multi-select-component/dist/lib/interfaces';
|
import { Option } from 'react-multi-select-component/dist/lib/interfaces';
|
||||||
|
import MessagesTable from './MessagesTable';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
|
@ -174,42 +170,6 @@ const Messages: React.FC<Props> = ({
|
||||||
fetchTopicMessages(clusterName, topicName, queryParams);
|
fetchTopicMessages(clusterName, topicName, queryParams);
|
||||||
}, [clusterName, topicName, queryParams]);
|
}, [clusterName, topicName, queryParams]);
|
||||||
|
|
||||||
const getFormattedDate = (date: Date) => {
|
|
||||||
if (!date) return null;
|
|
||||||
return format(date, 'yyyy-MM-dd HH:mm:ss');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMessageContentBody = (content: Record<string, unknown>) => {
|
|
||||||
try {
|
|
||||||
const contentObj =
|
|
||||||
typeof content !== 'object' ? JSON.parse(content) : content;
|
|
||||||
return (
|
|
||||||
<JSONTree
|
|
||||||
data={contentObj}
|
|
||||||
hideRoot
|
|
||||||
invertTheme={false}
|
|
||||||
theme={{
|
|
||||||
tree: ({ style }) => ({
|
|
||||||
style: {
|
|
||||||
...style,
|
|
||||||
backgroundColor: undefined,
|
|
||||||
marginLeft: 0,
|
|
||||||
marginTop: 0,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
value: ({ style }) => ({
|
|
||||||
style: { ...style, marginLeft: 0 },
|
|
||||||
}),
|
|
||||||
base0D: '#3273dc',
|
|
||||||
base0B: '#363636',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onNext = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const onNext = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -236,52 +196,6 @@ const Messages: React.FC<Props> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTopicMessagesTable = () => {
|
|
||||||
return messages.length > 0 ? (
|
|
||||||
<div>
|
|
||||||
<table className="table is-striped is-fullwidth">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Timestamp</th>
|
|
||||||
<th>Offset</th>
|
|
||||||
<th>Partition</th>
|
|
||||||
<th>Content</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{messages.map((message) => (
|
|
||||||
<tr key={`${message.timestamp}${Math.random()}`}>
|
|
||||||
<td style={{ width: 200 }}>
|
|
||||||
{getFormattedDate(message.timestamp)}
|
|
||||||
</td>
|
|
||||||
<td style={{ width: 150 }}>{message.offset}</td>
|
|
||||||
<td style={{ width: 100 }}>{message.partition}</td>
|
|
||||||
<td key={Math.random()} style={{ wordBreak: 'break-word' }}>
|
|
||||||
{message.content &&
|
|
||||||
getMessageContentBody(
|
|
||||||
message.content as Record<string, unknown>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div className="columns">
|
|
||||||
<div className="column is-full">
|
|
||||||
<CustomParamButton
|
|
||||||
className="is-link is-pulled-right"
|
|
||||||
type={CustomParamButtonType.chevronRight}
|
|
||||||
onClick={onNext}
|
|
||||||
btnText="Next"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>No messages at selected topic</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isFetched) {
|
if (!isFetched) {
|
||||||
return <PageLoader isFullHeight={false} />;
|
return <PageLoader isFullHeight={false} />;
|
||||||
}
|
}
|
||||||
|
@ -366,7 +280,7 @@ const Messages: React.FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{getTopicMessagesTable()}
|
<MessagesTable messages={messages} onNext={onNext} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { TopicMessage } from 'generated-sources';
|
||||||
|
import CustomParamButton, {
|
||||||
|
CustomParamButtonType,
|
||||||
|
} from '../../shared/Form/CustomParams/CustomParamButton';
|
||||||
|
import MessageItem from './MessageItem';
|
||||||
|
|
||||||
|
interface MessagesTableProp {
|
||||||
|
messages: TopicMessage[];
|
||||||
|
onNext(event: React.MouseEvent<HTMLButtonElement>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessagesTable: React.FC<MessagesTableProp> = ({ messages, onNext }) => {
|
||||||
|
if (!messages.length) {
|
||||||
|
return <div>No messages at selected topic</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<table className="table is-striped is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Offset</th>
|
||||||
|
<th>Partition</th>
|
||||||
|
<th>Content</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{messages.map(
|
||||||
|
({ partition, offset, timestamp, content }: TopicMessage) => (
|
||||||
|
<MessageItem
|
||||||
|
key={timestamp.toString()}
|
||||||
|
partition={partition}
|
||||||
|
offset={offset}
|
||||||
|
timestamp={timestamp}
|
||||||
|
content={content as Record<string, unknown>}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column is-full">
|
||||||
|
<CustomParamButton
|
||||||
|
className="is-link is-pulled-right"
|
||||||
|
type={CustomParamButtonType.chevronRight}
|
||||||
|
onClick={onNext}
|
||||||
|
btnText="Next"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessagesTable;
|
|
@ -1,5 +1,5 @@
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { Topic } from 'generated-sources';
|
import { Topic, TopicMessage } from 'generated-sources';
|
||||||
import { Action, TopicsState } from 'redux/interfaces';
|
import { Action, TopicsState } from 'redux/interfaces';
|
||||||
import ActionType from 'redux/actionType';
|
import ActionType from 'redux/actionType';
|
||||||
|
|
||||||
|
@ -41,6 +41,31 @@ const addToTopicList = (state: TopicsState, payload: Topic): TopicsState => {
|
||||||
return newState;
|
return newState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const transformTopicMessages = (
|
||||||
|
state: TopicsState,
|
||||||
|
messages: TopicMessage[]
|
||||||
|
): TopicsState => ({
|
||||||
|
...state,
|
||||||
|
messages: messages.map((mes) => {
|
||||||
|
const { content } = mes;
|
||||||
|
let parsedContent = content;
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
try {
|
||||||
|
parsedContent =
|
||||||
|
typeof content !== 'object' ? JSON.parse(content) : content;
|
||||||
|
} catch (_) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...mes,
|
||||||
|
content: parsedContent,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const reducer = (state = initialState, action: Action): TopicsState => {
|
const reducer = (state = initialState, action: Action): TopicsState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType.GET_TOPICS__SUCCESS:
|
case ActionType.GET_TOPICS__SUCCESS:
|
||||||
|
@ -57,10 +82,7 @@ const reducer = (state = initialState, action: Action): TopicsState => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case ActionType.GET_TOPIC_MESSAGES__SUCCESS:
|
case ActionType.GET_TOPIC_MESSAGES__SUCCESS:
|
||||||
return {
|
return transformTopicMessages(state, action.payload);
|
||||||
...state,
|
|
||||||
messages: action.payload,
|
|
||||||
};
|
|
||||||
case ActionType.GET_TOPIC_CONFIG__SUCCESS:
|
case ActionType.GET_TOPIC_CONFIG__SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
Loading…
Add table
Reference in a new issue