Skip to content

Commit

Permalink
Merge pull request #78 from gimral/feature/field-preview
Browse files Browse the repository at this point in the history
[Fixes #6 ] Feature/field preview
  • Loading branch information
gimral authored Mar 17, 2024
2 parents 69ffdc2 + be4da5b commit 89a581c
Show file tree
Hide file tree
Showing 17 changed files with 648 additions and 350 deletions.
33 changes: 14 additions & 19 deletions kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import useDataSaver from 'lib/hooks/useDataSaver';
import { TopicMessage } from 'generated-sources';
import MessageToggleIcon from 'components/common/Icons/MessageToggleIcon';
import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
import { Dropdown, DropdownItem } from 'components/common/Dropdown';
Expand All @@ -9,19 +8,20 @@ import { JSONPath } from 'jsonpath-plus';
import Ellipsis from 'components/common/Ellipsis/Ellipsis';
import WarningRedIcon from 'components/common/Icons/WarningRedIcon';
import Tooltip from 'components/common/Tooltip/Tooltip';
import { TopicParsedMessage } from 'redux/interfaces';

import MessageContent from './MessageContent/MessageContent';
import * as S from './MessageContent/MessageContent.styled';

export interface PreviewFilter {
field: string;
displayName: string;
path: string;
}

export interface Props {
keyFilters: PreviewFilter[];
contentFilters: PreviewFilter[];
message: TopicMessage;
message: TopicParsedMessage;
}

const Message: React.FC<Props> = ({
Expand All @@ -37,6 +37,8 @@ const Message: React.FC<Props> = ({
headers,
valueSerde,
keySerde,
keyJson,
contentJson,
},
keyFilters,
contentFilters,
Expand All @@ -61,29 +63,22 @@ const Message: React.FC<Props> = ({

const [vEllipsisOpen, setVEllipsisOpen] = React.useState(false);

const getParsedJson = (jsonValue: string) => {
try {
return JSON.parse(jsonValue);
} catch (e) {
return {};
}
};

const renderFilteredJson = (
jsonValue?: string,
rawValue?: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedValue?: any,
filters?: PreviewFilter[]
) => {
if (!filters?.length || !jsonValue) return jsonValue;
const parsedJson = getParsedJson(jsonValue);
if (!filters?.length || !rawValue) return rawValue;

return (
<>
{filters.map((item) => {
return (
<div key={`${item.path}--${item.field}`}>
{item.field}:{' '}
<div key={`${item.path}--${item.displayName}`}>
{item.displayName || item.path}:{' '}
{JSON.stringify(
JSONPath({ path: item.path, json: parsedJson, wrap: false })
JSONPath({ path: item.path, json: parsedValue, wrap: false })
)}
</div>
);
Expand All @@ -110,7 +105,7 @@ const Message: React.FC<Props> = ({
<div>{formatTimestamp(timestamp)}</div>
</td>
<S.DataCell title={key}>
<Ellipsis text={renderFilteredJson(key, keyFilters)}>
<Ellipsis text={renderFilteredJson(key, keyJson, keyFilters)}>
{keySerde === 'Fallback' && (
<Tooltip
value={<WarningRedIcon />}
Expand All @@ -124,7 +119,7 @@ const Message: React.FC<Props> = ({
<S.Metadata>
<S.MetadataValue>
<Ellipsis
text={renderFilteredJson(content, contentFilters)}
text={renderFilteredJson(content, contentJson, contentFilters)}
style={{ maxWidth: '100%' }}
>
{valueSerde === 'Fallback' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import PageLoader from 'components/common/PageLoader/PageLoader';
import { Table } from 'components/common/table/Table/Table.styled';
import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
import { TopicMessage } from 'generated-sources';
import React, { useContext, useState } from 'react';
import React, { Suspense, useContext, useState } from 'react';
import {
getTopicMessges,
getIsTopicMessagesFetching,
getTopicMessgesFields,
} from 'redux/reducers/topicMessages/selectors';
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
import { useAppSelector } from 'lib/hooks/redux';
Expand All @@ -14,9 +15,10 @@ import { useSearchParams } from 'react-router-dom';
import { MESSAGES_PER_PAGE } from 'lib/constants';
import * as S from 'components/common/NewTable/Table.styled';
import { getSerdeOptions } from 'components/Topics/Topic/SendMessage/utils';
import SlidingSidebar from 'components/common/SlidingSidebar';

import * as SE from './MessagesTable.styled';
import PreviewModal from './PreviewModal';
import PreviewFields from './PreviewFields';
import Message, { PreviewFilter } from './Message';

const MessagesTable: React.FC = () => {
Expand All @@ -38,6 +40,9 @@ const MessagesTable: React.FC = () => {
} = useContext(TopicMessagesContext);

const messages = useAppSelector(getTopicMessges);
const { messageKeyFields, messageContentFields } = useAppSelector(
getTopicMessgesFields
);
const isFetching = useAppSelector(getIsTopicMessagesFetching);

// Pagination is disabled in live mode, also we don't want to show the button
Expand All @@ -60,17 +65,6 @@ const MessagesTable: React.FC = () => {

return (
<div style={{ position: 'relative' }}>
{previewFor !== null && (
<PreviewModal
values={previewFor === 'key' ? keyFilters : contentFilters}
toggleIsOpen={() => setPreviewFor(null)}
setFilters={(payload: PreviewFilter[]) =>
previewFor === 'key'
? setKeyFilters(payload)
: setContentFilters(payload)
}
/>
)}
<Table isFullwidth>
<thead>
<tr>
Expand All @@ -80,9 +74,13 @@ const MessagesTable: React.FC = () => {
<TableHeaderCell title="Timestamp" style={{ width: '180px' }} />
<TableHeaderCell
title="Key"
previewText={`Preview ${
keyFilters.length ? `(${keyFilters.length} selected)` : ''
}`}
previewText={
messageKeyFields && messageKeyFields.length === 0
? ''
: `Preview ${
keyFilters.length ? `(${keyFilters.length} selected)` : ''
}`
}
onPreview={() => setPreviewFor('key')}
style={{ width: '300px' }}
>
Expand All @@ -101,11 +99,15 @@ const MessagesTable: React.FC = () => {
</TableHeaderCell>
<TableHeaderCell
title="Value"
previewText={`Preview ${
contentFilters.length
? `(${contentFilters.length} selected)`
: ''
}`}
previewText={
messageContentFields && messageContentFields.length === 0
? ''
: `Preview ${
contentFilters.length
? `(${contentFilters.length} selected)`
: ''
}`
}
onPreview={() => setPreviewFor('content')}
>
<SE.SerdeSelectWrapper>
Expand Down Expand Up @@ -172,6 +174,28 @@ const MessagesTable: React.FC = () => {
</Button>
</S.Pages>
</S.Pagination>
<SlidingSidebar
open={previewFor !== null}
onClose={() => setPreviewFor(null)}
title="Field Preview"
>
{previewFor !== null && (
<Suspense fallback={<PageLoader />}>
<PreviewFields
values={previewFor === 'key' ? keyFilters : contentFilters}
toggleIsOpen={() => setPreviewFor(null)}
setFilters={(payload: PreviewFilter[]) =>
previewFor === 'key'
? setKeyFilters(payload)
: setContentFilters(payload)
}
messageFields={
previewFor === 'key' ? messageKeyFields : messageContentFields
}
/>
</Suspense>
)}
</SlidingSidebar>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import styled, { css } from 'styled-components';
import CloseIcon from 'components/common/Icons/CloseIcon';
import EditIcon from 'components/common/Icons/EditIcon';

export const PreviewFields = styled.div`
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
h4 {
margin-bottom: 8px;
margin-top: 12px;
padding-top: 8px;
border-top: 1px solid
${({ theme }) => theme.messagePreviewFilter.seperatorColor};
}
`;

export const InputWrapper = styled.div`
margin-top: 10px;
`;

export const ButtonWrapper = styled.div`
width: 100%;
height: 64px;
display: flex;
justify-content: right;
align-items: center;
// padding-top: 20px;
// margin-top: 20px;
// gap: 10px;
// border-top: 1px solid
// ${({ theme }) => theme.messagePreviewFilter.seperatorColor};
`;

export const ContentWrapper = styled.div`
height: 100%;
border-bottom: 1px solid
${({ theme }) => theme.messagePreviewFilter.seperatorColor};
`;

export const EditForm = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 8px;
margin-bottom: 4px;
height: 40px;
color: ${({ theme }) => theme.activeFilter.color};
background: ${({ theme }) => theme.activeFilter.backgroundColor};
border-radius: 4px;
font-size: 14px;
line-height: 20px;
`;

export const Field = styled.div`
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-right: 5px;
color: ${({ theme }) => theme.modal.color};
`;

export const EditFilterIcon = styled.div(
({ theme: { icons } }) => css`
color: ${icons.editIcon.normal};
display: flex;
align-items: center;
justify-content: center;
height: 32px;
width: 32px;
cursor: pointer;
&:hover {
${EditIcon} {
fill: ${icons.editIcon.hover};
}
}
&:active {
${EditIcon} {
fill: ${icons.editIcon.active};
}
}
`
);

export const DeleteFilterIcon = styled.div(
({ theme: { icons } }) => css`
color: ${icons.closeIcon.normal};
display: flex;
align-items: center;
justify-content: center;
height: 32px;
width: 32px;
cursor: pointer;
svg {
height: 14px;
width: 14px;
}
&:hover {
${CloseIcon} {
fill: ${icons.closeIcon.hover};
}
}
&:active {
${CloseIcon} {
fill: ${icons.closeIcon.active};
}
}
`
);
Loading

0 comments on commit 89a581c

Please sign in to comment.