1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-18 13:48:58 +02:00

feat: rename health to technical debt (#10063)

On insights and project status, we would like to show "technica debt"
instead of "health". New value is that of `1/health`, or simplified:
`healthy flags / total flags`
This commit is contained in:
Tymoteusz Czech 2025-06-04 11:01:17 +02:00 committed by GitHub
parent 8d7a0fdd7f
commit 37548c3436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 254 additions and 75 deletions

View File

@ -18,6 +18,7 @@ import { allOption } from 'component/common/ProjectSelect/ProjectSelect';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { WidgetTitle } from './components/WidgetTitle/WidgetTitle.tsx'; import { WidgetTitle } from './components/WidgetTitle/WidgetTitle.tsx';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useFlag } from '@unleash/proxy-client-react';
export interface IChartsProps { export interface IChartsProps {
flagTrends: InstanceInsightsSchema['flagTrends']; flagTrends: InstanceInsightsSchema['flagTrends'];
@ -104,6 +105,7 @@ export const InsightsCharts: FC<IChartsProps> = ({
const showAllProjects = projects[0] === allOption.id; const showAllProjects = projects[0] === allOption.id;
const isOneProjectSelected = projects.length === 1; const isOneProjectSelected = projects.length === 1;
const { isEnterprise } = useUiConfig(); const { isEnterprise } = useUiConfig();
const healthToDebtEnabled = useFlag('healthToTechDebt');
const lastUserTrend = userTrends[userTrends.length - 1]; const lastUserTrend = userTrends[userTrends.length - 1];
const lastFlagTrend = flagTrends[flagTrends.length - 1]; const lastFlagTrend = flagTrends[flagTrends.length - 1];
@ -189,9 +191,15 @@ export const InsightsCharts: FC<IChartsProps> = ({
potentiallyStale={summary.potentiallyStale} potentiallyStale={summary.potentiallyStale}
title={ title={
<WidgetTitle <WidgetTitle
title='Health' title={
healthToDebtEnabled
? 'Technical debt'
: 'Health'
}
tooltip={ tooltip={
'Percentage of flags that are not stale or potentially stale.' healthToDebtEnabled
? 'Percentage of stale and potentially stale flags.'
: 'Percentage of flags that are not stale or potentially stale.'
} }
/> />
} }

View File

@ -5,6 +5,8 @@ import { Badge } from 'component/common/Badge/Badge';
import type { TooltipState } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip'; import type { TooltipState } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip';
import { HorizontalDistributionChart } from 'component/insights/components/HorizontalDistributionChart/HorizontalDistributionChart'; import { HorizontalDistributionChart } from 'component/insights/components/HorizontalDistributionChart/HorizontalDistributionChart';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useFlag } from '@unleash/proxy-client-react';
import { getTechnicalDebtColor } from 'utils/getTechnicalDebtColor.ts';
const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({ const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({
padding: theme.spacing(2), padding: theme.spacing(2),
@ -33,6 +35,13 @@ const getHealthBadgeColor = (health?: number | null) => {
return 'error'; return 'error';
}; };
const getTechnicalDebtBadgeColor = (technicalDebt?: number | null) => {
if (technicalDebt === undefined || technicalDebt === null) {
return 'info';
}
return getTechnicalDebtColor(technicalDebt);
};
const Distribution = ({ stale = 0, potentiallyStale = 0, total = 0 }) => { const Distribution = ({ stale = 0, potentiallyStale = 0, total = 0 }) => {
const healthyFlagCount = total - stale - potentiallyStale; const healthyFlagCount = total - stale - potentiallyStale;
@ -99,12 +108,16 @@ const Distribution = ({ stale = 0, potentiallyStale = 0, total = 0 }) => {
export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
tooltip, tooltip,
}) => { }) => {
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const data = tooltip?.dataPoints.map((point) => { const data = tooltip?.dataPoints.map((point) => {
return { return {
label: point.label, label: point.label,
title: point.dataset.label, title: point.dataset.label,
color: point.dataset.borderColor, color: point.dataset.borderColor,
value: point.raw as InstanceInsightsSchemaProjectFlagTrendsItem, value: point.raw as InstanceInsightsSchemaProjectFlagTrendsItem & {
technicalDebt?: number | null;
}, // TODO: get from backend
}; };
}); });
@ -137,7 +150,9 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
color='textSecondary' color='textSecondary'
component='span' component='span'
> >
Project health {healthToTechDebtEnabled
? 'Technical debt'
: 'Project health'}
</Typography> </Typography>
</StyledItemHeader> </StyledItemHeader>
<StyledItemHeader> <StyledItemHeader>
@ -150,9 +165,21 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
</Typography> </Typography>
<strong>{point.title}</strong> <strong>{point.title}</strong>
</Typography> </Typography>
<Badge color={getHealthBadgeColor(point.value.health)}> {healthToTechDebtEnabled ? (
{point.value.health}% <Badge
</Badge> color={getTechnicalDebtBadgeColor(
point.value.technicalDebt,
)}
>
{point.value.technicalDebt}%
</Badge>
) : (
<Badge
color={getHealthBadgeColor(point.value.health)}
>
{point.value.health}%
</Badge>
)}
</StyledItemHeader>{' '} </StyledItemHeader>{' '}
<Divider <Divider
sx={(theme) => ({ margin: theme.spacing(1.5, 0) })} sx={(theme) => ({ margin: theme.spacing(1.5, 0) })}

View File

@ -2,7 +2,10 @@ import 'chartjs-adapter-date-fns';
import { type FC, useMemo } from 'react'; import { type FC, useMemo } from 'react';
import type { InstanceInsightsSchema } from 'openapi'; import type { InstanceInsightsSchema } from 'openapi';
import { HealthTooltip } from './HealthChartTooltip/HealthChartTooltip.tsx'; import { HealthTooltip } from './HealthChartTooltip/HealthChartTooltip.tsx';
import { useProjectChartData } from 'component/insights/hooks/useProjectChartData'; import {
calculateTechDebt,
useProjectChartData,
} from 'component/insights/hooks/useProjectChartData';
import { import {
fillGradientPrimary, fillGradientPrimary,
LineChart, LineChart,
@ -11,6 +14,7 @@ import {
import { useTheme } from '@mui/material'; import { useTheme } from '@mui/material';
import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends'; import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends';
import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData'; import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData';
import { useFlag } from '@unleash/proxy-client-react';
interface IProjectHealthChartProps { interface IProjectHealthChartProps {
projectFlagTrends: GroupedDataByProject< projectFlagTrends: GroupedDataByProject<
@ -42,6 +46,7 @@ export const ProjectHealthChart: FC<IProjectHealthChartProps> = ({
const projectsData = useProjectChartData(projectFlagTrends); const projectsData = useProjectChartData(projectFlagTrends);
const theme = useTheme(); const theme = useTheme();
const placeholderData = usePlaceholderData(); const placeholderData = usePlaceholderData();
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const aggregateHealthData = useMemo(() => { const aggregateHealthData = useMemo(() => {
const labels = Array.from( const labels = Array.from(
@ -80,9 +85,18 @@ export const ProjectHealthChart: FC<IProjectHealthChartProps> = ({
return { return {
datasets: [ datasets: [
{ {
label: 'Health', label: healthToTechDebtEnabled
? 'Technical debt'
: 'Health',
data: weeks.map((item) => ({ data: weeks.map((item) => ({
health: item.total ? calculateHealth(item) : undefined, health: item.total ? calculateHealth(item) : undefined,
...(healthToTechDebtEnabled
? {
technicalDebt: item.total
? calculateTechDebt(item)
: undefined,
}
: {}),
date: item.date, date: item.date,
total: item.total, total: item.total,
stale: item.stale, stale: item.stale,
@ -117,7 +131,12 @@ export const ProjectHealthChart: FC<IProjectHealthChartProps> = ({
notEnoughData notEnoughData
? {} ? {}
: { : {
parsing: { yAxisKey: 'health', xAxisKey: 'date' }, parsing: {
yAxisKey: healthToTechDebtEnabled
? 'technicalDebt'
: 'health',
xAxisKey: 'date',
},
scales: { scales: {
y: { y: {
min: 0, min: 0,

View File

@ -1,6 +1,7 @@
import type { FC, ReactNode } from 'react'; import type { FC, ReactNode } from 'react';
import { Box, Divider, Link, styled } from '@mui/material'; import { Box, Divider, Link, styled } from '@mui/material';
import { ReactComponent as InstanceHealthIcon } from 'assets/icons/instance-health.svg'; import { ReactComponent as InstanceHealthIcon } from 'assets/icons/instance-health.svg';
import { useFlag } from '@unleash/proxy-client-react';
interface IHealthStatsProps { interface IHealthStatsProps {
value?: string | number; value?: string | number;
@ -69,43 +70,59 @@ export const HealthStats: FC<IHealthStatsProps> = ({
stale, stale,
potentiallyStale, potentiallyStale,
title, title,
}) => ( }) => {
<StyledContainer> const healthToDebtEnabled = useFlag('healthToTechDebt');
<StyledHeader>
<StyledSection>{title}</StyledSection> // TODO: get the following from backend
<StyledSection>{/* TODO: trend */}</StyledSection> const unhealthy = stale + potentiallyStale;
</StyledHeader> const technicalDebtValue = (
<Divider /> (unhealthy / (healthy + unhealthy)) *
<StyledSection> 100
<StyledStatsRow> ).toFixed(1);
<StyledIcon />
Instance health return (
<StyledMainValue>{`${value || 0}%`}</StyledMainValue> <StyledContainer>
</StyledStatsRow> <StyledHeader>
</StyledSection> <StyledSection>{title}</StyledSection>
<Divider /> </StyledHeader>
<FlagsSection> <Divider />
<StyledStatsRow> <StyledSection>
Healthy flags <StyledStatsRow>
<StyledValue>{healthy || 0}</StyledValue> <StyledIcon />
</StyledStatsRow> {healthToDebtEnabled ? 'Technical debt' : 'Instance health'}
<StyledStatsRow> {healthToDebtEnabled ? (
Stale flags <StyledMainValue>{`${technicalDebtValue}%`}</StyledMainValue>
<StyledValue>{stale || 0}</StyledValue> ) : (
</StyledStatsRow> <StyledMainValue>{`${value || 0}%`}</StyledMainValue>
<StyledStatsRow> )}
Potentially stale flags </StyledStatsRow>
<StyledValue>{potentiallyStale || 0}</StyledValue> </StyledSection>
</StyledStatsRow> <Divider />
<ExplanationRow> <FlagsSection>
<Link <StyledStatsRow>
href='https://docs.getunleash.io/reference/insights#health' Healthy flags
target='_blank' <StyledValue>{healthy || 0}</StyledValue>
rel='noreferrer' </StyledStatsRow>
> <StyledStatsRow>
What affects instance health? Stale flags
</Link> <StyledValue>{stale || 0}</StyledValue>
</ExplanationRow> </StyledStatsRow>
</FlagsSection> <StyledStatsRow>
</StyledContainer> Potentially stale flags
); <StyledValue>{potentiallyStale || 0}</StyledValue>
</StyledStatsRow>
<ExplanationRow>
<Link
href='https://docs.getunleash.io/reference/insights#health'
target='_blank'
rel='noreferrer'
>
{healthToDebtEnabled
? 'What affects technical debt?'
: 'What affects instance health?'}
</Link>
</ExplanationRow>
</FlagsSection>
</StyledContainer>
);
};

View File

@ -4,13 +4,29 @@ import { useProjectColor } from './useProjectColor.js';
import { useTheme } from '@mui/material'; import { useTheme } from '@mui/material';
import type { GroupedDataByProject } from './useGroupedProjectTrends.js'; import type { GroupedDataByProject } from './useGroupedProjectTrends.js';
import useProjects from 'hooks/api/getters/useProjects/useProjects'; import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { useFlag } from '@unleash/proxy-client-react';
type ProjectFlagTrends = InstanceInsightsSchema['projectFlagTrends']; type ProjectFlagTrends = InstanceInsightsSchema['projectFlagTrends'];
export const calculateTechDebt = (item: {
total: number;
stale: number;
potentiallyStale: number;
}) => {
if (!item.total) {
return '0';
}
return (((item.stale + item.potentiallyStale) / item.total) * 100).toFixed(
2,
);
};
export const useProjectChartData = ( export const useProjectChartData = (
projectFlagTrends: GroupedDataByProject<ProjectFlagTrends>, projectFlagTrends: GroupedDataByProject<ProjectFlagTrends>,
) => { ) => {
const theme = useTheme(); const theme = useTheme();
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const getProjectColor = useProjectColor(); const getProjectColor = useProjectColor();
const { projects } = useProjects(); const { projects } = useProjects();
const projectNames = new Map( const projectNames = new Map(
@ -23,7 +39,17 @@ export const useProjectChartData = (
const color = getProjectColor(project); const color = getProjectColor(project);
return { return {
label: projectNames.get(project) || project, label: projectNames.get(project) || project,
data: trends, data: trends.map((item) => ({
...item,
...(healthToTechDebtEnabled
? {
technicalDebt: item.total
? calculateTechDebt(item)
: undefined,
}
: {}),
})),
borderColor: color, borderColor: color,
backgroundColor: color, backgroundColor: color,
fill: false, fill: false,

View File

@ -23,6 +23,7 @@ import {
StyledWidgetContent, StyledWidgetContent,
StyledWidgetStats, StyledWidgetStats,
} from '../InsightsCharts.styles'; } from '../InsightsCharts.styles';
import { useFlag } from '@unleash/proxy-client-react';
export const PerformanceInsights: FC = () => { export const PerformanceInsights: FC = () => {
const statePrefix = 'performance-'; const statePrefix = 'performance-';
@ -47,6 +48,8 @@ export const PerformanceInsights: FC = () => {
state[`${statePrefix}to`]?.values[0], state[`${statePrefix}to`]?.values[0],
); );
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const projects = state[`${statePrefix}project`]?.values ?? [allOption.id]; const projects = state[`${statePrefix}project`]?.values ?? [allOption.id];
const showAllProjects = projects[0] === allOption.id; const showAllProjects = projects[0] === allOption.id;
@ -131,12 +134,21 @@ export const PerformanceInsights: FC = () => {
stale={summary.stale} stale={summary.stale}
potentiallyStale={summary.potentiallyStale} potentiallyStale={summary.potentiallyStale}
title={ title={
<WidgetTitle healthToTechDebtEnabled ? (
title='Health' <WidgetTitle
tooltip={ title='Technical debt'
'Percentage of flags that are not stale or potentially stale.' tooltip={
} 'Percentage of flags that are stale or potentially stale.'
/> }
/>
) : (
<WidgetTitle
title='Health'
tooltip={
'Percentage of flags that are not stale or potentially stale.'
}
/>
)
} }
/> />
</StyledWidgetStats> </StyledWidgetStats>

View File

@ -39,10 +39,14 @@ import { ActionBox } from './ActionBox.tsx';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { NoProjectsContactAdmin } from './NoProjectsContactAdmin.tsx'; import { NoProjectsContactAdmin } from './NoProjectsContactAdmin.tsx';
import { AskOwnerToAddYouToTheirProject } from './AskOwnerToAddYouToTheirProject.tsx'; import { AskOwnerToAddYouToTheirProject } from './AskOwnerToAddYouToTheirProject.tsx';
import { useFlag } from '@unleash/proxy-client-react';
const ActiveProjectDetails: FC<{ const ActiveProjectDetails: FC<{
project: PersonalDashboardSchemaProjectsItem; project: PersonalDashboardSchemaProjectsItem;
}> = ({ project }) => { }> = ({ project }) => {
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const techicalDebt = 100 - project.health; // TODO: health to technical debt from backend
return ( return (
<Box sx={{ display: 'flex', gap: 2 }}> <Box sx={{ display: 'flex', gap: 2 }}>
<Box sx={{ display: 'flex', flexDirection: 'column' }}> <Box sx={{ display: 'flex', flexDirection: 'column' }}>
@ -63,10 +67,10 @@ const ActiveProjectDetails: FC<{
</Box> </Box>
<Box sx={{ display: 'flex', flexDirection: 'column' }}> <Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant='subtitle2' color='primary'> <Typography variant='subtitle2' color='primary'>
{project.health}% {healthToTechDebtEnabled ? techicalDebt : project.health}%
</Typography> </Typography>
<Typography variant='caption' color='text.secondary'> <Typography variant='caption' color='text.secondary'>
health {healthToTechDebtEnabled ? 'technical debt' : 'health'}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>

View File

@ -4,6 +4,7 @@ import { Link } from 'react-router-dom';
import Lightbulb from '@mui/icons-material/LightbulbOutlined'; import Lightbulb from '@mui/icons-material/LightbulbOutlined';
import type { PersonalDashboardProjectDetailsSchemaInsights } from 'openapi'; import type { PersonalDashboardProjectDetailsSchemaInsights } from 'openapi';
import { ActionBox } from './ActionBox.tsx'; import { ActionBox } from './ActionBox.tsx';
import { useFlag } from '@unleash/proxy-client-react';
const PercentageScore = styled('span')(({ theme }) => ({ const PercentageScore = styled('span')(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold, fontWeight: theme.typography.fontWeightBold,
@ -57,9 +58,11 @@ const ProjectHealthMessage: FC<{
insights: PersonalDashboardProjectDetailsSchemaInsights; insights: PersonalDashboardProjectDetailsSchemaInsights;
project: string; project: string;
}> = ({ trend, insights, project }) => { }> = ({ trend, insights, project }) => {
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
const { avgHealthCurrentWindow, avgHealthPastWindow, health } = insights; const { avgHealthCurrentWindow, avgHealthPastWindow, health } = insights;
const improveMessage = const improveMessage = healthToTechDebtEnabled
'Remember to archive your stale feature flags to keep the project health growing.'; ? 'Remember to archive your stale feature flags to keep the technical debt low.'
: 'Remember to archive your stale feature flags to keep the project health growing.';
const keepDoingMessage = const keepDoingMessage =
'This indicates that you are doing a good job of archiving your feature flags.'; 'This indicates that you are doing a good job of archiving your feature flags.';

View File

@ -373,6 +373,7 @@ export const Project = () => {
}} }}
/> />
<Routes> <Routes>
{/* FIXME: remove /health with `healthToTechDebt` flag - redirect to project status */}
<Route path='health' element={<ProjectHealth />} /> <Route path='health' element={<ProjectHealth />} />
<Route <Route
path='access/*' path='access/*'

View File

@ -5,6 +5,8 @@ import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectS
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { HealthGridTile } from './ProjectHealthGrid.styles'; import { HealthGridTile } from './ProjectHealthGrid.styles';
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber'; import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
import { useFlag } from '@unleash/proxy-client-react';
import { getTechnicalDebtColor } from 'utils/getTechnicalDebtColor.ts';
const ChartRadius = 40; const ChartRadius = 40;
const ChartStrokeWidth = 13; const ChartStrokeWidth = 13;
@ -80,6 +82,29 @@ const UnhealthyFlagBox = ({ flagCount }: { flagCount: number }) => {
); );
}; };
const useHealthColor = (healthRating: number) => {
const theme = useTheme();
if (healthRating <= 24) {
return theme.palette.error.main;
}
if (healthRating <= 74) {
return theme.palette.warning.border;
}
return theme.palette.success.border;
};
const useTechnicalDebtColor = (techicalDebt: number) => {
const theme = useTheme();
switch (getTechnicalDebtColor(techicalDebt)) {
case 'error':
return theme.palette.error.main;
case 'warning':
return theme.palette.warning.border;
default:
return theme.palette.success.border;
}
};
const Wrapper = styled(HealthGridTile)(({ theme }) => ({ const Wrapper = styled(HealthGridTile)(({ theme }) => ({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -92,22 +117,21 @@ export const ProjectHealth = () => {
const { const {
data: { health, staleFlags }, data: { health, staleFlags },
} = useProjectStatus(projectId); } = useProjectStatus(projectId);
const healthRating = health.current;
const { isOss } = useUiConfig(); const { isOss } = useUiConfig();
const theme = useTheme(); const theme = useTheme();
const circumference = 2 * Math.PI * ChartRadius; // const healthToDebtEnabled = useFlag('healthToTechDebt');
const circumference = 2 * Math.PI * ChartRadius;
const healthRating = health.current;
const technicalDebt = 100 - healthRating; // TODO: get from backend
const gapLength = 0.3; const gapLength = 0.3;
const filledLength = 1 - gapLength; const filledLength = 1 - gapLength;
const offset = 0.75 - gapLength / 2; const offset = 0.75 - gapLength / 2;
const healthLength = (healthRating / 100) * circumference * 0.7; const healthLength = (healthRating / 100) * circumference * 0.7;
const technicalDebtLength = (technicalDebt / 100) * circumference * 0.7;
const healthColor = const healthColor = useHealthColor(healthRating);
healthRating >= 0 && healthRating <= 24 const technicalDebtColor = useTechnicalDebtColor(technicalDebt);
? theme.palette.error.main
: healthRating >= 25 && healthRating <= 74
? theme.palette.warning.border
: theme.palette.success.border;
return ( return (
<Wrapper> <Wrapper>
@ -129,9 +153,17 @@ export const ProjectHealth = () => {
cy='50' cy='50'
r={ChartRadius} r={ChartRadius}
fill='none' fill='none'
stroke={healthColor} stroke={
healthToDebtEnabled
? technicalDebtColor
: healthColor
}
strokeWidth={ChartStrokeWidth} strokeWidth={ChartStrokeWidth}
strokeDasharray={`${healthLength} ${circumference - healthLength}`} strokeDasharray={
healthToDebtEnabled
? `${technicalDebtLength} ${circumference - technicalDebtLength}`
: `${healthLength} ${circumference - healthLength}`
}
strokeDashoffset={offset * circumference} strokeDashoffset={offset * circumference}
/> />
<text <text
@ -142,17 +174,30 @@ export const ProjectHealth = () => {
fill={theme.palette.text.primary} fill={theme.palette.text.primary}
fontSize={theme.typography.h1.fontSize} fontSize={theme.typography.h1.fontSize}
> >
{healthRating}% {healthToDebtEnabled ? technicalDebt : healthRating}
%
</text> </text>
</StyledSVG> </StyledSVG>
</SVGWrapper> </SVGWrapper>
<TextContainer> <TextContainer>
<Typography> <Typography>
Your current project health rating is {healthRating}% {healthToDebtEnabled ? (
<>
Your current technical debt rating is{' '}
{technicalDebt}%.
</>
) : (
<>
Your current project health rating is{' '}
{healthRating}%.
</>
)}
</Typography> </Typography>
{!isOss() && ( {!isOss() && (
<Link to={`/insights?project=IS%3A${projectId}`}> <Link to={`/insights?project=IS%3A${projectId}`}>
View health over time {healthToDebtEnabled
? 'View technical debt over time'
: 'View health over time'}
</Link> </Link>
)} )}
</TextContainer> </TextContainer>

View File

@ -9,6 +9,7 @@ import { ProjectHealthGrid } from './ProjectHealthGrid.tsx';
import { useFeedback } from 'component/feedbackNew/useFeedback'; import { useFeedback } from 'component/feedbackNew/useFeedback';
import FeedbackIcon from '@mui/icons-material/ChatOutlined'; import FeedbackIcon from '@mui/icons-material/ChatOutlined';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useFlag } from '@unleash/proxy-client-react';
const ModalContentContainer = styled('section')(({ theme }) => ({ const ModalContentContainer = styled('section')(({ theme }) => ({
minHeight: '100vh', minHeight: '100vh',
@ -140,6 +141,7 @@ export const ProjectStatusModal = ({ open, onClose, onFollowLink }: Props) => {
}); });
}; };
const { isOss } = useUiConfig(); const { isOss } = useUiConfig();
const healthToDebtEnabled = useFlag('healthToTechDebt');
return ( return (
<DynamicSidebarModal <DynamicSidebarModal
@ -159,7 +161,9 @@ export const ProjectStatusModal = ({ open, onClose, onFollowLink }: Props) => {
</HeaderRow> </HeaderRow>
<WidgetContainer> <WidgetContainer>
<Row> <Row>
<RowHeader>Health</RowHeader> <RowHeader>
{healthToDebtEnabled ? 'Technical debt' : 'Health'}
</RowHeader>
<ProjectHealthGrid /> <ProjectHealthGrid />
</Row> </Row>
{!isOss() && ( {!isOss() && (

View File

@ -0,0 +1,13 @@
/**
* Consistent values for boundries between healthy, warning and error colors
* @param technicalDebt {Number} 0-100
*/
export const getTechnicalDebtColor = (technicalDebt: number) => {
if (technicalDebt >= 50) {
return 'error';
}
if (technicalDebt >= 25) {
return 'warning';
}
return 'success';
};