fix(ui): revamp asset filtering style (#13542)

* revamp asset filtering style

* minor changes

* make parent menu disabled if whole list doesn't have data

* fix code smell

* changes made as per comments

* fix popup issue

* supported all asset when landing and when removing all filter

* supported entity icon in asset cards
This commit is contained in:
Ashish Gupta 2023-10-14 20:10:02 +05:30 committed by GitHub
parent 7c25b5fddd
commit 31ffe5d168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 623 additions and 156 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -420,6 +420,7 @@ const DataProductsDetailsPage = ({
}
rightPanelWidth={400}>
<AssetsTabs
assetCount={assetCount}
isSummaryPanelOpen={false}
permissions={dataProductPermission}
ref={assetTabRef}

View File

@ -481,6 +481,7 @@ const DomainDetailsPage = ({
}
rightPanelWidth={400}>
<AssetsTabs
assetCount={assetCount}
isSummaryPanelOpen={false}
permissions={domainPermission}
ref={assetTabRef}

View File

@ -25,6 +25,7 @@ export interface ExploreSearchCardProps {
details: SearchedDataProps['data'][number]['_source'],
entityType: string
) => void;
showEntityIcon?: boolean;
checked?: boolean;
showCheckboxes?: boolean;
showTags?: boolean;

View File

@ -32,7 +32,11 @@ import {
} from '../../../utils/EntityUtils';
import { getDomainPath } from '../../../utils/RouterUtils';
import { stringToHTML } from '../../../utils/StringsUtils';
import { getServiceIcon, getUsagePercentile } from '../../../utils/TableUtils';
import {
getEntityIcon,
getServiceIcon,
getUsagePercentile,
} from '../../../utils/TableUtils';
import TitleBreadcrumb from '../../common/title-breadcrumb/title-breadcrumb.component';
import TableDataCardBody from '../../TableDataCardBody/TableDataCardBody';
import { useTourProvider } from '../../TourProvider/TourProvider';
@ -49,6 +53,7 @@ const ExploreSearchCard: React.FC<ExploreSearchCardProps> = forwardRef<
className,
source,
matches,
showEntityIcon,
handleSummaryPanelDisplay,
showTags = true,
openEntityInNewPage,
@ -151,24 +156,32 @@ const ExploreSearchCard: React.FC<ExploreSearchCardProps> = forwardRef<
</Typography.Text>
</Button>
) : (
<Link
className="no-underline"
data-testid="entity-link"
target={openEntityInNewPage ? '_blank' : '_self'}
to={
source.fullyQualifiedName && source.entityType
? getEntityLinkFromType(
source.fullyQualifiedName,
source.entityType as EntityType
)
: ''
}>
<Typography.Text
className="text-lg font-medium text-link-color"
data-testid="entity-header-display-name">
{stringToHTML(getEntityName(source))}
</Typography.Text>
</Link>
<div className="w-full d-flex items-start">
{showEntityIcon && (
<span className="w-6 h-6 m-r-xs d-flex text-xl">
{getEntityIcon(source.entityType ?? '')}
</span>
)}
<Link
className="no-underline w-full"
data-testid="entity-link"
target={openEntityInNewPage ? '_blank' : '_self'}
to={
source.fullyQualifiedName && source.entityType
? getEntityLinkFromType(
source.fullyQualifiedName,
source.entityType as EntityType
)
: ''
}>
<Typography.Text
className="text-lg font-medium text-link-color"
data-testid="entity-header-display-name">
{stringToHTML(getEntityName(source))}
</Typography.Text>
</Link>
</div>
)}
</Col>
</Row>

View File

@ -167,6 +167,7 @@ const GlossaryTermsV1 = ({
key: 'assets',
children: (
<AssetsTabs
assetCount={assetCount}
isSummaryPanelOpen={isSummaryPanelOpen}
permissions={permissions}
ref={assetTabRef}

View File

@ -11,11 +11,22 @@
* limitations under the License.
*/
import { Button, Col, Menu, Row, Skeleton, Space } from 'antd';
import type { ButtonType } from 'antd/lib/button';
import {
Badge,
Button,
Checkbox,
Col,
Menu,
MenuProps,
Popover,
Row,
Skeleton,
Space,
Typography,
} from 'antd';
import classNames from 'classnames';
import { t } from 'i18next';
import { find, startCase } from 'lodash';
import { find, isEmpty } from 'lodash';
import React, {
forwardRef,
useCallback,
@ -28,6 +39,8 @@ import { useParams } from 'react-router-dom';
import {
AssetsFilterOptions,
ASSETS_INDEXES,
ASSET_MENU_KEYS,
ASSET_SUB_MENU_FILTER,
} from '../../../../constants/Assets.constants';
import { PAGE_SIZE } from '../../../../constants/constants';
import { GLOSSARIES_DOCS } from '../../../../constants/docs.constants';
@ -46,6 +59,9 @@ import {
SearchedDataProps,
SourceType,
} from '../../../searched-data/SearchedData.interface';
import { FilterOutlined } from '@ant-design/icons';
import { getEntityIcon } from '../../../../utils/TableUtils';
import './assets-tabs.less';
import {
AssetsOfEntity,
@ -65,29 +81,107 @@ const AssetsTabs = forwardRef(
onAssetClick,
isSummaryPanelOpen,
onAddAsset,
assetCount,
type = AssetsOfEntity.GLOSSARY,
viewType = AssetsViewType.PILLS,
}: AssetsTabsProps,
ref
) => {
const [itemCount, setItemCount] = useState<Record<EntityType, number>>({
table: 0,
pipeline: 0,
mlmodel: 0,
container: 0,
topic: 0,
dashboard: 0,
glossaryTerm: 0,
} as Record<EntityType, number>);
const [activeFilter, setActiveFilter] = useState<SearchIndex>(
SearchIndex.TABLE
const popupRef = React.useRef<HTMLElement>(null);
const [itemCount, setItemCount] = useState<Record<EntityType, number>>(
{} as Record<EntityType, number>
);
const [activeFilter, setActiveFilter] = useState<SearchIndex[]>([]);
const { fqn } = useParams<{ fqn: string }>();
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState<SearchedDataProps['data']>([]);
const [total, setTotal] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
const [selectedCard, setSelectedCard] = useState<SourceType>();
const [visible, setVisible] = useState<boolean>(false);
const [openKeys, setOpenKeys] = useState<EntityType[]>([]);
const queryParam = useMemo(() => {
if (type === AssetsOfEntity.DOMAIN) {
return `(domain.fullyQualifiedName:"${fqn}")`;
} else if (type === AssetsOfEntity.DATA_PRODUCT) {
return `(dataProducts.fullyQualifiedName:"${fqn}")`;
} else if (type === AssetsOfEntity.TEAM) {
return `(owner.fullyQualifiedName:"${fqn}")`;
} else {
return `(tags.tagFQN:"${fqn}")`;
}
}, [type, fqn]);
const fetchAssets = useCallback(
async ({
index = activeFilter,
page = 1,
}: {
index?: SearchIndex[];
page?: number;
}) => {
try {
setIsLoading(true);
const res = await searchData(
'',
page,
PAGE_SIZE,
queryParam,
'',
'',
index
);
// Extract useful details from the Response
const totalCount = res?.data?.hits?.total.value ?? 0;
const hits = res?.data?.hits?.hits;
// Find EntityType for selected searchIndex
const entityType = AssetsFilterOptions.find((f) =>
activeFilter.includes(f.value)
)?.label;
// Update states
setTotal(totalCount);
entityType &&
setItemCount((prevCount) => ({
...prevCount,
[entityType]: totalCount,
}));
setData(hits as SearchedDataProps['data']);
// Select first card to show summary right panel
hits[0] && setSelectedCard(hits[0]._source as SourceType);
} catch (_) {
// Nothing here
} finally {
setIsLoading(false);
}
},
[activeFilter, currentPage]
);
const onOpenChange: MenuProps['onOpenChange'] = (keys) => {
const latestOpenKey = keys.find(
(key) => openKeys.indexOf(key as EntityType) === -1
);
if (ASSET_MENU_KEYS.indexOf(latestOpenKey as EntityType) === -1) {
setOpenKeys(keys as EntityType[]);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey as EntityType] : []);
}
};
const handleAssetButtonVisibleChange = (newVisible: boolean) =>
setVisible(newVisible);
const handleActiveFilter = (key: SearchIndex) => {
if (activeFilter.includes(key)) {
setActiveFilter((prev) => prev.filter((item) => item !== key));
} else {
setActiveFilter((prev) => [...prev, key]);
}
};
const tabs = useMemo(() => {
return AssetsFilterOptions.map((option) => {
@ -102,7 +196,7 @@ const AssetsTabs = forwardRef(
{getCountBadge(
itemCount[option.key],
'',
activeFilter === option.value
activeFilter.includes(option.value)
)}
</span>
</div>
@ -111,19 +205,79 @@ const AssetsTabs = forwardRef(
value: option.value,
};
});
}, [itemCount]);
}, [activeFilter, itemCount]);
const queryParam = useMemo(() => {
if (type === AssetsOfEntity.DOMAIN) {
return `(domain.fullyQualifiedName:"${fqn}")`;
} else if (type === AssetsOfEntity.DATA_PRODUCT) {
return `(dataProducts.fullyQualifiedName:"${fqn}")`;
} else if (type === AssetsOfEntity.TEAM) {
return `(owner.fullyQualifiedName:"${fqn}")`;
} else {
return `(tags.tagFQN:"${fqn}")`;
}
}, [type, fqn]);
const getAssetMenuCount = useCallback(
(key: EntityType) =>
ASSET_SUB_MENU_FILTER.find((item) => item.key === key)
?.children.map((item) => itemCount[item.value] ?? 0)
?.reduce((acc, cv) => acc + cv, 0),
[itemCount]
);
const getOptions = useCallback(
(
option: {
label: string;
key: EntityType | SearchIndex;
value?: EntityType;
},
isChildren?: boolean
) => {
const assetCount = isChildren
? itemCount[option.value as EntityType]
: getAssetMenuCount(option.key as EntityType);
return {
label: isChildren ? (
<div className="w-full d-flex justify-between">
<div className="w-full d-flex items-center justify-between p-r-xss">
<span className="d-flex items-center">
<span className="m-r-xs w-4 d-flex">
{getEntityIcon(option.key)}
</span>
<Typography.Text
className="asset-sub-menu-title text-color-inherit"
ellipsis={{ tooltip: true }}>
{option.label}
</Typography.Text>
</span>
<span>
{getCountBadge(assetCount, 'asset-badge-container')}
</span>
</div>
<Checkbox
checked={activeFilter.includes(option.key as SearchIndex)}
className="asset-sub-menu-checkbox"
/>
</div>
) : (
<div className="d-flex justify-between">
<span>{option.label}</span>
<span>{getCountBadge(assetCount, 'asset-badge-container')}</span>
</div>
),
key: option.key,
value: option.key,
};
},
[
getEntityIcon,
setCurrentPage,
handleActiveFilter,
setSelectedCard,
activeFilter,
]
);
const subMenuItems = useMemo(() => {
return ASSET_SUB_MENU_FILTER.map((option) => ({
...getOptions(option),
children: option.children.map((item) => getOptions(item, true)),
}));
}, [itemCount, getOptions]);
const searchIndexes = useMemo(() => {
const indexesToFetch = [...ASSETS_INDEXES];
@ -160,6 +314,9 @@ const AssetsTabs = forwardRef(
pipelineServiceResponse,
storageServiceResponse,
searchServiceResponse,
domainResponse,
dataProductResponse,
tagResponse,
glossaryResponse,
]) => {
const counts = {
@ -191,7 +348,10 @@ const AssetsTabs = forwardRef(
storageServiceResponse.data.hits.total.value,
[EntityType.SEARCH_SERVICE]:
searchServiceResponse.data.hits.total.value,
[EntityType.DOMAIN]: domainResponse.data.hits.total.value,
[EntityType.DATA_PRODUCT]:
dataProductResponse.data.hits.total.value,
[EntityType.TAG]: tagResponse.data.hits.total.value,
[EntityType.GLOSSARY_TERM]:
type !== AssetsOfEntity.GLOSSARY
? glossaryResponse.data.hits.total.value
@ -200,18 +360,22 @@ const AssetsTabs = forwardRef(
setItemCount(counts as Record<EntityType, number>);
find(counts, (count, key) => {
if (count > 0) {
const option = AssetsFilterOptions.find((el) => el.key === key);
if (option) {
setActiveFilter(option.value);
if (viewType !== AssetsViewType.PILLS) {
find(counts, (count, key) => {
if (count > 0) {
const option = AssetsFilterOptions.find(
(el) => el.key === key
);
if (option) {
handleActiveFilter(option.value);
}
return true;
}
return true;
}
return false;
});
return false;
});
}
}
)
.catch((err) => {
@ -228,55 +392,6 @@ const AssetsTabs = forwardRef(
};
}, []);
const fetchAssets = useCallback(
async ({
index = activeFilter,
page = 1,
}: {
index?: SearchIndex;
page?: number;
}) => {
try {
setIsLoading(true);
const res = await searchData(
'',
page,
PAGE_SIZE,
queryParam,
'',
'',
index
);
// Extract useful details from the Response
const totalCount = res?.data?.hits?.total.value ?? 0;
const hits = res?.data?.hits?.hits;
// Find EntityType for selected searchIndex
const entityType = AssetsFilterOptions.find(
(f) => f.value === activeFilter
)?.label;
// Update states
setTotal(totalCount);
entityType &&
setItemCount((prevCount) => ({
...prevCount,
[entityType]: totalCount,
}));
setData(hits as SearchedDataProps['data']);
// Select first card to show summary right panel
hits[0] && setSelectedCard(hits[0]._source as SourceType);
} catch (_) {
// Nothing here
} finally {
setIsLoading(false);
}
},
[activeFilter, currentPage]
);
const assetListing = useMemo(() => {
if (isLoading) {
return (
@ -295,6 +410,7 @@ const AssetsTabs = forwardRef(
<div className="assets-data-container">
{data.map(({ _source, _id = '' }, index) => (
<ExploreSearchCard
showEntityIcon
className={classNames(
'm-b-sm cursor-pointer',
selectedCard?.id === _source.id ? 'highlight-card' : ''
@ -320,64 +436,97 @@ const AssetsTabs = forwardRef(
</div>
) : (
<div className="m-t-xlg">
<ErrorPlaceHolder
doc={GLOSSARIES_DOCS}
heading={t('label.asset')}
permission={permissions.Create}
type={ERROR_PLACEHOLDER_TYPE.CREATE}
onClick={onAddAsset}
/>
{!isEmpty(activeFilter) ? (
<ErrorPlaceHolder
heading={t('label.asset')}
type={ERROR_PLACEHOLDER_TYPE.FILTER}
/>
) : (
<ErrorPlaceHolder
doc={GLOSSARIES_DOCS}
heading={t('label.asset')}
permission={permissions.Create}
type={ERROR_PLACEHOLDER_TYPE.CREATE}
onClick={onAddAsset}
/>
)}
</div>
);
}, [data, isLoading, total, currentPage, selectedCard, setSelectedCard]);
const assetsHeader = useMemo(() => {
if (viewType === AssetsViewType.PILLS) {
return AssetsFilterOptions.map((option) => {
const buttonStyle =
activeFilter === option.value
? {
ghost: true,
type: 'primary' as ButtonType,
style: { background: 'white' },
}
: {};
return itemCount[option.key] > 0 ? (
<Button
{...buttonStyle}
className="m-r-sm m-b-sm"
key={option.value}
onClick={() => {
setCurrentPage(1);
setActiveFilter(option.value);
}}>
{startCase(option.label)}{' '}
<span className="p-l-xs">
{getCountBadge(
itemCount[option.key],
'',
activeFilter === option.value
)}
</span>
</Button>
) : null;
});
return (
<div className="w-full d-flex justify-end">
<Popover
align={{ targetOffset: [0, 10] }}
content={
<Menu
multiple
items={subMenuItems}
mode="inline"
openKeys={openKeys}
rootClassName="asset-multi-menu-selector"
selectedKeys={activeFilter}
style={{ width: 256, height: 340 }}
onClick={(value) => {
setCurrentPage(1);
handleActiveFilter(value.key as SearchIndex);
setSelectedCard(undefined);
}}
onOpenChange={onOpenChange}
/>
}
getPopupContainer={(triggerNode: HTMLElement) =>
popupRef.current ?? triggerNode
}
key="asset-options-popover"
open={visible}
overlayClassName="ant-popover-asset"
placement="bottomRight"
showArrow={false}
trigger="click"
onOpenChange={handleAssetButtonVisibleChange}>
{Boolean(assetCount) && (
<Badge count={activeFilter.length}>
<Button
ghost
icon={<FilterOutlined />}
ref={popupRef}
style={{ background: 'white' }}
type="primary">
{t('label.filter-plural')}
</Button>
</Badge>
)}
</Popover>
</div>
);
} else {
return (
<Menu
className="p-t-sm"
items={tabs}
selectedKeys={[activeFilter]}
selectedKeys={activeFilter}
onClick={(value) => {
setCurrentPage(1);
setActiveFilter(value.key as SearchIndex);
setActiveFilter([value.key as SearchIndex]);
setSelectedCard(undefined);
}}
/>
);
}
}, [viewType, activeFilter, currentPage, tabs, itemCount]);
}, [
viewType,
activeFilter,
openKeys,
visible,
currentPage,
tabs,
itemCount,
onOpenChange,
handleAssetButtonVisibleChange,
]);
const layout = useMemo(() => {
if (viewType === AssetsViewType.PILLS) {
@ -397,7 +546,10 @@ const AssetsTabs = forwardRef(
}, [viewType, assetsHeader, assetListing, selectedCard]);
useEffect(() => {
fetchAssets({ index: activeFilter, page: currentPage });
fetchAssets({
index: isEmpty(activeFilter) ? [SearchIndex.ALL] : activeFilter,
page: currentPage,
});
}, [activeFilter, currentPage]);
useImperativeHandle(ref, () => ({

View File

@ -29,6 +29,7 @@ export enum AssetsViewType {
export interface AssetsTabsProps {
onAddAsset: () => void;
permissions: OperationPermission;
assetCount: number;
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
isSummaryPanelOpen: boolean;
type?: AssetsOfEntity;

View File

@ -12,5 +12,62 @@
*/
@import url('../../../../styles/variables.less');
.assets-tab-container {
.ant-popover-asset {
.ant-popover-inner-content {
padding: 0;
overflow-y: scroll;
}
}
.asset-multi-menu-selector.ant-menu:not(.ant-menu-horizontal)
.ant-menu-item-selected {
background-color: transparent;
color: initial;
}
.asset-multi-menu-selector {
.ant-menu-submenu-open {
background: @user-profile-background;
}
.ant-menu-inline {
.asset-sub-menu-title {
width: 150px;
}
.asset-sub-menu-checkbox {
padding-right: 2px;
}
.ant-menu-item::after {
border: none;
}
}
.ant-menu-submenu-inline {
.ant-menu-submenu-title {
padding-left: 14px !important;
}
.ant-menu-sub {
background: @user-profile-background;
.ant-menu-item-only-child {
padding-left: 22px !important;
padding-right: 14px;
color: initial;
}
}
}
.ant-menu-submenu-selected {
.ant-menu-submenu-title {
.asset-badge-container {
background: @primary-color;
color: @white;
}
}
}
.asset-badge-container {
background: rgba(41, 41, 41, 0.1);
border: none;
padding: 2px 6px;
}
}

View File

@ -722,13 +722,14 @@ const TeamDetailsV1 = ({
() => (
<AssetsTabs
isSummaryPanelOpen
assetCount={assetsCount}
permissions={entityPermissions}
type={AssetsOfEntity.TEAM}
onAddAsset={() => history.push(ROUTES.EXPLORE)}
onAssetClick={setPreviewAsset}
/>
),
[entityPermissions, setPreviewAsset]
[entityPermissions, assetsCount, setPreviewAsset]
);
const rolesTabRender = useMemo(

View File

@ -133,6 +133,195 @@ export const AssetsFilterOptions: Array<{
},
];
export const ASSET_SUB_MENU_FILTER: Array<{
label: string;
key: EntityType;
children: {
label: string;
value: EntityType;
key: SearchIndex;
}[];
}> = [
{
key: EntityType.DOMAIN,
label: i18n.t('label.domain-plural'),
children: [
{
key: SearchIndex.DOMAIN,
label: i18n.t('label.domain-plural'),
value: EntityType.DOMAIN,
},
{
key: SearchIndex.DATA_PRODUCT,
label: i18n.t('label.data-product-plural'),
value: EntityType.DATA_PRODUCT,
},
],
},
{
label: i18n.t('label.database-plural'),
key: EntityType.DATABASE,
children: [
{
label: i18n.t('label.entity-service', {
entity: i18n.t('label.database'),
}),
value: EntityType.SEARCH_SERVICE,
key: SearchIndex.DATABASE_SERVICE,
},
{
key: SearchIndex.DATABASE,
label: i18n.t('label.database'),
value: EntityType.DATABASE,
},
{
key: SearchIndex.DATABASE_SCHEMA,
label: i18n.t('label.database-schema'),
value: EntityType.DATABASE_SCHEMA,
},
{
key: SearchIndex.TABLE,
label: i18n.t('label.table'),
value: EntityType.TABLE,
},
{
key: SearchIndex.STORED_PROCEDURE,
label: i18n.t('label.stored-procedure'),
value: EntityType.STORED_PROCEDURE,
},
],
},
{
key: EntityType.TOPIC,
label: i18n.t('label.messaging-plural'),
children: [
{
key: SearchIndex.MESSAGING_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.messaging'),
}),
value: EntityType.MESSAGING_SERVICE,
},
{
key: SearchIndex.TOPIC,
label: i18n.t('label.topic'),
value: EntityType.TOPIC,
},
],
},
{
key: EntityType.DASHBOARD,
label: i18n.t('label.dashboard-plural'),
children: [
{
key: SearchIndex.DASHBOARD_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.dashboard'),
}),
value: EntityType.DASHBOARD_SERVICE,
},
{
key: SearchIndex.DASHBOARD,
label: i18n.t('label.dashboard'),
value: EntityType.DASHBOARD,
},
{
key: SearchIndex.DASHBOARD_DATA_MODEL,
label: i18n.t('label.dashboard-data-model-plural'),
value: EntityType.DASHBOARD_DATA_MODEL,
},
],
},
{
key: EntityType.MLMODEL,
label: i18n.t('label.machine-learning'),
children: [
{
key: SearchIndex.ML_MODEL_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.ml-model'),
}),
value: EntityType.MLMODEL_SERVICE,
},
{
key: SearchIndex.MLMODEL,
label: i18n.t('label.ml-model'),
value: EntityType.MLMODEL,
},
],
},
{
key: EntityType.PIPELINE,
label: i18n.t('label.pipeline-plural'),
children: [
{
key: SearchIndex.PIPELINE_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.pipeline'),
}),
value: EntityType.PIPELINE_SERVICE,
},
{
key: SearchIndex.PIPELINE,
label: i18n.t('label.pipeline'),
value: EntityType.PIPELINE,
},
],
},
{
key: EntityType.CONTAINER,
label: i18n.t('label.storage'),
children: [
{
key: SearchIndex.STORAGE_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.storage'),
}),
value: EntityType.STORAGE_SERVICE,
},
{
key: SearchIndex.CONTAINER,
label: i18n.t('label.container'),
value: EntityType.CONTAINER,
},
],
},
{
key: EntityType.SEARCH_INDEX,
label: i18n.t('label.search'),
children: [
{
key: SearchIndex.SEARCH_SERVICE,
label: i18n.t('label.entity-service', {
entity: i18n.t('label.search'),
}),
value: EntityType.SEARCH_SERVICE,
},
{
key: SearchIndex.SEARCH_INDEX,
label: i18n.t('label.search-index'),
value: EntityType.SEARCH_INDEX,
},
],
},
{
key: EntityType.GOVERN,
label: i18n.t('label.governance'),
children: [
{
key: SearchIndex.GLOSSARY,
label: i18n.t('label.glossary-plural'),
value: EntityType.GLOSSARY,
},
{
key: SearchIndex.TAG,
label: i18n.t('label.tag-plural'),
value: EntityType.TAG,
},
],
},
];
export const ASSETS_INDEXES = [
SearchIndex.TABLE,
SearchIndex.TOPIC,
@ -152,4 +341,19 @@ export const ASSETS_INDEXES = [
SearchIndex.PIPELINE_SERVICE,
SearchIndex.STORAGE_SERVICE,
SearchIndex.SEARCH_SERVICE,
SearchIndex.DOMAIN,
SearchIndex.DATA_PRODUCT,
SearchIndex.TAG,
];
export const ASSET_MENU_KEYS = [
EntityType.DOMAIN,
EntityType.DATABASE,
EntityType.TOPIC,
EntityType.PIPELINE,
EntityType.DASHBOARD,
EntityType.MLMODEL,
EntityType.CONTAINER,
EntityType.SEARCH_INDEX,
EntityType.GOVERN,
];

View File

@ -58,6 +58,7 @@ export enum EntityType {
DOC_STORE = 'docStore',
PAGE = 'Page',
knowledgePanels = 'KnowLedgePanels',
GOVERN = 'govern',
}
export enum AssetsType {

View File

@ -12,6 +12,7 @@
*/
export enum SearchIndex {
ALL = 'all',
TABLE = 'table_search_index',
TOPIC = 'topic_search_index',
DASHBOARD = 'dashboard_search_index',

View File

@ -172,6 +172,7 @@ export type ExploreSearchSource =
| SearchIndexSearchSource;
export type SearchIndexSearchSourceMapping = {
[SearchIndex.ALL]: TableSearchSource;
[SearchIndex.TABLE]: TableSearchSource;
[SearchIndex.MLMODEL]: MlmodelSearchSource;
[SearchIndex.PIPELINE]: PipelineSearchSource;

View File

@ -571,6 +571,7 @@
"login": "Anmelden",
"logo-url": "URL des Logos",
"logout": "Abmelden",
"machine-learning": "Machine Learning",
"major": "Haupt-",
"manage-entity": "{{entity}} verwalten",
"manage-rule": "Regeln verwalten",

View File

@ -571,6 +571,7 @@
"login": "Login",
"logo-url": "Logo URL",
"logout": "Logout",
"machine-learning": "Machine Learning",
"major": "Major",
"manage-entity": "Manage {{entity}}",
"manage-rule": "Manage Rule",

View File

@ -571,6 +571,7 @@
"login": "Iniciar sesión",
"logo-url": "Logo URL",
"logout": "Cerrar sesión",
"machine-learning": "Machine Learning",
"major": "Principal",
"manage-entity": "Administrar {{entity}}",
"manage-rule": "Administrar regla",

View File

@ -571,6 +571,7 @@
"login": "Se Connecter",
"logo-url": "URL du Logo",
"logout": "Se Déconnecter",
"machine-learning": "Machine Learning",
"major": "Majeur",
"manage-entity": "Gérer {{entity}}",
"manage-rule": "Gérer les Règles",

View File

@ -571,6 +571,7 @@
"login": "ログイン",
"logo-url": "Logo URL",
"logout": "ログアウト",
"machine-learning": "Machine Learning",
"major": "メジャー",
"manage-entity": "{{entity}}の管理",
"manage-rule": "ルールの管理",

View File

@ -571,6 +571,7 @@
"login": "Entrar",
"logo-url": "Logo URL",
"logout": "Sair",
"machine-learning": "Machine Learning",
"major": "Principal",
"manage-entity": "Gerenciar {{numberOfDays}}",
"manage-rule": "Gerenciar regra",

View File

@ -571,6 +571,7 @@
"login": "Логин",
"logo-url": "URL-адрес логотипа",
"logout": "Выйти",
"machine-learning": "Machine Learning",
"major": "Главный",
"manage-entity": "Управление {{entity}}",
"manage-rule": "Правила управления",

View File

@ -571,6 +571,7 @@
"login": "登录",
"logo-url": "Logo URL",
"logout": "退出",
"machine-learning": "Machine Learning",
"major": "主要",
"manage-entity": "管理{{entity}}",
"manage-rule": "管理规则",

View File

@ -88,6 +88,10 @@ pre {
color: @primary-color;
}
}
.text-inherit {
font-size: inherit;
}
.text-color-inherit {
color: inherit;
}

View File

@ -12,7 +12,6 @@
*/
import Icon, { FilterOutlined, SearchOutlined } from '@ant-design/icons';
import { EntityTags } from 'Models';
import { Tooltip } from 'antd';
import { ExpandableConfig } from 'antd/lib/table/interface';
import classNames from 'classnames';
@ -27,6 +26,7 @@ import {
uniqueId,
upperCase,
} from 'lodash';
import { EntityTags } from 'Models';
import React from 'react';
import { ReactComponent as IconTerm } from '../assets/svg/book.svg';
import { ReactComponent as ClassificationIcon } from '../assets/svg/classification.svg';
@ -53,17 +53,14 @@ import { ReactComponent as IconNotNull } from '../assets/svg/icon-not-null.svg';
import { ReactComponent as IconUniqueLineThrough } from '../assets/svg/icon-unique-line-through.svg';
import { ReactComponent as IconUnique } from '../assets/svg/icon-unique.svg';
import { SourceType } from '../components/searched-data/SearchedData.interface';
import { GlobalSettingsMenuCategory } from '../constants/GlobalSettings.constants';
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
import {
DE_ACTIVE_COLOR,
PRIMERY_COLOR,
TEXT_BODY_COLOR,
getContainerDetailPath,
getDashboardDetailsPath,
getDataModelDetailsPath,
getDatabaseDetailsPath,
getDatabaseSchemaDetailsPath,
getDataModelDetailsPath,
getEditWebhookPath,
getMlModelPath,
getPipelineDetailsPath,
@ -73,7 +70,10 @@ import {
getTableTabPath,
getTagsDetailsPath,
getTopicDetailsPath,
PRIMERY_COLOR,
TEXT_BODY_COLOR,
} from '../constants/constants';
import { GlobalSettingsMenuCategory } from '../constants/GlobalSettings.constants';
import { EntityTabs, EntityType, FqnPart } from '../enums/entity.enum';
import { SearchIndex } from '../enums/search.enum';
import { ConstraintTypes, PrimaryTableDataTypes } from '../enums/table.enum';
@ -321,22 +321,27 @@ export const getEntityIcon = (indexType: string) => {
switch (indexType) {
case SearchIndex.TOPIC:
case EntityType.TOPIC:
case SearchIndex.MESSAGING_SERVICE:
return <TopicIcon />;
case SearchIndex.DASHBOARD:
case EntityType.DASHBOARD:
case SearchIndex.DASHBOARD_SERVICE:
return <DashboardIcon />;
case SearchIndex.MLMODEL:
case EntityType.MLMODEL:
case SearchIndex.ML_MODEL_SERVICE:
return <MlModelIcon />;
case SearchIndex.PIPELINE:
case EntityType.PIPELINE:
case SearchIndex.PIPELINE_SERVICE:
return <PipelineIcon />;
case SearchIndex.CONTAINER:
case EntityType.CONTAINER:
case SearchIndex.STORAGE_SERVICE:
return <ContainerIcon />;
case SearchIndex.DASHBOARD_DATA_MODEL:
@ -347,8 +352,10 @@ export const getEntityIcon = (indexType: string) => {
case EntityType.STORED_PROCEDURE:
return <IconStoredProcedure />;
case SearchIndex.TAG:
case EntityType.TAG:
return <ClassificationIcon />;
case SearchIndex.GLOSSARY:
case EntityType.GLOSSARY:
return <GlossaryIcon />;
case EntityType.GLOSSARY_TERM:
@ -356,7 +363,20 @@ export const getEntityIcon = (indexType: string) => {
case EntityType.SEARCH_INDEX:
case SearchIndex.SEARCH_INDEX:
return <SearchOutlined className="text-sm" />;
case EntityType.SEARCH_SERVICE:
case SearchIndex.SEARCH_SERVICE:
return (
<SearchOutlined
className="text-sm text-inherit"
style={{ color: DE_ACTIVE_COLOR }}
/>
);
case EntityType.DOMAIN:
case EntityType.DATA_PRODUCT:
case SearchIndex.DATA_PRODUCT:
case SearchIndex.DOMAIN:
return <DomainIcon />;
case SearchIndex.TABLE:
case EntityType.TABLE: