Skip to content

Commit

Permalink
feature(fe): data quality dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyNenashev committed Nov 7, 2023
1 parent c09f94e commit aad4b1f
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 12 deletions.
29 changes: 27 additions & 2 deletions odd-platform-ui/src/components/DataQuality/DataQuality.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,48 @@ export const Container = styled.section(
`
);

export const SectionWrapper = styled.div(
export const Section = styled.div(
({ theme }) => css`
display: flex;
flex-direction: column;
width: fit-content;
padding: ${theme.spacing(3)};
border: 1px solid ${theme.palette.border.primary};
border-radius: ${theme.spacing(1)};
gap: ${theme.spacing(2)};
`
);

interface FlexDirection {
$direction?: 'column' | 'row';
}

export const SubSection = styled.div<FlexDirection>(
({ theme, $direction = 'row' }) => css`
display: flex;
flex-direction: ${$direction};
gap: ${theme.spacing(3)};
`
);

export const DashboardLegend = styled.div(
({ theme }) => css`
display: flex;
justify-content: space-between;
gap: ${theme.spacing(4)};
`
);

export const ChartWrapper = styled.div(
({ theme }) => css`
display: flex;
flex-direction: column;
justify-content: space-evenly;
padding-top: ${theme.spacing(2)};
background: ${theme.palette.backgrounds.tertiary};
border-radius: ${theme.spacing(1)};
`
);

interface DashboardLegendItemProps {
$status: DataEntityRunStatus;
}
Expand Down
102 changes: 94 additions & 8 deletions odd-platform-ui/src/components/DataQuality/DataQuality.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DonutChart } from 'components/shared/elements';
import { useTheme } from 'styled-components';
import { useTranslation } from 'react-i18next';
import * as S from './DataQuality.styles';
import TestCategoryResults from './TestResults/TestCategoryResults';

function capitalizeFirstLetter(str: string) {
return [...str][0].toUpperCase() + str.slice(1);
Expand Down Expand Up @@ -43,30 +44,115 @@ const DataQuality: React.FC = () => {
}));
}, [data?.testResults]);

const tableHealthData = useMemo(() => {
if (!data) return [];
return [
{
title: 'Healthy',
value: 10,
color: palette.runStatus.SUCCESS.color,
},
{
title: 'Unhealthy',
value: 5,
color: palette.runStatus.FAILED.color,
},
{
title: 'Unknown',
value: 2,
color: palette.runStatus.BROKEN.color,
},
];
}, [data?.testResults]);

const tableMonitoredTables = useMemo(() => {
if (!data) return [];
return [
{
title: 'Monitored',
value: 10,
color: palette.runStatus.SUCCESS.color,
},
{
title: 'Not Monitored',
value: 5,
color: palette.runStatus.UNKNOWN.color,
},
];
}, [data?.testResults]);

const testResults = useMemo(() => {
if (!data) return [];
return data.testResults.sort(({ category: a }, { category: b }) =>
a.localeCompare(b)
);
}, [data?.testResults]);

return (
<S.Container>
<S.SectionWrapper>
<S.Section>
<S.DashboardLegend>
{Object.values(DataEntityRunStatus).map(status => (
<S.DashboardLegendItem key={status} $status={status}>
<Typography variant='subtitle1'>
<Typography variant='label'>
{capitalizeFirstLetter(status.toLowerCase())}
</Typography>
</S.DashboardLegendItem>
))}
</S.DashboardLegend>
{isSuccess ? (
<S.SubSection>
<S.ChartWrapper>
<Typography variant='title' component='h4' align='center'>
{t('Table Health')}
</Typography>
<DonutChart
width={300}
height={300}
innerRadius={66}
outerRadius={90}
label={t('Total Tables')}
data={tableHealthData}
/>
</S.ChartWrapper>
<S.ChartWrapper>
<Typography variant='title' component='h4' align='center'>
{t('Test Results Breakdown')}
</Typography>
<DonutChart
width={300}
height={300}
innerRadius={66}
outerRadius={90}
label={t('Total Tests')}
data={testResultsBreakdownChartData}
/>
</S.ChartWrapper>
<S.SubSection $direction='column'>
{isSuccess &&
testResults.map(categoryResults => (
<TestCategoryResults
key={categoryResults.category}
categoryResults={categoryResults}
/>
))}
</S.SubSection>
</S.SubSection>
</S.Section>
<S.Section>
<S.ChartWrapper>
<Typography variant='title' component='h4' align='center'>
{t('Monitored Tables')}
</Typography>
<DonutChart
width={300}
height={300}
innerRadius={66}
outerRadius={90}
label={t('Total Tests')}
data={testResultsBreakdownChartData}
label={t('Total Tables')}
data={tableMonitoredTables}
/>
) : null}
</S.SectionWrapper>
<S.SectionWrapper>Table Monitor Section</S.SectionWrapper>
</S.ChartWrapper>
</S.Section>
</S.Container>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { DataEntityRunStatus } from 'generated-sources';
import styled, { css } from 'styled-components';

export const TestCategoryResultsWrapper = styled.div(
({ theme }) => css`
display: flex;
justify-content: space-between;
align-items: center;
width: 560px;
background: ${theme.palette.backgrounds.tertiary};
padding: ${theme.spacing(1)} ${theme.spacing(2)};
border-radius: ${theme.spacing(1)};
`
);

export const TestCategoryResults = styled.div(
({ theme }) => css`
display: flex;
align-items: center;
gap: ${theme.spacing(1)};
`
);

interface Status {
$status: DataEntityRunStatus;
}
export const TestCategoryResultsItem = styled.div<Status>(
({ theme, $status }) => css`
text-align: center;
width: ${theme.spacing(5)};
padding: ${theme.spacing(1 / 2)} ${theme.spacing(1)};
border-radius: ${theme.spacing(1 / 2)};
color: ${theme.palette.runStatus[$status].color};
background: ${theme.palette.backgrounds.default};
`
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Typography } from '@mui/material';
import React, { useMemo } from 'react';
import type { DataQualityCategoryResults } from 'generated-sources';
import { DataEntityRunStatus } from 'generated-sources';
import * as S from './TestCategoryResults.styles';

interface TestCategoryResultsProps {
categoryResults: DataQualityCategoryResults;
}

const TestCategoryResults = ({ categoryResults }: TestCategoryResultsProps) => {
const { category, results } = categoryResults;

const total = useMemo(
() => results.reduce((acc, { count }) => acc + count, 0),
[results]
);

const sortedResults = useMemo(
() =>
Object.values(DataEntityRunStatus)
.map(status => results.find(result => result.status === status))
.flatMap(f => (f ? [f] : [])),
[results]
);

return (
<S.TestCategoryResultsWrapper key={category}>
<Typography variant='title' component='h4' align='center'>
{category}
</Typography>
<S.TestCategoryResults>
<Typography variant='h1' align='center'>
{total}
</Typography>
{sortedResults.map(({ status, count }) => (
<S.TestCategoryResultsItem key={status} $status={status}>
<Typography color='inherit' variant='title'>
{count > 0 ? count : '\u2013'}
</Typography>
</S.TestCategoryResultsItem>
))}
</S.TestCategoryResults>
</S.TestCategoryResultsWrapper>
);
};

export default TestCategoryResults;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { forwardRef, useMemo } from 'react';
import React, { useMemo } from 'react';
import { PieChart, Pie, Cell, Text, Label } from 'recharts';
import type { LabelProps, type PieLabelRenderProps } from 'recharts';
import type { LabelProps, PieLabelRenderProps } from 'recharts';
import { typography } from 'theme/typography';
import type { PolarViewBox } from 'recharts/types/util/types';
import { palette } from 'theme/palette';
Expand Down
5 changes: 5 additions & 0 deletions odd-platform-ui/src/theme/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type EntityStatus = Record<DataEntityStatusEnum, ItemCondition>;
interface TextType {
primary: string;
secondary: string;
secondaryVariant: string;
info: string;
hint: string;
action: string;
Expand Down Expand Up @@ -169,13 +170,17 @@ declare module '@mui/material/styles/createTypography' {
interface TypographyOptions extends Record<ButtonFont, TypographyStyle> {
errorCode?: TypographyStyleOptions;
totalCountTitle?: TypographyStyleOptions;
label?: TypographyStyleOptions;
title?: TypographyStyleOptions;
h0?: TypographyStyleOptions;
}
}
declare module '@mui/material/Typography/Typography' {
interface TypographyPropsVariantOverrides extends Record<ButtonFont, true> {
errorCode: true;
totalCountTitle: true;
label: true;
title: true;
h0: true;
}
}
1 change: 1 addition & 0 deletions odd-platform-ui/src/theme/palette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const palette = createPalette({
texts: {
primary: colors.black90,
secondary: colors.black50,
secondaryVariant: colors.black40,
hint: colors.black30,
info: colors.black70,
action: colors.blue60,
Expand Down
13 changes: 13 additions & 0 deletions odd-platform-ui/src/theme/typography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ export const typography = createTypography(palette, {
lineHeight: pxToRem(16),
fontWeight: 500,
},
title: {
fontSize: pxToRem(14),
lineHeight: pxToRem(20),
color: palette.texts.secondary,
fontWeight: 500,
...breakpointDownLgBody2,
},
subtitle1: {
fontSize: pxToRem(14),
lineHeight: pxToRem(20),
Expand Down Expand Up @@ -105,6 +112,12 @@ export const typography = createTypography(palette, {
lineHeight: pxToRem(36),
fontWeight: 500,
},
label: {
fontSize: pxToRem(14),
lineHeight: pxToRem(20),
fontWeight: 400,
color: palette.texts.secondaryVariant,
},
...mapKeysToValue(
[
getButtonFontType('main', 'lg'),
Expand Down

0 comments on commit aad4b1f

Please sign in to comment.