From 4e48d90ed8f0ca67e872c7056b99e1a7d73ce325 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:50:29 +0200 Subject: [PATCH] fix: use technicalDebt property from backend (#10111) Frontend should load `technicaDebt` from backend instead of re-calculating it. --- .../HealthChartTooltip/HealthChartTooltip.tsx | 4 +- .../HealthStats/HealthStats.tsx | 11 +--- .../hooks/useFilteredFlagsSummary.test.ts | 5 ++ .../insights/hooks/useFilteredFlagsSummary.ts | 6 ++ .../insights/sections/PerformanceInsights.tsx | 1 + .../personalDashboard/MyProjects.tsx | 2 +- .../Project/ProjectStatus/ProjectHealth.tsx | 14 +++-- .../productivity-report-view-model.test.ts | 18 +++++- .../productivity-report-view-model.ts | 1 + src/lib/services/email-service.test.ts | 55 ++++++++++++++++--- .../productivity-report.html.mustache | 4 +- .../productivity-report.plain.mustache | 2 +- 12 files changed, 92 insertions(+), 31 deletions(-) diff --git a/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx b/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx index 7f3162d904..42eab9ceff 100644 --- a/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx +++ b/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx @@ -115,9 +115,7 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({ label: point.label, title: point.dataset.label, color: point.dataset.borderColor, - value: point.raw as InstanceInsightsSchemaProjectFlagTrendsItem & { - technicalDebt?: number | null; - }, // TODO: get from backend + value: point.raw as InstanceInsightsSchemaProjectFlagTrendsItem, }; }); diff --git a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx index 80ada4d234..0943d143bd 100644 --- a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx +++ b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx @@ -5,6 +5,7 @@ import { useFlag } from '@unleash/proxy-client-react'; interface IHealthStatsProps { value?: string | number; + technicalDebt?: string | number; healthy: number; stale: number; potentiallyStale: number; @@ -66,6 +67,7 @@ const StyledMainValue = styled(StyledValue)(({ theme }) => ({ export const HealthStats: FC = ({ value, + technicalDebt, healthy, stale, potentiallyStale, @@ -73,13 +75,6 @@ export const HealthStats: FC = ({ }) => { const healthToDebtEnabled = useFlag('healthToTechDebt'); - // TODO: get the following from backend - const unhealthy = stale + potentiallyStale; - const technicalDebtValue = ( - (unhealthy / (healthy + unhealthy)) * - 100 - ).toFixed(1); - return ( @@ -91,7 +86,7 @@ export const HealthStats: FC = ({ {healthToDebtEnabled ? 'Technical debt' : 'Instance health'} {healthToDebtEnabled ? ( - {`${technicalDebtValue}%`} + {`${technicalDebt}%`} ) : ( {`${value || 0}%`} )} diff --git a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.test.ts b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.test.ts index 8eb92b3f2c..9e5442fdc6 100644 --- a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.test.ts +++ b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.test.ts @@ -64,6 +64,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 2, averageHealth: '79', medianTimeToProduction: 3, + technicalDebt: '21', }); }); @@ -92,6 +93,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, averageUsers: 0, averageHealth: '100', + technicalDebt: '0', medianTimeToProduction: 4, }); }); @@ -134,6 +136,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 1.5, averageHealth: '100', medianTimeToProduction: 3.5, + technicalDebt: '0', }); }); @@ -163,6 +166,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 0, averageHealth: '100', medianTimeToProduction: undefined, + technicalDebt: '0', }); }); @@ -216,6 +220,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 0, averageHealth: '100', medianTimeToProduction: 5, + technicalDebt: '0', }); }); }); diff --git a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts index 3ff263aea7..028214bcce 100644 --- a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts +++ b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts @@ -71,6 +71,12 @@ export const useFilteredFlagsSummary = ( averageHealth: sum.total ? ((sum.active / (sum.total || 1)) * 100).toFixed(0) : '100', + technicalDebt: sum.total + ? ( + ((sum.stale + sum.potentiallyStale) / sum.total) * + 100 + ).toFixed(0) + : '0', medianTimeToProduction, }; }, [filteredProjectFlagTrends]); diff --git a/frontend/src/component/insights/sections/PerformanceInsights.tsx b/frontend/src/component/insights/sections/PerformanceInsights.tsx index cb03518bd5..847f5ed085 100644 --- a/frontend/src/component/insights/sections/PerformanceInsights.tsx +++ b/frontend/src/component/insights/sections/PerformanceInsights.tsx @@ -130,6 +130,7 @@ export const PerformanceInsights: FC = () => { = ({ project }) => { const healthToTechDebtEnabled = useFlag('healthToTechDebt'); - const techicalDebt = 100 - project.health; // TODO: health to technical debt from backend + const techicalDebt = project.technicalDebt; return ( diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx index 3cee2a65aa..5130f4ab6d 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx @@ -115,23 +115,25 @@ const Wrapper = styled(HealthGridTile)(({ theme }) => ({ export const ProjectHealth = () => { const projectId = useRequiredPathParam('projectId'); const { - data: { health, staleFlags }, + data: { health, technicalDebt, staleFlags }, } = useProjectStatus(projectId); const { isOss } = useUiConfig(); const theme = useTheme(); 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 filledLength = 1 - gapLength; const offset = 0.75 - gapLength / 2; 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 technicalDebtColor = useTechnicalDebtColor(technicalDebt); + const technicalDebtColor = useTechnicalDebtColor( + technicalDebt.current || 0, + ); return ( @@ -174,7 +176,9 @@ export const ProjectHealth = () => { fill={theme.palette.text.primary} fontSize={theme.typography.h1.fontSize} > - {healthToDebtEnabled ? technicalDebt : healthRating} + {healthToDebtEnabled + ? technicalDebt.current || 0 + : healthRating} % diff --git a/src/lib/features/productivity-report/productivity-report-view-model.test.ts b/src/lib/features/productivity-report/productivity-report-view-model.test.ts index ffb0be50c9..8d6c8ab60f 100644 --- a/src/lib/features/productivity-report/productivity-report-view-model.test.ts +++ b/src/lib/features/productivity-report/productivity-report-view-model.test.ts @@ -20,6 +20,18 @@ const mockMetrics = { }; describe('productivityReportViewModel', () => { + it('should show technical debt', () => { + const viewModel = productivityReportViewModel({ + ...mockData, + metrics: { + ...mockMetrics, + health: 85, + }, + }); + + expect(viewModel.technicalDebt).toBe('15'); + }); + describe('healthColor', () => { it('returns RED for health between 0 and 24', () => { const metrics: ProductivityReportMetrics = { @@ -65,7 +77,7 @@ describe('productivityReportViewModel', () => { }); describe('healthTrendMessage', () => { - it('returns correct trend message when health increased', () => { + it('returns correct trend message when technica debt decreased', () => { const metrics: ProductivityReportMetrics = { ...mockMetrics, 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 = { ...mockMetrics, 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 = { ...mockMetrics, health: 70, diff --git a/src/lib/features/productivity-report/productivity-report-view-model.ts b/src/lib/features/productivity-report/productivity-report-view-model.ts index e267f2db92..5b87f3985a 100644 --- a/src/lib/features/productivity-report/productivity-report-view-model.ts +++ b/src/lib/features/productivity-report/productivity-report-view-model.ts @@ -27,6 +27,7 @@ export const productivityReportViewModel = ({ userName, userEmail, ...metrics, + technicalDebt: Math.max(0, 100 - metrics.health).toString(), unleashUrl, healthColor() { const healthRating = this.health; diff --git a/src/lib/services/email-service.test.ts b/src/lib/services/email-service.test.ts index 8c485cb36d..e09aef1cab 100644 --- a/src/lib/services/email-service.test.ts +++ b/src/lib/services/email-service.test.ts @@ -141,14 +141,53 @@ test('Can send productivity report email', async () => { ); expect(content.from).toBe('noreply@getunleash.ai'); expect(content.subject).toBe('Unleash - productivity report'); - expect(content.html.includes('Productivity Report')).toBe(true); - expect(content.html.includes('localhost/insights')).toBe(true); - expect(content.html.includes('localhost/profile')).toBe(true); - expect(content.html.includes('#68a611')).toBe(true); - expect(content.html.includes('10% more than previous month')).toBe(true); - expect(content.text.includes('localhost/insights')).toBe(true); - expect(content.text.includes('localhost/profile')).toBe(true); - expect(content.text.includes('localhost/profile')).toBe(true); + expect(content.html).toContain('Productivity Report'); + expect(content.html).toContain('localhost/insights'); + expect(content.html).toContain('localhost/profile'); + expect(content.html).toContain('#68a611'); + expect(content.html).toContain('1%'); + expect(content.html).toContain('10% more than previous month'); + expect(content.text).toContain('localhost/insights'); + 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 () => { diff --git a/src/mailtemplates/productivity-report/productivity-report.html.mustache b/src/mailtemplates/productivity-report/productivity-report.html.mustache index 3182ddfdfb..806961f060 100644 --- a/src/mailtemplates/productivity-report/productivity-report.html.mustache +++ b/src/mailtemplates/productivity-report/productivity-report.html.mustache @@ -27,8 +27,8 @@
- {{health}}%
- your instance health
+ {{technicalDebt}}%
+ your instance technical debt
{{{healthTrendMessage}}}
{{actionText}}
diff --git a/src/mailtemplates/productivity-report/productivity-report.plain.mustache b/src/mailtemplates/productivity-report/productivity-report.plain.mustache index 967f66c52e..487ba11d8a 100644 --- a/src/mailtemplates/productivity-report/productivity-report.plain.mustache +++ b/src/mailtemplates/productivity-report/productivity-report.plain.mustache @@ -5,7 +5,7 @@ Hi {{userName}}, 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. -Your instance health: {{health}} +Your instance technical debt: {{technicalDebt}}% Flags created last month: {{flagsCreated}}