UI : Refactoring of TopicDetails Page (#888)

* UI : Refactoring of TopicDetails Page

* changed page name in routes

* addressing review comment

* added toast notification for error

* minor changes

* minor fix

* minor fix
This commit is contained in:
Sachin Chaurasiya 2021-10-21 21:46:33 +05:30 committed by GitHub
parent 2787ff1b17
commit 53eb2aad21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 596 additions and 435 deletions

View File

@ -0,0 +1,309 @@
import { EntityTags } from 'Models';
import React, { useEffect, useState } from 'react';
import { getTeamDetailsPath } from '../../constants/constants';
import { Topic } from '../../generated/entity/data/topic';
import { User } from '../../generated/entity/teams/user';
import { LabelType, State } from '../../generated/type/tagLabel';
import { useAuth } from '../../hooks/authHooks';
import { getCurrentUserId, getUserTeams } from '../../utils/CommonUtils';
import { bytesToSize } from '../../utils/StringsUtils';
import { getTagsWithoutTier } from '../../utils/TableUtils';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer';
import ManageTabComponent from '../ManageTab/ManageTab.component';
import SchemaEditor from '../schema-editor/SchemaEditor';
import { TopicDetailsProps } from './TopicDetails.interface';
const TopicDetails: React.FC<TopicDetailsProps> = ({
users,
topicDetails,
partitions,
cleanupPolicies,
maximumMessageSize,
replicationFactor,
retentionSize,
schemaText,
schemaType,
tagList,
topicTags,
activeTab,
entityName,
owner,
description,
tier,
followers,
slashedTopicName,
setActiveTabHandler,
settingsUpdateHandler,
followTopicHandler,
unfollowTopicHandler,
descriptionUpdateHandler,
tagUpdateHandler,
}: TopicDetailsProps) => {
const { isAuthDisabled } = useAuth();
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
const [isFollowing, setIsFollowing] = useState(false);
const hasEditAccess = () => {
if (owner?.type === 'user') {
return owner.id === getCurrentUserId();
} else {
return getUserTeams().some((team) => team.id === owner?.id);
}
};
const setFollowersData = (followers: Array<User>) => {
setIsFollowing(
followers.some(({ id }: { id: string }) => id === getCurrentUserId())
);
setFollowersCount(followers?.length);
};
const getConfigDetails = () => {
return [
{ key: 'Partitions', value: partitions },
{ key: 'Replication Factor', value: replicationFactor },
{ key: 'Retention Size', value: bytesToSize(retentionSize) },
{ key: 'CleanUp Policies', value: cleanupPolicies.join(',') },
{ key: 'Max Message Size', value: bytesToSize(maximumMessageSize) },
];
};
const getConfigObject = () => {
return {
Partitions: partitions,
'Replication Factor': replicationFactor,
'Retention Size': retentionSize,
'CleanUp Policies': cleanupPolicies,
'Max Message Size': maximumMessageSize,
};
};
const tabs = [
{
name: 'Schema',
icon: {
alt: 'schema',
name: 'icon-schema',
title: 'Schema',
},
isProtected: false,
position: 1,
},
{
name: 'Config',
icon: {
alt: 'config',
name: 'icon-config',
title: 'Config',
},
isProtected: false,
position: 2,
},
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 3,
},
];
const extraInfo = [
{
key: 'Owner',
value:
owner?.type === 'team'
? getTeamDetailsPath(owner?.name || '')
: owner?.name || '',
placeholderText: owner?.displayName || '',
isLink: owner?.type === 'team',
openInNewTab: false,
},
{ key: 'Tier', value: tier ? tier.split('.')[1] : '' },
...getConfigDetails(),
];
const onDescriptionEdit = (): void => {
setIsEdit(true);
};
const onCancel = () => {
setIsEdit(false);
};
const onDescriptionUpdate = (updatedHTML: string) => {
if (description !== updatedHTML) {
const updatedTopicDetails = {
...topicDetails,
description: updatedHTML,
};
descriptionUpdateHandler(updatedTopicDetails);
setIsEdit(false);
} else {
setIsEdit(false);
}
};
const onSettingsUpdate = (newOwner?: Topic['owner'], newTier?: string) => {
if (newOwner || newTier) {
const tierTag: Topic['tags'] = newTier
? [
...getTagsWithoutTier(topicDetails.tags as Array<EntityTags>),
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: topicDetails.tags;
const updatedTopicDetails = {
...topicDetails,
owner: newOwner
? {
...topicDetails.owner,
...newOwner,
}
: topicDetails.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedTopicDetails);
} else {
return Promise.reject();
}
};
const followTopic = () => {
if (isFollowing) {
setFollowersCount((preValu) => preValu - 1);
setIsFollowing(false);
unfollowTopicHandler();
} else {
setFollowersCount((preValu) => preValu + 1);
setIsFollowing(true);
followTopicHandler();
}
};
const getInfoBadge = (infos: Array<Record<string, string | number>>) => {
return (
<div className="tw-flex tw-justify-between">
<div className="tw-flex tw-gap-3">
{infos.map((info, index) => (
<div className="tw-mt-4" key={index}>
<span className="tw-py-1.5 tw-px-2 tw-rounded-l tw-bg-tag ">
{info.key}
</span>
<span className="tw-py-1.5 tw-px-2 tw-bg-primary-lite tw-font-normal tw-rounded-r">
{info.value}
</span>
</div>
))}
</div>
<div />
</div>
);
};
const onTagUpdate = (selectedTags?: Array<string>) => {
if (selectedTags) {
const prevTags =
topicDetails?.tags?.filter((tag) =>
selectedTags.includes(tag?.tagFQN as string)
) || [];
const newTags = selectedTags
.filter((tag) => {
return !prevTags?.map((prevTag) => prevTag.tagFQN).includes(tag);
})
.map((tag) => ({
labelType: LabelType.Manual,
state: State.Confirmed,
tagFQN: tag,
}));
const updatedTags = [...prevTags, ...newTags];
const updatedTopic = { ...topicDetails, tags: updatedTags };
tagUpdateHandler(updatedTopic);
}
};
useEffect(() => {
if (isAuthDisabled && users.length && followers.length) {
setFollowersData(followers);
}
}, [users, followers]);
useEffect(() => {
setFollowersData(followers);
}, [followers]);
return (
<PageContainer>
<div className="tw-px-4 w-full">
<EntityPageInfo
isTagEditable
entityName={entityName}
extraInfo={extraInfo}
followers={followersCount}
followersList={followers}
followHandler={followTopic}
hasEditAccess={hasEditAccess()}
isFollowing={isFollowing}
owner={owner}
tagList={tagList}
tags={topicTags}
tagsHandler={onTagUpdate}
tier={tier ?? ''}
titleLinks={slashedTopicName}
/>
<div className="tw-block tw-mt-1">
<TabsPane
activeTab={activeTab}
setActiveTab={setActiveTabHandler}
tabs={tabs}
/>
<div className="tw-bg-white tw--mx-4 tw-p-4 tw-min-h-tab">
{activeTab === 1 && (
<>
<div className="tw-grid tw-grid-cols-4 tw-gap-4 w-full">
<div className="tw-col-span-full">
<Description
description={description}
hasEditAccess={hasEditAccess()}
isEdit={isEdit}
owner={owner}
onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit}
onDescriptionUpdate={onDescriptionUpdate}
/>
</div>
</div>
{getInfoBadge([{ key: 'Schema', value: schemaType }])}
<div className="tw-my-4 tw-border tw-border-main tw-rounded-md tw-py-4">
<SchemaEditor value={schemaText} />
</div>
</>
)}
{activeTab === 3 && (
<ManageTabComponent
currentTier={tier}
currentUser={owner?.id}
hasEditAccess={hasEditAccess()}
onSave={onSettingsUpdate}
/>
)}
{activeTab === 2 && (
<SchemaEditor value={JSON.stringify(getConfigObject())} />
)}
</div>
</div>
</div>
</PageContainer>
);
};
export default TopicDetails;

View File

@ -0,0 +1,31 @@
import { EntityTags, TableDetail } from 'Models';
import { Topic } from '../../generated/entity/data/topic';
import { User } from '../../generated/entity/teams/user';
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
export interface TopicDetailsProps {
tagList: Array<string>;
schemaText: string;
schemaType: string;
partitions: number;
cleanupPolicies: Array<string>;
maximumMessageSize: number;
replicationFactor: number;
retentionSize: number;
users: Array<User>;
topicDetails: Topic;
entityName: string;
activeTab: number;
owner: TableDetail['owner'];
description: string;
tier: string;
followers: Array<User>;
topicTags: Array<EntityTags>;
slashedTopicName: TitleBreadcrumbProps['titleLinks'];
setActiveTabHandler: (value: number) => void;
followTopicHandler: () => void;
unfollowTopicHandler: () => void;
settingsUpdateHandler: (updatedTopic: Topic) => Promise<void>;
descriptionUpdateHandler: (updatedTopic: Topic) => void;
tagUpdateHandler: (updatedTopic: Topic) => void;
}

View File

@ -0,0 +1,249 @@
import { AxiosError, AxiosResponse } from 'axios';
import { compare } from 'fast-json-patch';
import { observer } from 'mobx-react';
import { EntityTags, TableDetail } from 'Models';
import React, { FunctionComponent, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import AppState from '../../AppState';
import { getServiceById } from '../../axiosAPIs/serviceAPI';
import {
addFollower,
getTopicByFqn,
patchTopicDetails,
removeFollower,
} from '../../axiosAPIs/topicsAPI';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import Loader from '../../components/Loader/Loader';
import TopicDetails from '../../components/TopicDetails/TopicDetails.component';
import { getServiceDetailsPath } from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
import { Topic } from '../../generated/entity/data/topic';
import { User } from '../../generated/entity/teams/user';
import useToastContext from '../../hooks/useToastContext';
import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils';
import {
getOwnerFromId,
getTagsWithoutTier,
getTierFromTableTags,
} from '../../utils/TableUtils';
import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
const TopicDetailsPage: FunctionComponent = () => {
const USERId = getCurrentUserId();
const showToast = useToastContext();
const [tagList, setTagList] = useState<Array<string>>([]);
const { topicFQN } = useParams() as Record<string, string>;
const [topicDetails, setTopicDetails] = useState<Topic>({} as Topic);
const [topicId, setTopicId] = useState<string>('');
const [isLoading, setLoading] = useState<boolean>(false);
const [description, setDescription] = useState<string>('');
const [followers, setFollowers] = useState<Array<User>>([]);
const [owner, setOwner] = useState<TableDetail['owner']>();
const [tier, setTier] = useState<string>();
const [schemaType, setSchemaType] = useState<string>('');
const [tags, setTags] = useState<Array<EntityTags>>([]);
const [activeTab, setActiveTab] = useState<number>(1);
const [partitions, setPartitions] = useState<number>(0);
const [cleanupPolicies, setCleanupPolicies] = useState<Array<string>>([]);
const [maximumMessageSize, setMaximumMessageSize] = useState<number>(0);
const [replicationFactor, setReplicationFactor] = useState<number>(0);
const [retentionSize, setRetentionSize] = useState<number>(0);
const [name, setName] = useState<string>('');
const [schemaText, setSchemaText] = useState<string>('{}');
const [slashedTopicName, setSlashedTopicName] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const activeTabHandler = (tabValue: number) => {
setActiveTab(tabValue);
};
const saveUpdatedTopicData = (updatedData: Topic): Promise<AxiosResponse> => {
const jsonPatch = compare(topicDetails, updatedData);
return patchTopicDetails(
topicId,
jsonPatch
) as unknown as Promise<AxiosResponse>;
};
const fetchTags = () => {
getTagCategories().then((res) => {
setTagList(getTaglist(res.data));
});
};
const fetchTopicDetail = (topicFQN: string) => {
setLoading(true);
getTopicByFqn(topicFQN, ['owner', 'service', 'followers', 'tags'])
.then((res: AxiosResponse) => {
const {
id,
description,
followers,
fullyQualifiedName,
name,
schemaType,
schemaText,
service,
tags,
owner,
partitions,
cleanupPolicies,
maximumMessageSize,
replicationFactor,
retentionSize,
} = res.data;
setName(name);
setTopicDetails(res.data);
setTopicId(id);
setDescription(description ?? '');
setSchemaType(schemaType);
setFollowers(followers);
setOwner(getOwnerFromId(owner?.id));
setTier(getTierFromTableTags(tags));
setTags(getTagsWithoutTier(tags));
setSchemaText(schemaText);
setPartitions(partitions);
setCleanupPolicies(cleanupPolicies);
setMaximumMessageSize(maximumMessageSize);
setReplicationFactor(replicationFactor);
setRetentionSize(retentionSize);
getServiceById('messagingServices', service?.id)
.then((serviceRes: AxiosResponse) => {
setSlashedTopicName([
{
name: serviceRes.data.name,
url: serviceRes.data.name
? getServiceDetailsPath(
serviceRes.data.name,
serviceRes.data.serviceType
)
: '',
imgSrc: serviceRes.data.serviceType
? serviceTypeLogo(serviceRes.data.serviceType)
: undefined,
},
{
name: name,
url: '',
activeTitle: true,
},
]);
addToRecentViewed({
entityType: EntityType.TOPIC,
fqn: fullyQualifiedName,
serviceType: serviceRes.data.serviceType,
timestamp: 0,
});
setLoading(false);
})
.catch((err: AxiosError) => {
const errMsg =
err.message || `Error while fetching service for ${name}`;
showToast({
variant: 'error',
body: errMsg,
});
});
})
.catch((err: AxiosError) => {
const errMsg = err.message || 'Error while fetching topic details';
showToast({
variant: 'error',
body: errMsg,
});
});
};
const followTopic = () => {
addFollower(topicId, USERId).then((res: AxiosResponse) => {
const { followers } = res.data;
setFollowers(followers);
});
};
const unfollowTopic = () => {
removeFollower(topicId, USERId).then((res: AxiosResponse) => {
const { followers } = res.data;
setFollowers(followers);
});
};
const descriptionUpdateHandler = (updatedTopic: Topic) => {
saveUpdatedTopicData(updatedTopic).then((res: AxiosResponse) => {
const { description } = res.data;
setTopicDetails(res.data);
setDescription(description);
});
};
const settingsUpdateHandler = (updatedTopic: Topic): Promise<void> => {
return new Promise<void>((resolve, reject) => {
saveUpdatedTopicData(updatedTopic)
.then((res) => {
setTopicDetails(res.data);
setOwner(getOwnerFromId(res.data.owner?.id));
setTier(getTierFromTableTags(res.data.tags));
resolve();
})
.catch(() => reject());
});
};
const onTagUpdate = (updatedTopic: Topic) => {
saveUpdatedTopicData(updatedTopic).then((res: AxiosResponse) => {
setTier(getTierFromTableTags(res.data.tags));
setTags(getTagsWithoutTier(res.data.tags));
});
};
useEffect(() => {
fetchTopicDetail(topicFQN);
}, [topicFQN]);
useEffect(() => {
fetchTags();
}, []);
return (
<>
{isLoading ? (
<Loader />
) : (
<TopicDetails
activeTab={activeTab}
cleanupPolicies={cleanupPolicies}
description={description}
descriptionUpdateHandler={descriptionUpdateHandler}
entityName={name}
followers={followers}
followTopicHandler={followTopic}
maximumMessageSize={maximumMessageSize}
owner={owner}
partitions={partitions}
replicationFactor={replicationFactor}
retentionSize={retentionSize}
schemaText={schemaText}
schemaType={schemaType}
setActiveTabHandler={activeTabHandler}
settingsUpdateHandler={settingsUpdateHandler}
slashedTopicName={slashedTopicName}
tagList={tagList}
tagUpdateHandler={onTagUpdate}
tier={tier as string}
topicDetails={topicDetails}
topicTags={tags}
unfollowTopicHandler={unfollowTopic}
users={AppState.users}
/>
)}
</>
);
};
export default observer(TopicDetailsPage);

View File

@ -242,7 +242,7 @@ const ServicesPage = () => {
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
showToast({ showToast({
variant: 'error', variant: 'error',
body: err.response?.data?.responseMessage ?? 'Something went wrong!', body: err.message || 'Something went wrong!',
}); });
}); });
}; };

View File

@ -1,430 +0,0 @@
import { AxiosResponse } from 'axios';
import { compare } from 'fast-json-patch';
import { ColumnTags, TableDetail, Topic } from 'Models';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import AppState from '../../AppState';
import { getServiceById } from '../../axiosAPIs/serviceAPI';
import {
addFollower,
getTopicByFqn,
patchTopicDetails,
removeFollower,
} from '../../axiosAPIs/topicsAPI';
import Description from '../../components/common/description/Description';
import EntityPageInfo from '../../components/common/entityPageInfo/EntityPageInfo';
import TabsPane from '../../components/common/TabsPane/TabsPane';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import PageContainer from '../../components/containers/PageContainer';
import Loader from '../../components/Loader/Loader';
import ManageTab from '../../components/ManageTab/ManageTab.component';
import SchemaEditor from '../../components/schema-editor/SchemaEditor';
import {
getServiceDetailsPath,
getTeamDetailsPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
import { User } from '../../generated/entity/teams/user';
import { useAuth } from '../../hooks/authHooks';
import {
addToRecentViewed,
getCurrentUserId,
getUserTeams,
} from '../../utils/CommonUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { bytesToSize } from '../../utils/StringsUtils';
import {
getOwnerFromId,
getTagsWithoutTier,
getTierFromTableTags,
} from '../../utils/TableUtils';
import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
const MyTopicDetailPage = () => {
const USERId = getCurrentUserId();
const { isAuthDisabled } = useAuth();
const [tagList, setTagList] = useState<Array<string>>([]);
const { topicFQN } = useParams() as Record<string, string>;
const [topicDetails, setTopicDetails] = useState<Topic>({} as Topic);
const [topicId, setTopicId] = useState<string>('');
const [isLoading, setLoading] = useState<boolean>(false);
const [description, setDescription] = useState<string>('');
const [followers, setFollowers] = useState<Array<User>>([]);
const [followersCount, setFollowersCount] = useState<number>(0);
const [isFollowing, setIsFollowing] = useState(false);
const [owner, setOwner] = useState<TableDetail['owner']>();
const [tier, setTier] = useState<string>();
const [schemaType, setSchemaType] = useState<string>('');
const [tags, setTags] = useState<Array<ColumnTags>>([]);
const [activeTab, setActiveTab] = useState<number>(1);
const [partitions, setPartitions] = useState<number>(0);
const [cleanupPolicies, setCleanupPolicies] = useState<Array<string>>([]);
const [maximumMessageSize, setMaximumMessageSize] = useState<number>(0);
const [replicationFactor, setReplicationFactor] = useState<number>(0);
const [retentionSize, setRetentionSize] = useState<number>(0);
const [name, setName] = useState<string>('');
const [isEdit, setIsEdit] = useState<boolean>(false);
const [schemaText, setSchemaText] = useState<string>('{}');
const [slashedTopicName, setSlashedTopicName] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const hasEditAccess = () => {
if (owner?.type === 'user') {
return owner.id === getCurrentUserId();
} else {
return getUserTeams().some((team) => team.id === owner?.id);
}
};
const tabs = [
{
name: 'Schema',
icon: {
alt: 'schema',
name: 'icon-schema',
title: 'Schema',
},
isProtected: false,
position: 1,
},
{
name: 'Config',
icon: {
alt: 'config',
name: 'icon-config',
title: 'Config',
},
isProtected: false,
position: 2,
},
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 3,
},
];
const fetchTags = () => {
getTagCategories().then((res) => {
if (res.data) {
setTagList(getTaglist(res.data));
}
});
};
const setFollowersData = (followers: Array<User>) => {
// need to check if already following or not with logedIn user id
setIsFollowing(followers.some(({ id }: { id: string }) => id === USERId));
setFollowersCount(followers?.length);
};
const fetchTopicDetail = (topicFQN: string) => {
setLoading(true);
getTopicByFqn(topicFQN, ['owner', 'service', 'followers', 'tags']).then(
(res: AxiosResponse) => {
const {
id,
description,
followers,
fullyQualifiedName,
name,
schemaType,
schemaText,
service,
tags,
owner,
partitions,
cleanupPolicies,
maximumMessageSize,
replicationFactor,
retentionSize,
} = res.data;
setName(name);
setTopicDetails(res.data);
setTopicId(id);
setDescription(description ?? '');
setSchemaType(schemaType);
setFollowers(followers);
setFollowersData(followers);
setOwner(getOwnerFromId(owner?.id));
setTier(getTierFromTableTags(tags));
setTags(getTagsWithoutTier(tags));
setSchemaText(schemaText);
setPartitions(partitions);
setCleanupPolicies(cleanupPolicies);
setMaximumMessageSize(maximumMessageSize);
setReplicationFactor(replicationFactor);
setRetentionSize(retentionSize);
getServiceById('messagingServices', service?.id).then(
(serviceRes: AxiosResponse) => {
setSlashedTopicName([
{
name: serviceRes.data.name,
url: serviceRes.data.name
? getServiceDetailsPath(
serviceRes.data.name,
serviceRes.data.serviceType
)
: '',
imgSrc: serviceRes.data.serviceType
? serviceTypeLogo(serviceRes.data.serviceType)
: undefined,
},
{
name: name,
url: '',
activeTitle: true,
},
]);
addToRecentViewed({
entityType: EntityType.TOPIC,
fqn: fullyQualifiedName,
serviceType: serviceRes.data.serviceType,
timestamp: 0,
});
}
);
setLoading(false);
}
);
};
const followTopic = (): void => {
if (isFollowing) {
removeFollower(topicId, USERId).then((res: AxiosResponse) => {
const { followers } = res.data;
setFollowers(followers);
setFollowersCount((preValu) => preValu - 1);
setIsFollowing(false);
});
} else {
addFollower(topicId, USERId).then((res: AxiosResponse) => {
const { followers } = res.data;
setFollowers(followers);
setFollowersCount((preValu) => preValu + 1);
setIsFollowing(true);
});
}
};
const onDescriptionUpdate = (updatedHTML: string) => {
const updatedTopic = { ...topicDetails, description: updatedHTML };
const jsonPatch = compare(topicDetails, updatedTopic);
patchTopicDetails(topicId, jsonPatch).then((res: AxiosResponse) => {
setDescription(res.data.description);
});
setIsEdit(false);
};
const onDescriptionEdit = (): void => {
setIsEdit(true);
};
const onCancel = () => {
setIsEdit(false);
};
const onSettingsUpdate = (
newOwner?: TableDetail['owner'],
newTier?: TableDetail['tier']
): Promise<void> => {
return new Promise<void>((resolve, reject) => {
if (newOwner || newTier) {
const tierTag: TableDetail['tags'] = newTier
? [
...getTagsWithoutTier(topicDetails.tags),
{ tagFQN: newTier, labelType: 'Manual', state: 'Confirmed' },
]
: topicDetails.tags;
const updatedTopic = {
...topicDetails,
owner: newOwner
? { ...topicDetails.owner, ...newOwner }
: topicDetails.owner,
tags: tierTag,
};
const jsonPatch = compare(topicDetails, updatedTopic);
patchTopicDetails(topicId, jsonPatch)
.then((res: AxiosResponse) => {
setTopicDetails(res.data);
setOwner(getOwnerFromId(res.data.owner?.id));
setTier(getTierFromTableTags(res.data.tags));
resolve();
})
.catch(() => reject());
} else {
reject();
}
});
};
const onTagUpdate = (selectedTags?: Array<string>) => {
if (selectedTags) {
const prevTags = topicDetails.tags.filter((tag) =>
selectedTags.includes(tag.tagFQN)
);
const newTags: Array<ColumnTags> = selectedTags
.filter((tag) => {
return !prevTags.map((prevTag) => prevTag.tagFQN).includes(tag);
})
.map((tag) => ({
labelType: 'Manual',
state: 'Confirmed',
tagFQN: tag,
}));
const updatedTags = [...prevTags, ...newTags];
const updatedTopic = { ...topicDetails, tags: updatedTags };
const jsonPatch = compare(topicDetails, updatedTopic);
patchTopicDetails(topicId, jsonPatch).then((res: AxiosResponse) => {
setTier(getTierFromTableTags(res.data.tags));
setTags(getTagsWithoutTier(res.data.tags));
});
}
};
const getInfoBadge = (infos: Array<Record<string, string | number>>) => {
return (
<div className="tw-flex tw-justify-between">
<div className="tw-flex tw-gap-3">
{infos.map((info, index) => (
<div className="tw-mt-4" key={index}>
<span className="tw-py-1.5 tw-px-2 tw-rounded-l tw-bg-tag ">
{info.key}
</span>
<span className="tw-py-1.5 tw-px-2 tw-bg-primary-lite tw-font-normal tw-rounded-r">
{info.value}
</span>
</div>
))}
</div>
<div />
</div>
);
};
const getConfigDetails = () => {
return [
{ key: 'Partitions', value: partitions },
{ key: 'Replication Factor', value: replicationFactor },
{ key: 'Retention Size', value: bytesToSize(retentionSize) },
{ key: 'CleanUp Policies', value: cleanupPolicies.join(',') },
{ key: 'Max Message Size', value: bytesToSize(maximumMessageSize) },
];
};
const getConfigObject = () => {
return {
Partitions: partitions,
'Replication Factor': replicationFactor,
'Retention Size': retentionSize,
'CleanUp Policies': cleanupPolicies,
'Max Message Size': maximumMessageSize,
};
};
useEffect(() => {
fetchTopicDetail(topicFQN);
}, [topicFQN]);
useEffect(() => {
if (isAuthDisabled && AppState.users.length && followers.length) {
setFollowersData(followers);
}
}, [AppState.users, followers]);
useEffect(() => {
fetchTags();
}, []);
return (
<PageContainer>
{isLoading ? (
<Loader />
) : (
<div className="tw-px-4 w-full">
<EntityPageInfo
isTagEditable
entityName={name}
extraInfo={[
{
key: 'Owner',
value:
owner?.type === 'team'
? getTeamDetailsPath(owner?.name || '')
: owner?.name || '',
placeholderText: owner?.displayName || '',
isLink: owner?.type === 'team',
openInNewTab: false,
},
{ key: 'Tier', value: tier ? tier.split('.')[1] : '' },
...getConfigDetails(),
]}
followers={followersCount}
followersList={followers}
followHandler={followTopic}
hasEditAccess={hasEditAccess()}
isFollowing={isFollowing}
owner={owner}
tagList={tagList}
tags={tags}
tagsHandler={onTagUpdate}
tier={tier ?? ''}
titleLinks={slashedTopicName}
/>
<div className="tw-block tw-mt-1">
<TabsPane
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={tabs}
/>
<div className="tw-bg-white tw--mx-4 tw-p-4 tw-min-h-tab">
{activeTab === 1 && (
<>
<div className="tw-grid tw-grid-cols-4 tw-gap-4 w-full">
<div className="tw-col-span-full">
<Description
description={description}
hasEditAccess={hasEditAccess()}
isEdit={isEdit}
owner={owner}
onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit}
onDescriptionUpdate={onDescriptionUpdate}
/>
</div>
</div>
{getInfoBadge([{ key: 'Schema', value: schemaType }])}
<div className="tw-my-4 tw-border tw-border-main tw-rounded-md tw-py-4">
<SchemaEditor value={schemaText} />
</div>
</>
)}
{activeTab === 3 && (
<ManageTab
currentTier={tier}
currentUser={owner?.id}
hasEditAccess={hasEditAccess()}
onSave={onSettingsUpdate}
/>
)}
{activeTab === 2 && (
<SchemaEditor value={JSON.stringify(getConfigObject())} />
)}
</div>
</div>
</div>
)}
</PageContainer>
);
};
export default MyTopicDetailPage;

View File

@ -38,7 +38,7 @@ import StorePage from '../pages/store';
import SwaggerPage from '../pages/swagger'; import SwaggerPage from '../pages/swagger';
import TagsPage from '../pages/tags'; import TagsPage from '../pages/tags';
import TeamsPage from '../pages/teams'; import TeamsPage from '../pages/teams';
import MyTopicDetailPage from '../pages/topic-details'; import TopicDetailsPage from '../pages/TopicDetails/TopicDetailsPage.component';
import TourPage from '../pages/tour-page'; import TourPage from '../pages/tour-page';
import UsersPage from '../pages/users'; import UsersPage from '../pages/users';
import WorkflowsPage from '../pages/workflows'; import WorkflowsPage from '../pages/workflows';
@ -69,7 +69,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
<Route exact component={TagsPage} path={ROUTES.TAGS} /> <Route exact component={TagsPage} path={ROUTES.TAGS} />
<Route component={DatabaseDetails} path={ROUTES.DATABASE_DETAILS} /> <Route component={DatabaseDetails} path={ROUTES.DATABASE_DETAILS} />
<Route component={DatasetDetailsPage} path={ROUTES.DATASET_DETAILS} /> <Route component={DatasetDetailsPage} path={ROUTES.DATASET_DETAILS} />
<Route component={MyTopicDetailPage} path={ROUTES.TOPIC_DETAILS} /> <Route component={TopicDetailsPage} path={ROUTES.TOPIC_DETAILS} />
<Route component={MyDashBoardPage} path={ROUTES.DASHBOARD_DETAILS} /> <Route component={MyDashBoardPage} path={ROUTES.DASHBOARD_DETAILS} />
<Route component={MyPipelinePage} path={ROUTES.PIPELINE_DETAILS} /> <Route component={MyPipelinePage} path={ROUTES.PIPELINE_DETAILS} />
<Route component={Onboarding} path={ROUTES.ONBOARDING} /> <Route component={Onboarding} path={ROUTES.ONBOARDING} />

View File

@ -40,12 +40,14 @@ export const getTagCategories = async (fields?: Array<string> | string) => {
} }
}; };
export const getTaglist = (categories: Array<TagCategory>): Array<string> => { export const getTaglist = (
categories: Array<TagCategory> = []
): Array<string> => {
const children = categories.map((category: TagCategory) => { const children = categories.map((category: TagCategory) => {
return category.children || []; return category.children || [];
}); });
const allChildren = flatten(children); const allChildren = flatten(children);
const tagList = (allChildren as unknown as TagClass[])?.map((tag) => { const tagList = (allChildren as unknown as TagClass[]).map((tag) => {
return tag?.fullyQualifiedName || ''; return tag?.fullyQualifiedName || '';
}); });