mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-29 19:35:56 +00:00
UI: DataInsight feedback part 4 (#9345)
* UI: Data insight feedback part 4 * layout fix added data assets type field in top view entities table * Fixed Unit test and added localization
This commit is contained in:
parent
a2b34dd0f4
commit
01f3e3f914
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Progress, Typography } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||||
|
|
||||||
|
interface DataInsightProgressBarProps {
|
||||||
|
width?: number;
|
||||||
|
progress: number;
|
||||||
|
className?: string;
|
||||||
|
showLabel?: boolean;
|
||||||
|
showSuccessInfo?: boolean;
|
||||||
|
label?: string;
|
||||||
|
target?: number;
|
||||||
|
successValue?: number | string;
|
||||||
|
startValue?: number | string;
|
||||||
|
suffix?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DataInsightProgressBar = ({
|
||||||
|
width,
|
||||||
|
progress,
|
||||||
|
className,
|
||||||
|
target,
|
||||||
|
startValue,
|
||||||
|
label,
|
||||||
|
suffix = '%',
|
||||||
|
successValue = 100,
|
||||||
|
showLabel = true,
|
||||||
|
showSuccessInfo = false,
|
||||||
|
}: DataInsightProgressBarProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(className)} style={{ width }}>
|
||||||
|
{showLabel && (
|
||||||
|
<Typography.Text className="data-insight-label-text">
|
||||||
|
{label ?? t('label.latest')}
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
<div className="flex">
|
||||||
|
<Progress
|
||||||
|
className="data-insight-progress-bar"
|
||||||
|
format={(per) => (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{startValue ?? per}
|
||||||
|
{suffix}
|
||||||
|
</span>
|
||||||
|
{target && (
|
||||||
|
<span
|
||||||
|
className="data-insight-kpi-target"
|
||||||
|
style={{ width: `${target}%` }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>
|
||||||
|
{successValue}
|
||||||
|
{suffix}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
percent={progress}
|
||||||
|
strokeColor="#B3D4F4"
|
||||||
|
/>
|
||||||
|
{showSuccessInfo && progress >= 100 && (
|
||||||
|
<SVGIcons className="m-l-xs" icon={Icons.SUCCESS_BADGE} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataInsightProgressBar;
|
@ -15,7 +15,6 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { act } from 'react-test-renderer';
|
import { act } from 'react-test-renderer';
|
||||||
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
|
||||||
import { DataInsightTabs } from '../../interface/data-insight.interface';
|
import { DataInsightTabs } from '../../interface/data-insight.interface';
|
||||||
import DataInsightSummary from './DataInsightSummary';
|
import DataInsightSummary from './DataInsightSummary';
|
||||||
|
|
||||||
@ -55,12 +54,7 @@ describe('Test DataInsightSummary Component', () => {
|
|||||||
|
|
||||||
const summaryCard = screen.getByTestId('summary-card');
|
const summaryCard = screen.getByTestId('summary-card');
|
||||||
|
|
||||||
const totalEntitiesByType = screen.getByTestId(
|
|
||||||
`summary-item-${DataInsightChartType.TotalEntitiesByType}`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(summaryCard).toBeInTheDocument();
|
expect(summaryCard).toBeInTheDocument();
|
||||||
expect(totalEntitiesByType).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should render only the data assets summary', async () => {
|
it('Should render only the data assets summary', async () => {
|
||||||
@ -74,9 +68,11 @@ describe('Test DataInsightSummary Component', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const dataAssetSummary = await screen.findByTestId('data-assets-summary');
|
const dataAssetSummary = await screen.findAllByTestId(
|
||||||
|
'data-assets-summary'
|
||||||
|
);
|
||||||
|
|
||||||
expect(dataAssetSummary).toBeInTheDocument();
|
expect(dataAssetSummary).toHaveLength(4);
|
||||||
|
|
||||||
// should not render the app analytics summary
|
// should not render the app analytics summary
|
||||||
expect(screen.queryByTestId('app-analytics-summary')).toBeNull();
|
expect(screen.queryByTestId('app-analytics-summary')).toBeNull();
|
||||||
@ -95,11 +91,11 @@ describe('Test DataInsightSummary Component', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const appAnalyticsSummary = await screen.findByTestId(
|
const appAnalyticsSummary = await screen.findAllByTestId(
|
||||||
'app-analytics-summary'
|
'app-analytics-summary'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(appAnalyticsSummary).toBeInTheDocument();
|
expect(appAnalyticsSummary).toHaveLength(2);
|
||||||
|
|
||||||
// should not render the data assets summary
|
// should not render the data assets summary
|
||||||
expect(screen.queryByTestId('data-assets-summary')).toBeNull();
|
expect(screen.queryByTestId('data-assets-summary')).toBeNull();
|
||||||
|
@ -174,12 +174,12 @@ const DataInsightSummary: FC<Props> = ({ chartFilter, onScrollToChart }) => {
|
|||||||
}>
|
}>
|
||||||
<Row data-testid="summary-card-content" gutter={[16, 16]}>
|
<Row data-testid="summary-card-content" gutter={[16, 16]}>
|
||||||
{tab === DataInsightTabs.DATA_ASSETS && (
|
{tab === DataInsightTabs.DATA_ASSETS && (
|
||||||
<div data-testid="data-assets-summary">
|
<>
|
||||||
{/* summary of entity charts */}
|
{/* summary of entity charts */}
|
||||||
{entitiesSummaryList.map((summary) => (
|
{entitiesSummaryList.map((summary) => (
|
||||||
<Col
|
<Col
|
||||||
className="summary-card-item"
|
className="summary-card-item"
|
||||||
data-testid={`summary-item-${summary.id}`}
|
data-testid="data-assets-summary"
|
||||||
key={summary.id}
|
key={summary.id}
|
||||||
span={6}
|
span={6}
|
||||||
onClick={() => onScrollToChart(summary.id)}>
|
onClick={() => onScrollToChart(summary.id)}>
|
||||||
@ -188,19 +188,22 @@ const DataInsightSummary: FC<Props> = ({ chartFilter, onScrollToChart }) => {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Typography className="font-semibold text-2xl m--ml-0.5">
|
<Typography className="font-semibold text-2xl m--ml-0.5">
|
||||||
{summary.latest}
|
{summary.latest}
|
||||||
{summary.id.startsWith('Percentage') ? '%' : ''}
|
{summary.id.startsWith('Percentage') ||
|
||||||
|
summary.id.includes(DataInsightChartType.TotalEntitiesByTier)
|
||||||
|
? '%'
|
||||||
|
: ''}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
{tab === DataInsightTabs.APP_ANALYTICS && (
|
{tab === DataInsightTabs.APP_ANALYTICS && (
|
||||||
<div data-testid="app-analytics-summary">
|
<>
|
||||||
{/* summary for web charts */}
|
{/* summary for web charts */}
|
||||||
{webSummaryList.map((summary) => (
|
{webSummaryList.map((summary) => (
|
||||||
<Col
|
<Col
|
||||||
className="summary-card-item"
|
className="summary-card-item"
|
||||||
data-testid={`summary-item-${summary.id}`}
|
data-testid="app-analytics-summary"
|
||||||
key={summary.id}
|
key={summary.id}
|
||||||
span={6}
|
span={6}
|
||||||
onClick={() => onScrollToChart(summary.id)}>
|
onClick={() => onScrollToChart(summary.id)}>
|
||||||
@ -237,7 +240,7 @@ const DataInsightSummary: FC<Props> = ({ chartFilter, onScrollToChart }) => {
|
|||||||
</UserPopOverCard>
|
</UserPopOverCard>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -62,7 +62,10 @@ describe('Test DescriptionInsight Component', () => {
|
|||||||
it('Should render the graph', async () => {
|
it('Should render the graph', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<DescriptionInsight chartFilter={INITIAL_CHART_FILTER} />
|
<DescriptionInsight
|
||||||
|
chartFilter={INITIAL_CHART_FILTER}
|
||||||
|
kpi={undefined}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
const card = screen.getByTestId('entity-description-percentage-card');
|
const card = screen.getByTestId('entity-description-percentage-card');
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
XAxis,
|
XAxis,
|
||||||
|
YAxis,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
||||||
import {
|
import {
|
||||||
@ -41,8 +42,12 @@ import {
|
|||||||
DataInsightChartResult,
|
DataInsightChartResult,
|
||||||
DataInsightChartType,
|
DataInsightChartType,
|
||||||
} from '../../generated/dataInsight/dataInsightChartResult';
|
} from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
|
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
|
||||||
import { ChartFilter } from '../../interface/data-insight.interface';
|
import { ChartFilter } from '../../interface/data-insight.interface';
|
||||||
import { updateActiveChartFilter } from '../../utils/ChartUtils';
|
import {
|
||||||
|
axisTickFormatter,
|
||||||
|
updateActiveChartFilter,
|
||||||
|
} from '../../utils/ChartUtils';
|
||||||
import {
|
import {
|
||||||
CustomTooltip,
|
CustomTooltip,
|
||||||
getGraphDataByEntityType,
|
getGraphDataByEntityType,
|
||||||
@ -50,13 +55,15 @@ import {
|
|||||||
} from '../../utils/DataInsightUtils';
|
} from '../../utils/DataInsightUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import './DataInsightDetail.less';
|
import './DataInsightDetail.less';
|
||||||
|
import DataInsightProgressBar from './DataInsightProgressBar';
|
||||||
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chartFilter: ChartFilter;
|
chartFilter: ChartFilter;
|
||||||
|
kpi: Kpi | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DescriptionInsight: FC<Props> = ({ chartFilter }) => {
|
const DescriptionInsight: FC<Props> = ({ chartFilter, kpi }) => {
|
||||||
const [totalEntitiesDescriptionByType, setTotalEntitiesDescriptionByType] =
|
const [totalEntitiesDescriptionByType, setTotalEntitiesDescriptionByType] =
|
||||||
useState<DataInsightChartResult>();
|
useState<DataInsightChartResult>();
|
||||||
|
|
||||||
@ -73,6 +80,14 @@ const DescriptionInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const targetValue = useMemo(() => {
|
||||||
|
if (kpi?.targetDefinition) {
|
||||||
|
return Number(kpi.targetDefinition[0].value) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}, [kpi]);
|
||||||
|
|
||||||
const fetchTotalEntitiesDescriptionByType = async () => {
|
const fetchTotalEntitiesDescriptionByType = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
@ -125,6 +140,12 @@ const DescriptionInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</>
|
</>
|
||||||
}>
|
}>
|
||||||
|
<DataInsightProgressBar
|
||||||
|
className="m-b-md"
|
||||||
|
progress={Number(total)}
|
||||||
|
target={targetValue}
|
||||||
|
width={250}
|
||||||
|
/>
|
||||||
{data.length ? (
|
{data.length ? (
|
||||||
<ResponsiveContainer
|
<ResponsiveContainer
|
||||||
debounce={1}
|
debounce={1}
|
||||||
@ -133,11 +154,14 @@ const DescriptionInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
||||||
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} vertical={false} />
|
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} vertical={false} />
|
||||||
<XAxis dataKey="timestamp" />
|
<XAxis dataKey="timestamp" />
|
||||||
|
<YAxis
|
||||||
|
tickFormatter={(value: number) => axisTickFormatter(value, '%')}
|
||||||
|
/>
|
||||||
<Tooltip content={<CustomTooltip isPercentage />} />
|
<Tooltip content={<CustomTooltip isPercentage />} />
|
||||||
<Legend
|
<Legend
|
||||||
align="left"
|
align="left"
|
||||||
content={(props) =>
|
content={(props) =>
|
||||||
renderLegend(props as LegendProps, `${total}%`, activeKeys)
|
renderLegend(props as LegendProps, total, activeKeys, false)
|
||||||
}
|
}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
verticalAlign="top"
|
verticalAlign="top"
|
||||||
|
@ -34,11 +34,7 @@ import {
|
|||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
import {
|
import { getLatestKpiResult, getListKpiResult } from '../../axiosAPIs/KpiAPI';
|
||||||
getLatestKpiResult,
|
|
||||||
getListKpiResult,
|
|
||||||
getListKPIs,
|
|
||||||
} from '../../axiosAPIs/KpiAPI';
|
|
||||||
import { GRAPH_BACKGROUND_COLOR, ROUTES } from '../../constants/constants';
|
import { GRAPH_BACKGROUND_COLOR, ROUTES } from '../../constants/constants';
|
||||||
import {
|
import {
|
||||||
BAR_CHART_MARGIN,
|
BAR_CHART_MARGIN,
|
||||||
@ -63,13 +59,14 @@ import KPILatestResults from './KPILatestResults';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chartFilter: ChartFilter;
|
chartFilter: ChartFilter;
|
||||||
|
kpiList: Array<Kpi>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KPIChart: FC<Props> = ({ chartFilter }) => {
|
const KPIChart: FC<Props> = ({ chartFilter, kpiList }) => {
|
||||||
const { isAdminUser } = useAuth();
|
const { isAdminUser } = useAuth();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
|
||||||
const [kpiResults, setKpiResults] = useState<KpiResult[]>([]);
|
const [kpiResults, setKpiResults] = useState<KpiResult[]>([]);
|
||||||
const [kpiLatestResults, setKpiLatestResults] =
|
const [kpiLatestResults, setKpiLatestResults] =
|
||||||
useState<Record<string, UIKpiResult>>();
|
useState<Record<string, UIKpiResult>>();
|
||||||
@ -77,18 +74,6 @@ const KPIChart: FC<Props> = ({ chartFilter }) => {
|
|||||||
|
|
||||||
const handleAddKpi = () => history.push(ROUTES.ADD_KPI);
|
const handleAddKpi = () => history.push(ROUTES.ADD_KPI);
|
||||||
|
|
||||||
const fetchKpiList = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const response = await getListKPIs();
|
|
||||||
setKpiList(response.data);
|
|
||||||
} catch (_err) {
|
|
||||||
setKpiList([]);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchKpiResults = async () => {
|
const fetchKpiResults = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
@ -167,10 +152,6 @@ const KPIChart: FC<Props> = ({ chartFilter }) => {
|
|||||||
return { ...getKpiGraphData(kpiResults, kpiList), kpiTooltipRecord };
|
return { ...getKpiGraphData(kpiResults, kpiList), kpiTooltipRecord };
|
||||||
}, [kpiResults, kpiList]);
|
}, [kpiResults, kpiList]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchKpiList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setKpiResults([]);
|
setKpiResults([]);
|
||||||
setKpiLatestResults(undefined);
|
setKpiLatestResults(undefined);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Progress, Space, Typography } from 'antd';
|
import { Space, Typography } from 'antd';
|
||||||
import { toNumber, uniqueId } from 'lodash';
|
import { toNumber, uniqueId } from 'lodash';
|
||||||
|
|
||||||
import React, { FC, useMemo } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
@ -6,6 +6,7 @@ import { KpiTargetType } from '../../generated/api/dataInsight/kpi/createKpiRequ
|
|||||||
import { UIKpiResult } from '../../interface/data-insight.interface';
|
import { UIKpiResult } from '../../interface/data-insight.interface';
|
||||||
import { getKpiResultFeedback } from '../../utils/DataInsightUtils';
|
import { getKpiResultFeedback } from '../../utils/DataInsightUtils';
|
||||||
import { getNumberOfDaysForTimestamp } from '../../utils/TimeUtils';
|
import { getNumberOfDaysForTimestamp } from '../../utils/TimeUtils';
|
||||||
|
import DataInsightProgressBar from './DataInsightProgressBar';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
kpiLatestResultsRecord: Record<string, UIKpiResult>;
|
kpiLatestResultsRecord: Record<string, UIKpiResult>;
|
||||||
@ -49,15 +50,18 @@ const KPILatestResults: FC<Props> = ({ kpiLatestResultsRecord }) => {
|
|||||||
<Typography.Text className="data-insight-label-text">
|
<Typography.Text className="data-insight-label-text">
|
||||||
{resultData.displayName ?? name}
|
{resultData.displayName ?? name}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Space className="w-full justify-between">
|
|
||||||
<Typography.Text>{`${
|
<DataInsightProgressBar
|
||||||
isPercentage ? targetPercentValue : targetValue
|
showSuccessInfo
|
||||||
}${suffix}`}</Typography.Text>
|
progress={Number(currentProgress)}
|
||||||
<Typography.Text>{`${
|
showLabel={false}
|
||||||
|
startValue={isPercentage ? targetPercentValue : targetValue}
|
||||||
|
successValue={
|
||||||
isPercentage ? targetMetPercentValue : targetMetValue
|
isPercentage ? targetMetPercentValue : targetMetValue
|
||||||
}${suffix}`}</Typography.Text>
|
}
|
||||||
</Space>
|
suffix={suffix}
|
||||||
<Progress percent={currentProgress} showInfo={isTargetMet} />
|
/>
|
||||||
|
|
||||||
<Typography.Text className="data-insight-label-text">
|
<Typography.Text className="data-insight-label-text">
|
||||||
{getKpiResultFeedback(daysLeft, Boolean(isTargetMet))}
|
{getKpiResultFeedback(daysLeft, Boolean(isTargetMet))}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
XAxis,
|
XAxis,
|
||||||
|
YAxis,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
import { getAggregateChartData } from '../../axiosAPIs/DataInsightAPI';
|
||||||
import {
|
import {
|
||||||
@ -41,8 +42,12 @@ import {
|
|||||||
DataInsightChartResult,
|
DataInsightChartResult,
|
||||||
DataInsightChartType,
|
DataInsightChartType,
|
||||||
} from '../../generated/dataInsight/dataInsightChartResult';
|
} from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
|
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
|
||||||
import { ChartFilter } from '../../interface/data-insight.interface';
|
import { ChartFilter } from '../../interface/data-insight.interface';
|
||||||
import { updateActiveChartFilter } from '../../utils/ChartUtils';
|
import {
|
||||||
|
axisTickFormatter,
|
||||||
|
updateActiveChartFilter,
|
||||||
|
} from '../../utils/ChartUtils';
|
||||||
import {
|
import {
|
||||||
CustomTooltip,
|
CustomTooltip,
|
||||||
getGraphDataByEntityType,
|
getGraphDataByEntityType,
|
||||||
@ -50,13 +55,15 @@ import {
|
|||||||
} from '../../utils/DataInsightUtils';
|
} from '../../utils/DataInsightUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import './DataInsightDetail.less';
|
import './DataInsightDetail.less';
|
||||||
|
import DataInsightProgressBar from './DataInsightProgressBar';
|
||||||
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chartFilter: ChartFilter;
|
chartFilter: ChartFilter;
|
||||||
|
kpi: Kpi | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OwnerInsight: FC<Props> = ({ chartFilter }) => {
|
const OwnerInsight: FC<Props> = ({ chartFilter, kpi }) => {
|
||||||
const [totalEntitiesOwnerByType, setTotalEntitiesOwnerByType] =
|
const [totalEntitiesOwnerByType, setTotalEntitiesOwnerByType] =
|
||||||
useState<DataInsightChartResult>();
|
useState<DataInsightChartResult>();
|
||||||
|
|
||||||
@ -71,6 +78,14 @@ const OwnerInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
);
|
);
|
||||||
}, [totalEntitiesOwnerByType]);
|
}, [totalEntitiesOwnerByType]);
|
||||||
|
|
||||||
|
const targetValue = useMemo(() => {
|
||||||
|
if (kpi?.targetDefinition) {
|
||||||
|
return Number(kpi.targetDefinition[0].value) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}, [kpi]);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const fetchTotalEntitiesOwnerByType = async () => {
|
const fetchTotalEntitiesOwnerByType = async () => {
|
||||||
@ -124,16 +139,25 @@ const OwnerInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</>
|
</>
|
||||||
}>
|
}>
|
||||||
|
<DataInsightProgressBar
|
||||||
|
className="m-b-md"
|
||||||
|
progress={Number(total)}
|
||||||
|
target={targetValue}
|
||||||
|
width={250}
|
||||||
|
/>
|
||||||
{data.length ? (
|
{data.length ? (
|
||||||
<ResponsiveContainer debounce={1} minHeight={400}>
|
<ResponsiveContainer debounce={1} minHeight={400}>
|
||||||
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
||||||
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} vertical={false} />
|
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} vertical={false} />
|
||||||
<XAxis dataKey="timestamp" />
|
<XAxis dataKey="timestamp" />
|
||||||
|
<YAxis
|
||||||
|
tickFormatter={(value: number) => axisTickFormatter(value, '%')}
|
||||||
|
/>
|
||||||
<Tooltip content={<CustomTooltip isPercentage />} />
|
<Tooltip content={<CustomTooltip isPercentage />} />
|
||||||
<Legend
|
<Legend
|
||||||
align="left"
|
align="left"
|
||||||
content={(props) =>
|
content={(props) =>
|
||||||
renderLegend(props as LegendProps, `${total}%`, activeKeys)
|
renderLegend(props as LegendProps, total, activeKeys, true)
|
||||||
}
|
}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
verticalAlign="top"
|
verticalAlign="top"
|
||||||
|
@ -51,6 +51,7 @@ import {
|
|||||||
} from '../../utils/DataInsightUtils';
|
} from '../../utils/DataInsightUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import './DataInsightDetail.less';
|
import './DataInsightDetail.less';
|
||||||
|
import DataInsightProgressBar from './DataInsightProgressBar';
|
||||||
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -121,6 +122,11 @@ const TierInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</>
|
</>
|
||||||
}>
|
}>
|
||||||
|
<DataInsightProgressBar
|
||||||
|
className="m-b-md"
|
||||||
|
progress={Number(total)}
|
||||||
|
width={250}
|
||||||
|
/>
|
||||||
{data.length ? (
|
{data.length ? (
|
||||||
<ResponsiveContainer debounce={1} minHeight={400}>
|
<ResponsiveContainer debounce={1} minHeight={400}>
|
||||||
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
<LineChart data={data} margin={BAR_CHART_MARGIN}>
|
||||||
@ -131,7 +137,7 @@ const TierInsight: FC<Props> = ({ chartFilter }) => {
|
|||||||
<Legend
|
<Legend
|
||||||
align="left"
|
align="left"
|
||||||
content={(props) =>
|
content={(props) =>
|
||||||
renderLegend(props as LegendProps, `${total}%`, activeKeys)
|
renderLegend(props as LegendProps, total, activeKeys, false)
|
||||||
}
|
}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
verticalAlign="top"
|
verticalAlign="top"
|
||||||
|
@ -74,6 +74,14 @@ const TopViewEntities: FC<Props> = ({ chartFilter }) => {
|
|||||||
return <Link to={pathname || '#'}>{getDecodedFqn(entityFqn)}</Link>;
|
return <Link to={pathname || '#'}>{getDecodedFqn(entityFqn)}</Link>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('label.data-asset-type'),
|
||||||
|
dataIndex: 'entityType',
|
||||||
|
key: 'entityType',
|
||||||
|
render: (entityType: string) => (
|
||||||
|
<Typography.Text>{entityType}</Typography.Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('label.owner'),
|
title: t('label.owner'),
|
||||||
dataIndex: 'owner',
|
dataIndex: 'owner',
|
||||||
|
@ -550,7 +550,8 @@
|
|||||||
"dbt-Configuration-source": "DBT Configuration Source",
|
"dbt-Configuration-source": "DBT Configuration Source",
|
||||||
"select-dbt-source": "Select DBT Source",
|
"select-dbt-source": "Select DBT Source",
|
||||||
"no-selected-dbt": "No source selected for DBT Configuration.",
|
"no-selected-dbt": "No source selected for DBT Configuration.",
|
||||||
"dbt": "DBT"
|
"dbt": "DBT",
|
||||||
|
"latest": "Latest"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"service-email-required": "Service account Email is required",
|
"service-email-required": "Service account Email is required",
|
||||||
|
@ -80,3 +80,41 @@
|
|||||||
.side-panel-icons {
|
.side-panel-icons {
|
||||||
color: @icon-color;
|
color: @icon-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-insight-progress-bar {
|
||||||
|
.ant-progress-bg,
|
||||||
|
.ant-progress-success-bg {
|
||||||
|
// library has given height directly via style attribute , so to override need to provide !important
|
||||||
|
height: 30px !important;
|
||||||
|
}
|
||||||
|
.ant-progress-outer {
|
||||||
|
margin-right: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.ant-progress-inner,
|
||||||
|
.ant-progress-bg,
|
||||||
|
.ant-progress-success-bg {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.ant-progress-text {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
top: 0px;
|
||||||
|
right: 2px;
|
||||||
|
left: 2px;
|
||||||
|
padding: 0 16px 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.data-insight-kpi-target {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
border-right: 2px solid #1890ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,8 +23,9 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
|
import { getListKPIs } from '../../axiosAPIs/KpiAPI';
|
||||||
import { searchQuery } from '../../axiosAPIs/searchAPI';
|
import { searchQuery } from '../../axiosAPIs/searchAPI';
|
||||||
import PageLayoutV1 from '../../components/containers/PageLayoutV1';
|
import PageLayoutV1 from '../../components/containers/PageLayoutV1';
|
||||||
import DailyActiveUsersChart from '../../components/DataInsightDetail/DailyActiveUsersChart';
|
import DailyActiveUsersChart from '../../components/DataInsightDetail/DailyActiveUsersChart';
|
||||||
@ -49,6 +50,7 @@ import {
|
|||||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
|
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
|
||||||
import { useAuth } from '../../hooks/authHooks';
|
import { useAuth } from '../../hooks/authHooks';
|
||||||
import {
|
import {
|
||||||
ChartFilter,
|
ChartFilter,
|
||||||
@ -79,9 +81,25 @@ const DataInsightPage = () => {
|
|||||||
const [activeTab, setActiveTab] = useState(DataInsightTabs.DATA_ASSETS);
|
const [activeTab, setActiveTab] = useState(DataInsightTabs.DATA_ASSETS);
|
||||||
const [chartFilter, setChartFilter] =
|
const [chartFilter, setChartFilter] =
|
||||||
useState<ChartFilter>(INITIAL_CHART_FILTER);
|
useState<ChartFilter>(INITIAL_CHART_FILTER);
|
||||||
|
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
||||||
|
|
||||||
const [selectedChart, setSelectedChart] = useState<DataInsightChartType>();
|
const [selectedChart, setSelectedChart] = useState<DataInsightChartType>();
|
||||||
|
|
||||||
|
const { descriptionKpi, ownerKpi } = useMemo(() => {
|
||||||
|
return {
|
||||||
|
descriptionKpi: kpiList.find(
|
||||||
|
(kpi) =>
|
||||||
|
kpi.dataInsightChart.name ===
|
||||||
|
DataInsightChartType.PercentageOfEntitiesWithDescriptionByType
|
||||||
|
),
|
||||||
|
ownerKpi: kpiList.find(
|
||||||
|
(kpi) =>
|
||||||
|
kpi.dataInsightChart.name ===
|
||||||
|
DataInsightChartType.PercentageOfEntitiesWithOwnerByType
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [kpiList]);
|
||||||
|
|
||||||
const handleTierChange = (tiers: string[] = []) => {
|
const handleTierChange = (tiers: string[] = []) => {
|
||||||
setChartFilter((previous) => ({
|
setChartFilter((previous) => ({
|
||||||
...previous,
|
...previous,
|
||||||
@ -137,6 +155,15 @@ const DataInsightPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchKpiList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getListKPIs({ fields: 'dataInsightChart' });
|
||||||
|
setKpiList(response.data);
|
||||||
|
} catch (_err) {
|
||||||
|
setKpiList([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleScrollToChart = (chartType: DataInsightChartType) => {
|
const handleScrollToChart = (chartType: DataInsightChartType) => {
|
||||||
if (ENTITIES_CHARTS.includes(chartType)) {
|
if (ENTITIES_CHARTS.includes(chartType)) {
|
||||||
history.push(getDataInsightPathWithFqn(DataInsightTabs.DATA_ASSETS));
|
history.push(getDataInsightPathWithFqn(DataInsightTabs.DATA_ASSETS));
|
||||||
@ -162,6 +189,7 @@ const DataInsightPage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDefaultTeamOptions();
|
fetchDefaultTeamOptions();
|
||||||
|
fetchKpiList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -260,7 +288,7 @@ const DataInsightPage = () => {
|
|||||||
{/* Do not show KPIChart for app analytics */}
|
{/* Do not show KPIChart for app analytics */}
|
||||||
{tab !== DataInsightTabs.APP_ANALYTICS && (
|
{tab !== DataInsightTabs.APP_ANALYTICS && (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<KPIChart chartFilter={chartFilter} />
|
<KPIChart chartFilter={chartFilter} kpiList={kpiList} />
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
{activeTab === DataInsightTabs.DATA_ASSETS && (
|
{activeTab === DataInsightTabs.DATA_ASSETS && (
|
||||||
@ -269,10 +297,13 @@ const DataInsightPage = () => {
|
|||||||
<TotalEntityInsight chartFilter={chartFilter} />
|
<TotalEntityInsight chartFilter={chartFilter} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<DescriptionInsight chartFilter={chartFilter} />
|
<DescriptionInsight
|
||||||
|
chartFilter={chartFilter}
|
||||||
|
kpi={descriptionKpi}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<OwnerInsight chartFilter={chartFilter} />
|
<OwnerInsight chartFilter={chartFilter} kpi={ownerKpi} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TierInsight chartFilter={chartFilter} />
|
<TierInsight chartFilter={chartFilter} />
|
||||||
|
@ -62,21 +62,26 @@ const checkIsPercentageGraph = (dataInsightChartType: DataInsightChartType) =>
|
|||||||
|
|
||||||
export const renderLegend = (
|
export const renderLegend = (
|
||||||
legendData: LegendProps,
|
legendData: LegendProps,
|
||||||
latest: string,
|
latest: string | number,
|
||||||
activeKeys = [] as string[]
|
activeKeys = [] as string[],
|
||||||
|
showLatestValue = true
|
||||||
) => {
|
) => {
|
||||||
const { payload = [] } = legendData;
|
const { payload = [] } = legendData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography.Text className="data-insight-label-text">
|
{showLatestValue && (
|
||||||
Latest
|
<>
|
||||||
</Typography.Text>
|
<Typography.Text className="data-insight-label-text">
|
||||||
<Typography
|
Latest
|
||||||
className="font-bold text-lg"
|
</Typography.Text>
|
||||||
style={{ margin: '0px 0px 16px' }}>
|
<Typography
|
||||||
{latest}
|
className="font-bold text-lg"
|
||||||
</Typography>
|
style={{ margin: '0px 0px 16px' }}>
|
||||||
|
{latest}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<ul className="mr-2">
|
<ul className="mr-2">
|
||||||
{payload.map((entry, index) => {
|
{payload.map((entry, index) => {
|
||||||
const isActive =
|
const isActive =
|
||||||
@ -399,7 +404,7 @@ export const getGraphDataByTierType = (rawData: TotalEntitiesByTier[]) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
[tiering]: (data?.entityCountFraction || 0) * 100,
|
[tiering]: ((data?.entityCountFraction || 0) * 100).toFixed(2),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user