Skip to content

Commit 07f7dce

Browse files
committed
feat: add loading indicator and pagination to hotspots
1 parent 3954986 commit 07f7dce

5 files changed

Lines changed: 102 additions & 65 deletions

File tree

frontend/src/app/hotspot/hotspot.component.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
padding: 10px
1212
}
1313

14+
.metric-cell {
15+
width: 100px;
16+
}
17+
1418
/* Anpassung für größere Bildschirme (z.B. Laptops) */
1519
@media (min-width: 1024px) {
1620
.aggregated {

frontend/src/app/hotspot/hotspot.component.html

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
1-
<!-- <div class="p10">
2-
<mat-form-field>
3-
<mat-label>Min. Hotspot Score</mat-label>
4-
<input matInput type="number" [formControl]="minScoreControl" min="0" step="1">
5-
</mat-form-field>
6-
</div> -->
7-
81
<div class="forensic-filter">
92
<div>
103
<mat-form-field class="min-score">
114
<mat-label>Min. Score</mat-label>
12-
<input matInput type="number" [formControl]="minScoreControl" min="0" step="1">
5+
<input
6+
matInput
7+
type="number"
8+
[formControl]="minScoreControl"
9+
min="0"
10+
step="1"
11+
/>
1312
</mat-form-field>
1413

1514
<mat-form-field appearance="fill" class="form-field metric">
1615
<mat-label>Complexity Metric</mat-label>
1716
<mat-select [(ngModel)]="metric">
1817
@for (option of metricOptions; track option.id) {
19-
<mat-option value="{{option.id}}">{{option.label}}</mat-option>
18+
<mat-option value="{{ option.id }}">{{ option.label }}</mat-option>
2019
}
2120
</mat-select>
2221
</mat-form-field>
23-
</div>
24-
22+
</div>
23+
2524
<app-limits [(limits)]="limits" [totalCommits]="totalCommits()"></app-limits>
2625
</div>
2726

2827
<div class="aggregated">
2928
<div class="p10">
29+
@if(loadingAggregated()) { Determining Hotspots ...
30+
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
31+
} @else {
3032
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
3133
<ng-container matColumnDef="module">
3234
<th mat-header-cell *matHeaderCellDef>Module</th>
@@ -46,40 +48,47 @@
4648
[class.selected]="selectedRow === row"
4749
></tr>
4850
</table>
51+
}
4952
</div>
5053
</div>
5154

52-
@if(hotspotResult?.hotspots.length > 0) {
5355
<div class="detail">
5456
<div class="p10">
55-
<table mat-table [dataSource]="detailDataSource" class="mat-elevation-z8">
56-
<ng-container matColumnDef="fileName">
57-
<th mat-header-cell *matHeaderCellDef>Module</th>
58-
<td mat-cell *matCellDef="let element">{{ element.fileName }}</td>
59-
</ng-container>
57+
@if (loadingHotspots()) { Determining Hotspots ...
58+
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
59+
} @else if(hotspotResult?.hotspots.length > 0) {
6060

61-
<ng-container matColumnDef="commits">
62-
<th mat-header-cell *matHeaderCellDef>Commits</th>
63-
<td mat-cell *matCellDef="let element">{{ element.commits }}</td>
64-
</ng-container>
61+
<div class="mat-elevation-z8">
62+
<table mat-table [dataSource]="detailDataSource">
63+
<ng-container matColumnDef="fileName">
64+
<th mat-header-cell *matHeaderCellDef>Module</th>
65+
<td mat-cell *matCellDef="let element">{{ element.fileName }}</td>
66+
</ng-container>
6567

66-
<ng-container matColumnDef="complexity">
67-
<th mat-header-cell *matHeaderCellDef>Complexity</th>
68-
<td mat-cell *matCellDef="let element">{{ element.complexity }}</td>
69-
</ng-container>
68+
<ng-container matColumnDef="commits">
69+
<th mat-header-cell *matHeaderCellDef class="metric-cell">Commits</th>
70+
<td mat-cell *matCellDef="let element" class="metric-cell">{{ element.commits }}</td>
71+
</ng-container>
7072

71-
<ng-container matColumnDef="score">
72-
<th mat-header-cell *matHeaderCellDef>Score</th>
73-
<td mat-cell *matCellDef="let element">{{ element.score }}</td>
74-
</ng-container>
73+
<ng-container matColumnDef="complexity">
74+
<th mat-header-cell *matHeaderCellDef class="metric-cell">Complexity</th>
75+
<td mat-cell *matCellDef="let element" class="metric-cell">{{ element.complexity }}</td>
76+
</ng-container>
7577

78+
<ng-container matColumnDef="score">
79+
<th mat-header-cell *matHeaderCellDef class="metric-cell">Score</th>
80+
<td mat-cell *matCellDef="let element" class="metric-cell">{{ element.score }}</td>
81+
</ng-container>
7682

77-
<tr mat-header-row *matHeaderRowDef="detailColumns"></tr>
78-
<tr
79-
mat-row
80-
*matRowDef="let row; columns: detailColumns"
81-
></tr>
82-
</table>
83+
<tr mat-header-row *matHeaderRowDef="detailColumns"></tr>
84+
<tr mat-row *matRowDef="let row; columns: detailColumns"></tr>
85+
</table>
86+
<mat-paginator
87+
[pageSizeOptions]="[5, 10, 15, 20, 25]"
88+
[pageSize]="10"
89+
showFirstLastButtons
90+
></mat-paginator>
91+
</div>
92+
}
8393
</div>
8494
</div>
85-
}

frontend/src/app/hotspot/hotspot.component.ts

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, inject, signal } from '@angular/core';
1+
import { Component, effect, inject, signal, viewChild } from '@angular/core';
22
import { HotspotService } from './hotspot.service';
33
import {
44
AggregatedHotspot,
@@ -18,11 +18,12 @@ import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
1818
import { combineLatest, debounceTime, startWith } from 'rxjs';
1919
import { EventService } from '../event.service';
2020
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
21-
import { LimitsComponent } from "../ui/limits/limits.component";
21+
import { LimitsComponent } from '../ui/limits/limits.component';
2222
import { initLimits } from '../model/limits';
2323
import { MatSelectModule } from '@angular/material/select';
24-
import { MatCheckboxModule } from '@angular/material/checkbox';
2524
import { StatusStore } from '../data/status.store';
25+
import { MatProgressBarModule } from '@angular/material/progress-bar';
26+
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
2627

2728
type Option = {
2829
id: ComplexityMetric;
@@ -38,10 +39,12 @@ type Option = {
3839
MatFormFieldModule,
3940
MatInputModule,
4041
MatSelectModule,
42+
MatProgressBarModule,
43+
MatPaginatorModule,
4144
ReactiveFormsModule,
4245
LimitsComponent,
4346
FormsModule,
44-
],
47+
],
4548
templateUrl: './hotspot.component.html',
4649
styleUrl: './hotspot.component.css',
4750
})
@@ -69,24 +72,36 @@ export class HotspotComponent {
6972
metric = signal<ComplexityMetric>('Length');
7073

7174
metricOptions: Option[] = [
72-
{ id: 'Length', label: 'File Length'},
73-
{ id: 'McCabe', label: 'Cyclomatic Complexity'},
75+
{ id: 'Length', label: 'File Length' },
76+
{ id: 'McCabe', label: 'Cyclomatic Complexity' },
7477
];
7578

79+
loadingAggregated = signal(false);
80+
loadingHotspots = signal(false);
81+
82+
paginator = viewChild(MatPaginator);
83+
7684
constructor() {
85+
effect(() => {
86+
this.detailDataSource.paginator = this.paginator();
87+
});
88+
7789
combineLatest([
7890
this.eventService.filterChanged.pipe(startWith(null)),
79-
this.minScoreControl.valueChanges.pipe(startWith(this.minScoreControl.value), debounceTime(300)),
91+
this.minScoreControl.valueChanges.pipe(
92+
startWith(this.minScoreControl.value),
93+
debounceTime(300)
94+
),
8095
toObservable(this.limits),
8196
toObservable(this.metric),
8297
])
83-
.pipe(takeUntilDestroyed())
84-
.subscribe(() => {
85-
this.loadAggregated();
86-
if (this.selectedModule) {
87-
this.loadHotspots();
88-
}
89-
});
98+
.pipe(takeUntilDestroyed())
99+
.subscribe(() => {
100+
this.loadAggregated();
101+
if (this.selectedModule) {
102+
this.loadHotspots();
103+
}
104+
});
90105
}
91106

92107
selectRow(row: AggregatedHotspot, index: number) {
@@ -114,34 +129,44 @@ export class HotspotComponent {
114129
const criteria: HotspotCriteria = {
115130
metric: this.metric(),
116131
minScore: this.minScoreControl.value,
117-
module: ''
132+
module: '',
118133
};
119134

120-
this.hotspotService
121-
.loadAggregated(criteria, this.limits())
122-
.subscribe((aggregatedResult) => {
135+
this.loadingAggregated.set(true);
136+
137+
this.hotspotService.loadAggregated(criteria, this.limits()).subscribe({
138+
next: (aggregatedResult) => {
123139
this.aggregatedResult = aggregatedResult;
124140
this.dataSource.data = this.formatAggregated(
125141
aggregatedResult.aggregated
126142
);
127-
});
143+
},
144+
complete: () => {
145+
this.loadingAggregated.set(false);
146+
},
147+
});
128148
}
129149

130150
private loadHotspots() {
131151
const criteria: HotspotCriteria = {
132152
metric: this.metric(),
133153
minScore: this.minScoreControl.value,
134-
module: this.selectedModule
154+
module: this.selectedModule,
135155
};
136156

137-
this.hotspotService
138-
.load(criteria, this.limits())
139-
.subscribe((hotspotResult) => {
157+
this.loadingHotspots.set(true);
158+
159+
this.hotspotService.load(criteria, this.limits()).subscribe({
160+
next: (hotspotResult) => {
140161
this.hotspotResult = hotspotResult;
141162
this.detailDataSource.data = this.formatHotspots(
142163
hotspotResult.hotspots
143164
);
144-
});
165+
},
166+
complete: () => {
167+
this.loadingHotspots.set(false);
168+
},
169+
});
145170
}
146171
}
147172

frontend/src/app/ui/limits/limits.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
@if (selected() === 'COMMITS') {
1111
<mat-form-field appearance="outline" class="form-field commits">
12-
<mat-label>Commits</mat-label>
12+
<mat-label>Max. Commits</mat-label>
1313
<input [matTooltip]="commitToolTip()" [ngModel]="limits().limitCommits" (ngModelChange)="update($event, 0)" matInput type="number" min="1">
1414
</mat-form-field>
1515
}
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { Component } from '@angular/core';
2-
import {MatProgressBarModule} from '@angular/material/progress-bar';
2+
import { MatProgressBarModule } from '@angular/material/progress-bar';
33

44
@Component({
55
selector: 'app-loading',
66
standalone: true,
77
imports: [MatProgressBarModule],
88
templateUrl: './loading.component.html',
9-
styleUrl: './loading.component.css'
9+
styleUrl: './loading.component.css',
1010
})
11-
export class LoadingComponent {
12-
}
11+
export class LoadingComponent {}

0 commit comments

Comments
 (0)