1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-27 11:02:16 +01:00
unleash.unleash/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx
unleash-bot[bot] 96f59bccfa
chore(AI): healthToTechDebt flag cleanup (#10346)
Co-authored-by: unleash-bot <194219037+unleash-bot[bot]@users.noreply.github.com>
Co-authored-by: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
2025-07-11 14:15:55 +02:00

176 lines
6.2 KiB
TypeScript

import type { FC } from 'react';
import type { InstanceInsightsSchemaProjectFlagTrendsItem } from 'openapi';
import { Box, Divider, Paper, Typography, styled } from '@mui/material';
import { Badge } from 'component/common/Badge/Badge';
import type { TooltipState } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip';
import { HorizontalDistributionChart } from 'component/insights/components/HorizontalDistributionChart/HorizontalDistributionChart';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { getTechnicalDebtColor } from 'utils/getTechnicalDebtColor.ts';
const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({
padding: theme.spacing(2),
}));
const StyledItemHeader = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
gap: theme.spacing(2),
alignItems: 'center',
}));
const getTechnicalDebtBadgeColor = (technicalDebt?: number | null) => {
if (technicalDebt === undefined || technicalDebt === null) {
return 'info';
}
return getTechnicalDebtColor(technicalDebt);
};
const Distribution = ({ stale = 0, potentiallyStale = 0, total = 0 }) => {
const healthyFlagCount = total - stale - potentiallyStale;
return (
<>
<HorizontalDistributionChart
sections={[
{
type: 'error',
value: (stale / total) * 100,
},
{
type: 'warning',
value: (potentiallyStale / total) * 100,
},
{
type: 'success',
value: (healthyFlagCount / total) * 100,
},
]}
size='small'
/>
<Typography
variant='body2'
component='p'
sx={(theme) => ({ marginTop: theme.spacing(0.5) })}
>
<Typography
component='span'
sx={(theme) => ({
color: theme.palette.error.border,
})}
>
{'● '}
</Typography>
Stale flags: {stale}
</Typography>
<Typography variant='body2' component='p'>
<Typography
component='span'
sx={(theme) => ({
color: theme.palette.warning.border,
})}
>
{'● '}
</Typography>
Potentially stale flags: {potentiallyStale}
</Typography>
<Typography variant='body2' component='p'>
<Typography
component='span'
sx={(theme) => ({
color: theme.palette.success.border,
})}
>
{'● '}
</Typography>
Healthy flags: {healthyFlagCount}
</Typography>
</>
);
};
export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
tooltip,
}) => {
const data = tooltip?.dataPoints.map((point) => {
return {
label: point.label,
title: point.dataset.label,
color: point.dataset.borderColor,
value: point.raw as InstanceInsightsSchemaProjectFlagTrendsItem,
};
});
const limitedData = data?.slice(0, 5);
return (
<Box
sx={(theme) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
width: '300px',
})}
>
{limitedData?.map((point, index) => (
<StyledTooltipItemContainer
elevation={3}
key={`${point.title}-${index}`}
>
<StyledItemHeader>
<Typography
variant='body2'
color='textSecondary'
component='span'
>
{point.label}
</Typography>
<Typography
variant='body2'
color='textSecondary'
component='span'
>
Technical debt
</Typography>
</StyledItemHeader>
<StyledItemHeader>
<Typography variant='body2' component='span'>
<Typography
sx={{ color: point.color }}
component='span'
>
{'● '}
</Typography>
<strong>{point.title}</strong>
</Typography>
<Badge
color={getTechnicalDebtBadgeColor(
point.value.technicalDebt,
)}
>
{point.value.technicalDebt}%
</Badge>
</StyledItemHeader>{' '}
<Divider
sx={(theme) => ({ margin: theme.spacing(1.5, 0) })}
/>
<Typography
variant='body2'
component='p'
sx={(theme) => ({
marginBottom: theme.spacing(0.5),
})}
>
Total flags: {point.value.total}
</Typography>
<ConditionallyRender
condition={Boolean(
point.value.stale || point.value.potentiallyStale,
)}
show={<Distribution {...point.value} />}
/>
</StyledTooltipItemContainer>
)) || null}
</Box>
);
};