mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-27 01:19:00 +02:00
fix: use technicalDebt property from backend (#10111)
Frontend should load `technicaDebt` from backend instead of re-calculating it.
This commit is contained in:
parent
d7da5fe6a4
commit
4e48d90ed8
@ -115,9 +115,7 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
|
|||||||
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
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { useFlag } from '@unleash/proxy-client-react';
|
|||||||
|
|
||||||
interface IHealthStatsProps {
|
interface IHealthStatsProps {
|
||||||
value?: string | number;
|
value?: string | number;
|
||||||
|
technicalDebt?: string | number;
|
||||||
healthy: number;
|
healthy: number;
|
||||||
stale: number;
|
stale: number;
|
||||||
potentiallyStale: number;
|
potentiallyStale: number;
|
||||||
@ -66,6 +67,7 @@ const StyledMainValue = styled(StyledValue)(({ theme }) => ({
|
|||||||
|
|
||||||
export const HealthStats: FC<IHealthStatsProps> = ({
|
export const HealthStats: FC<IHealthStatsProps> = ({
|
||||||
value,
|
value,
|
||||||
|
technicalDebt,
|
||||||
healthy,
|
healthy,
|
||||||
stale,
|
stale,
|
||||||
potentiallyStale,
|
potentiallyStale,
|
||||||
@ -73,13 +75,6 @@ export const HealthStats: FC<IHealthStatsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const healthToDebtEnabled = useFlag('healthToTechDebt');
|
const healthToDebtEnabled = useFlag('healthToTechDebt');
|
||||||
|
|
||||||
// TODO: get the following from backend
|
|
||||||
const unhealthy = stale + potentiallyStale;
|
|
||||||
const technicalDebtValue = (
|
|
||||||
(unhealthy / (healthy + unhealthy)) *
|
|
||||||
100
|
|
||||||
).toFixed(1);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
@ -91,7 +86,7 @@ export const HealthStats: FC<IHealthStatsProps> = ({
|
|||||||
<StyledIcon />
|
<StyledIcon />
|
||||||
{healthToDebtEnabled ? 'Technical debt' : 'Instance health'}
|
{healthToDebtEnabled ? 'Technical debt' : 'Instance health'}
|
||||||
{healthToDebtEnabled ? (
|
{healthToDebtEnabled ? (
|
||||||
<StyledMainValue>{`${technicalDebtValue}%`}</StyledMainValue>
|
<StyledMainValue>{`${technicalDebt}%`}</StyledMainValue>
|
||||||
) : (
|
) : (
|
||||||
<StyledMainValue>{`${value || 0}%`}</StyledMainValue>
|
<StyledMainValue>{`${value || 0}%`}</StyledMainValue>
|
||||||
)}
|
)}
|
||||||
|
@ -64,6 +64,7 @@ describe('useFilteredFlagTrends', () => {
|
|||||||
averageUsers: 2,
|
averageUsers: 2,
|
||||||
averageHealth: '79',
|
averageHealth: '79',
|
||||||
medianTimeToProduction: 3,
|
medianTimeToProduction: 3,
|
||||||
|
technicalDebt: '21',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ describe('useFilteredFlagTrends', () => {
|
|||||||
potentiallyStale: 0,
|
potentiallyStale: 0,
|
||||||
averageUsers: 0,
|
averageUsers: 0,
|
||||||
averageHealth: '100',
|
averageHealth: '100',
|
||||||
|
technicalDebt: '0',
|
||||||
medianTimeToProduction: 4,
|
medianTimeToProduction: 4,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -134,6 +136,7 @@ describe('useFilteredFlagTrends', () => {
|
|||||||
averageUsers: 1.5,
|
averageUsers: 1.5,
|
||||||
averageHealth: '100',
|
averageHealth: '100',
|
||||||
medianTimeToProduction: 3.5,
|
medianTimeToProduction: 3.5,
|
||||||
|
technicalDebt: '0',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -163,6 +166,7 @@ describe('useFilteredFlagTrends', () => {
|
|||||||
averageUsers: 0,
|
averageUsers: 0,
|
||||||
averageHealth: '100',
|
averageHealth: '100',
|
||||||
medianTimeToProduction: undefined,
|
medianTimeToProduction: undefined,
|
||||||
|
technicalDebt: '0',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -216,6 +220,7 @@ describe('useFilteredFlagTrends', () => {
|
|||||||
averageUsers: 0,
|
averageUsers: 0,
|
||||||
averageHealth: '100',
|
averageHealth: '100',
|
||||||
medianTimeToProduction: 5,
|
medianTimeToProduction: 5,
|
||||||
|
technicalDebt: '0',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -71,6 +71,12 @@ export const useFilteredFlagsSummary = (
|
|||||||
averageHealth: sum.total
|
averageHealth: sum.total
|
||||||
? ((sum.active / (sum.total || 1)) * 100).toFixed(0)
|
? ((sum.active / (sum.total || 1)) * 100).toFixed(0)
|
||||||
: '100',
|
: '100',
|
||||||
|
technicalDebt: sum.total
|
||||||
|
? (
|
||||||
|
((sum.stale + sum.potentiallyStale) / sum.total) *
|
||||||
|
100
|
||||||
|
).toFixed(0)
|
||||||
|
: '0',
|
||||||
medianTimeToProduction,
|
medianTimeToProduction,
|
||||||
};
|
};
|
||||||
}, [filteredProjectFlagTrends]);
|
}, [filteredProjectFlagTrends]);
|
||||||
|
@ -130,6 +130,7 @@ export const PerformanceInsights: FC = () => {
|
|||||||
<StyledWidgetStats width={350} padding={0}>
|
<StyledWidgetStats width={350} padding={0}>
|
||||||
<HealthStats
|
<HealthStats
|
||||||
value={summary.averageHealth}
|
value={summary.averageHealth}
|
||||||
|
technicalDebt={summary.technicalDebt}
|
||||||
healthy={summary.active}
|
healthy={summary.active}
|
||||||
stale={summary.stale}
|
stale={summary.stale}
|
||||||
potentiallyStale={summary.potentiallyStale}
|
potentiallyStale={summary.potentiallyStale}
|
||||||
|
@ -46,7 +46,7 @@ const ActiveProjectDetails: FC<{
|
|||||||
}> = ({ project }) => {
|
}> = ({ project }) => {
|
||||||
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
|
const healthToTechDebtEnabled = useFlag('healthToTechDebt');
|
||||||
|
|
||||||
const techicalDebt = 100 - project.health; // TODO: health to technical debt from backend
|
const techicalDebt = project.technicalDebt;
|
||||||
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' }}>
|
||||||
|
@ -115,23 +115,25 @@ const Wrapper = styled(HealthGridTile)(({ theme }) => ({
|
|||||||
export const ProjectHealth = () => {
|
export const ProjectHealth = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const {
|
const {
|
||||||
data: { health, staleFlags },
|
data: { health, technicalDebt, staleFlags },
|
||||||
} = useProjectStatus(projectId);
|
} = useProjectStatus(projectId);
|
||||||
const { isOss } = useUiConfig();
|
const { isOss } = useUiConfig();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const healthToDebtEnabled = useFlag('healthToTechDebt');
|
const healthToDebtEnabled = useFlag('healthToTechDebt');
|
||||||
const circumference = 2 * Math.PI * ChartRadius;
|
const circumference = 2 * Math.PI * ChartRadius;
|
||||||
const healthRating = health.current;
|
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 technicalDebtLength =
|
||||||
|
((technicalDebt.current || 0) / 100) * circumference * 0.7;
|
||||||
|
|
||||||
const healthColor = useHealthColor(healthRating);
|
const healthColor = useHealthColor(healthRating);
|
||||||
const technicalDebtColor = useTechnicalDebtColor(technicalDebt);
|
const technicalDebtColor = useTechnicalDebtColor(
|
||||||
|
technicalDebt.current || 0,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -174,7 +176,9 @@ export const ProjectHealth = () => {
|
|||||||
fill={theme.palette.text.primary}
|
fill={theme.palette.text.primary}
|
||||||
fontSize={theme.typography.h1.fontSize}
|
fontSize={theme.typography.h1.fontSize}
|
||||||
>
|
>
|
||||||
{healthToDebtEnabled ? technicalDebt : healthRating}
|
{healthToDebtEnabled
|
||||||
|
? technicalDebt.current || 0
|
||||||
|
: healthRating}
|
||||||
%
|
%
|
||||||
</text>
|
</text>
|
||||||
</StyledSVG>
|
</StyledSVG>
|
||||||
|
@ -20,6 +20,18 @@ const mockMetrics = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('productivityReportViewModel', () => {
|
describe('productivityReportViewModel', () => {
|
||||||
|
it('should show technical debt', () => {
|
||||||
|
const viewModel = productivityReportViewModel({
|
||||||
|
...mockData,
|
||||||
|
metrics: {
|
||||||
|
...mockMetrics,
|
||||||
|
health: 85,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(viewModel.technicalDebt).toBe('15');
|
||||||
|
});
|
||||||
|
|
||||||
describe('healthColor', () => {
|
describe('healthColor', () => {
|
||||||
it('returns RED for health between 0 and 24', () => {
|
it('returns RED for health between 0 and 24', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
@ -65,7 +77,7 @@ describe('productivityReportViewModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('healthTrendMessage', () => {
|
describe('healthTrendMessage', () => {
|
||||||
it('returns correct trend message when health increased', () => {
|
it('returns correct trend message when technica debt decreased', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 80,
|
health: 80,
|
||||||
@ -82,7 +94,7 @@ describe('productivityReportViewModel', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns correct trend message when health decreased', () => {
|
it('returns correct trend message when technical debt increased', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 60,
|
health: 60,
|
||||||
@ -99,7 +111,7 @@ describe('productivityReportViewModel', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns correct message when health is the same', () => {
|
it('returns correct message when technical debt is the same', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 70,
|
health: 70,
|
||||||
|
@ -27,6 +27,7 @@ export const productivityReportViewModel = ({
|
|||||||
userName,
|
userName,
|
||||||
userEmail,
|
userEmail,
|
||||||
...metrics,
|
...metrics,
|
||||||
|
technicalDebt: Math.max(0, 100 - metrics.health).toString(),
|
||||||
unleashUrl,
|
unleashUrl,
|
||||||
healthColor() {
|
healthColor() {
|
||||||
const healthRating = this.health;
|
const healthRating = this.health;
|
||||||
|
@ -141,14 +141,53 @@ test('Can send productivity report email', async () => {
|
|||||||
);
|
);
|
||||||
expect(content.from).toBe('noreply@getunleash.ai');
|
expect(content.from).toBe('noreply@getunleash.ai');
|
||||||
expect(content.subject).toBe('Unleash - productivity report');
|
expect(content.subject).toBe('Unleash - productivity report');
|
||||||
expect(content.html.includes('Productivity Report')).toBe(true);
|
expect(content.html).toContain('Productivity Report');
|
||||||
expect(content.html.includes('localhost/insights')).toBe(true);
|
expect(content.html).toContain('localhost/insights');
|
||||||
expect(content.html.includes('localhost/profile')).toBe(true);
|
expect(content.html).toContain('localhost/profile');
|
||||||
expect(content.html.includes('#68a611')).toBe(true);
|
expect(content.html).toContain('#68a611');
|
||||||
expect(content.html.includes('10% more than previous month')).toBe(true);
|
expect(content.html).toContain('1%');
|
||||||
expect(content.text.includes('localhost/insights')).toBe(true);
|
expect(content.html).toContain('10% more than previous month');
|
||||||
expect(content.text.includes('localhost/profile')).toBe(true);
|
expect(content.text).toContain('localhost/insights');
|
||||||
expect(content.text.includes('localhost/profile')).toBe(true);
|
expect(content.text).toContain('localhost/profile');
|
||||||
|
expect(content.text).toContain('localhost/profile');
|
||||||
|
expect(content.text).toContain('Your instance technical debt: 1%');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Sets correct color for technical debt', async () => {
|
||||||
|
const emailService = new EmailService({
|
||||||
|
server: {
|
||||||
|
unleashUrl: 'http://localhost',
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
host: 'test',
|
||||||
|
port: 587,
|
||||||
|
secure: false,
|
||||||
|
smtpuser: '',
|
||||||
|
smtppass: '',
|
||||||
|
sender: 'noreply@getunleash.ai',
|
||||||
|
},
|
||||||
|
getLogger: noLoggerProvider,
|
||||||
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
|
const content = await emailService.sendProductivityReportEmail(
|
||||||
|
'user@user.com',
|
||||||
|
'customerId',
|
||||||
|
{
|
||||||
|
flagsCreated: 1,
|
||||||
|
productionUpdates: 2,
|
||||||
|
health: 20,
|
||||||
|
previousMonth: {
|
||||||
|
health: 50,
|
||||||
|
flagsCreated: 1,
|
||||||
|
productionUpdates: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(content.html).not.toContain('#68a611');
|
||||||
|
expect(content.html).toContain('#d93644');
|
||||||
|
expect(content.html).toContain(
|
||||||
|
'Remember to archive stale flags to reduce technical debt and keep your project healthy',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should add optional headers to productivity email', async () => {
|
test('Should add optional headers to productivity email', async () => {
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
<div class="shaded"
|
<div class="shaded"
|
||||||
style="margin: 0;padding: 24px 8px 24px 8px; background: #f0f0f5;border-width: 3px;border-color: #ffffff;border-style: solid;">
|
style="margin: 0;padding: 24px 8px 24px 8px; background: #f0f0f5;border-width: 3px;border-color: #ffffff;border-style: solid;">
|
||||||
<div style="padding-top: 12px;">
|
<div style="padding-top: 12px;">
|
||||||
<span style="color: {{healthColor}};">{{health}}%</span><br>
|
<span style="color: {{healthColor}};">{{technicalDebt}}%</span><br>
|
||||||
<span style="font-size: 16px; color: #1A4049; font-weight: 700">your instance health</span><br>
|
<span style="font-size: 16px; color: #1A4049; font-weight: 700">your instance technical debt</span><br>
|
||||||
<span style="font-size: 12px; color: #6E6E70; font-weight: 400; line-height: 14px">{{{healthTrendMessage}}}</span>
|
<span style="font-size: 12px; color: #6E6E70; font-weight: 400; line-height: 14px">{{{healthTrendMessage}}}</span>
|
||||||
<div style="font-size: 12px; margin-top: 16px; font-weight: 400; line-height: 14px;">{{actionText}}</div>
|
<div style="font-size: 12px; margin-top: 16px; font-weight: 400; line-height: 14px;">{{actionText}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ Hi {{userName}},
|
|||||||
We are excited to share the latest insights for your instance. As always if you
|
We are excited to share the latest insights for your instance. As always if you
|
||||||
have any questions or concerns let us know - we are here for you.
|
have any questions or concerns let us know - we are here for you.
|
||||||
|
|
||||||
Your instance health: {{health}}
|
Your instance technical debt: {{technicalDebt}}%
|
||||||
|
|
||||||
Flags created last month: {{flagsCreated}}
|
Flags created last month: {{flagsCreated}}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user