mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
fix: health-technical debt trends in emails (#10308)
This commit is contained in:
parent
cb4beb71ac
commit
212c538a75
@ -33,7 +33,7 @@ describe('productivityReportViewModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('healthColor', () => {
|
describe('healthColor', () => {
|
||||||
it('returns RED for health between 0 and 24', () => {
|
it('returns RED for technical debt between 75 and 100', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 20,
|
health: 20,
|
||||||
@ -44,10 +44,10 @@ describe('productivityReportViewModel', () => {
|
|||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthColor()).toBe('#d93644');
|
expect(viewModel.technicalDebtColor()).toBe('#d93644');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns ORANGE for health between 25 and 74', () => {
|
it('returns ORANGE for technical debt between 25 and 74', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 50,
|
health: 50,
|
||||||
@ -58,10 +58,10 @@ describe('productivityReportViewModel', () => {
|
|||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthColor()).toBe('#d76500');
|
expect(viewModel.technicalDebtColor()).toBe('#d76500');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns GREEN for health 75 or above', () => {
|
it('returns GREEN for technical debt below 25', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 80,
|
health: 80,
|
||||||
@ -72,25 +72,24 @@ describe('productivityReportViewModel', () => {
|
|||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthColor()).toBe('#68a611');
|
expect(viewModel.technicalDebtColor()).toBe('#68a611');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('healthTrendMessage', () => {
|
describe('technicalDebtTrendMessage', () => {
|
||||||
it('returns correct trend message when technica debt decreased', () => {
|
it('returns correct trend message when technica debt decreased', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
...mockMetrics,
|
...mockMetrics,
|
||||||
health: 80,
|
health: 80,
|
||||||
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthTrendMessage()).toBe(
|
expect(viewModel.technicalDebtTrendMessage()).toMatchInlineSnapshot(
|
||||||
"<span style='color: #68a611'>▲</span> 10% more than previous month",
|
`"<span style='color: #68a611'>▼</span> 10% less than previous month"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,14 +99,13 @@ describe('productivityReportViewModel', () => {
|
|||||||
health: 60,
|
health: 60,
|
||||||
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthTrendMessage()).toBe(
|
expect(viewModel.technicalDebtTrendMessage()).toMatchInlineSnapshot(
|
||||||
"<span style='color: #d93644'>▼</span> 10% less than previous month",
|
`"<span style='color: #d93644'>▲</span> 10% more than previous month"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -117,13 +115,14 @@ describe('productivityReportViewModel', () => {
|
|||||||
health: 70,
|
health: 70,
|
||||||
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
previousMonth: { ...mockMetrics.previousMonth, health: 70 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthTrendMessage()).toBe('Same as last month');
|
expect(viewModel.technicalDebtTrendMessage()).toMatchInlineSnapshot(
|
||||||
|
`"Same as last month"`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,18 +136,15 @@ describe('productivityReportViewModel', () => {
|
|||||||
flagsCreated: 8,
|
flagsCreated: 8,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
expect(viewModel.flagsCreatedTrendMessage()).toMatchInlineSnapshot(
|
||||||
expect(viewModel.flagsCreatedTrendMessage()).toBe(
|
`"<span style='color: #68a611'>▲</span> 2 more than previous month"`,
|
||||||
"<span style='color: #68a611'>▲</span> 2 more than previous month",
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('productionUpdatedTrendMessage', () => {
|
describe('productionUpdatedTrendMessage', () => {
|
||||||
it('returns correct trend message for productionUpdates decrease', () => {
|
it('returns correct trend message for productionUpdates decrease', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
@ -159,18 +155,17 @@ describe('productivityReportViewModel', () => {
|
|||||||
productionUpdates: 8,
|
productionUpdates: 8,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
expect(
|
||||||
expect(viewModel.productionUpdatedTrendMessage()).toBe(
|
viewModel.productionUpdatedTrendMessage(),
|
||||||
"<span style='color: #d93644'>▼</span> 3 less than previous month",
|
).toMatchInlineSnapshot(
|
||||||
|
`"<span style='color: #d93644'>▼</span> 3 less than previous month"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Missing previous month data', () => {
|
describe('Missing previous month data', () => {
|
||||||
it('returns no trends messages', () => {
|
it('returns no trends messages', () => {
|
||||||
const metrics: ProductivityReportMetrics = {
|
const metrics: ProductivityReportMetrics = {
|
||||||
@ -179,13 +174,12 @@ describe('productivityReportViewModel', () => {
|
|||||||
productionUpdates: 5,
|
productionUpdates: 5,
|
||||||
previousMonth: null,
|
previousMonth: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewModel = productivityReportViewModel({
|
const viewModel = productivityReportViewModel({
|
||||||
...mockData,
|
...mockData,
|
||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.healthTrendMessage()).toBe(null);
|
expect(viewModel.technicalDebtTrendMessage()).toBe(null);
|
||||||
expect(viewModel.flagsCreatedTrendMessage()).toBe(null);
|
expect(viewModel.flagsCreatedTrendMessage()).toBe(null);
|
||||||
expect(viewModel.productionUpdatedTrendMessage()).toBe(null);
|
expect(viewModel.productionUpdatedTrendMessage()).toBe(null);
|
||||||
});
|
});
|
||||||
@ -225,8 +219,8 @@ describe('productivityReportViewModel', () => {
|
|||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.actionText()).toBe(
|
expect(viewModel.actionText()).toMatchInlineSnapshot(
|
||||||
'Remember to archive stale flags to reduce technical debt and keep your project healthy',
|
`"Remember to archive stale flags to reduce technical debt and keep your project healthy"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -245,8 +239,8 @@ describe('productivityReportViewModel', () => {
|
|||||||
metrics,
|
metrics,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(viewModel.actionText()).toBe(
|
expect(viewModel.actionText()).toMatchInlineSnapshot(
|
||||||
'Remember to archive stale flags to reduce technical debt and keep your project healthy',
|
`"Remember to archive stale flags to reduce technical debt and keep your project healthy"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ const RED = '#d93644';
|
|||||||
const GREEN = '#68a611';
|
const GREEN = '#68a611';
|
||||||
const ORANGE = '#d76500';
|
const ORANGE = '#d76500';
|
||||||
|
|
||||||
|
const ARROW_UP = '▲';
|
||||||
|
const ARROW_DOWN = '▼';
|
||||||
|
|
||||||
export const productivityReportViewModel = ({
|
export const productivityReportViewModel = ({
|
||||||
unleashUrl,
|
unleashUrl,
|
||||||
userEmail,
|
userEmail,
|
||||||
@ -23,69 +26,82 @@ export const productivityReportViewModel = ({
|
|||||||
userEmail: string;
|
userEmail: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
metrics: ProductivityReportMetrics;
|
metrics: ProductivityReportMetrics;
|
||||||
}) => ({
|
}) => {
|
||||||
userName,
|
const { health, previousMonth, flagsCreated, productionUpdates } = metrics;
|
||||||
userEmail,
|
const technicalDebt = Math.max(0, 100 - health) || 0;
|
||||||
...metrics,
|
|
||||||
technicalDebt: Math.max(0, 100 - metrics.health).toString(),
|
return {
|
||||||
unleashUrl,
|
userName,
|
||||||
healthColor() {
|
userEmail,
|
||||||
const healthRating = this.health;
|
flagsCreated,
|
||||||
const healthColor =
|
productionUpdates,
|
||||||
healthRating >= 0 && healthRating <= 24
|
previousMonth,
|
||||||
? RED
|
health,
|
||||||
: healthRating >= 25 && healthRating <= 74
|
technicalDebt: technicalDebt.toString(),
|
||||||
? ORANGE
|
unleashUrl,
|
||||||
: GREEN;
|
technicalDebtColor() {
|
||||||
return healthColor;
|
if (technicalDebt < 25) {
|
||||||
},
|
return GREEN;
|
||||||
actionText(): string | null {
|
}
|
||||||
const improveMessage =
|
if (technicalDebt < 75) {
|
||||||
'Remember to archive stale flags to reduce technical debt and keep your project healthy';
|
return ORANGE;
|
||||||
const previousHealth = this.previousMonth?.health || 0;
|
}
|
||||||
if (this.health <= 74) {
|
return RED;
|
||||||
return improveMessage;
|
},
|
||||||
}
|
actionText(): string | null {
|
||||||
if (this.health < previousHealth) {
|
const improveMessage =
|
||||||
return improveMessage;
|
'Remember to archive stale flags to reduce technical debt and keep your project healthy';
|
||||||
}
|
const previousHealth = previousMonth?.health || 0;
|
||||||
return null;
|
if (health <= 74) {
|
||||||
},
|
return improveMessage;
|
||||||
healthTrendMessage() {
|
}
|
||||||
return this.previousMonthText(
|
if (health < previousHealth) {
|
||||||
'%',
|
return improveMessage;
|
||||||
this.health,
|
}
|
||||||
this.previousMonth?.health,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
flagsCreatedTrendMessage() {
|
|
||||||
return this.previousMonthText(
|
|
||||||
'',
|
|
||||||
this.flagsCreated,
|
|
||||||
this.previousMonth?.flagsCreated,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
productionUpdatedTrendMessage() {
|
|
||||||
return this.previousMonthText(
|
|
||||||
'',
|
|
||||||
this.productionUpdates,
|
|
||||||
this.previousMonth?.productionUpdates,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
previousMonthText(
|
|
||||||
unit: '' | '%',
|
|
||||||
currentValue: number,
|
|
||||||
previousValue?: number,
|
|
||||||
) {
|
|
||||||
if (previousValue == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
if (currentValue > previousValue) {
|
technicalDebtTrendMessage() {
|
||||||
return `<span style='color: ${GREEN}'>▲</span> ${currentValue - previousValue}${unit} more than previous month`;
|
if (!previousMonth || Number.isNaN(previousMonth.health)) {
|
||||||
}
|
return null;
|
||||||
if (previousValue > currentValue) {
|
}
|
||||||
return `<span style='color: ${RED}'>▼</span> ${previousValue - currentValue}${unit} less than previous month`;
|
const previousTechnicalDebt = Math.max(
|
||||||
}
|
0,
|
||||||
return `Same as last month`;
|
100 - previousMonth.health,
|
||||||
},
|
);
|
||||||
});
|
|
||||||
|
if (technicalDebt > previousTechnicalDebt) {
|
||||||
|
return `<span style='color: ${RED}'>${ARROW_UP}</span> ${technicalDebt - previousTechnicalDebt}% more than previous month`;
|
||||||
|
}
|
||||||
|
if (previousTechnicalDebt > technicalDebt) {
|
||||||
|
return `<span style='color: ${GREEN}'>${ARROW_DOWN}</span> ${previousTechnicalDebt - technicalDebt}% less than previous month`;
|
||||||
|
}
|
||||||
|
return 'Same as last month';
|
||||||
|
},
|
||||||
|
flagsCreatedTrendMessage() {
|
||||||
|
if (!previousMonth) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagsCreated > previousMonth.flagsCreated) {
|
||||||
|
return `<span style='color: ${GREEN}'>${ARROW_UP}</span> ${flagsCreated - previousMonth.flagsCreated} more than previous month`;
|
||||||
|
}
|
||||||
|
if (previousMonth.flagsCreated > flagsCreated) {
|
||||||
|
return `<span style='color: ${RED}'>${ARROW_DOWN}</span> ${previousMonth.flagsCreated - flagsCreated} less than previous month`;
|
||||||
|
}
|
||||||
|
return 'Same as last month';
|
||||||
|
},
|
||||||
|
productionUpdatedTrendMessage() {
|
||||||
|
if (!previousMonth) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productionUpdates > previousMonth.productionUpdates) {
|
||||||
|
return `<span style='color: ${GREEN}'>${ARROW_UP}</span> ${productionUpdates - previousMonth.productionUpdates} more than previous month`;
|
||||||
|
}
|
||||||
|
if (previousMonth.productionUpdates > productionUpdates) {
|
||||||
|
return `<span style='color: ${RED}'>${ARROW_DOWN}</span> ${previousMonth.productionUpdates - productionUpdates} less than previous month`;
|
||||||
|
}
|
||||||
|
return 'Same as last month';
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -146,7 +146,7 @@ test('Can send productivity report email', async () => {
|
|||||||
expect(content.html).toContain('localhost/profile');
|
expect(content.html).toContain('localhost/profile');
|
||||||
expect(content.html).toContain('#68a611');
|
expect(content.html).toContain('#68a611');
|
||||||
expect(content.html).toContain('1%');
|
expect(content.html).toContain('1%');
|
||||||
expect(content.html).toContain('10% more than previous month');
|
expect(content.html).toContain('10% less than previous month');
|
||||||
expect(content.text).toContain('localhost/insights');
|
expect(content.text).toContain('localhost/insights');
|
||||||
expect(content.text).toContain('localhost/profile');
|
expect(content.text).toContain('localhost/profile');
|
||||||
expect(content.text).toContain('localhost/profile');
|
expect(content.text).toContain('localhost/profile');
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
<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}};">{{technicalDebt}}%</span><br>
|
<span style="color: {{technicalDebtColor}};">{{technicalDebt}}%</span><br>
|
||||||
<span style="font-size: 16px; color: #1A4049; font-weight: 700">your instance technical debt</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">{{{technicalDebtTrendMessage}}}</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>
|
||||||
</div>
|
</div>
|
||||||
@ -75,8 +75,8 @@
|
|||||||
style="color: #1a4049;margin: 0 10px;text-decoration: none;">getunleash.io</a> |
|
style="color: #1a4049;margin: 0 10px;text-decoration: none;">getunleash.io</a> |
|
||||||
<a href="https://github.com/unleash"
|
<a href="https://github.com/unleash"
|
||||||
style="color: #1a4049;margin: 0 10px;text-decoration: none;">GitHub</a> |
|
style="color: #1a4049;margin: 0 10px;text-decoration: none;">GitHub</a> |
|
||||||
<a href="https://twitter.com/unleash"
|
<a href="https://www.linkedin.com/company/getunleash/"
|
||||||
style="color: #1a4049;margin: 0 10px;text-decoration: none;">Twitter</a>
|
style="color: #1a4049;margin: 0 10px;text-decoration: none;">LinkedIn</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="unsubscribe" style="font-size: 12px;color: #888;margin-top: 10px;">
|
<div class="unsubscribe" style="font-size: 12px;color: #888;margin-top: 10px;">
|
||||||
This email was sent to {{userEmail}}. You’ve received this as you are a user of Unleash.<br>
|
This email was sent to {{userEmail}}. You’ve received this as you are a user of Unleash.<br>
|
||||||
|
Loading…
Reference in New Issue
Block a user