Skip to content

Commit f69054b

Browse files
authored
Add component to list Serval draft jobs (#3368)
1 parent 6c9813e commit f69054b

29 files changed

+1193
-71
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/core/sf-project.service.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,19 @@ export class SFProjectService extends ProjectService<SFProject, SFProjectDoc> {
342342
): Promise<QueryResults<EventMetric> | undefined> {
343343
return await this.onlineInvoke<QueryResults<EventMetric>>('eventMetrics', { projectId, pageIndex, pageSize });
344344
}
345+
346+
async onlineAllEventMetrics(projectId?: string, daysBack?: number): Promise<QueryResults<EventMetric> | undefined> {
347+
const params: any = {
348+
projectId: projectId ?? null,
349+
scopes: [3] // Drafting scope
350+
};
351+
352+
if (daysBack != null) {
353+
const fromDate = new Date();
354+
fromDate.setDate(fromDate.getDate() - daysBack);
355+
params.fromDate = fromDate.toISOString();
356+
}
357+
358+
return await this.onlineInvoke<QueryResults<EventMetric>>('eventMetrics', params);
359+
}
345360
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
@if (isOnline) {
2+
@if (!isLoading) {
3+
<div class="filter-controls">
4+
<mat-form-field>
5+
<mat-label>Time Period</mat-label>
6+
<mat-select [(value)]="daysBack">
7+
@for (option of dayOptions; track option.value) {
8+
<mat-option [value]="option.value">{{ option.label }}</mat-option>
9+
}
10+
</mat-select>
11+
</mat-form-field>
12+
</div>
13+
14+
@if (currentProjectFilter) {
15+
<app-notice icon="filter_alt" mode="fill-dark">
16+
Showing draft jobs for project <strong>{{ filteredProjectName || currentProjectFilter }}</strong
17+
>.
18+
</app-notice>
19+
}
20+
21+
@if (rows.length > 0) {
22+
<div class="table-wrapper">
23+
<table mat-table id="draft-jobs-table" [dataSource]="rows">
24+
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
25+
<tr mat-row *matRowDef="let row; columns: columnsToDisplay"></tr>
26+
<ng-container matColumnDef="status">
27+
<th mat-header-cell *matHeaderCellDef>Status</th>
28+
<td mat-cell *matCellDef="let row">
29+
<div
30+
class="status-cell"
31+
matTooltip="Broken does not indicate failure, but that the sequence of events is not creating a coherent picture."
32+
[matTooltipDisabled]="row.job.status !== 'broken'"
33+
>
34+
<mat-icon
35+
class="status-icon"
36+
[class.successful]="row.job.status === 'success'"
37+
[class.failed]="row.job.status === 'failed'"
38+
[class.running]="row.job.status === 'running'"
39+
[class.cancelled]="row.job.status === 'cancelled'"
40+
[class.broken]="row.job.status === 'broken'"
41+
>
42+
@if (row.job.status === "success") {
43+
check_circle
44+
} @else if (row.job.status === "failed") {
45+
error
46+
} @else if (row.job.status === "running") {
47+
schedule
48+
} @else if (row.job.status === "cancelled") {
49+
cancel
50+
} @else if (row.job.status === "broken") {
51+
warning
52+
}
53+
</mat-icon>
54+
<span>{{ row.status }}</span>
55+
</div>
56+
</td>
57+
</ng-container>
58+
<ng-container matColumnDef="projectId">
59+
<th mat-header-cell *matHeaderCellDef>Project</th>
60+
<td mat-cell *matCellDef="let row">
61+
@if (!row.projectDeleted) {
62+
<a [routerLink]="['/serval-administration', row.projectId]">{{ row.projectName || "Unknown" }}</a>
63+
} @else {
64+
<div class="deleted-project">
65+
<mat-icon class="deleted-project-icon" matTooltip="Project has been deleted">delete</mat-icon>
66+
<span>{{ row.projectId }}</span>
67+
</div>
68+
}
69+
</td>
70+
</ng-container>
71+
<ng-container matColumnDef="trainingBooks">
72+
<th mat-header-cell *matHeaderCellDef>Training Books</th>
73+
<td mat-cell *matCellDef="let row">
74+
@if (row.trainingBooksTooltip) {
75+
<span [matTooltip]="row.trainingBooksTooltip">{{ row.trainingBooks }}</span>
76+
} @else {
77+
{{ row.trainingBooks }}
78+
}
79+
</td>
80+
</ng-container>
81+
<ng-container matColumnDef="translationBooks">
82+
<th mat-header-cell *matHeaderCellDef>Translation Books</th>
83+
<td mat-cell *matCellDef="let row">
84+
@if (row.translationBooksTooltip) {
85+
<span [matTooltip]="row.translationBooksTooltip">{{ row.translationBooks }}</span>
86+
} @else {
87+
{{ row.translationBooks }}
88+
}
89+
</td>
90+
</ng-container>
91+
<ng-container matColumnDef="startTime">
92+
<th mat-header-cell *matHeaderCellDef>Start Time</th>
93+
<td mat-cell *matCellDef="let row">
94+
{{ row.startTimeStamp }}
95+
</td>
96+
</ng-container>
97+
<ng-container matColumnDef="duration">
98+
<th mat-header-cell *matHeaderCellDef>Duration</th>
99+
<td mat-cell *matCellDef="let row">
100+
@if (row.job.status === "running") {
101+
<span class="running-text">In progress</span>
102+
} @else if (row.durationTooltip) {
103+
<span [matTooltip]="row.durationTooltip">{{ row.duration || "Not available" }}</span>
104+
} @else {
105+
{{ row.duration || "Not available" }}
106+
}
107+
</td>
108+
</ng-container>
109+
<ng-container matColumnDef="author">
110+
<th mat-header-cell *matHeaderCellDef>Author</th>
111+
<td mat-cell *matCellDef="let row">
112+
<app-owner
113+
[ownerRef]="row.userId"
114+
[includeAvatar]="true"
115+
[dateTime]="row.startTimeStamp"
116+
[showTimeZone]="true"
117+
></app-owner>
118+
</td>
119+
</ng-container>
120+
<ng-container matColumnDef="buildId">
121+
<th mat-header-cell *matHeaderCellDef>Build ID & ClearML link</th>
122+
<td mat-cell *matCellDef="let row">
123+
<a [href]="row.clearmlUrl" target="_blank" rel="noopener noreferrer" matTooltip="Open in ClearML">
124+
{{ row.job.buildId }}
125+
</a>
126+
</td>
127+
</ng-container>
128+
<ng-container matColumnDef="details">
129+
<th mat-header-cell *matHeaderCellDef>Events</th>
130+
<td mat-cell *matCellDef="let row">
131+
<button mat-stroked-button (click)="openDetailsDialog(row.job)" color="primary" class="details-button">
132+
<mat-icon class="mirror-rtl">list</mat-icon> Events
133+
</button>
134+
</td>
135+
</ng-container>
136+
</table>
137+
</div>
138+
} @else if (rows.length === 0) {
139+
<p>No draft jobs found for the selected time period.</p>
140+
}
141+
}
142+
} @else {
143+
<p>You are offline.</p>
144+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
@use 'src/variables' as sfColors;
2+
3+
.table-wrapper {
4+
overflow-x: auto;
5+
display: block;
6+
}
7+
8+
app-owner,
9+
.details-button {
10+
white-space: nowrap;
11+
}
12+
13+
.status-cell {
14+
display: flex;
15+
align-items: center;
16+
gap: 8px;
17+
}
18+
19+
.status-icon {
20+
font-size: 20px;
21+
width: 20px;
22+
height: 20px;
23+
}
24+
25+
.status-icon.successful {
26+
color: sfColors.$greenDark;
27+
}
28+
29+
.status-icon.failed {
30+
color: sfColors.$red;
31+
}
32+
33+
.status-icon.running {
34+
color: sfColors.$orange;
35+
}
36+
37+
.status-icon.cancelled {
38+
color: sfColors.$greyLight;
39+
}
40+
41+
.status-icon.broken {
42+
color: sfColors.$orange;
43+
}
44+
45+
.running-text {
46+
color: sfColors.$orange;
47+
font-style: italic;
48+
}
49+
50+
.deleted-project-icon {
51+
color: sfColors.$greyLight;
52+
font-size: 20px;
53+
width: 20px;
54+
height: 20px;
55+
}
56+
57+
.deleted-project {
58+
display: flex;
59+
align-items: center;
60+
gap: 8px;
61+
}
62+
63+
.filter-controls {
64+
display: flex;
65+
align-items: center;
66+
margin-bottom: 24px;
67+
padding-top: 8px; // Add top padding to prevent cutoff
68+
69+
mat-form-field {
70+
min-width: 150px;
71+
}
72+
}

0 commit comments

Comments
 (0)