mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-27 13:49:10 +02:00
feat: create flags created vs archived chart
This commit is contained in:
parent
df01f53aed
commit
ffffe02792
@ -0,0 +1,87 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import { Box, Paper, Typography, styled } from '@mui/material';
|
||||||
|
import type { TooltipState } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip';
|
||||||
|
import { ChartTooltipContainer } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip';
|
||||||
|
|
||||||
|
const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
width: 200,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledFlagItem = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: theme.spacing(0.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface CreationArchiveRatioTooltipProps {
|
||||||
|
tooltip: TooltipState | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreationArchiveRatioTooltip: FC<
|
||||||
|
CreationArchiveRatioTooltipProps
|
||||||
|
> = ({ tooltip }) => {
|
||||||
|
if (!tooltip?.dataPoints) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter for the percentage line dataset
|
||||||
|
const ratioDataPoint = tooltip.dataPoints.find(
|
||||||
|
(point) => point.dataset.label === 'Flags archived / Flags created',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!ratioDataPoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the raw data to extract counts
|
||||||
|
const rawData = ratioDataPoint.raw as any;
|
||||||
|
|
||||||
|
if (!rawData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const archivedCount = rawData.archivedFlags || 0;
|
||||||
|
const createdCount = rawData.totalCreatedFlags || 0;
|
||||||
|
const ratio = Math.round(ratioDataPoint.parsed.y as number);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChartTooltipContainer tooltip={tooltip}>
|
||||||
|
<StyledTooltipItemContainer elevation={3}>
|
||||||
|
<Typography
|
||||||
|
variant='body2'
|
||||||
|
component='div'
|
||||||
|
fontWeight='bold'
|
||||||
|
sx={{ marginBottom: 1 }}
|
||||||
|
>
|
||||||
|
Ratio {ratio}%
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<StyledFlagItem>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
<Typography sx={{ color: '#4caf50' }} component='span'>
|
||||||
|
{'● '}
|
||||||
|
</Typography>
|
||||||
|
Flags created
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
{createdCount}
|
||||||
|
</Typography>
|
||||||
|
</StyledFlagItem>
|
||||||
|
|
||||||
|
<StyledFlagItem>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
<Typography sx={{ color: '#9e9e9e' }} component='span'>
|
||||||
|
{'● '}
|
||||||
|
</Typography>
|
||||||
|
Flags archived
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
{archivedCount}
|
||||||
|
</Typography>
|
||||||
|
</StyledFlagItem>
|
||||||
|
</StyledTooltipItemContainer>
|
||||||
|
</ChartTooltipContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,101 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import { Box, Paper, Typography, styled } from '@mui/material';
|
||||||
|
import type { TooltipState } from '../../components/LineChart/ChartTooltip/ChartTooltip.tsx';
|
||||||
|
import { ChartTooltipContainer } from '../../components/LineChart/ChartTooltip/ChartTooltip.tsx';
|
||||||
|
|
||||||
|
const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
width: 240,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledFlagTypeItem = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface CreationArchiveTooltipProps {
|
||||||
|
tooltip: TooltipState | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreationArchiveTooltip: FC<CreationArchiveTooltipProps> = ({
|
||||||
|
tooltip,
|
||||||
|
}) => {
|
||||||
|
if (!tooltip?.dataPoints) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter for created flag type datasets only
|
||||||
|
const createdFlagDataPoints = tooltip.dataPoints.filter((point) =>
|
||||||
|
point.dataset.label?.startsWith('Created:'),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (createdFlagDataPoints.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the data from the first point (they all have the same raw data)
|
||||||
|
const rawData = createdFlagDataPoints[0]?.raw as any;
|
||||||
|
|
||||||
|
if (!rawData?.createdFlagsByType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag type colors matching the chart
|
||||||
|
const flagTypeColors = [
|
||||||
|
'#66bb6a', // theme.palette.success.border
|
||||||
|
'#4caf50', // theme.palette.success.main
|
||||||
|
'#388e3c', // theme.palette.success.dark
|
||||||
|
'#4D8007',
|
||||||
|
'#7D935E',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get flag type names from the chart datasets
|
||||||
|
const flagTypeNames = createdFlagDataPoints.map(
|
||||||
|
(point) => point.dataset.label?.replace('Created: ', '') || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create entries for each flag type with count > 0
|
||||||
|
const flagTypeEntries = Object.entries(rawData.createdFlagsByType)
|
||||||
|
.filter(([, count]) => (count as number) > 0)
|
||||||
|
.map(([flagType, count], index) => ({
|
||||||
|
type: flagType,
|
||||||
|
count: count as number,
|
||||||
|
color:
|
||||||
|
flagTypeColors[flagTypeNames.indexOf(flagType)] ||
|
||||||
|
flagTypeColors[index % flagTypeColors.length],
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (flagTypeEntries.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChartTooltipContainer tooltip={tooltip}>
|
||||||
|
<StyledTooltipItemContainer elevation={3}>
|
||||||
|
<Typography
|
||||||
|
variant='body2'
|
||||||
|
component='div'
|
||||||
|
fontWeight='bold'
|
||||||
|
sx={{ marginBottom: 1 }}
|
||||||
|
>
|
||||||
|
Flag type
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{flagTypeEntries.map(({ type, count, color }) => (
|
||||||
|
<StyledFlagTypeItem key={type}>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
<Typography sx={{ color }} component='span'>
|
||||||
|
{'● '}
|
||||||
|
</Typography>
|
||||||
|
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='body2' component='span'>
|
||||||
|
{count}
|
||||||
|
</Typography>
|
||||||
|
</StyledFlagTypeItem>
|
||||||
|
))}
|
||||||
|
</StyledTooltipItemContainer>
|
||||||
|
</ChartTooltipContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,76 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import { Box, Typography, Link, styled } from '@mui/material';
|
||||||
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||||
|
import InfoOutlined from '@mui/icons-material/InfoOutlined';
|
||||||
|
import Lightbulb from '@mui/icons-material/LightbulbOutlined';
|
||||||
|
import { StatsExplanation } from 'component/insights/InsightsCharts.styles';
|
||||||
|
|
||||||
|
const StyledRatioContainer = styled(Box)(({ theme }) => ({
|
||||||
|
backgroundColor: theme.palette.background.elevation1,
|
||||||
|
borderRadius: theme.spacing(2),
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(0.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledPercentageRow = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledRatioTypography = styled(Typography)(({ theme }) => ({
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
fontFamily: 'inherit',
|
||||||
|
fontSize: '20px',
|
||||||
|
fontStyle: 'normal',
|
||||||
|
fontWeight: 700,
|
||||||
|
lineHeight: '28px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledInfoIcon = styled(InfoOutlined)(({ theme }) => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledLink = styled(Link)(({ theme }) => ({
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
textDecoration: 'none',
|
||||||
|
'&:hover': {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface CreationArchiveStatsProps {
|
||||||
|
currentRatio: number;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreationArchiveStats: FC<CreationArchiveStatsProps> = ({
|
||||||
|
currentRatio,
|
||||||
|
isLoading,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledRatioContainer>
|
||||||
|
<StyledPercentageRow>
|
||||||
|
<StyledRatioTypography>
|
||||||
|
{isLoading ? '...' : `${currentRatio}%`}
|
||||||
|
</StyledRatioTypography>
|
||||||
|
<HelpIcon tooltip='Ratio of archived flags to created flags'>
|
||||||
|
<StyledInfoIcon />
|
||||||
|
</HelpIcon>
|
||||||
|
</StyledPercentageRow>
|
||||||
|
<Typography variant='body2'>Current ratio</Typography>
|
||||||
|
</StyledRatioContainer>
|
||||||
|
<StatsExplanation>
|
||||||
|
<Lightbulb color='primary' />
|
||||||
|
Do you create more flags than you archive? Or do you have good
|
||||||
|
process for cleaning up?
|
||||||
|
</StatsExplanation>
|
||||||
|
<StyledLink href='/search?lifecycle=IS:completed' variant='body2'>
|
||||||
|
View flags in cleanup stage
|
||||||
|
</StyledLink>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user