mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-12 09:18:20 +00:00
feat(ui): Update Profiler components for improved styling and functionality
This commit is contained in:
parent
3132d61efc
commit
d4683a2e91
@ -43,9 +43,3 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profiler-latest-value {
|
|
||||||
.ant-statistic-title {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -42,6 +42,7 @@ export interface ProfilerDetailsCardProps {
|
|||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
noDataPlaceholderText?: ReactNode;
|
noDataPlaceholderText?: ReactNode;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
chartType?: 'line' | 'area';
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TableProfilerTab {
|
export enum TableProfilerTab {
|
||||||
@ -78,6 +79,7 @@ export interface ProfilerLatestValueProps {
|
|||||||
information: MetricChartType['information'];
|
information: MetricChartType['information'];
|
||||||
tickFormatter?: string;
|
tickFormatter?: string;
|
||||||
stringValue?: boolean;
|
stringValue?: boolean;
|
||||||
|
onClick?: (dataKey: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TestCaseAction = {
|
export type TestCaseAction = {
|
||||||
|
@ -11,15 +11,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Typography } from 'antd';
|
|
||||||
import React, { useMemo, useState } from 'react';
|
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
|
Card,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from '@mui/material';
|
||||||
|
import React, { Fragment, useMemo, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Area,
|
||||||
Brush,
|
Brush,
|
||||||
CartesianGrid,
|
CartesianGrid,
|
||||||
Legend,
|
ComposedChart,
|
||||||
LegendProps,
|
|
||||||
Line,
|
Line,
|
||||||
LineChart,
|
|
||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
XAxis,
|
XAxis,
|
||||||
@ -47,7 +53,9 @@ const ProfilerDetailsCard: React.FC<ProfilerDetailsCardProps> = ({
|
|||||||
title,
|
title,
|
||||||
isLoading,
|
isLoading,
|
||||||
noDataPlaceholderText,
|
noDataPlaceholderText,
|
||||||
|
chartType = 'line',
|
||||||
}: ProfilerDetailsCardProps) => {
|
}: ProfilerDetailsCardProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
const { data, information } = chartCollection;
|
const { data, information } = chartCollection;
|
||||||
const [activeKeys, setActiveKeys] = useState<string[]>([]);
|
const [activeKeys, setActiveKeys] = useState<string[]>([]);
|
||||||
const { showBrush, endIndex } = useMemo(() => {
|
const { showBrush, endIndex } = useMemo(() => {
|
||||||
@ -57,38 +65,60 @@ const ProfilerDetailsCard: React.FC<ProfilerDetailsCardProps> = ({
|
|||||||
};
|
};
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const handleClick: LegendProps['onClick'] = (event) => {
|
const handleClick = (dataKey: string) => {
|
||||||
setActiveKeys((prevActiveKeys) =>
|
setActiveKeys((prevActiveKeys) =>
|
||||||
updateActiveChartFilter(event.dataKey, prevActiveKeys)
|
updateActiveChartFilter(dataKey, prevActiveKeys)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Skeleton height={380} variant="rounded" width="100%" />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Box>
|
||||||
className="shadow-none global-border-radius"
|
{title && (
|
||||||
data-testid="profiler-details-card-container"
|
<Typography
|
||||||
loading={isLoading}>
|
sx={{
|
||||||
<Row gutter={[16, 16]}>
|
fontSize: '16px',
|
||||||
{title && (
|
color: theme.palette.grey[900],
|
||||||
<Col span={24}>
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
<Typography.Title level={5}>{title}</Typography.Title>
|
mb: 3,
|
||||||
</Col>
|
}}
|
||||||
)}
|
variant="h6">
|
||||||
<Col span={4}>
|
{title}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Card
|
||||||
|
data-testid="profiler-details-card-container"
|
||||||
|
sx={{
|
||||||
|
p: 4,
|
||||||
|
borderRadius: '10px',
|
||||||
|
border: `1px solid ${theme.palette.grey[200]}`,
|
||||||
|
boxShadow: 'none',
|
||||||
|
}}>
|
||||||
|
<Stack spacing={4}>
|
||||||
<ProfilerLatestValue
|
<ProfilerLatestValue
|
||||||
information={information}
|
information={information}
|
||||||
tickFormatter={tickFormatter}
|
tickFormatter={tickFormatter}
|
||||||
|
onClick={handleClick}
|
||||||
/>
|
/>
|
||||||
</Col>
|
|
||||||
<Col span={20}>
|
|
||||||
{data.length > 0 ? (
|
{data.length > 0 ? (
|
||||||
<ResponsiveContainer
|
<ResponsiveContainer
|
||||||
className="custom-legend"
|
className="custom-legend"
|
||||||
debounce={200}
|
debounce={200}
|
||||||
id={`${name}_graph`}
|
id={`${name}_graph`}
|
||||||
minHeight={300}>
|
minHeight={300}>
|
||||||
<LineChart className="w-full" data={data} margin={{ left: 16 }}>
|
<ComposedChart
|
||||||
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} />
|
className="w-full"
|
||||||
|
data={data}
|
||||||
|
margin={{ left: 16 }}>
|
||||||
|
<CartesianGrid
|
||||||
|
stroke={GRAPH_BACKGROUND_COLOR}
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
vertical={false}
|
||||||
|
/>
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="name"
|
dataKey="name"
|
||||||
padding={{ left: 16, right: 16 }}
|
padding={{ left: 16, right: 16 }}
|
||||||
@ -116,20 +146,37 @@ const ProfilerDetailsCard: React.FC<ProfilerDetailsCardProps> = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{information.map((info) => (
|
{information.map((info) => (
|
||||||
<Line
|
<Fragment key={info.dataKey}>
|
||||||
dataKey={info.dataKey}
|
{chartType === 'area' && (
|
||||||
hide={
|
<Area
|
||||||
activeKeys.length
|
dataKey={info.dataKey}
|
||||||
? !activeKeys.includes(info.dataKey)
|
fill={info.color}
|
||||||
: false
|
fillOpacity={0.1}
|
||||||
}
|
hide={
|
||||||
key={info.dataKey}
|
activeKeys.length
|
||||||
name={info.title}
|
? !activeKeys.includes(info.dataKey)
|
||||||
stroke={info.color}
|
: false
|
||||||
type={curveType ?? 'monotone'}
|
}
|
||||||
/>
|
key={info.dataKey}
|
||||||
|
name={info.title}
|
||||||
|
stroke={info.color}
|
||||||
|
type={curveType ?? 'monotone'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Line
|
||||||
|
dataKey={info.dataKey}
|
||||||
|
hide={
|
||||||
|
activeKeys.length
|
||||||
|
? !activeKeys.includes(info.dataKey)
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
key={info.dataKey}
|
||||||
|
name={info.title}
|
||||||
|
stroke={info.color}
|
||||||
|
type={curveType ?? 'monotone'}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
<Legend onClick={handleClick} />
|
|
||||||
{showBrush && (
|
{showBrush && (
|
||||||
<Brush
|
<Brush
|
||||||
data={data}
|
data={data}
|
||||||
@ -139,21 +186,17 @@ const ProfilerDetailsCard: React.FC<ProfilerDetailsCardProps> = ({
|
|||||||
padding={{ left: 16, right: 16 }}
|
padding={{ left: 16, right: 16 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</LineChart>
|
</ComposedChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<Row align="middle" className="h-full w-full" justify="center">
|
<ErrorPlaceHolder
|
||||||
<Col>
|
className="mt-0-important"
|
||||||
<ErrorPlaceHolder
|
placeholderText={noDataPlaceholderText}
|
||||||
className="mt-0-important"
|
/>
|
||||||
placeholderText={noDataPlaceholderText}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Stack>
|
||||||
</Row>
|
</Card>
|
||||||
</Card>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Space, Statistic, Typography } from 'antd';
|
import { Box, Stack, Typography, useTheme } from '@mui/material';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import { getStatisticsDisplayValue } from '../../../../utils/CommonUtils';
|
import { getStatisticsDisplayValue } from '../../../../utils/CommonUtils';
|
||||||
import '../ProfilerDashboard/profiler-dashboard.less';
|
import '../ProfilerDashboard/profiler-dashboard.less';
|
||||||
@ -21,7 +21,10 @@ const ProfilerLatestValue = ({
|
|||||||
information,
|
information,
|
||||||
tickFormatter,
|
tickFormatter,
|
||||||
stringValue = false,
|
stringValue = false,
|
||||||
|
onClick,
|
||||||
}: ProfilerLatestValueProps) => {
|
}: ProfilerLatestValueProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const getLatestValue = (value?: number | string) => {
|
const getLatestValue = (value?: number | string) => {
|
||||||
if (isUndefined(value)) {
|
if (isUndefined(value)) {
|
||||||
return '--';
|
return '--';
|
||||||
@ -35,23 +38,50 @@ const ProfilerLatestValue = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space data-testid="data-summary-container" direction="vertical" size={16}>
|
<Stack
|
||||||
|
data-testid="data-summary-container"
|
||||||
|
direction="row"
|
||||||
|
spacing={4}
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: theme.palette.grey[50],
|
||||||
|
borderRadius: '10px',
|
||||||
|
p: '16px 30px',
|
||||||
|
}}>
|
||||||
{information.map((info) => (
|
{information.map((info) => (
|
||||||
<Statistic
|
<Box
|
||||||
className="profiler-latest-value"
|
|
||||||
key={info.title}
|
key={info.title}
|
||||||
title={
|
sx={{
|
||||||
<Typography.Text
|
cursor: onClick ? 'pointer' : 'default',
|
||||||
className="text-grey-body break-all"
|
}}
|
||||||
data-testid="title">
|
onClick={() => onClick?.(info.dataKey)}>
|
||||||
{info.title}
|
<Typography
|
||||||
</Typography.Text>
|
className="break-all"
|
||||||
}
|
data-testid="title"
|
||||||
value={getLatestValue(info.latestValue)}
|
sx={{
|
||||||
valueStyle={{ color: info.color, fontSize: '18px', fontWeight: 700 }}
|
color: theme.palette.grey[700],
|
||||||
/>
|
fontSize: '12px',
|
||||||
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
borderLeft: `4px solid ${info.color}`,
|
||||||
|
paddingLeft: '8px',
|
||||||
|
lineHeight: '12px',
|
||||||
|
mb: 1,
|
||||||
|
}}>
|
||||||
|
{info.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
className="break-all"
|
||||||
|
data-testid="value"
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.grey[900],
|
||||||
|
fontSize: '18px',
|
||||||
|
fontWeight: 700,
|
||||||
|
}}>
|
||||||
|
{getLatestValue(info.latestValue)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Grid, Stack } from '@mui/material';
|
import { Box, Grid, Stack } from '@mui/material';
|
||||||
import { Col } from 'antd';
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { DateRangeObject } from 'Models';
|
import { DateRangeObject } from 'Models';
|
||||||
@ -220,50 +219,49 @@ const TableProfilerChart = ({
|
|||||||
{showHeader && (
|
{showHeader && (
|
||||||
<>
|
<>
|
||||||
{!isSummaryLoading && !isProfilingEnabled && (
|
{!isSummaryLoading && !isProfilingEnabled && (
|
||||||
<Col span={24}>
|
<Box>
|
||||||
<NoProfilerBanner />
|
<NoProfilerBanner />
|
||||||
</Col>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Col span={24}>
|
|
||||||
<Grid container spacing={5}>
|
<Grid container spacing={5}>
|
||||||
{overallSummary?.map((summary) => (
|
{overallSummary?.map((summary) => (
|
||||||
<Grid key={summary.title} size="grow">
|
<Grid key={summary.title} size="grow">
|
||||||
<SummaryCardV1
|
<SummaryCardV1
|
||||||
extra={summary.extra}
|
extra={summary.extra}
|
||||||
icon={summary.icon}
|
icon={summary.icon}
|
||||||
isLoading={isSummaryLoading}
|
isLoading={isSummaryLoading}
|
||||||
title={summary.title}
|
title={summary.title}
|
||||||
value={summary.value}
|
value={summary.value}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Col>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Col data-testid="row-metrics" span={24}>
|
<Box data-testid="row-metrics">
|
||||||
<ProfilerDetailsCard
|
<ProfilerDetailsCard
|
||||||
chartCollection={rowCountMetrics}
|
chartCollection={rowCountMetrics}
|
||||||
curveType="stepAfter"
|
chartType="area"
|
||||||
isLoading={isTableProfilerLoading}
|
isLoading={isTableProfilerLoading}
|
||||||
name="rowCount"
|
name="rowCount"
|
||||||
noDataPlaceholderText={noProfilerMessage}
|
noDataPlaceholderText={noProfilerMessage}
|
||||||
title={t('label.data-volume')}
|
title={t('label.data-volume')}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Box>
|
||||||
{showSystemMetrics && (
|
{showSystemMetrics && (
|
||||||
<>
|
<>
|
||||||
<Col span={24}>{operationDateMetricsCard}</Col>
|
<Box>{operationDateMetricsCard}</Box>
|
||||||
<Col span={24}>{operationMetricsCard}</Col>
|
<Box>{operationMetricsCard}</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Col span={24}>
|
<Box>
|
||||||
<CustomMetricGraphs
|
<CustomMetricGraphs
|
||||||
customMetrics={customMetrics}
|
customMetrics={customMetrics}
|
||||||
customMetricsGraphData={tableCustomMetricsProfiling}
|
customMetricsGraphData={tableCustomMetricsProfiling}
|
||||||
isLoading={isTableProfilerLoading || isSummaryLoading}
|
isLoading={isTableProfilerLoading || isSummaryLoading}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@ export const TEXT_BODY_COLOR = '#37352F';
|
|||||||
export const TEXT_GREY_MUTED = '#757575';
|
export const TEXT_GREY_MUTED = '#757575';
|
||||||
export const SUCCESS_COLOR = '#008376';
|
export const SUCCESS_COLOR = '#008376';
|
||||||
export const DE_ACTIVE_COLOR = '#6B7280';
|
export const DE_ACTIVE_COLOR = '#6B7280';
|
||||||
export const GRAPH_BACKGROUND_COLOR = '#f5f5f5';
|
export const GRAPH_BACKGROUND_COLOR = '#E9EAEB';
|
||||||
export const GRAYED_OUT_COLOR = '#959595';
|
export const GRAYED_OUT_COLOR = '#959595';
|
||||||
export const BORDER_COLOR = '#0000001a';
|
export const BORDER_COLOR = '#0000001a';
|
||||||
export const BLACK_COLOR = '#000000';
|
export const BLACK_COLOR = '#000000';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user