diff --git a/frontend/src/component/insights/InsightsCharts.styles.ts b/frontend/src/component/insights/InsightsCharts.styles.ts
index 4d9dfa9698..f744a4f138 100644
--- a/frontend/src/component/insights/InsightsCharts.styles.ts
+++ b/frontend/src/component/insights/InsightsCharts.styles.ts
@@ -3,46 +3,60 @@ import { Box, Paper, styled, Typography } from '@mui/material';
export const StyledContainer = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
- gap: theme.spacing(4),
+ gap: theme.spacing(3),
+}));
+
+export const StyledCompactCard = styled(Paper)(({ theme }) => ({
+ borderRadius: theme.shape.borderRadiusLarge,
+ padding: theme.spacing(2),
+ boxShadow: 'none',
+ border: `1px solid ${theme.palette.divider}`,
+ transition: 'all 0.2s ease',
+ '&:hover': {
+ boxShadow: theme.shadows[2],
+ transform: 'translateY(-2px)',
+ },
}));
export const StyledWidget = styled(Paper)(({ theme }) => ({
borderRadius: `${theme.shape.borderRadiusLarge}px`,
boxShadow: 'none',
display: 'flex',
- flexWrap: 'wrap',
- [theme.breakpoints.up('md')]: {
- flexDirection: 'row',
- flexWrap: 'nowrap',
+ flexDirection: 'column',
+ overflow: 'hidden',
+ border: `1px solid ${theme.palette.divider}`,
+ transition: 'box-shadow 0.2s ease',
+ '&:hover': {
+ boxShadow: theme.shadows[2],
},
}));
export const StyledWidgetContent = styled(Box)(({ theme }) => ({
- padding: theme.spacing(3),
+ padding: theme.spacing(2),
width: '100%',
}));
export const StyledWidgetStats = styled(Box)<{
width?: number;
padding?: number;
-}>(({ theme, width = 300, padding = 3 }) => ({
+}>(({ theme, width = 300, padding = 2 }) => ({
display: 'flex',
flexDirection: 'column',
- gap: theme.spacing(2),
+ gap: theme.spacing(1.5),
padding: theme.spacing(padding),
- minWidth: '100%',
- [theme.breakpoints.up('md')]: {
- minWidth: `${width}px`,
- borderRight: `1px solid ${theme.palette.background.application}`,
- },
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ backgroundColor: theme.palette.background.elevation1,
}));
export const StyledChartContainer = styled(Box)(({ theme }) => ({
position: 'relative',
minWidth: 0, // bugfix, see: https://github.com/chartjs/Chart.js/issues/4156#issuecomment-295180128
- flexGrow: 1,
- margin: 'auto 0',
- padding: theme.spacing(3),
+ width: '100%',
+ padding: theme.spacing(2),
+ height: '300px',
+ '& canvas': {
+ maxHeight: '280px',
+ },
}));
export const StatsExplanation = styled(Typography)(({ theme }) => ({
diff --git a/frontend/src/component/insights/sections/PerformanceInsights.tsx b/frontend/src/component/insights/sections/PerformanceInsights.tsx
index efc4d4895b..a817ff0ce3 100644
--- a/frontend/src/component/insights/sections/PerformanceInsights.tsx
+++ b/frontend/src/component/insights/sections/PerformanceInsights.tsx
@@ -6,29 +6,18 @@ import { usePersistentTableState } from 'hooks/usePersistentTableState';
import type { FC } from 'react';
import { withDefault } from 'use-query-params';
import { FilterItemParam } from 'utils/serializeQueryParams';
-import { WidgetTitle } from 'component/insights/components/WidgetTitle/WidgetTitle';
import { FlagsChart } from 'component/insights/componentsChart/FlagsChart/FlagsChart';
import { FlagsProjectChart } from 'component/insights/componentsChart/FlagsProjectChart/FlagsProjectChart';
import { MetricsSummaryChart } from 'component/insights/componentsChart/MetricsSummaryChart/MetricsSummaryChart';
import { ProjectHealthChart } from 'component/insights/componentsChart/ProjectHealthChart/ProjectHealthChart';
import { UpdatesPerEnvironmentTypeChart } from 'component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart';
-import { FlagStats } from 'component/insights/componentsStat/FlagStats/FlagStats';
-import { HealthStats } from 'component/insights/componentsStat/HealthStats/HealthStats';
import { useInsightsData } from 'component/insights/hooks/useInsightsData';
import { InsightsSection } from 'component/insights/sections/InsightsSection';
import { InsightsFilters } from 'component/insights/InsightsFilters';
-import {
- StyledChartContainer,
- StyledWidget,
- StyledWidgetContent,
- StyledWidgetStats,
- StatsExplanation,
-} from '../InsightsCharts.styles';
+import { Box, Typography, GlobalStyles, Tooltip, Link } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag';
import { NewProductionFlagsChart } from '../componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx';
-import Lightbulb from '@mui/icons-material/LightbulbOutlined';
import { CreationArchiveChart } from '../componentsChart/CreationArchiveChart/CreationArchiveChart.tsx';
-import { CreationArchiveStats } from '../componentsStat/CreationArchiveStats/CreationArchiveStats.tsx';
export const PerformanceInsights: FC = () => {
const statePrefix = 'performance-';
@@ -83,7 +72,50 @@ export const PerformanceInsights: FC = () => {
const isLifecycleGraphsEnabled = useUiFlag('lifecycleGraphs');
+ function getCurrentArchiveRatio() {
+ if (!groupedCreationArchiveData || Object.keys(groupedCreationArchiveData).length === 0) {
+ return 0;
+ }
+
+ let totalArchived = 0;
+ let totalCreated = 0;
+
+ Object.values(groupedCreationArchiveData).forEach((projectData) => {
+ const latestData = projectData[projectData.length - 1];
+ if (latestData) {
+ totalArchived += latestData.archivedFlags || 0;
+ const createdSum = latestData.createdFlags
+ ? Object.values(latestData.createdFlags).reduce(
+ (sum: number, count: number) => sum + count,
+ 0,
+ )
+ : 0;
+ totalCreated += createdSum;
+ }
+ });
+
+ return totalCreated > 0
+ ? Math.round((totalArchived / totalCreated) * 100)
+ : 0;
+ }
+
+ const currentRatio = getCurrentArchiveRatio();
+
return (
+ <>
+
{
/>
}
>
+ *': {
+ minWidth: 0,
+ }
+ }}>
{isLifecycleGraphsEnabled && isEnterprise() ? (
-
-
-
-
-
- How often do flags go live in production?
-
-
-
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+ Production Deployments
+
+
+
+
-
-
+
+
) : null}
{isLifecycleGraphsEnabled && isEnterprise() ? (
-
-
-
-
-
-
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Lifecycle Balance
+
+
+
+
+
+ {loading ? '...' : `${currentRatio}%`}
+
+
+
+ View cleanup
+
+
+
+
+
-
-
+
+
) : null}
{showAllProjects ? (
-
-
-
-
-
-
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Flags
+
+
+
+
+
+ {loading ? '...' : flagsTotal}
+
+
+
+
+
+
-
-
+
+
) : (
-
-
-
-
-
-
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Flags
+
+
+
+
+
+ {loading ? '...' : summary.total}
+
+
+
+
+
+
-
-
+
+
)}
{isEnterprise() ? (
-
-
-
- }
- />
-
-
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Technical Debt
+
+
+
+
+ 50 ? 'error.main' : 'primary.main',
+ fontWeight: 600,
+ cursor: 'help'
+ }}
+ >
+ {loading ? '...' : `${summary.technicalDebt}%`}
+
+
+
+ Learn more
+
+
+
+
+
-
-
+
+
) : null}
{isEnterprise() ? (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Flag Evaluation Metrics
+
+
+
+
+ ?
+
+
+
+
+
+
+
+
) : null}
+ {isEnterprise() ? (
+
+ theme.palette.mode === 'light'
+ ? 'linear-gradient(to right, #f8f9fa, #ffffff)'
+ : theme.palette.background.default
+ }}>
+
+
+
+
+ Updates per Environment Type
+
+
+
+
+ ?
+
+
+
+
+
+
+
+
+ ) : null}
+
+ >
);
};