mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-26 09:22:14 +00:00
Fix(UI): Explore page bugs (#8607)
* - Made changes to show more information about tables in data card on explore page - Fixed bugs related to entity summary panel * Replaced Space components for vertical flex with Row and Col for ColumnSummary
This commit is contained in:
parent
3e3cf971c2
commit
a6822aa094
@ -11,10 +11,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Divider, Space, Typography } from 'antd';
|
||||
import { Col, Divider, Row, Space, Typography } from 'antd';
|
||||
import { toLower } from 'lodash';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MAX_CHAR_LIMIT_ENTITY_SUMMARY } from '../../../constants/constants';
|
||||
import { getTagValue } from '../../../utils/CommonUtils';
|
||||
import SVGIcons from '../../../utils/SvgUtils';
|
||||
import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer';
|
||||
@ -38,50 +39,59 @@ export default function ColumnSummary({ columns }: ColumnSummaryProps) {
|
||||
}, [columns]);
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Row>
|
||||
{columns &&
|
||||
formattedColumnsData.map((column) => (
|
||||
<React.Fragment key={column.name}>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Text className="column-name">{column.name}</Text>
|
||||
<Space className="text-xs" size={4}>
|
||||
<Space size={4}>
|
||||
<Text className="text-gray">{`${t('label.type')}:`}</Text>
|
||||
<Text className="text-semi-bold">{toLower(column.type)}</Text>
|
||||
</Space>
|
||||
{column.tags?.length !== 0 && (
|
||||
<>
|
||||
<Divider type="vertical" />
|
||||
<Space size={4}>
|
||||
<SVGIcons
|
||||
alt="icon-tag"
|
||||
icon="icon-tag-grey"
|
||||
width="12"
|
||||
/>
|
||||
<Col key={column.name} span={24}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Text className="column-name">{column.name}</Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Space className="text-xs" size={4}>
|
||||
<Space size={4}>
|
||||
<Text className="text-gray">{`${t('label.type')}:`}</Text>
|
||||
<Text className="text-semi-bold">
|
||||
{toLower(column.type)}
|
||||
</Text>
|
||||
</Space>
|
||||
{column.tags?.length !== 0 && (
|
||||
<>
|
||||
<Divider type="vertical" />
|
||||
<Space size={4}>
|
||||
<SVGIcons
|
||||
alt="icon-tag"
|
||||
icon="icon-tag-grey"
|
||||
width="12"
|
||||
/>
|
||||
|
||||
<TagsViewer
|
||||
sizeCap={-1}
|
||||
tags={(column.tags || []).map((tag) =>
|
||||
getTagValue(tag)
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
<Paragraph className="text-gray">
|
||||
{column.description ? (
|
||||
<RichTextEditorPreviewer
|
||||
markdown={column.description || ''}
|
||||
/>
|
||||
) : (
|
||||
t('label.no-description')
|
||||
)}
|
||||
</Paragraph>
|
||||
</Space>
|
||||
<TagsViewer
|
||||
sizeCap={-1}
|
||||
tags={(column.tags || []).map((tag) =>
|
||||
getTagValue(tag)
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Paragraph className="text-gray">
|
||||
{column.description ? (
|
||||
<RichTextEditorPreviewer
|
||||
markdown={column.description || ''}
|
||||
maxLength={MAX_CHAR_LIMIT_ENTITY_SUMMARY}
|
||||
/>
|
||||
) : (
|
||||
t('label.no-description')
|
||||
)}
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider />
|
||||
</React.Fragment>
|
||||
</Col>
|
||||
))}
|
||||
</Space>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Col, Divider, Row, Space, Typography } from 'antd';
|
||||
import { Col, Divider, Row, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -46,54 +46,70 @@ export default function EntitySummaryPanel({
|
||||
'summary-panel-container',
|
||||
showPanel ? 'show-panel' : ''
|
||||
)}>
|
||||
<Space
|
||||
className={classNames('basic-info-container m-md')}
|
||||
direction="vertical">
|
||||
<Typography.Title level={5}>{entityDetails.name}</Typography.Title>
|
||||
<Space className={classNames('w-full')} direction="vertical">
|
||||
{Object.keys(basicTableInfo).map((fieldName) => (
|
||||
<Row gutter={16} key={fieldName}>
|
||||
<Col className="text-gray" span={10}>
|
||||
{fieldName}
|
||||
<Row className={classNames('m-md')}>
|
||||
<Col span={24}>
|
||||
<Typography.Title level={5}>{entityDetails.name}</Typography.Title>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row>
|
||||
{Object.keys(basicTableInfo).map((fieldName) => (
|
||||
<Col key={fieldName} span={24}>
|
||||
<Row gutter={16}>
|
||||
<Col className="text-gray" span={10}>
|
||||
{fieldName}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
{basicTableInfo[fieldName as keyof BasicTableInfo]}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
{basicTableInfo[fieldName as keyof BasicTableInfo]}
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider className="m-0" />
|
||||
<Row className={classNames('m-md')} gutter={[0, 16]}>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="section-header">
|
||||
{t('label.profiler-amp-data-quality')}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{overallSummery.map((field) => (
|
||||
<Col key={field.title} span={10}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="text-gray">
|
||||
{field.title}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Typography.Text
|
||||
className={classNames(
|
||||
'tw-text-2xl tw-font-semibold',
|
||||
field.className
|
||||
)}>
|
||||
{field.value}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</Space>
|
||||
</Space>
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider className="m-0" />
|
||||
<Space className={classNames('m-md')} direction="vertical">
|
||||
<Typography.Text className="section-header">
|
||||
{t('label.profiler-amp-data-quality')}
|
||||
</Typography.Text>
|
||||
<Row gutter={[16, 16]}>
|
||||
{overallSummery.map((field) => (
|
||||
<Col key={field.title} span={10}>
|
||||
<Space direction="vertical" size={6}>
|
||||
<Typography.Text className="text-gray">
|
||||
{field.title}
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
className={classNames(
|
||||
'tw-text-2xl tw-font-semibold',
|
||||
field.className
|
||||
)}>
|
||||
{field.value}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Space>
|
||||
<Divider className="m-0" />
|
||||
<Space className={classNames('m-md')} direction="vertical">
|
||||
<Typography.Text className="section-header">
|
||||
{t('label.schema')}
|
||||
</Typography.Text>
|
||||
<ColumnSummary columns={columns} />
|
||||
</Space>
|
||||
<Row className={classNames('m-md')} gutter={[0, 16]}>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="section-header">
|
||||
{t('label.schema')}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ColumnSummary columns={columns} />
|
||||
</Col>
|
||||
</Row>
|
||||
<CloseOutlined className="close-icon" onClick={handleClosePanel} />
|
||||
</div>
|
||||
);
|
||||
|
@ -37,10 +37,6 @@
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
.basic-info-container {
|
||||
width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: @label-color;
|
||||
}
|
||||
@ -64,7 +60,7 @@
|
||||
}
|
||||
|
||||
.ant-divider-horizontal {
|
||||
margin: 8px 0px;
|
||||
margin: 16px 0px;
|
||||
}
|
||||
|
||||
div.ant-typography {
|
||||
|
@ -16,10 +16,18 @@ import {
|
||||
faSortAmountUpAlt,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Card, Space, Tabs } from 'antd';
|
||||
import { Card, Tabs } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import unique from 'fork-ts-checker-webpack-plugin/lib/utils/array/unique';
|
||||
import { isNil, isNumber, lowerCase, noop, omit, toUpper } from 'lodash';
|
||||
import {
|
||||
isNil,
|
||||
isNumber,
|
||||
lowerCase,
|
||||
noop,
|
||||
omit,
|
||||
toLower,
|
||||
toUpper,
|
||||
} from 'lodash';
|
||||
import { EntityType } from 'Models';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -43,6 +51,7 @@ import {
|
||||
import { updateTestResults } from '../../utils/DataQualityAndProfilerUtils';
|
||||
import { generateEntityLink } from '../../utils/TableUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { Entities } from '../AddWebhook/WebhookConstants';
|
||||
import AdvancedSearch from '../AdvancedSearch/AdvancedSearch.component';
|
||||
import { FacetFilterProps } from '../common/facetfilter/facetFilter.interface';
|
||||
import PageLayoutV1 from '../containers/PageLayoutV1';
|
||||
@ -293,6 +302,7 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
}
|
||||
onChange={(tab) => {
|
||||
tab && onChangeSearchIndex(tab as ExploreSearchIndex);
|
||||
setShowSummaryPanel(false);
|
||||
}}>
|
||||
{Object.entries(tabsInfo).map(([tabSearchIndex, tabDetail]) => (
|
||||
<Tabs.TabPane
|
||||
@ -314,46 +324,50 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
<Space>
|
||||
<div
|
||||
style={{
|
||||
marginRight: showSummaryPanel ? '380px' : '',
|
||||
}}>
|
||||
<AdvancedSearch
|
||||
jsonTree={advancedSearchJsonTree}
|
||||
searchIndex={searchIndex}
|
||||
onChangeJsonTree={(nTree) => onChangeAdvancedSearchJsonTree(nTree)}
|
||||
onChangeQueryFilter={(nQueryFilter) =>
|
||||
onChangeAdvancedSearchQueryFilter(nQueryFilter)
|
||||
<div
|
||||
style={{
|
||||
marginRight: showSummaryPanel ? '390px' : '',
|
||||
}}>
|
||||
<AdvancedSearch
|
||||
jsonTree={advancedSearchJsonTree}
|
||||
searchIndex={searchIndex}
|
||||
onChangeJsonTree={(nTree) => onChangeAdvancedSearchJsonTree(nTree)}
|
||||
onChangeQueryFilter={(nQueryFilter) =>
|
||||
onChangeAdvancedSearchQueryFilter(nQueryFilter)
|
||||
}
|
||||
/>
|
||||
{!loading ? (
|
||||
<SearchedData
|
||||
isFilterSelected
|
||||
showResultCount
|
||||
currentPage={page}
|
||||
data={searchResults?.hits.hits ?? []}
|
||||
handleSummaryPanelDisplay={
|
||||
tab === toLower(Entities.table)
|
||||
? handleSummaryPanelDisplay
|
||||
: undefined
|
||||
}
|
||||
paginate={(value) => {
|
||||
if (isNumber(value)) {
|
||||
onChangePage(value);
|
||||
} else if (!isNaN(Number.parseInt(value))) {
|
||||
onChangePage(Number.parseInt(value));
|
||||
}
|
||||
}}
|
||||
totalValue={searchResults?.hits.total.value ?? 0}
|
||||
/>
|
||||
{!loading ? (
|
||||
<SearchedData
|
||||
isFilterSelected
|
||||
showResultCount
|
||||
currentPage={page}
|
||||
data={searchResults?.hits.hits ?? []}
|
||||
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
||||
paginate={(value) => {
|
||||
if (isNumber(value)) {
|
||||
onChangePage(value);
|
||||
} else if (!isNaN(Number.parseInt(value))) {
|
||||
onChangePage(Number.parseInt(value));
|
||||
}
|
||||
}}
|
||||
totalValue={searchResults?.hits.total.value ?? 0}
|
||||
/>
|
||||
) : (
|
||||
<Loader />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Loader />
|
||||
)}
|
||||
</div>
|
||||
{tab === toLower(Entities.table) && (
|
||||
<EntitySummaryPanel
|
||||
entityDetails={entityDetails || ({} as Table)}
|
||||
handleClosePanel={handleClosePanel}
|
||||
overallSummery={overallSummery}
|
||||
showPanel={showSummaryPanel}
|
||||
/>
|
||||
</Space>
|
||||
)}
|
||||
</PageLayoutV1>
|
||||
);
|
||||
};
|
||||
|
@ -219,7 +219,10 @@ const EntitySummaryDetails = ({
|
||||
data-testid="owner-link"
|
||||
href={data.value as string}
|
||||
rel="noopener noreferrer"
|
||||
target={data.openInNewTab ? '_blank' : '_self'}>
|
||||
target={data.openInNewTab ? '_blank' : '_self'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
{displayVal}
|
||||
{data.openInNewTab && (
|
||||
<>
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { isNil, isString, startCase, uniqueId } from 'lodash';
|
||||
import { isString, startCase, uniqueId } from 'lodash';
|
||||
import { ExtraInfo } from 'Models';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
@ -49,7 +49,7 @@ export interface TableDataCardPropsV2 {
|
||||
value: number;
|
||||
}[];
|
||||
searchIndex: SearchIndex | EntityType;
|
||||
handleSummaryPanelDisplay: (source: Table) => void;
|
||||
handleSummaryPanelDisplay?: (source: Table) => void;
|
||||
}
|
||||
|
||||
const TableDataCardV2: React.FC<TableDataCardPropsV2> = ({
|
||||
@ -62,19 +62,8 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = ({
|
||||
const location = useLocation();
|
||||
|
||||
const otherDetails = useMemo(() => {
|
||||
const _otherDetails: ExtraInfo[] = [];
|
||||
|
||||
if ('tier' in source && !isNil(source.tier)) {
|
||||
_otherDetails.push({
|
||||
key: 'Tier',
|
||||
value: isString(source.tier)
|
||||
? source.tier
|
||||
: source.tier.tagFQN.split(FQN_SEPARATOR_CHAR)[1],
|
||||
});
|
||||
}
|
||||
|
||||
if ('owner' in source && !isNil(source.owner)) {
|
||||
_otherDetails.push({
|
||||
const _otherDetails: ExtraInfo[] = [
|
||||
{
|
||||
key: 'Owner',
|
||||
value: getOwnerValue(source.owner as EntityReference),
|
||||
placeholderText: getEntityPlaceHolder(
|
||||
@ -89,8 +78,16 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = ({
|
||||
source.owner?.type === OwnerType.USER
|
||||
? source.owner?.name
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'Tier',
|
||||
value: source.tier
|
||||
? isString(source.tier)
|
||||
? source.tier
|
||||
: source.tier?.tagFQN.split(FQN_SEPARATOR_CHAR)[1]
|
||||
: '',
|
||||
},
|
||||
];
|
||||
|
||||
if ('usageSummary' in source) {
|
||||
_otherDetails.push({
|
||||
@ -110,7 +107,8 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = ({
|
||||
return _otherDetails;
|
||||
}, [source]);
|
||||
|
||||
const handleLinkClick = () => {
|
||||
const handleLinkClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (location.pathname.includes(ROUTES.TOUR)) {
|
||||
AppState.currentTourPage = CurrentTourPageType.DATASET_PAGE;
|
||||
}
|
||||
@ -145,7 +143,9 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = ({
|
||||
<div
|
||||
className="tw-bg-white tw-p-3 tw-border tw-border-main tw-rounded-md"
|
||||
data-testid="table-data-card"
|
||||
onClick={() => handleSummaryPanelDisplay(source as Table)}>
|
||||
onClick={() => {
|
||||
handleSummaryPanelDisplay && handleSummaryPanelDisplay(source as Table);
|
||||
}}>
|
||||
<div>
|
||||
{'databaseSchema' in source && 'database' in source && (
|
||||
<span
|
||||
|
@ -68,5 +68,5 @@ export interface SearchedDataProps {
|
||||
showOnboardingTemplate?: boolean;
|
||||
showOnlyChildren?: boolean;
|
||||
isFilterSelected: boolean;
|
||||
handleSummaryPanelDisplay: (source: Table) => void;
|
||||
handleSummaryPanelDisplay?: (source: Table) => void;
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ export const ADD_USER_CONTAINER_HEIGHT = 250;
|
||||
export const INGESTION_PROGRESS_START_VAL = 20;
|
||||
export const INGESTION_PROGRESS_END_VAL = 80;
|
||||
export const DEPLOYED_PROGRESS_VAL = 100;
|
||||
export const MAX_CHAR_LIMIT_ENTITY_SUMMARY = 130;
|
||||
export const LOCALSTORAGE_RECENTLY_VIEWED = `recentlyViewedData_${COOKIE_VERSION}`;
|
||||
export const LOCALSTORAGE_RECENTLY_SEARCHED = `recentlySearchedData_${COOKIE_VERSION}`;
|
||||
export const LOCALSTORAGE_USER_PROFILES = 'userProfiles';
|
||||
|
Loading…
x
Reference in New Issue
Block a user