diff --git a/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveRatioTooltip.tsx b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveRatioTooltip.tsx new file mode 100644 index 0000000000..850c8eef6b --- /dev/null +++ b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveRatioTooltip.tsx @@ -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 ( + + + + Ratio {ratio}% + + + + + + {'● '} + + Flags created + + + {createdCount} + + + + + + + {'● '} + + Flags archived + + + {archivedCount} + + + + + ); +}; diff --git a/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveTooltip.tsx b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveTooltip.tsx new file mode 100644 index 0000000000..c95ee77bb5 --- /dev/null +++ b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveTooltip.tsx @@ -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 = ({ + 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 ( + + + + Flag type + + + {flagTypeEntries.map(({ type, count, color }) => ( + + + + {'● '} + + {type.charAt(0).toUpperCase() + type.slice(1)} + + + {count} + + + ))} + + + ); +}; diff --git a/frontend/src/component/insights/componentsStat/CreationArchiveStats/CreationArchiveStats.tsx b/frontend/src/component/insights/componentsStat/CreationArchiveStats/CreationArchiveStats.tsx new file mode 100644 index 0000000000..91c4308677 --- /dev/null +++ b/frontend/src/component/insights/componentsStat/CreationArchiveStats/CreationArchiveStats.tsx @@ -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 = ({ + currentRatio, + isLoading, +}) => { + return ( + <> + + + + {isLoading ? '...' : `${currentRatio}%`} + + + + + + Current ratio + + + + Do you create more flags than you archive? Or do you have good + process for cleaning up? + + + View flags in cleanup stage + + + ); +};