Skip to content

Commit 3751808

Browse files
committed
feat: add target recalculation functionality and UI controls for saving and resetting targets
Also, fixed time savings chart to use configured targets & max values
1 parent 200adfe commit 3751808

File tree

6 files changed

+77
-13
lines changed

6 files changed

+77
-13
lines changed

backend/src/services/target-calculation-service.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,8 @@ RESULT:
702702
const userTimeSavings = distinctUsers.map(userId => {
703703
const userSurveys = this.surveysWeekly.filter(survey => survey.userId === userId);
704704
const totalPercent = userSurveys.reduce((sum, survey) => {
705-
const percentTimeSaved = typeof survey.percentTimeSaved === 'number' ? survey.percentTimeSaved : 0;
705+
// Always parse percentTimeSaved as float
706+
const percentTimeSaved = survey.percentTimeSaved != null ? parseFloat(survey.percentTimeSaved as any) : 0;
706707
return sum + percentTimeSaved;
707708
}, 0);
708709
return totalPercent / userSurveys.length; // Average percent time saved per user
@@ -711,17 +712,17 @@ RESULT:
711712
// Average across all users
712713
const avgPercentTimeSaved = userTimeSavings.reduce((sum, percent) => sum + percent, 0) / userTimeSavings.length;
713714

714-
// Convert settings values to numbers
715-
const hoursPerYear = typeof this.settings.hoursPerYear === 'number' ? this.settings.hoursPerYear : 2000;
716-
const percentCoding = typeof this.settings.percentCoding === 'number' ? this.settings.percentCoding : 50;
715+
// Convert settings values to numbers (parse from string if needed)
716+
const hoursPerYear = this.settings.hoursPerYear != null ? parseFloat(this.settings.hoursPerYear as any) : 2000;
717+
const percentCoding = this.settings.percentCoding != null ? parseFloat(this.settings.percentCoding as any) : 50;
717718

718719
// Calculate weekly hours saved based on settings and average percent
719720
const weeklyHours = hoursPerYear / 50; // Assuming 50 working weeks
720721
const weeklyDevHours = weeklyHours * (percentCoding / 100);
721722
const avgWeeklyTimeSaved = weeklyDevHours * (avgPercentTimeSaved / 100);
722723

723724
// Calculate max based on settings
724-
const maxPercentTimeSaved = typeof this.settings.percentTimeSaved === 'number' ? this.settings.percentTimeSaved : 20;
725+
const maxPercentTimeSaved = this.settings.percentTimeSaved != null ? parseFloat(this.settings.percentTimeSaved as any) : 20;
725726
const maxWeeklyTimeSaved = weeklyDevHours * (maxPercentTimeSaved / 100);
726727

727728
const result = {
@@ -785,11 +786,11 @@ RESULT:
785786
const adoptedDevs = this.calculateAdoptedDevs().current;
786787
const weeklyTimeSavedHrs = this.calculateWeeklyTimeSavedHrs().current;
787788

788-
// Ensure all values are properly typed as numbers
789-
const hoursPerYear = typeof this.settings.hoursPerYear === 'number' ? this.settings.hoursPerYear : 2000;
789+
// Always parse settings values as numbers (from string if needed)
790+
const hoursPerYear = this.settings.hoursPerYear != null ? parseFloat(this.settings.hoursPerYear as any) : 2000;
790791
const weeksInYear = Math.round(hoursPerYear / 40) || 50; // Calculate weeks and ensure it's a number
791792

792-
const devCostPerYear = typeof this.settings.devCostPerYear === 'number' ? this.settings.devCostPerYear : 0;
793+
const devCostPerYear = this.settings.devCostPerYear != null ? parseFloat(this.settings.devCostPerYear as any) : 0;
793794
const hourlyRate = devCostPerYear > 0 ? (devCostPerYear / hoursPerYear) : 50;
794795

795796
const annualSavings = weeklyTimeSavedHrs * weeksInYear * hourlyRate * adoptedDevs;
@@ -824,8 +825,8 @@ RESULT:
824825
const adoptedDevs = this.calculateAdoptedDevs().current;
825826
const weeklyTimeSavedHrs = this.calculateWeeklyTimeSavedHrs().current;
826827

827-
// Convert hours per year to number
828-
const hoursPerYear = typeof this.settings.hoursPerYear === 'number' ? this.settings.hoursPerYear : 2000;
828+
// Always parse hours per year as number
829+
const hoursPerYear = this.settings.hoursPerYear != null ? parseFloat(this.settings.hoursPerYear as any) : 2000;
829830
const hoursPerWeek = hoursPerYear / 50 || 40; // Default to 40 if undefined
830831

831832
// Calculate productivity boost factor (not percentage)

backend/src/services/value-modeling-explanation.md

Whitespace-only changes.

frontend/src/app/main/copilot/copilot-value-modeling/copilot-value-modeling.component.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
<div class="page-container">
22
<div class="page-header">
33
<h1>Value Modeling & Targeting</h1>
4+
<button mat-stroked-button color="warn" style="margin-left: 2rem;" (click)="resetTargets()">
5+
<mat-icon>refresh</mat-icon>
6+
Recalculate Targets
7+
</button>
8+
<button *ngIf="showSaveAllButton" mat-stroked-button color="primary" style="margin-left: 1rem;" (click)="saveTargets()">
9+
<mat-icon>save</mat-icon>
10+
Save All Targets
11+
</button>
412
</div>
513

614
<h4 style="margin-top: 0">Org Metrics</h4>

frontend/src/app/main/copilot/copilot-value-modeling/copilot-value-modeling.component.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class CopilotValueModelingComponent implements OnInit {
3737
orgDataSource: TableTarget[] = [];
3838
userDataSource: TableTarget[] = [];
3939
impactDataSource: TableTarget[] = [];
40+
showSaveAllButton = false;
4041
private readonly _destroy$ = new Subject<void>();
4142
keyToNameMap: Record<string, string> = {
4243
seats: 'Seats',
@@ -128,7 +129,9 @@ export class CopilotValueModelingComponent implements OnInit {
128129

129130
saveTargets() {
130131
const targets: Targets = this.transformBackToTargets(this.orgDataSource, this.userDataSource, this.impactDataSource);
131-
this.targetsService.saveTargets(targets).subscribe();
132+
this.targetsService.saveTargets(targets).subscribe(() => {
133+
this.showSaveAllButton = false;
134+
});
132135
}
133136

134137
openEditDialog(target: Target) {
@@ -144,6 +147,17 @@ export class CopilotValueModelingComponent implements OnInit {
144147
}
145148
});
146149
}
150+
151+
resetTargets() {
152+
// Call the backend endpoint to recalculate targets
153+
this.targetsService.recalculateTargets().subscribe((result: any) => {
154+
const targets = result.targets || result; // handle both {targets, logs} and just targets
155+
this.orgDataSource = this.transformTargets(targets.org);
156+
this.userDataSource = this.transformTargets(targets.user);
157+
this.impactDataSource = this.transformTargets(targets.impact);
158+
this.showSaveAllButton = true;
159+
});
160+
}
147161
}
148162

149163
@Component({

frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class TimeSavedChartComponent implements OnInit, OnChanges {
2929
text: 'Time Saved (Hrs per Week)'
3030
},
3131
min: 0,
32-
max: 10,
32+
max: 10, // Will be updated dynamically
3333
labels: {
3434
format: '{value}hrs'
3535
},
@@ -45,7 +45,7 @@ export class TimeSavedChartComponent implements OnInit, OnChanges {
4545
}
4646
}],
4747
plotLines: [{
48-
value: 5,
48+
value: 5, // Will be updated dynamically
4949
color: 'var(--sys-primary)',
5050
dashStyle: 'Dash',
5151
width: 2,
@@ -91,6 +91,7 @@ export class TimeSavedChartComponent implements OnInit, OnChanges {
9191
this._chartOptions.yAxis = Object.assign({}, this.chartOptions?.yAxis, this._chartOptions.yAxis);
9292
this._chartOptions.tooltip = Object.assign({}, this.chartOptions?.tooltip, this._chartOptions.tooltip);
9393
this._chartOptions = Object.assign({}, this.chartOptions, this._chartOptions);
94+
this.updateYAxisFromTargets();
9495
}
9596

9697
ngOnChanges() {
@@ -101,6 +102,41 @@ export class TimeSavedChartComponent implements OnInit, OnChanges {
101102
};
102103
this.updateFlag = true;
103104
}
105+
this.updateYAxisFromTargets();
106+
}
107+
108+
private updateYAxisFromTargets() {
109+
if (this.targets?.user?.weeklyTimeSavedHrs) {
110+
const targetValue = this.targets.user.weeklyTimeSavedHrs.target;
111+
const maxValue = Math.max(
112+
targetValue * 1.5,
113+
this.targets.user.weeklyTimeSavedHrs.max || 10,
114+
10
115+
);
116+
const yAxis = {
117+
...this._chartOptions.yAxis,
118+
max: maxValue,
119+
plotLines: [{
120+
value: targetValue,
121+
color: 'var(--sys-primary)',
122+
dashStyle: 'Dash' as Highcharts.DashStyleValue,
123+
width: 2,
124+
label: {
125+
text: 'Target Level',
126+
align: 'left' as Highcharts.AlignValue,
127+
style: {
128+
color: 'var(--sys-primary)'
129+
}
130+
},
131+
zIndex: 2
132+
}]
133+
};
134+
this._chartOptions = {
135+
...this._chartOptions,
136+
yAxis
137+
};
138+
this.updateFlag = true;
139+
}
104140
}
105141

106142
}

frontend/src/app/services/api/targets.service.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,10 @@ export class TargetsService {
7777
saveTargets(targets: Targets) {
7878
return this.http.post<Targets>(`${this.apiUrl}`, targets);
7979
}
80+
81+
recalculateTargets() {
82+
// Calls the backend endpoint to recalculate targets
83+
return this.http.get<any>(`${this.apiUrl}/calculate`);
84+
}
8085
}
8186

0 commit comments

Comments
 (0)