mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-19 04:41:02 +00:00
Co-authored-by: darth-coder00 <86726556+darth-coder00@users.noreply.github.com>
This commit is contained in:
parent
f0dd85b9f7
commit
3c89a11aa2
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { EditorContentRef, FormatedUsersData } from 'Models';
|
import { EditorContentRef, FormattedUsersData } from 'Models';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { UrlEntityCharRegEx } from '../../constants/regex.constants';
|
import { UrlEntityCharRegEx } from '../../constants/regex.constants';
|
||||||
import { PageLayoutType } from '../../enums/layout.enum';
|
import { PageLayoutType } from '../../enums/layout.enum';
|
||||||
@ -52,13 +52,13 @@ const AddGlossary = ({
|
|||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [description] = useState<string>('');
|
const [description] = useState<string>('');
|
||||||
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
||||||
const [reviewer, setReviewer] = useState<Array<FormatedUsersData>>([]);
|
const [reviewer, setReviewer] = useState<Array<FormattedUsersData>>([]);
|
||||||
|
|
||||||
const onReviewerModalCancel = () => {
|
const onReviewerModalCancel = () => {
|
||||||
setShowRevieweModal(false);
|
setShowRevieweModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReviewerSave = (reviewer: Array<FormatedUsersData>) => {
|
const handleReviewerSave = (reviewer: Array<FormattedUsersData>) => {
|
||||||
setReviewer(reviewer);
|
setReviewer(reviewer);
|
||||||
onReviewerModalCancel();
|
onReviewerModalCancel();
|
||||||
};
|
};
|
||||||
|
@ -15,8 +15,8 @@ import classNames from 'classnames';
|
|||||||
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
|
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
|
||||||
import {
|
import {
|
||||||
EditorContentRef,
|
EditorContentRef,
|
||||||
FormatedGlossaryTermData,
|
FormattedGlossaryTermData,
|
||||||
FormatedUsersData,
|
FormattedUsersData,
|
||||||
} from 'Models';
|
} from 'Models';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { PageLayoutType } from '../../enums/layout.enum';
|
import { PageLayoutType } from '../../enums/layout.enum';
|
||||||
@ -68,16 +68,16 @@ const AddGlossaryTerm = ({
|
|||||||
const [description] = useState<string>('');
|
const [description] = useState<string>('');
|
||||||
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
||||||
const [showRelatedTermsModal, setShowRelatedTermsModal] = useState(false);
|
const [showRelatedTermsModal, setShowRelatedTermsModal] = useState(false);
|
||||||
const [reviewer, setReviewer] = useState<Array<FormatedUsersData>>([]);
|
const [reviewer, setReviewer] = useState<Array<FormattedUsersData>>([]);
|
||||||
const [relatedTerms, setRelatedTerms] = useState<
|
const [relatedTerms, setRelatedTerms] = useState<
|
||||||
Array<FormatedGlossaryTermData>
|
Array<FormattedGlossaryTermData>
|
||||||
>([]);
|
>([]);
|
||||||
const [synonyms, setSynonyms] = useState('');
|
const [synonyms, setSynonyms] = useState('');
|
||||||
const [references, setReferences] = useState<TermReference[]>([]);
|
const [references, setReferences] = useState<TermReference[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (glossaryData?.reviewers && glossaryData?.reviewers.length) {
|
if (glossaryData?.reviewers && glossaryData?.reviewers.length) {
|
||||||
setReviewer(glossaryData?.reviewers as FormatedUsersData[]);
|
setReviewer(glossaryData?.reviewers as FormattedUsersData[]);
|
||||||
}
|
}
|
||||||
}, [glossaryData]);
|
}, [glossaryData]);
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ const AddGlossaryTerm = ({
|
|||||||
setShowRelatedTermsModal(false);
|
setShowRelatedTermsModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRelatedTermsSave = (terms: Array<FormatedGlossaryTermData>) => {
|
const handleRelatedTermsSave = (terms: Array<FormattedGlossaryTermData>) => {
|
||||||
setRelatedTerms(terms);
|
setRelatedTerms(terms);
|
||||||
onRelatedTermsModalCancel();
|
onRelatedTermsModalCancel();
|
||||||
};
|
};
|
||||||
@ -94,7 +94,7 @@ const AddGlossaryTerm = ({
|
|||||||
setShowRevieweModal(false);
|
setShowRevieweModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReviewerSave = (reviewer: Array<FormatedUsersData>) => {
|
const handleReviewerSave = (reviewer: Array<FormattedUsersData>) => {
|
||||||
setReviewer(reviewer);
|
setReviewer(reviewer);
|
||||||
onReviewerModalCancel();
|
onReviewerModalCancel();
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { EntityTags } from 'Models';
|
import { EntityTags, TagOption } from 'Models';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useAuthContext } from '../../auth-provider/AuthProvider';
|
import { useAuthContext } from '../../auth-provider/AuthProvider';
|
||||||
@ -50,6 +50,10 @@ import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDe
|
|||||||
import TagsContainer from '../tags-container/tags-container';
|
import TagsContainer from '../tags-container/tags-container';
|
||||||
import Tags from '../tags/tags';
|
import Tags from '../tags/tags';
|
||||||
import { ChartType, DashboardDetailsProps } from './DashboardDetails.interface';
|
import { ChartType, DashboardDetailsProps } from './DashboardDetails.interface';
|
||||||
|
import {
|
||||||
|
fetchGlossaryTerms,
|
||||||
|
getGlossaryTermlist,
|
||||||
|
} from '../../utils/GlossaryUtils';
|
||||||
|
|
||||||
const DashboardDetails = ({
|
const DashboardDetails = ({
|
||||||
entityName,
|
entityName,
|
||||||
@ -104,7 +108,8 @@ const DashboardDetails = ({
|
|||||||
chart: ChartType;
|
chart: ChartType;
|
||||||
index: number;
|
index: number;
|
||||||
}>();
|
}>();
|
||||||
const [tagList, setTagList] = useState<Array<string>>([]);
|
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
||||||
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
const [threadLink, setThreadLink] = useState<string>('');
|
const [threadLink, setThreadLink] = useState<string>('');
|
||||||
const [selectedField, setSelectedField] = useState<string>('');
|
const [selectedField, setSelectedField] = useState<string>('');
|
||||||
@ -292,10 +297,7 @@ const DashboardDetails = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChartTagSelection = (
|
const handleChartTagSelection = (selectedTags?: Array<EntityTags>) => {
|
||||||
selectedTags?: Array<EntityTags>,
|
|
||||||
allTags?: Array<string>
|
|
||||||
) => {
|
|
||||||
if (selectedTags && editChartTags) {
|
if (selectedTags && editChartTags) {
|
||||||
const prevTags = editChartTags.chart.tags?.filter((tag) =>
|
const prevTags = editChartTags.chart.tags?.filter((tag) =>
|
||||||
selectedTags.some((selectedTag) => selectedTag.tagFQN === tag.tagFQN)
|
selectedTags.some((selectedTag) => selectedTag.tagFQN === tag.tagFQN)
|
||||||
@ -310,7 +312,7 @@ const DashboardDetails = ({
|
|||||||
.map((tag) => ({
|
.map((tag) => ({
|
||||||
labelType: 'Manual',
|
labelType: 'Manual',
|
||||||
state: 'Confirmed',
|
state: 'Confirmed',
|
||||||
source: (allTags || []).includes(tag.tagFQN) ? 'Tag' : 'Glossary',
|
source: tag.source,
|
||||||
tagFQN: tag.tagFQN,
|
tagFQN: tag.tagFQN,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -330,11 +332,30 @@ const DashboardDetails = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchTags = () => {
|
const fetchTagsAndGlossaryTerms = () => {
|
||||||
setIsTagLoading(true);
|
setIsTagLoading(true);
|
||||||
getTagCategories()
|
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||||
.then((res) => {
|
.then((values) => {
|
||||||
setTagList(getTaglist(res.data));
|
let tagsAndTerms: TagOption[] = [];
|
||||||
|
if (values[0].data) {
|
||||||
|
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||||
|
return { fqn: tag, source: 'Tag' };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (values[1] && values[1].length > 0) {
|
||||||
|
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||||
|
(tag) => {
|
||||||
|
return { fqn: tag, source: 'Glossary' };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||||
|
}
|
||||||
|
setTagList(tagsAndTerms);
|
||||||
|
setTagFetchFailed(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setTagList([]);
|
||||||
|
setTagFetchFailed(true);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsTagLoading(false);
|
setIsTagLoading(false);
|
||||||
@ -505,7 +526,10 @@ const DashboardDetails = ({
|
|||||||
className="tw-group tw-relative tableBody-cell"
|
className="tw-group tw-relative tableBody-cell"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!editChartTags) {
|
if (!editChartTags) {
|
||||||
fetchTags();
|
// Fetch tags and terms only once
|
||||||
|
if (tagList.length === 0 || tagFetchFailed) {
|
||||||
|
fetchTagsAndGlossaryTerms();
|
||||||
|
}
|
||||||
handleEditChartTag(chart, index);
|
handleEditChartTag(chart, index);
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
@ -532,7 +556,6 @@ const DashboardDetails = ({
|
|||||||
position="left"
|
position="left"
|
||||||
trigger="click">
|
trigger="click">
|
||||||
<TagsContainer
|
<TagsContainer
|
||||||
allowGlossary
|
|
||||||
editable={editChartTags?.index === index}
|
editable={editChartTags?.index === index}
|
||||||
isLoading={
|
isLoading={
|
||||||
isTagLoading &&
|
isTagLoading &&
|
||||||
@ -546,7 +569,7 @@ const DashboardDetails = ({
|
|||||||
handleChartTagSelection();
|
handleChartTagSelection();
|
||||||
}}
|
}}
|
||||||
onSelectionChange={(tags) => {
|
onSelectionChange={(tags) => {
|
||||||
handleChartTagSelection(tags, tagList);
|
handleChartTagSelection(tags);
|
||||||
}}>
|
}}>
|
||||||
{chart.tags?.length ? (
|
{chart.tags?.length ? (
|
||||||
<button
|
<button
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { cloneDeep, isNil, isUndefined, lowerCase } from 'lodash';
|
import { cloneDeep, isNil, isUndefined, lowerCase } from 'lodash';
|
||||||
import { EntityFieldThreads, EntityTags } from 'Models';
|
import { EntityFieldThreads, EntityTags, TagOption } from 'Models';
|
||||||
import React, { Fragment, useEffect, useState } from 'react';
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useExpanded, useTable } from 'react-table';
|
import { useExpanded, useTable } from 'react-table';
|
||||||
@ -52,6 +52,10 @@ import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPr
|
|||||||
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
||||||
import TagsContainer from '../tags-container/tags-container';
|
import TagsContainer from '../tags-container/tags-container';
|
||||||
import Tags from '../tags/tags';
|
import Tags from '../tags/tags';
|
||||||
|
import {
|
||||||
|
fetchGlossaryTerms,
|
||||||
|
getGlossaryTermlist,
|
||||||
|
} from '../../utils/GlossaryUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
owner: Table['owner'];
|
owner: Table['owner'];
|
||||||
@ -141,16 +145,34 @@ const EntityTable = ({
|
|||||||
index: number;
|
index: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [allTags, setAllTags] = useState<Array<string>>([]);
|
const [allTags, setAllTags] = useState<Array<TagOption>>([]);
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
|
|
||||||
const fetchTags = () => {
|
const fetchTagsAndGlossaryTerms = () => {
|
||||||
setIsTagLoading(true);
|
setIsTagLoading(true);
|
||||||
getTagCategories()
|
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||||
.then((res) => {
|
.then((values) => {
|
||||||
if (res.data) {
|
let tagsAndTerms: TagOption[] = [];
|
||||||
setAllTags(getTaglist(res.data));
|
if (values[0].data) {
|
||||||
|
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||||
|
return { fqn: tag, source: 'Tag' };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
if (values[1] && values[1].length > 0) {
|
||||||
|
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||||
|
(tag) => {
|
||||||
|
return { fqn: tag, source: 'Glossary' };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||||
|
}
|
||||||
|
setAllTags(tagsAndTerms);
|
||||||
|
setTagFetchFailed(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setAllTags([]);
|
||||||
|
setTagFetchFailed(true);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsTagLoading(false);
|
setIsTagLoading(false);
|
||||||
@ -189,22 +211,24 @@ const EntityTable = ({
|
|||||||
const updateColumnTags = (
|
const updateColumnTags = (
|
||||||
tableCols: ModifiedTableColumn[],
|
tableCols: ModifiedTableColumn[],
|
||||||
changedColName: string,
|
changedColName: string,
|
||||||
newColumnTags: Array<string>
|
newColumnTags: Array<TagOption>
|
||||||
) => {
|
) => {
|
||||||
const getUpdatedTags = (column: Column) => {
|
const getUpdatedTags = (column: Column) => {
|
||||||
const prevTags = column?.tags?.filter((tag) => {
|
const prevTags = column?.tags?.filter((tag) => {
|
||||||
return newColumnTags.includes(tag?.tagFQN as string);
|
return newColumnTags
|
||||||
|
.map((tag) => tag.fqn)
|
||||||
|
.includes(tag?.tagFQN as string);
|
||||||
});
|
});
|
||||||
|
|
||||||
const newTags: Array<EntityTags> = newColumnTags
|
const newTags: Array<EntityTags> = newColumnTags
|
||||||
.filter((tag) => {
|
.filter((tag) => {
|
||||||
return !prevTags?.map((prevTag) => prevTag.tagFQN).includes(tag);
|
return !prevTags?.map((prevTag) => prevTag.tagFQN).includes(tag.fqn);
|
||||||
})
|
})
|
||||||
.map((tag) => ({
|
.map((tag) => ({
|
||||||
labelType: LabelType.Manual,
|
labelType: LabelType.Manual,
|
||||||
state: State.Confirmed,
|
state: State.Confirmed,
|
||||||
source: allTags.includes(tag) ? 'Tag' : 'Glossary',
|
source: tag.source,
|
||||||
tagFQN: tag,
|
tagFQN: tag.fqn,
|
||||||
}));
|
}));
|
||||||
const updatedTags = [...(prevTags as TagLabel[]), ...newTags];
|
const updatedTags = [...(prevTags as TagLabel[]), ...newTags];
|
||||||
|
|
||||||
@ -240,7 +264,11 @@ const EntityTable = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
||||||
const newSelectedTags = selectedTags?.map((tag) => tag.tagFQN);
|
const newSelectedTags: TagOption[] | undefined = selectedTags?.map(
|
||||||
|
(tag) => {
|
||||||
|
return { fqn: tag.tagFQN, source: tag.source };
|
||||||
|
}
|
||||||
|
);
|
||||||
if (newSelectedTags && editColumnTag) {
|
if (newSelectedTags && editColumnTag) {
|
||||||
const tableCols = cloneDeep(tableColumns);
|
const tableCols = cloneDeep(tableColumns);
|
||||||
updateColumnTags(tableCols, editColumnTag.column.name, newSelectedTags);
|
updateColumnTags(tableCols, editColumnTag.column.name, newSelectedTags);
|
||||||
@ -485,7 +513,10 @@ const EntityTable = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!editColumnTag) {
|
if (!editColumnTag) {
|
||||||
handleEditColumnTag(row.original, row.id);
|
handleEditColumnTag(row.original, row.id);
|
||||||
fetchTags();
|
// Fetch tags and terms only once
|
||||||
|
if (allTags.length === 0 || tagFetchFailed) {
|
||||||
|
fetchTagsAndGlossaryTerms();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<NonAdminAction
|
<NonAdminAction
|
||||||
@ -495,7 +526,6 @@ const EntityTable = ({
|
|||||||
position="left"
|
position="left"
|
||||||
trigger="click">
|
trigger="click">
|
||||||
<TagsContainer
|
<TagsContainer
|
||||||
allowGlossary
|
|
||||||
editable={editColumnTag?.index === row.id}
|
editable={editColumnTag?.index === row.id}
|
||||||
isLoading={
|
isLoading={
|
||||||
isTagLoading &&
|
isTagLoading &&
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { cloneDeep, includes, isEqual } from 'lodash';
|
import { cloneDeep, includes, isEqual } from 'lodash';
|
||||||
import { EntityTags, FormatedUsersData } from 'Models';
|
import { EntityTags, FormattedUsersData } from 'Models';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
LIST_SIZE,
|
LIST_SIZE,
|
||||||
@ -50,7 +50,7 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
|||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
const [showRevieweModal, setShowRevieweModal] = useState(false);
|
||||||
const [reviewer, setReviewer] = useState<Array<FormatedUsersData>>([]);
|
const [reviewer, setReviewer] = useState<Array<FormattedUsersData>>([]);
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
@ -70,7 +70,7 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
|||||||
setShowRevieweModal(false);
|
setShowRevieweModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReviewerSave = (data: Array<FormatedUsersData>) => {
|
const handleReviewerSave = (data: Array<FormattedUsersData>) => {
|
||||||
if (!isEqual(data, reviewer)) {
|
if (!isEqual(data, reviewer)) {
|
||||||
let updatedGlossary = cloneDeep(glossary);
|
let updatedGlossary = cloneDeep(glossary);
|
||||||
const oldReviewer = data.filter((d) => includes(reviewer, d));
|
const oldReviewer = data.filter((d) => includes(reviewer, d));
|
||||||
@ -171,7 +171,7 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
|||||||
if (glossary.reviewers && glossary.reviewers.length) {
|
if (glossary.reviewers && glossary.reviewers.length) {
|
||||||
setReviewer(
|
setReviewer(
|
||||||
glossary.reviewers.map((d) => ({
|
glossary.reviewers.map((d) => ({
|
||||||
...(d as FormatedUsersData),
|
...(d as FormattedUsersData),
|
||||||
type: 'user',
|
type: 'user',
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
@ -15,8 +15,8 @@ import classNames from 'classnames';
|
|||||||
import { cloneDeep, includes, isEqual } from 'lodash';
|
import { cloneDeep, includes, isEqual } from 'lodash';
|
||||||
import {
|
import {
|
||||||
EntityTags,
|
EntityTags,
|
||||||
FormatedGlossaryTermData,
|
FormattedGlossaryTermData,
|
||||||
FormatedUsersData,
|
FormattedUsersData,
|
||||||
GlossaryTermAssets,
|
GlossaryTermAssets,
|
||||||
} from 'Models';
|
} from 'Models';
|
||||||
import React, { Fragment, useEffect, useState } from 'react';
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
@ -73,7 +73,7 @@ const GlossaryTermsV1 = ({
|
|||||||
);
|
);
|
||||||
const [references, setReferences] = useState(glossaryTerm.references || []);
|
const [references, setReferences] = useState(glossaryTerm.references || []);
|
||||||
const [reviewer, setReviewer] = useState<Array<FormatedUsersData>>([]);
|
const [reviewer, setReviewer] = useState<Array<FormatedUsersData>>([]);
|
||||||
const [relatedTerms, setRelatedTerms] = useState<FormatedGlossaryTermData[]>(
|
const [relatedTerms, setRelatedTerms] = useState<FormattedGlossaryTermData[]>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ const GlossaryTermsV1 = ({
|
|||||||
setShowRevieweModal(false);
|
setShowRevieweModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReviewerSave = (data: Array<FormatedUsersData>) => {
|
const handleReviewerSave = (data: Array<FormattedUsersData>) => {
|
||||||
if (!isEqual(data, reviewer)) {
|
if (!isEqual(data, reviewer)) {
|
||||||
let updatedGlossaryTerm = cloneDeep(glossaryTerm);
|
let updatedGlossaryTerm = cloneDeep(glossaryTerm);
|
||||||
const oldReviewer = data.filter((d) => includes(reviewer, d));
|
const oldReviewer = data.filter((d) => includes(reviewer, d));
|
||||||
@ -277,7 +277,7 @@ const GlossaryTermsV1 = ({
|
|||||||
if (glossaryTerm.reviewers && glossaryTerm.reviewers.length) {
|
if (glossaryTerm.reviewers && glossaryTerm.reviewers.length) {
|
||||||
setReviewer(
|
setReviewer(
|
||||||
glossaryTerm.reviewers.map((d) => ({
|
glossaryTerm.reviewers.map((d) => ({
|
||||||
...(d as FormatedUsersData),
|
...(d as FormattedUsersData),
|
||||||
type: 'user',
|
type: 'user',
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import { FormatedGlossaryTermData, SearchResponse } from 'Models';
|
import { FormattedGlossaryTermData, SearchResponse } from 'Models';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { searchData } from '../../../axiosAPIs/miscAPI';
|
import { searchData } from '../../../axiosAPIs/miscAPI';
|
||||||
import { PAGE_SIZE } from '../../../constants/constants';
|
import { PAGE_SIZE } from '../../../constants/constants';
|
||||||
@ -24,9 +24,9 @@ import Searchbar from '../../common/searchbar/Searchbar';
|
|||||||
import Loader from '../../Loader/Loader';
|
import Loader from '../../Loader/Loader';
|
||||||
|
|
||||||
type RelatedTermsModalProp = {
|
type RelatedTermsModalProp = {
|
||||||
relatedTerms?: Array<FormatedGlossaryTermData>;
|
relatedTerms?: Array<FormattedGlossaryTermData>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSave: (terms: Array<FormatedGlossaryTermData>) => void;
|
onSave: (terms: Array<FormattedGlossaryTermData>) => void;
|
||||||
header: string;
|
header: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,14 +38,14 @@ const RelatedTermsModal = ({
|
|||||||
}: RelatedTermsModalProp) => {
|
}: RelatedTermsModalProp) => {
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText, setSearchText] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [options, setOptions] = useState<FormatedGlossaryTermData[]>([]);
|
const [options, setOptions] = useState<FormattedGlossaryTermData[]>([]);
|
||||||
const [selectedOption, setSelectedOption] = useState<
|
const [selectedOption, setSelectedOption] = useState<
|
||||||
FormatedGlossaryTermData[]
|
FormattedGlossaryTermData[]
|
||||||
>(relatedTerms ?? []);
|
>(relatedTerms ?? []);
|
||||||
|
|
||||||
const getSearchedTerms = (searchedData: FormatedGlossaryTermData[]) => {
|
const getSearchedTerms = (searchedData: FormattedGlossaryTermData[]) => {
|
||||||
const currOptions = selectedOption.map((item) => item.fqdn || item.name);
|
const currOptions = selectedOption.map((item) => item.fqdn || item.name);
|
||||||
const data = searchedData.filter((item: FormatedGlossaryTermData) => {
|
const data = searchedData.filter((item: FormattedGlossaryTermData) => {
|
||||||
return !currOptions.includes(item.fqdn);
|
return !currOptions.includes(item.fqdn);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,8 +81,8 @@ const RelatedTermsModal = ({
|
|||||||
if (!isChecked) {
|
if (!isChecked) {
|
||||||
setSelectedOption((pre) => pre.filter((option) => option.id !== id));
|
setSelectedOption((pre) => pre.filter((option) => option.id !== id));
|
||||||
} else {
|
} else {
|
||||||
const newOption: FormatedGlossaryTermData =
|
const newOption: FormattedGlossaryTermData =
|
||||||
options.find((d) => d.id === id) || ({} as FormatedGlossaryTermData);
|
options.find((d) => d.id === id) || ({} as FormattedGlossaryTermData);
|
||||||
setSelectedOption([...selectedOption, newOption]);
|
setSelectedOption([...selectedOption, newOption]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import { FormatedUsersData, SearchResponse } from 'Models';
|
import { FormattedUsersData, SearchResponse } from 'Models';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { getSuggestions, searchData } from '../../../axiosAPIs/miscAPI';
|
import { getSuggestions, searchData } from '../../../axiosAPIs/miscAPI';
|
||||||
import { WILD_CARD_CHAR } from '../../../constants/char.constants';
|
import { WILD_CARD_CHAR } from '../../../constants/char.constants';
|
||||||
@ -25,9 +25,9 @@ import Searchbar from '../../common/searchbar/Searchbar';
|
|||||||
import Loader from '../../Loader/Loader';
|
import Loader from '../../Loader/Loader';
|
||||||
|
|
||||||
type ReviewerModalProp = {
|
type ReviewerModalProp = {
|
||||||
reviewer?: Array<FormatedUsersData>;
|
reviewer?: Array<FormattedUsersData>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSave: (reviewer: Array<FormatedUsersData>) => void;
|
onSave: (reviewer: Array<FormattedUsersData>) => void;
|
||||||
header: string;
|
header: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ const ReviewerModal = ({
|
|||||||
}: ReviewerModalProp) => {
|
}: ReviewerModalProp) => {
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText, setSearchText] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [options, setOptions] = useState<FormatedUsersData[]>([]);
|
const [options, setOptions] = useState<FormattedUsersData[]>([]);
|
||||||
const [selectedOption, setSelectedOption] = useState<FormatedUsersData[]>(
|
const [selectedOption, setSelectedOption] = useState<FormattedUsersData[]>(
|
||||||
reviewer ?? []
|
reviewer ?? []
|
||||||
);
|
);
|
||||||
|
|
||||||
const getSearchedReviewers = (searchedData: FormatedUsersData[]) => {
|
const getSearchedReviewers = (searchedData: FormattedUsersData[]) => {
|
||||||
const currOptions = selectedOption.map((item) => item.name);
|
const currOptions = selectedOption.map((item) => item.name);
|
||||||
const data = searchedData.filter((item: FormatedUsersData) => {
|
const data = searchedData.filter((item: FormattedUsersData) => {
|
||||||
return !currOptions.includes(item.name);
|
return !currOptions.includes(item.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,8 +98,8 @@ const ReviewerModal = ({
|
|||||||
if (!isChecked) {
|
if (!isChecked) {
|
||||||
setSelectedOption((pre) => pre.filter((option) => option.id !== id));
|
setSelectedOption((pre) => pre.filter((option) => option.id !== id));
|
||||||
} else {
|
} else {
|
||||||
const newOption: FormatedUsersData =
|
const newOption: FormattedUsersData =
|
||||||
options.find((d) => d.id === id) || ({} as FormatedUsersData);
|
options.find((d) => d.id === id) || ({} as FormattedUsersData);
|
||||||
setSelectedOption([...selectedOption, newOption]);
|
setSelectedOption([...selectedOption, newOption]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,13 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { isEmpty, isUndefined } from 'lodash';
|
import { isEmpty, isUndefined } from 'lodash';
|
||||||
import { EntityFieldThreads, EntityTags, ExtraInfo, TableDetail } from 'Models';
|
import {
|
||||||
|
EntityFieldThreads,
|
||||||
|
EntityTags,
|
||||||
|
ExtraInfo,
|
||||||
|
TableDetail,
|
||||||
|
TagOption,
|
||||||
|
} from 'Models';
|
||||||
import React, { Fragment, useEffect, useState } from 'react';
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
import { FOLLOWERS_VIEW_CAP, LIST_SIZE } from '../../../constants/constants';
|
import { FOLLOWERS_VIEW_CAP, LIST_SIZE } from '../../../constants/constants';
|
||||||
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
||||||
@ -32,6 +38,10 @@ import PopOver from '../popover/PopOver';
|
|||||||
import TitleBreadcrumb from '../title-breadcrumb/title-breadcrumb.component';
|
import TitleBreadcrumb from '../title-breadcrumb/title-breadcrumb.component';
|
||||||
import { TitleBreadcrumbProps } from '../title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from '../title-breadcrumb/title-breadcrumb.interface';
|
||||||
import FollowersModal from './FollowersModal';
|
import FollowersModal from './FollowersModal';
|
||||||
|
import {
|
||||||
|
fetchGlossaryTerms,
|
||||||
|
getGlossaryTermlist,
|
||||||
|
} from '../../../utils/GlossaryUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
titleLinks: TitleBreadcrumbProps['titleLinks'];
|
titleLinks: TitleBreadcrumbProps['titleLinks'];
|
||||||
@ -85,7 +95,8 @@ const EntityPageInfo = ({
|
|||||||
const [entityFollowers, setEntityFollowers] =
|
const [entityFollowers, setEntityFollowers] =
|
||||||
useState<Array<User>>(followersList);
|
useState<Array<User>>(followersList);
|
||||||
const [isViewMore, setIsViewMore] = useState<boolean>(false);
|
const [isViewMore, setIsViewMore] = useState<boolean>(false);
|
||||||
const [tagList, setTagList] = useState<Array<string>>([]);
|
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
||||||
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
||||||
@ -105,7 +116,7 @@ const EntityPageInfo = ({
|
|||||||
.map((tag) => ({
|
.map((tag) => ({
|
||||||
labelType: LabelType.Manual,
|
labelType: LabelType.Manual,
|
||||||
state: State.Confirmed,
|
state: State.Confirmed,
|
||||||
source: tagList.includes(tag.tagFQN) ? 'Tag' : 'Glossary',
|
source: tag.source,
|
||||||
tagFQN: tag.tagFQN,
|
tagFQN: tag.tagFQN,
|
||||||
}));
|
}));
|
||||||
tagsHandler?.([...prevTags, ...newTags]);
|
tagsHandler?.([...prevTags, ...newTags]);
|
||||||
@ -206,11 +217,31 @@ const EntityPageInfo = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const fetchTags = () => {
|
|
||||||
|
const fetchTagsAndGlossaryTerms = () => {
|
||||||
setIsTagLoading(true);
|
setIsTagLoading(true);
|
||||||
getTagCategories()
|
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||||
.then((res) => {
|
.then((values) => {
|
||||||
setTagList(getTaglist(res.data));
|
let tagsAndTerms: TagOption[] = [];
|
||||||
|
if (values[0].data) {
|
||||||
|
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||||
|
return { fqn: tag, source: 'Tag' };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (values[1] && values[1].length > 0) {
|
||||||
|
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||||
|
(tag) => {
|
||||||
|
return { fqn: tag, source: 'Glossary' };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||||
|
}
|
||||||
|
setTagList(tagsAndTerms);
|
||||||
|
setTagFetchFailed(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setTagList([]);
|
||||||
|
setTagFetchFailed(true);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsTagLoading(false);
|
setIsTagLoading(false);
|
||||||
@ -392,11 +423,13 @@ const EntityPageInfo = ({
|
|||||||
<div
|
<div
|
||||||
className="tw-inline-block"
|
className="tw-inline-block"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
fetchTags();
|
// Fetch tags and terms only once
|
||||||
|
if (tagList.length === 0 || tagFetchFailed) {
|
||||||
|
fetchTagsAndGlossaryTerms();
|
||||||
|
}
|
||||||
setIsEditable(true);
|
setIsEditable(true);
|
||||||
}}>
|
}}>
|
||||||
<TagsContainer
|
<TagsContainer
|
||||||
allowGlossary
|
|
||||||
editable={isEditable}
|
editable={isEditable}
|
||||||
isLoading={isTagLoading}
|
isLoading={isTagLoading}
|
||||||
selectedTags={getSelectedTags()}
|
selectedTags={getSelectedTags()}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { TagOption } from 'Models';
|
||||||
|
|
||||||
export enum DropDownType {
|
export enum DropDownType {
|
||||||
LINK = 'link',
|
LINK = 'link',
|
||||||
@ -21,7 +22,7 @@ export enum DropDownType {
|
|||||||
|
|
||||||
export type DropDownListItem = {
|
export type DropDownListItem = {
|
||||||
name: string | React.ReactElement;
|
name: string | React.ReactElement;
|
||||||
value?: string;
|
value?: string | TagOption;
|
||||||
group?: string;
|
group?: string;
|
||||||
to?: string;
|
to?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
@ -11,17 +11,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EntityTags } from 'Models';
|
import { EntityTags, TagOption } from 'Models';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { TagProps } from '../tags/tags.interface';
|
import { TagProps } from '../tags/tags.interface';
|
||||||
|
|
||||||
export type TagsContainerProps = {
|
export type TagsContainerProps = {
|
||||||
allowGlossary?: boolean;
|
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
dropDownHorzPosRight?: boolean;
|
dropDownHorzPosRight?: boolean;
|
||||||
selectedTags: Array<EntityTags>;
|
selectedTags: Array<EntityTags>;
|
||||||
tagList: Array<string>;
|
tagList: Array<TagOption>;
|
||||||
type?: TagProps['type'];
|
type?: TagProps['type'];
|
||||||
showTags?: boolean;
|
showTags?: boolean;
|
||||||
onSelectionChange: (selectedTags: Array<EntityTags>) => void;
|
onSelectionChange: (selectedTags: Array<EntityTags>) => void;
|
||||||
|
@ -15,7 +15,11 @@ import { getByTestId, render } from '@testing-library/react';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TagsContainer from './tags-container';
|
import TagsContainer from './tags-container';
|
||||||
|
|
||||||
const tagList = ['tag 1', 'tag 2', 'tag 3'];
|
const tagList = [
|
||||||
|
{ fqn: 'tag 1', source: 'Tag' },
|
||||||
|
{ fqn: 'tag 2', source: 'Tag' },
|
||||||
|
{ fqn: 'tag 3', source: 'Glossary' },
|
||||||
|
];
|
||||||
const onCancel = jest.fn();
|
const onCancel = jest.fn();
|
||||||
const onSelectionChange = jest.fn();
|
const onSelectionChange = jest.fn();
|
||||||
|
|
||||||
@ -38,7 +42,7 @@ describe('Test TagsContainer Component', () => {
|
|||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const TagContainer = getByTestId(container, 'tag-conatiner');
|
const TagContainer = getByTestId(container, 'tag-container');
|
||||||
|
|
||||||
expect(TagContainer).toBeInTheDocument();
|
expect(TagContainer).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -12,20 +12,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { debounce, isEmpty, isNull } from 'lodash';
|
import { isNull } from 'lodash';
|
||||||
import { EntityTags, FormatedGlossaryTermData, SearchResponse } from 'Models';
|
import { EntityTags } from 'Models';
|
||||||
import React, {
|
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||||
FunctionComponent,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { searchData } from '../../axiosAPIs/miscAPI';
|
|
||||||
import { PAGE_SIZE } from '../../constants/constants';
|
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
|
||||||
import { withLoader } from '../../hoc/withLoader';
|
import { withLoader } from '../../hoc/withLoader';
|
||||||
import { formatSearchGlossaryTermResponse } from '../../utils/APIUtils';
|
|
||||||
import { Button } from '../buttons/Button/Button';
|
import { Button } from '../buttons/Button/Button';
|
||||||
import DropDownList from '../dropdown/DropDownList';
|
import DropDownList from '../dropdown/DropDownList';
|
||||||
import Tags from '../tags/tags';
|
import Tags from '../tags/tags';
|
||||||
@ -36,7 +26,6 @@ import { TagsContainerProps } from './tags-container.interface';
|
|||||||
// const INPUT_AUTO = 'auto';
|
// const INPUT_AUTO = 'auto';
|
||||||
|
|
||||||
const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
||||||
allowGlossary,
|
|
||||||
children,
|
children,
|
||||||
editable,
|
editable,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
@ -53,10 +42,6 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const node = useRef<HTMLDivElement>(null);
|
const node = useRef<HTMLDivElement>(null);
|
||||||
const [inputDomRect, setInputDomRect] = useState<DOMRect>();
|
const [inputDomRect, setInputDomRect] = useState<DOMRect>();
|
||||||
const [glossaryList, setGlossaryList] = useState<FormatedGlossaryTermData[]>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [glossaryLoading, setGlossaryLoading] = useState<boolean>(false);
|
|
||||||
// const [inputWidth, setInputWidth] = useState(INPUT_COLLAPED);
|
// const [inputWidth, setInputWidth] = useState(INPUT_COLLAPED);
|
||||||
// const [inputMinWidth, setInputMinWidth] = useState(INPUT_AUTO);
|
// const [inputMinWidth, setInputMinWidth] = useState(INPUT_AUTO);
|
||||||
|
|
||||||
@ -84,63 +69,20 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
}
|
}
|
||||||
}, [newTag]);
|
}, [newTag]);
|
||||||
|
|
||||||
const fetchGlossaryResults = (
|
|
||||||
searchText: string
|
|
||||||
): Promise<FormatedGlossaryTermData[]> => {
|
|
||||||
return new Promise<FormatedGlossaryTermData[]>((resolve, reject) => {
|
|
||||||
if (!isEmpty(searchText)) {
|
|
||||||
searchData(searchText, 1, PAGE_SIZE, '', '', '', SearchIndex.GLOSSARY)
|
|
||||||
.then((res: SearchResponse) => {
|
|
||||||
const data = formatSearchGlossaryTermResponse(
|
|
||||||
res?.data?.hits?.hits || []
|
|
||||||
);
|
|
||||||
resolve(data);
|
|
||||||
})
|
|
||||||
.catch(() => reject());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getGlossaryResults = useCallback(
|
|
||||||
(searchText: string) => {
|
|
||||||
if (allowGlossary) {
|
|
||||||
fetchGlossaryResults(searchText)
|
|
||||||
.then((res) => setGlossaryList(res))
|
|
||||||
.finally(() => {
|
|
||||||
setGlossaryLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[allowGlossary]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getTagList = () => {
|
const getTagList = () => {
|
||||||
const newTags = tagList
|
const newTags = tagList
|
||||||
.filter((tag) => {
|
.filter((tag) => {
|
||||||
return !tags.some((selectedTag) => selectedTag.tagFQN === tag);
|
return !tags.some((selectedTag) => selectedTag.tagFQN === tag.fqn);
|
||||||
})
|
})
|
||||||
.filter((tag) => !tag.includes('Tier'))
|
.filter((tag) => !tag.fqn?.includes('Tier'))
|
||||||
.map((tag) => {
|
.map((tag) => {
|
||||||
return {
|
return {
|
||||||
name: tag,
|
name: tag.fqn,
|
||||||
value: tag,
|
value: tag.fqn,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const newGlossaries = glossaryList
|
return newTags;
|
||||||
.filter((glossary) => {
|
|
||||||
return !tags.some(
|
|
||||||
(selectedTag) => selectedTag.tagFQN === glossary.fqdn
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((glossary) => {
|
|
||||||
return {
|
|
||||||
name: glossary.fqdn,
|
|
||||||
value: glossary.fqdn,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return allowGlossary ? [...newTags, ...newGlossaries] : newTags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTagSelection = (
|
const handleTagSelection = (
|
||||||
@ -151,13 +93,9 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (selectedTag) {
|
if (selectedTag) {
|
||||||
setTags((arrTags) => {
|
setTags((arrTags) => {
|
||||||
const tag =
|
const source = tagList.find((tag) => tag.fqn === selectedTag)?.source;
|
||||||
arrTags.filter((tag) => tag.tagFQN === selectedTag)[0] || {};
|
|
||||||
if (!isEmpty(tag)) {
|
return [...arrTags, { tagFQN: selectedTag, source }];
|
||||||
return [...arrTags, { ...tag, tagFQN: selectedTag }];
|
|
||||||
} else {
|
|
||||||
return [...arrTags, { tagFQN: selectedTag }];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setNewTag('');
|
setNewTag('');
|
||||||
@ -206,26 +144,9 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedOnSearch = useCallback(
|
|
||||||
(searchText: string): void => {
|
|
||||||
getGlossaryResults(searchText);
|
|
||||||
},
|
|
||||||
[getGlossaryResults]
|
|
||||||
);
|
|
||||||
|
|
||||||
const debounceOnSearch = useCallback(debounce(debouncedOnSearch, 500), [
|
|
||||||
debouncedOnSearch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<{ value: string }>): void => {
|
const handleChange = (e: React.ChangeEvent<{ value: string }>): void => {
|
||||||
const searchText = e.target.value;
|
const searchText = e.target.value;
|
||||||
setNewTag(searchText);
|
setNewTag(searchText);
|
||||||
if (allowGlossary && searchText) {
|
|
||||||
setGlossaryLoading(true);
|
|
||||||
debounceOnSearch(searchText);
|
|
||||||
} else {
|
|
||||||
setGlossaryLoading(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e: MouseEvent) => {
|
const handleClick = (e: MouseEvent) => {
|
||||||
@ -262,7 +183,7 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
{ 'tw-border-primary': hasFocus },
|
{ 'tw-border-primary': hasFocus },
|
||||||
{ 'hover:tw-border-main': !hasFocus }
|
{ 'hover:tw-border-main': !hasFocus }
|
||||||
)}
|
)}
|
||||||
data-testid="tag-conatiner"
|
data-testid="tag-container"
|
||||||
ref={node}
|
ref={node}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
if (editable) {
|
if (editable) {
|
||||||
@ -303,7 +224,6 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
|||||||
domPosition={inputDomRect}
|
domPosition={inputDomRect}
|
||||||
dropDownList={getTagList()}
|
dropDownList={getTagList()}
|
||||||
horzPosRight={dropDownHorzPosRight}
|
horzPosRight={dropDownHorzPosRight}
|
||||||
isLoading={glossaryLoading}
|
|
||||||
searchString={newTag}
|
searchString={newTag}
|
||||||
widthClass="tw-w-80"
|
widthClass="tw-w-80"
|
||||||
onSelect={handleTagSelection}
|
onSelect={handleTagSelection}
|
||||||
|
@ -31,7 +31,6 @@ const Tags: FunctionComponent<TagProps> = ({
|
|||||||
const baseStyle = tagStyles.base;
|
const baseStyle = tagStyles.base;
|
||||||
const layoutStyles = tagStyles[type];
|
const layoutStyles = tagStyles[type];
|
||||||
const textBaseStyle = tagStyles.text.base;
|
const textBaseStyle = tagStyles.text.base;
|
||||||
const textLayoutStyles = tagStyles.text[type] || tagStyles.text.default;
|
|
||||||
const textEditStyles = editable ? tagStyles.text.editable : '';
|
const textEditStyles = editable ? tagStyles.text.editable : '';
|
||||||
|
|
||||||
const getTagString = (tag: string) => {
|
const getTagString = (tag: string) => {
|
||||||
@ -43,12 +42,7 @@ const Tags: FunctionComponent<TagProps> = ({
|
|||||||
<span
|
<span
|
||||||
className={classNames(baseStyle, layoutStyles, className)}
|
className={classNames(baseStyle, layoutStyles, className)}
|
||||||
data-testid="tags">
|
data-testid="tags">
|
||||||
<span
|
<span className={classNames(textBaseStyle, textEditStyles)}>
|
||||||
className={classNames(
|
|
||||||
textBaseStyle,
|
|
||||||
textLayoutStyles,
|
|
||||||
textEditStyles
|
|
||||||
)}>
|
|
||||||
{`${startWith}${tag}`}
|
{`${startWith}${tag}`}
|
||||||
</span>
|
</span>
|
||||||
{editable && isRemovable && (
|
{editable && isRemovable && (
|
||||||
|
@ -209,7 +209,7 @@ declare module 'Models' {
|
|||||||
entityType?: string;
|
entityType?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormatedUsersData = {
|
export type FormattedUsersData = {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
email: string;
|
email: string;
|
||||||
@ -217,7 +217,7 @@ declare module 'Models' {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormatedGlossaryTermData = {
|
export type FormattedGlossaryTermData = {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
fqdn: string;
|
fqdn: string;
|
||||||
@ -226,7 +226,12 @@ declare module 'Models' {
|
|||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface FormatedGlossarySuggestion {
|
export type TagOption = {
|
||||||
|
fqn: string;
|
||||||
|
source: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface FormattedGlossarySuggestion {
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
@ -244,7 +249,7 @@ declare module 'Models' {
|
|||||||
_type?: string;
|
_type?: string;
|
||||||
_id?: string;
|
_id?: string;
|
||||||
_score?: number;
|
_score?: number;
|
||||||
_source: FormatedGlossarySuggestion;
|
_source: FormattedGlossarySuggestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GlossaryTermAssets {
|
export interface GlossaryTermAssets {
|
||||||
|
@ -15,7 +15,7 @@ import { AxiosError, AxiosResponse } from 'axios';
|
|||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { cloneDeep, extend } from 'lodash';
|
import { cloneDeep, extend } from 'lodash';
|
||||||
import {
|
import {
|
||||||
FormatedGlossarySuggestion,
|
FormattedGlossarySuggestion,
|
||||||
GlossarySuggestionHit,
|
GlossarySuggestionHit,
|
||||||
GlossaryTermAssets,
|
GlossaryTermAssets,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
@ -229,7 +229,7 @@ const GlossaryPageV1 = () => {
|
|||||||
const getSearchedGlossaries = (
|
const getSearchedGlossaries = (
|
||||||
arrGlossaries: ModifiedGlossaryData[],
|
arrGlossaries: ModifiedGlossaryData[],
|
||||||
newGlossaries: string[],
|
newGlossaries: string[],
|
||||||
searchedTerms: FormatedGlossarySuggestion[]
|
searchedTerms: FormattedGlossarySuggestion[]
|
||||||
) => {
|
) => {
|
||||||
if (newGlossaries.length) {
|
if (newGlossaries.length) {
|
||||||
let arrNewData: ModifiedGlossaryData[] = [];
|
let arrNewData: ModifiedGlossaryData[] = [];
|
||||||
@ -269,7 +269,7 @@ const GlossaryPageV1 = () => {
|
|||||||
SearchIndex.GLOSSARY
|
SearchIndex.GLOSSARY
|
||||||
).then((res: AxiosResponse) => {
|
).then((res: AxiosResponse) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
const searchedTerms: FormatedGlossarySuggestion[] =
|
const searchedTerms: FormattedGlossarySuggestion[] =
|
||||||
res.data.hits?.hits?.map(
|
res.data.hits?.hits?.map(
|
||||||
(item: GlossarySuggestionHit) => item._source
|
(item: GlossarySuggestionHit) => item._source
|
||||||
) || [];
|
) || [];
|
||||||
|
@ -11,11 +11,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FormatedGlossarySuggestion } from 'Models';
|
import {
|
||||||
|
FormattedGlossarySuggestion,
|
||||||
|
FormattedGlossaryTermData,
|
||||||
|
SearchResponse,
|
||||||
|
} from 'Models';
|
||||||
import { DataNode } from 'rc-tree/lib/interface';
|
import { DataNode } from 'rc-tree/lib/interface';
|
||||||
import { GlossaryTerm } from '../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../generated/entity/data/glossaryTerm';
|
||||||
import { ModifiedGlossaryData } from '../pages/GlossaryPage/GlossaryPageV1.component';
|
import { ModifiedGlossaryData } from '../pages/GlossaryPage/GlossaryPageV1.component';
|
||||||
import { getNameFromFQN } from './CommonUtils';
|
import { getNameFromFQN } from './CommonUtils';
|
||||||
|
import { searchData } from '../axiosAPIs/miscAPI';
|
||||||
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
|
import { formatSearchGlossaryTermResponse } from './APIUtils';
|
||||||
|
import { WILD_CARD_CHAR } from '../constants/char.constants';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
export interface GlossaryTermTreeNode {
|
export interface GlossaryTermTreeNode {
|
||||||
children?: GlossaryTermTreeNode[];
|
children?: GlossaryTermTreeNode[];
|
||||||
@ -23,6 +32,25 @@ export interface GlossaryTermTreeNode {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fetchGlossaryTerms = (): Promise<FormattedGlossaryTermData[]> => {
|
||||||
|
return new Promise<FormattedGlossaryTermData[]>((resolve, reject) => {
|
||||||
|
searchData(WILD_CARD_CHAR, 1, 1000, '', '', '', SearchIndex.GLOSSARY)
|
||||||
|
.then((res: SearchResponse) => {
|
||||||
|
const data = formatSearchGlossaryTermResponse(
|
||||||
|
res?.data?.hits?.hits || []
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
})
|
||||||
|
.catch((error: AxiosError) => reject(error.response));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGlossaryTermlist = (
|
||||||
|
terms: Array<FormattedGlossaryTermData> = []
|
||||||
|
): Array<string> => {
|
||||||
|
return terms.map((term: FormattedGlossaryTermData) => term?.fqdn);
|
||||||
|
};
|
||||||
|
|
||||||
export const generateTreeData = (data: ModifiedGlossaryData[]): DataNode[] => {
|
export const generateTreeData = (data: ModifiedGlossaryData[]): DataNode[] => {
|
||||||
return data.map((d) => {
|
return data.map((d) => {
|
||||||
return d.children?.length
|
return d.children?.length
|
||||||
@ -87,7 +115,7 @@ const optimiseGlossaryTermTree = (treeNodes?: GlossaryTermTreeNode[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getSearchedGlossaryTermTree = (
|
export const getSearchedGlossaryTermTree = (
|
||||||
searchedTerms: FormatedGlossarySuggestion[]
|
searchedTerms: FormattedGlossarySuggestion[]
|
||||||
): GlossaryTermTreeNode[] => {
|
): GlossaryTermTreeNode[] => {
|
||||||
const termTree: GlossaryTermTreeNode[] = [];
|
const termTree: GlossaryTermTreeNode[] = [];
|
||||||
for (const term of searchedTerms) {
|
for (const term of searchedTerms) {
|
||||||
@ -102,7 +130,7 @@ export const getSearchedGlossaryTermTree = (
|
|||||||
|
|
||||||
export const updateGlossaryListBySearchedTerms = (
|
export const updateGlossaryListBySearchedTerms = (
|
||||||
glossaries: ModifiedGlossaryData[],
|
glossaries: ModifiedGlossaryData[],
|
||||||
searchedTerms: FormatedGlossarySuggestion[]
|
searchedTerms: FormattedGlossarySuggestion[]
|
||||||
) => {
|
) => {
|
||||||
const searchedTermTree = getSearchedGlossaryTermTree(searchedTerms);
|
const searchedTermTree = getSearchedGlossaryTermTree(searchedTerms);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user