mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-28 10:56:02 +00:00
Feat #8639 UI : Show charts latest value in data insight overview card and rename datasets to data assets (#8641)
* Fix #8639 UI : Show charts latest value in data insight overview card and rename datasets to data assets * Add data summary for entities chart * Add Percentage symbol for Percentage chart * Add web charts summary data * Address review comments * Change active user index
This commit is contained in:
parent
b2e2d9cff3
commit
2e158237ea
@ -79,6 +79,7 @@ const DailyActiveUsersChart: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-active-user-card"
|
data-testid="entity-active-user-card"
|
||||||
|
id={DataInsightChartType.DailyActiveUsers}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@label-color: #37352f90;
|
@label-color: #37352f90;
|
||||||
|
@summary-card-bg-hover: #f0f1f3;
|
||||||
|
|
||||||
.data-insight-card {
|
.data-insight-card {
|
||||||
.ant-card-head {
|
.ant-card-head {
|
||||||
@ -22,3 +23,12 @@
|
|||||||
.ant-typography.data-insight-label-text {
|
.ant-typography.data-insight-label-text {
|
||||||
color: @label-color;
|
color: @label-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.summary-card-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
&:hover {
|
||||||
|
background-color: @summary-card-bg-hover;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-test-renderer';
|
import { act } from 'react-test-renderer';
|
||||||
|
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
import DataInsightSummary from './DataInsightSummary';
|
import DataInsightSummary from './DataInsightSummary';
|
||||||
|
|
||||||
jest.mock('react-i18next', () => ({
|
jest.mock('react-i18next', () => ({
|
||||||
@ -27,6 +28,8 @@ const mockFilter = {
|
|||||||
endTs: 1668000248671,
|
endTs: 1668000248671,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockScrollFunction = jest.fn();
|
||||||
|
|
||||||
jest.mock('../../axiosAPIs/DataInsightAPI', () => ({
|
jest.mock('../../axiosAPIs/DataInsightAPI', () => ({
|
||||||
getAggregateChartData: jest.fn().mockImplementation(() => Promise.resolve()),
|
getAggregateChartData: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||||
}));
|
}));
|
||||||
@ -34,14 +37,21 @@ jest.mock('../../axiosAPIs/DataInsightAPI', () => ({
|
|||||||
describe('Test DataInsightSummary Component', () => {
|
describe('Test DataInsightSummary Component', () => {
|
||||||
it('Should render the overview data', async () => {
|
it('Should render the overview data', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<DataInsightSummary chartFilter={mockFilter} />);
|
render(
|
||||||
|
<DataInsightSummary
|
||||||
|
chartFilter={mockFilter}
|
||||||
|
onScrollToChart={mockScrollFunction}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const summaryCard = screen.getByTestId('summary-card');
|
const summaryCard = screen.getByTestId('summary-card');
|
||||||
|
|
||||||
const allEntityCount = screen.getByTestId('summary-item-latest');
|
const totalEntitiesByType = screen.getByTestId(
|
||||||
|
`summary-item-${DataInsightChartType.TotalEntitiesByType}`
|
||||||
|
);
|
||||||
|
|
||||||
expect(summaryCard).toBeInTheDocument();
|
expect(summaryCard).toBeInTheDocument();
|
||||||
expect(allEntityCount).toBeInTheDocument();
|
expect(totalEntitiesByType).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,51 +11,138 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Typography } from 'antd';
|
import { Card, Col, Row, Space, Typography } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
||||||
|
import { getUserPath } from '../../constants/constants';
|
||||||
|
import {
|
||||||
|
ENTITIES_CHARTS,
|
||||||
|
WEB_CHARTS,
|
||||||
|
} from '../../constants/DataInsight.constants';
|
||||||
import { DataReportIndex } from '../../generated/dataInsight/dataInsightChart';
|
import { DataReportIndex } from '../../generated/dataInsight/dataInsightChart';
|
||||||
import {
|
import {
|
||||||
DataInsightChartResult,
|
DataInsightChartResult,
|
||||||
DataInsightChartType,
|
DataInsightChartType,
|
||||||
} from '../../generated/dataInsight/dataInsightChartResult';
|
} from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
|
import { MostActiveUsers } from '../../generated/dataInsight/type/mostActiveUsers';
|
||||||
import { ChartFilter } from '../../interface/data-insight.interface';
|
import { ChartFilter } from '../../interface/data-insight.interface';
|
||||||
import { getGraphDataByEntityType } from '../../utils/DataInsightUtils';
|
import {
|
||||||
|
getEntitiesChartSummary,
|
||||||
|
getWebChartSummary,
|
||||||
|
} from '../../utils/DataInsightUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
import UserPopOverCard from '../common/PopOverCard/UserPopOverCard';
|
||||||
|
import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
|
||||||
import './DataInsightDetail.less';
|
import './DataInsightDetail.less';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chartFilter: ChartFilter;
|
chartFilter: ChartFilter;
|
||||||
|
onScrollToChart: (chartType: DataInsightChartType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataInsightSummary: FC<Props> = ({ chartFilter }) => {
|
const DataInsightSummary: FC<Props> = ({ chartFilter, onScrollToChart }) => {
|
||||||
const [totalEntitiesByType, setTotalEntitiesByType] =
|
|
||||||
useState<DataInsightChartResult>();
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [entitiesCharts, setEntitiesChart] = useState<
|
||||||
|
(DataInsightChartResult | undefined)[]
|
||||||
|
>([]);
|
||||||
|
const [webCharts, setWebCharts] = useState<
|
||||||
|
(DataInsightChartResult | undefined)[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const { total, latestData = {} } = useMemo(() => {
|
const [mostActiveUser, setMostActiveUser] = useState<MostActiveUsers>();
|
||||||
return getGraphDataByEntityType(
|
|
||||||
totalEntitiesByType?.data ?? [],
|
const entitiesSummaryList = useMemo(
|
||||||
DataInsightChartType.TotalEntitiesByType
|
() => getEntitiesChartSummary(entitiesCharts),
|
||||||
);
|
[entitiesCharts]
|
||||||
}, [totalEntitiesByType]);
|
);
|
||||||
|
|
||||||
|
const webSummaryList = useMemo(
|
||||||
|
() => getWebChartSummary(webCharts),
|
||||||
|
[webCharts]
|
||||||
|
);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const fetchTotalEntitiesByType = async () => {
|
const fetchEntitiesChartData = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const promises = ENTITIES_CHARTS.map((chartName) => {
|
||||||
|
const params = {
|
||||||
|
...chartFilter,
|
||||||
|
dataInsightChartName: chartName,
|
||||||
|
dataReportIndex: DataReportIndex.EntityReportDataIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
return getAggregateChartData(params);
|
||||||
|
});
|
||||||
|
|
||||||
|
const responses = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
const chartDataList = responses
|
||||||
|
.map((response) => {
|
||||||
|
if (response.status === 'fulfilled') {
|
||||||
|
return response.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
setEntitiesChart(chartDataList);
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(error as AxiosError);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMostActiveUser = async () => {
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
...chartFilter,
|
...chartFilter,
|
||||||
dataInsightChartName: DataInsightChartType.TotalEntitiesByType,
|
dataInsightChartName: DataInsightChartType.MostActiveUsers,
|
||||||
dataReportIndex: DataReportIndex.EntityReportDataIndex,
|
dataReportIndex: DataReportIndex.WebAnalyticUserActivityReportDataIndex,
|
||||||
};
|
};
|
||||||
const response = await getAggregateChartData(params);
|
const response = await getAggregateChartData(params);
|
||||||
|
if (response.data && response.data.length) {
|
||||||
|
setMostActiveUser(response.data[0]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(error as AxiosError);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
setTotalEntitiesByType(response);
|
const fetchWebChartData = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const promises = WEB_CHARTS.map((chart) => {
|
||||||
|
const params = {
|
||||||
|
...chartFilter,
|
||||||
|
dataInsightChartName: chart.chart,
|
||||||
|
dataReportIndex: chart.index,
|
||||||
|
};
|
||||||
|
|
||||||
|
return getAggregateChartData(params);
|
||||||
|
});
|
||||||
|
|
||||||
|
const responses = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
const chartDataList = responses
|
||||||
|
.map((response) => {
|
||||||
|
if (response.status === 'fulfilled') {
|
||||||
|
return response.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
setWebCharts(chartDataList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
} finally {
|
} finally {
|
||||||
@ -64,7 +151,9 @@ const DataInsightSummary: FC<Props> = ({ chartFilter }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTotalEntitiesByType();
|
fetchEntitiesChartData();
|
||||||
|
fetchMostActiveUser();
|
||||||
|
fetchWebChartData();
|
||||||
}, [chartFilter]);
|
}, [chartFilter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -78,27 +167,65 @@ const DataInsightSummary: FC<Props> = ({ chartFilter }) => {
|
|||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
}>
|
}>
|
||||||
<Row data-testid="summary-card-content" gutter={[16, 16]}>
|
<Row data-testid="summary-card-content" gutter={[16, 16]}>
|
||||||
<Col data-testid="summary-item-latest" span={4}>
|
{/* summary of entity charts */}
|
||||||
<Typography.Text className="data-insight-label-text">
|
{entitiesSummaryList.map((summary) => (
|
||||||
Latest
|
<Col
|
||||||
</Typography.Text>
|
className="summary-card-item"
|
||||||
<Typography className="font-semibold text-2xl">{total}</Typography>
|
data-testid={`summary-item-${summary.id}`}
|
||||||
</Col>
|
key={summary.id}
|
||||||
{Object.entries(latestData).map((summary) => {
|
span={6}
|
||||||
const label = summary[0];
|
onClick={() => onScrollToChart(summary.id)}>
|
||||||
const value = summary[1] as number;
|
<Typography.Text className="data-insight-label-text">
|
||||||
|
{summary.label}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography className="font-semibold text-2xl m--ml-0.5">
|
||||||
|
{summary.latest}
|
||||||
|
{summary.id.startsWith('Percentage') ? '%' : ''}
|
||||||
|
</Typography>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
|
||||||
return label !== 'timestamp' ? (
|
{/* summary for web charts */}
|
||||||
<Col data-testid={`summary-item-${label}`} key={label} span={4}>
|
{webSummaryList.map((summary) => (
|
||||||
<Typography.Text className="data-insight-label-text">
|
<Col
|
||||||
{label}
|
className="summary-card-item"
|
||||||
</Typography.Text>
|
data-testid={`summary-item-${summary.id}`}
|
||||||
<Typography className="font-semibold text-2xl">
|
key={summary.id}
|
||||||
{value}
|
span={6}
|
||||||
</Typography>
|
onClick={() => onScrollToChart(summary.id)}>
|
||||||
</Col>
|
<Typography.Text className="data-insight-label-text">
|
||||||
) : null;
|
{summary.label}
|
||||||
})}
|
</Typography.Text>
|
||||||
|
<Typography className="font-semibold text-2xl m--ml-0.5">
|
||||||
|
{summary.latest}
|
||||||
|
{summary.id.startsWith('Percentage') ? '%' : ''}
|
||||||
|
</Typography>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* summary of most active user */}
|
||||||
|
{mostActiveUser && mostActiveUser.userName && (
|
||||||
|
<Col
|
||||||
|
data-testid={`summary-item-${DataInsightChartType.MostActiveUsers}`}
|
||||||
|
key={DataInsightChartType.MostActiveUsers}
|
||||||
|
span={6}>
|
||||||
|
<Typography.Text className="data-insight-label-text d-block">
|
||||||
|
{t('label.most-active-user')}
|
||||||
|
</Typography.Text>
|
||||||
|
<UserPopOverCard userName={mostActiveUser.userName}>
|
||||||
|
<Space>
|
||||||
|
<ProfilePicture
|
||||||
|
id=""
|
||||||
|
name={mostActiveUser.userName}
|
||||||
|
type="circle"
|
||||||
|
/>
|
||||||
|
<Link to={getUserPath(mostActiveUser.userName)}>
|
||||||
|
{mostActiveUser.userName}
|
||||||
|
</Link>
|
||||||
|
</Space>
|
||||||
|
</UserPopOverCard>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -93,6 +93,7 @@ const DescriptionInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-description-percentage-card"
|
data-testid="entity-description-percentage-card"
|
||||||
|
id={DataInsightChartType.PercentageOfEntitiesWithDescriptionByType}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -93,6 +93,7 @@ const OwnerInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-summary-card-percentage"
|
data-testid="entity-summary-card-percentage"
|
||||||
|
id={DataInsightChartType.PercentageOfEntitiesWithOwnerByType}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -90,6 +90,7 @@ const PageViewsByEntitiesChart: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-page-views-card"
|
data-testid="entity-page-views-card"
|
||||||
|
id={DataInsightChartType.PageViewsByEntities}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -89,6 +89,7 @@ const TierInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-summary-card-percentage"
|
data-testid="entity-summary-card-percentage"
|
||||||
|
id={DataInsightChartType.TotalEntitiesByTier}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -64,9 +64,9 @@ const TopViewEntities: FC<Props> = ({ chartFilter }) => {
|
|||||||
const columns: ColumnsType<MostViewedEntities> = useMemo(
|
const columns: ColumnsType<MostViewedEntities> = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: t('label.entity-name'),
|
title: t('label.data-asset'),
|
||||||
dataIndex: 'entityFqn',
|
dataIndex: 'entityFqn',
|
||||||
key: 'entityName',
|
key: 'dataAsset',
|
||||||
render: (entityFqn: string) => (
|
render: (entityFqn: string) => (
|
||||||
<Typography.Text>{getDecodedFqn(entityFqn)}</Typography.Text>
|
<Typography.Text>{getDecodedFqn(entityFqn)}</Typography.Text>
|
||||||
),
|
),
|
||||||
@ -82,7 +82,7 @@ const TopViewEntities: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Typography.Text>{owner}</Typography.Text>
|
<Typography.Text>{owner}</Typography.Text>
|
||||||
</Space>
|
</Space>
|
||||||
) : (
|
) : (
|
||||||
<Typography.Text>{t('label.no-owner')}</Typography.Text>
|
<Typography.Text>--</Typography.Text>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -107,7 +107,7 @@ const TopViewEntities: FC<Props> = ({ chartFilter }) => {
|
|||||||
{t('label.data-insight-top-viewed-entity-summary')}
|
{t('label.data-insight-top-viewed-entity-summary')}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Typography.Text className="data-insight-label-text">
|
<Typography.Text className="data-insight-label-text">
|
||||||
{t('message.most-viewed-datasets')}
|
{t('message.most-viewed-data-assets')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</>
|
</>
|
||||||
}>
|
}>
|
||||||
|
@ -92,6 +92,7 @@ const TotalEntityInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Card
|
<Card
|
||||||
className="data-insight-card"
|
className="data-insight-card"
|
||||||
data-testid="entity-summary-card"
|
data-testid="entity-summary-card"
|
||||||
|
id={DataInsightChartType.TotalEntitiesByType}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
import { Margin } from 'recharts/types/util/types';
|
import { Margin } from 'recharts/types/util/types';
|
||||||
|
import { DataReportIndex } from '../generated/dataInsight/dataInsightChart';
|
||||||
|
import { DataInsightChartType } from '../generated/dataInsight/dataInsightChartResult';
|
||||||
import { ChartFilter } from '../interface/data-insight.interface';
|
import { ChartFilter } from '../interface/data-insight.interface';
|
||||||
import {
|
import {
|
||||||
getCurrentDateTimeMillis,
|
getCurrentDateTimeMillis,
|
||||||
@ -67,7 +69,7 @@ export const TIER_BAR_COLOR_MAP: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DATA_INSIGHT_TAB = {
|
export const DATA_INSIGHT_TAB = {
|
||||||
Datasets: 'Datasets',
|
DataAssets: 'Data assets',
|
||||||
'Web Analytics': 'Web Analytics',
|
'Web Analytics': 'Web Analytics',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,3 +119,57 @@ export const INITIAL_CHART_FILTER: ChartFilter = {
|
|||||||
startTs: getPastDaysDateTimeMillis(DEFAULT_DAYS),
|
startTs: getPastDaysDateTimeMillis(DEFAULT_DAYS),
|
||||||
endTs: getCurrentDateTimeMillis(),
|
endTs: getCurrentDateTimeMillis(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ENTITIES_CHARTS = [
|
||||||
|
DataInsightChartType.TotalEntitiesByType,
|
||||||
|
DataInsightChartType.PercentageOfEntitiesWithDescriptionByType,
|
||||||
|
DataInsightChartType.PercentageOfEntitiesWithOwnerByType,
|
||||||
|
DataInsightChartType.TotalEntitiesByTier,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WEB_CHARTS = [
|
||||||
|
{
|
||||||
|
chart: DataInsightChartType.PageViewsByEntities,
|
||||||
|
index: DataReportIndex.WebAnalyticEntityViewReportDataIndex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chart: DataInsightChartType.DailyActiveUsers,
|
||||||
|
index: DataReportIndex.WebAnalyticUserActivityReportDataIndex,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WEB_SUMMARY_LIST = [
|
||||||
|
{
|
||||||
|
label: i18n.t('label.page-views-by-entities'),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.PageViewsByEntities,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t('label.daily-active-user'),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.DailyActiveUsers,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ENTITIES_SUMMARY_LIST = [
|
||||||
|
{
|
||||||
|
label: i18n.t('label.total-data-assets'),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.TotalEntitiesByType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t('label.data-assets-with-field', { field: 'description' }),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.PercentageOfEntitiesWithDescriptionByType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t('label.data-assets-with-field', { field: 'owners' }),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.PercentageOfEntitiesWithOwnerByType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t('label.total-data-assets-with-tiers'),
|
||||||
|
latest: 0,
|
||||||
|
id: DataInsightChartType.TotalEntitiesByTier,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -175,12 +175,12 @@
|
|||||||
"scopes-comma-separated": "Scopes value comma separated",
|
"scopes-comma-separated": "Scopes value comma separated",
|
||||||
"find-in-table": "Find in table",
|
"find-in-table": "Find in table",
|
||||||
"data-insight-summary": "OpenMetadata health at a glance",
|
"data-insight-summary": "OpenMetadata health at a glance",
|
||||||
"data-insight-description-summary": "Percentage of Datasets With Description",
|
"data-insight-description-summary": "Percentage of Data assets With Description",
|
||||||
"data-insight-owner-summary": "Percentage of Datasets With Owners",
|
"data-insight-owner-summary": "Percentage of Data assets With Owners",
|
||||||
"data-insight-tier-summary": "Total Datasets by Tier",
|
"data-insight-tier-summary": "Total Data assets by Tier",
|
||||||
"data-insight-active-user-summary": "Most Active Users",
|
"data-insight-active-user-summary": "Most Active Users",
|
||||||
"data-insight-top-viewed-entity-summary": "Most Viewed Datasets",
|
"data-insight-top-viewed-entity-summary": "Most Viewed Data assets",
|
||||||
"data-insight-total-entity-summary": "Total Datasets",
|
"data-insight-total-entity-summary": "Total Data assets",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"team": "Team",
|
"team": "Team",
|
||||||
"most-recent-session": "Most Recent Session",
|
"most-recent-session": "Most Recent Session",
|
||||||
@ -227,12 +227,17 @@
|
|||||||
"read-more": "read more",
|
"read-more": "read more",
|
||||||
"read-less": "read less",
|
"read-less": "read less",
|
||||||
"no-owner": "No Owner",
|
"no-owner": "No Owner",
|
||||||
"page-views-by-entities": "Page views by datasets",
|
"page-views-by-entities": "Page views by data assets",
|
||||||
"daily-active-user": "Daily active users on the platform",
|
"daily-active-user": "Daily active users on the platform",
|
||||||
"collapse-all": "Collapse All",
|
"collapse-all": "Collapse All",
|
||||||
"expand-all": "Expand All",
|
"expand-all": "Expand All",
|
||||||
"search-lineage": "Search Lineage",
|
"search-lineage": "Search Lineage",
|
||||||
"edit-lineage": "Edit Lineage"
|
"edit-lineage": "Edit Lineage",
|
||||||
|
"data-asset": "Data asset",
|
||||||
|
"total-data-assets": "Total data assets",
|
||||||
|
"data-assets-with-field": "Data assets with {{field}}",
|
||||||
|
"total-data-assets-with-tiers": "Total Data assets with tiers",
|
||||||
|
"most-active-user": "Most active user"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"service-email-required": "Service account Email is required",
|
"service-email-required": "Service account Email is required",
|
||||||
@ -255,11 +260,11 @@
|
|||||||
"no-ingestion-description": "To view Ingestion Data, run the MetaData Ingestion. Please refer to this doc to schedule the",
|
"no-ingestion-description": "To view Ingestion Data, run the MetaData Ingestion. Please refer to this doc to schedule the",
|
||||||
"fetch-pipeline-status-error": "Error while fetching pipeline status.",
|
"fetch-pipeline-status-error": "Error while fetching pipeline status.",
|
||||||
"data-insight-page-views": "Displays the number of time an dataset type was viewed.",
|
"data-insight-page-views": "Displays the number of time an dataset type was viewed.",
|
||||||
"field-insight": "Display the percentage of datasets with {{field}} by type.",
|
"field-insight": "Display the percentage of data assets with {{field}} by type.",
|
||||||
"total-entity-insight": "Display the total of datasets by type.",
|
"total-entity-insight": "Display the total of data assets by type.",
|
||||||
"active-users": "Display the number of users active.",
|
"active-users": "Display the number of users active.",
|
||||||
"most-active-users": "Displays the most active users on the platform based on page views.",
|
"most-active-users": "Displays the most active users on the platform based on page views.",
|
||||||
"most-viewed-datasets": "Displays the most viewed datasets."
|
"most-viewed-data-assets": "Displays the most viewed data assets."
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"no-followed-entities": "You have not followed anything yet.",
|
"no-followed-entities": "You have not followed anything yet.",
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { searchQuery } from '../../axiosAPIs/searchAPI';
|
import { searchQuery } from '../../axiosAPIs/searchAPI';
|
||||||
|
|
||||||
import { autocomplete } from '../../components/AdvancedSearch/AdvancedSearch.constants';
|
import { autocomplete } from '../../components/AdvancedSearch/AdvancedSearch.constants';
|
||||||
@ -40,10 +40,12 @@ import {
|
|||||||
DATA_INSIGHT_TAB,
|
DATA_INSIGHT_TAB,
|
||||||
DAY_FILTER,
|
DAY_FILTER,
|
||||||
DEFAULT_DAYS,
|
DEFAULT_DAYS,
|
||||||
|
ENTITIES_CHARTS,
|
||||||
INITIAL_CHART_FILTER,
|
INITIAL_CHART_FILTER,
|
||||||
TIER_FILTER,
|
TIER_FILTER,
|
||||||
} from '../../constants/DataInsight.constants';
|
} from '../../constants/DataInsight.constants';
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
|
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
import { ChartFilter } from '../../interface/data-insight.interface';
|
import { ChartFilter } from '../../interface/data-insight.interface';
|
||||||
import { getTeamFilter } from '../../utils/DataInsightUtils';
|
import { getTeamFilter } from '../../utils/DataInsightUtils';
|
||||||
import {
|
import {
|
||||||
@ -57,10 +59,12 @@ const fetchTeamSuggestions = autocomplete(SearchIndex.TEAM);
|
|||||||
|
|
||||||
const DataInsightPage = () => {
|
const DataInsightPage = () => {
|
||||||
const [teamsOptions, setTeamOptions] = useState<SelectProps['options']>([]);
|
const [teamsOptions, setTeamOptions] = useState<SelectProps['options']>([]);
|
||||||
const [activeTab, setActiveTab] = useState(DATA_INSIGHT_TAB.Datasets);
|
const [activeTab, setActiveTab] = useState(DATA_INSIGHT_TAB.DataAssets);
|
||||||
const [chartFilter, setChartFilter] =
|
const [chartFilter, setChartFilter] =
|
||||||
useState<ChartFilter>(INITIAL_CHART_FILTER);
|
useState<ChartFilter>(INITIAL_CHART_FILTER);
|
||||||
|
|
||||||
|
const [selectedChart, setSelectedChart] = useState<DataInsightChartType>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChartFilter(INITIAL_CHART_FILTER);
|
setChartFilter(INITIAL_CHART_FILTER);
|
||||||
}, []);
|
}, []);
|
||||||
@ -120,6 +124,25 @@ const DataInsightPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleScrollToChart = (chartType: DataInsightChartType) => {
|
||||||
|
if (ENTITIES_CHARTS.includes(chartType)) {
|
||||||
|
setActiveTab(DATA_INSIGHT_TAB.DataAssets);
|
||||||
|
} else {
|
||||||
|
setActiveTab(DATA_INSIGHT_TAB['Web Analytics']);
|
||||||
|
}
|
||||||
|
setSelectedChart(chartType);
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (selectedChart) {
|
||||||
|
const element = document.getElementById(selectedChart);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||||
|
setSelectedChart(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedChart]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDefaultTeamOptions();
|
fetchDefaultTeamOptions();
|
||||||
}, []);
|
}, []);
|
||||||
@ -187,7 +210,10 @@ const DataInsightPage = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<DataInsightSummary chartFilter={chartFilter} />
|
<DataInsightSummary
|
||||||
|
chartFilter={chartFilter}
|
||||||
|
onScrollToChart={handleScrollToChart}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
@ -200,7 +226,7 @@ const DataInsightPage = () => {
|
|||||||
onChange={(e) => setActiveTab(e.target.value)}
|
onChange={(e) => setActiveTab(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
{activeTab === DATA_INSIGHT_TAB.Datasets && (
|
{activeTab === DATA_INSIGHT_TAB.DataAssets && (
|
||||||
<>
|
<>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TotalEntityInsight chartFilter={chartFilter} />
|
<TotalEntityInsight chartFilter={chartFilter} />
|
||||||
@ -225,10 +251,10 @@ const DataInsightPage = () => {
|
|||||||
<PageViewsByEntitiesChart chartFilter={chartFilter} />
|
<PageViewsByEntitiesChart chartFilter={chartFilter} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TopActiveUsers chartFilter={chartFilter} />
|
<DailyActiveUsersChart chartFilter={chartFilter} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<DailyActiveUsersChart chartFilter={chartFilter} />
|
<TopActiveUsers chartFilter={chartFilter} />
|
||||||
</Col>
|
</Col>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -397,3 +397,7 @@
|
|||||||
.m--ml-1 {
|
.m--ml-1 {
|
||||||
margin-left: -0.25rem /* -4px */;
|
margin-left: -0.25rem /* -4px */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m--ml-0\.5 {
|
||||||
|
margin-left: -0.125rem /* -2px */;
|
||||||
|
}
|
||||||
|
@ -12,10 +12,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Typography } from 'antd';
|
import { Card, Typography } from 'antd';
|
||||||
import { isInteger, last, toNumber } from 'lodash';
|
import { isInteger, isUndefined, last, toNumber } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ListItem, ListValues } from 'react-awesome-query-builder';
|
import { ListItem, ListValues } from 'react-awesome-query-builder';
|
||||||
import { LegendProps, Surface } from 'recharts';
|
import { LegendProps, Surface } from 'recharts';
|
||||||
|
import {
|
||||||
|
ENTITIES_SUMMARY_LIST,
|
||||||
|
WEB_SUMMARY_LIST,
|
||||||
|
} from '../constants/DataInsight.constants';
|
||||||
import {
|
import {
|
||||||
DataInsightChartResult,
|
DataInsightChartResult,
|
||||||
DataInsightChartType,
|
DataInsightChartType,
|
||||||
@ -242,3 +246,62 @@ export const getFormattedActiveUsersData = (activeUsers: DailyActiveUsers[]) =>
|
|||||||
? getFormattedDateFromMilliSeconds(user.timestamp)
|
? getFormattedDateFromMilliSeconds(user.timestamp)
|
||||||
: '',
|
: '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const getEntitiesChartSummary = (
|
||||||
|
chartResults: (DataInsightChartResult | undefined)[]
|
||||||
|
) => {
|
||||||
|
const updatedSummaryList = ENTITIES_SUMMARY_LIST.map((summary) => {
|
||||||
|
// grab the current chart type
|
||||||
|
const chartData = chartResults.find(
|
||||||
|
(chart) => chart?.chartType === summary.id
|
||||||
|
);
|
||||||
|
|
||||||
|
// return default summary if chart data is undefined else calculate the latest count for chartType
|
||||||
|
if (isUndefined(chartData)) return summary;
|
||||||
|
else {
|
||||||
|
if (chartData.chartType === DataInsightChartType.TotalEntitiesByTier) {
|
||||||
|
const { total } = getGraphDataByTierType(chartData.data ?? []);
|
||||||
|
|
||||||
|
return { ...summary, latest: total };
|
||||||
|
} else {
|
||||||
|
const { total } = getGraphDataByEntityType(
|
||||||
|
chartData.data ?? [],
|
||||||
|
chartData.chartType
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ...summary, latest: total };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedSummaryList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWebChartSummary = (
|
||||||
|
chartResults: (DataInsightChartResult | undefined)[]
|
||||||
|
) => {
|
||||||
|
const updatedSummary = WEB_SUMMARY_LIST.map((summary) => {
|
||||||
|
// grab the current chart type
|
||||||
|
const chartData = chartResults.find(
|
||||||
|
(chart) => chart?.chartType === summary.id
|
||||||
|
);
|
||||||
|
// return default summary if chart data is undefined else calculate the latest count for chartType
|
||||||
|
if (isUndefined(chartData)) return summary;
|
||||||
|
else {
|
||||||
|
if (chartData.chartType === DataInsightChartType.DailyActiveUsers) {
|
||||||
|
const latestData = last(chartData.data);
|
||||||
|
|
||||||
|
return { ...summary, latest: latestData?.activeUsers ?? 0 };
|
||||||
|
} else {
|
||||||
|
const { total } = getGraphDataByEntityType(
|
||||||
|
chartData.data ?? [],
|
||||||
|
chartData.chartType
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ...summary, latest: total };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedSummary;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user