Skip to content

Commit 4d3ec27

Browse files
Adding topics details page (#369)
* Adding topics details page * added spport for Avro and json mode . * minor style changes * addressing review comment * addressing review comment
1 parent eff1de8 commit 4d3ec27

File tree

21 files changed

+790
-45
lines changed

21 files changed

+790
-45
lines changed

catalog-rest-service/src/main/resources/ui/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

catalog-rest-service/src/main/resources/ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"bootstrap": "^4.5.2",
2424
"buffer": "^6.0.3",
2525
"builtin-status-codes": "^3.0.0",
26+
"codemirror": "^5.62.3",
2627
"cookie-storage": "^6.1.0",
2728
"core-js": "^3.10.1",
2829
"draft-js": "^0.11.7",
@@ -53,6 +54,7 @@
5354
"prop-types": "^15.7.2",
5455
"react": "^16.14.0",
5556
"react-bootstrap": "^1.6.0",
57+
"react-codemirror2": "^7.2.1",
5658
"react-dom": "^16.14.0",
5759
"react-draft-wysiwyg": "^1.14.7",
5860
"react-js-pagination": "^3.0.3",

catalog-rest-service/src/main/resources/ui/src/axiosAPIs/topicsAPI.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { AxiosResponse } from 'axios';
19+
import { Topic } from 'Models';
1920
import { getURLWithQueryFields } from '../utils/APIUtils';
2021
import APIClient from './index';
2122

@@ -31,3 +32,48 @@ export const getTopics: Function = (
3132

3233
return APIClient.get(url);
3334
};
35+
36+
export const getTopicByFqn: Function = (
37+
fqn: string,
38+
arrQueryFields: string
39+
): Promise<AxiosResponse> => {
40+
const url = getURLWithQueryFields(`/topics/name/${fqn}`, arrQueryFields);
41+
42+
return APIClient.get(url);
43+
};
44+
45+
export const addFollower: Function = (
46+
topicId: string,
47+
userId: string
48+
): Promise<AxiosResponse> => {
49+
const configOptions = {
50+
headers: { 'Content-type': 'application/json' },
51+
};
52+
53+
return APIClient.put(`/topics/${topicId}/followers`, userId, configOptions);
54+
};
55+
56+
export const removeFollower: Function = (
57+
topicId: string,
58+
userId: string
59+
): Promise<AxiosResponse> => {
60+
const configOptions = {
61+
headers: { 'Content-type': 'application/json' },
62+
};
63+
64+
return APIClient.delete(
65+
`/topics/${topicId}/followers/${userId}`,
66+
configOptions
67+
);
68+
};
69+
70+
export const patchTopicDetails: Function = (
71+
id: string,
72+
data: Topic
73+
): Promise<AxiosResponse> => {
74+
const configOptions = {
75+
headers: { 'Content-type': 'application/json-patch+json' },
76+
};
77+
78+
return APIClient.patch(`/topics/${id}`, data, configOptions);
79+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.field-wrapper {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 2px;
5+
margin-left: 15px;
6+
border-left: 3px solid #d9ceee;
7+
padding-top: 10px;
8+
padding-bottom: 10px;
9+
}
10+
11+
.field-child {
12+
display: flex;
13+
gap: 4px;
14+
}
15+
.field-child::before {
16+
content: '';
17+
display: inline-block;
18+
width: 32px;
19+
height: 3px;
20+
background: #d9ceee;
21+
position: relative;
22+
top: 11px;
23+
}
24+
.field-child-icon {
25+
cursor: pointer;
26+
color: #7147e8;
27+
}
28+
.field-child-icon i {
29+
vertical-align: sub;
30+
margin-right: 2px;
31+
margin-left: 2px;
32+
}
33+
34+
.field-label {
35+
display: flex;
36+
gap: 4px;
37+
}
38+
39+
.field-label-name {
40+
padding: 4px 6px;
41+
}
42+
43+
.child-fields-wrapper {
44+
display: flex;
45+
flex-direction: column;
46+
/* margin-top: -11px; */
47+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React, { CSSProperties, useCallback, useState } from 'react';
2+
import './SchemaTreeStructure.css';
3+
4+
type Props = {
5+
positions?: Array<number>;
6+
name: string;
7+
type: string;
8+
fields?: Array<Props>;
9+
isCollapsed?: boolean;
10+
};
11+
12+
export const getStyle = (type: string) => {
13+
const sharedStyles = {
14+
padding: '4px 8px',
15+
borderRadius: '5px',
16+
minWidth: '60px',
17+
textAlign: 'center',
18+
display: 'inline-block',
19+
};
20+
switch (type) {
21+
case 'double':
22+
return {
23+
backgroundColor: '#B02AAC33',
24+
color: '#B02AAC',
25+
...sharedStyles,
26+
};
27+
28+
case 'string':
29+
return {
30+
backgroundColor: '#51c41a33',
31+
color: '#51c41a',
32+
...sharedStyles,
33+
};
34+
35+
case 'int':
36+
return {
37+
backgroundColor: '#1890FF33',
38+
color: '#1890FF',
39+
...sharedStyles,
40+
};
41+
42+
default:
43+
return {
44+
backgroundColor: '#EEEAF8',
45+
...sharedStyles,
46+
};
47+
}
48+
};
49+
50+
const SchemaTreeStructure = ({
51+
name,
52+
type,
53+
fields,
54+
isCollapsed = false,
55+
// to track position of element [L0,L1,L2,...Ln]
56+
positions = [],
57+
}: Props) => {
58+
const [showChildren, setShowChildren] = useState<boolean>(!isCollapsed);
59+
const flag = (fields ?? []).length > 0;
60+
61+
const showChildrenHandler = useCallback(() => {
62+
setShowChildren(!showChildren);
63+
}, [showChildren, setShowChildren]);
64+
65+
const getIcon = () => {
66+
return (
67+
flag &&
68+
(showChildren ? (
69+
<i className="fas fa-minus-circle" />
70+
) : (
71+
<i className="fas fa-plus-circle" />
72+
))
73+
);
74+
};
75+
76+
return (
77+
<div
78+
className="field-wrapper"
79+
style={{ paddingLeft: flag ? '26px' : '0px' }}>
80+
<div
81+
className="field-child"
82+
style={{ marginLeft: flag ? '-26px' : '0px' }}>
83+
<p className="field-child-icon" onClick={showChildrenHandler}>
84+
{getIcon()}
85+
</p>
86+
<p className="field-label">
87+
<span style={getStyle(type) as CSSProperties}>{type}</span>
88+
<span className="field-label-name">{name}</span>
89+
</p>
90+
</div>
91+
{flag && showChildren && (
92+
<div className="child-fields-wrapper">
93+
{(fields ?? []).map((field, index) => (
94+
<SchemaTreeStructure
95+
isCollapsed
96+
key={index}
97+
positions={[...positions, index]}
98+
{...field}
99+
/>
100+
))}
101+
</div>
102+
)}
103+
</div>
104+
);
105+
};
106+
107+
export default SchemaTreeStructure;

catalog-rest-service/src/main/resources/ui/src/components/common/TabsPane/TabsPane.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const TabsPane = ({ activeTab, setActiveTab, tabs }: Props) => {
2929
tab.isProtected ? (
3030
<NonAdminAction
3131
isOwner={tab.protectedState}
32+
key={tab.position}
3233
title="You need to be owner to perform this action">
3334
<button
3435
className={getTabClasses(tab.position, activeTab)}
@@ -46,6 +47,7 @@ const TabsPane = ({ activeTab, setActiveTab, tabs }: Props) => {
4647
<button
4748
className={getTabClasses(tab.position, activeTab)}
4849
data-testid="tab"
50+
key={tab.position}
4951
onClick={() => setActiveTab(tab.position)}>
5052
<SVGIcons
5153
alt={tab.icon.alt}

0 commit comments

Comments
 (0)