mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-05 04:56:54 +00:00
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:
parent
2787ff1b17
commit
53eb2aad21
@ -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;
|
||||||
@ -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;
|
||||||
|
}
|
||||||
@ -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);
|
||||||
@ -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!',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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} />
|
||||||
|
|||||||
@ -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 || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user