Skip to content

Commit 782c459

Browse files
committed
Show Serval administration projects and draft jobs in narrow viewport
1 parent 99001d5 commit 782c459

11 files changed

+495
-71
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/event-metrics/event-metrics.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<app-mobile-not-supported></app-mobile-not-supported>
12
<ng-container *transloco="let t; read: 'event_metrics'">
23
<h1>{{ t("event_log") }}</h1>
34
<app-event-metrics-log></app-event-metrics-log>
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Component } from '@angular/core';
22
import { TranslocoModule } from '@ngneat/transloco';
3+
import { MobileNotSupportedComponent } from '../shared/mobile-not-supported/mobile-not-supported.component';
34
import { EventMetricsLogComponent } from './event-metrics-log.component';
45

56
@Component({
67
selector: 'app-event-metrics',
78
templateUrl: './event-metrics.component.html',
89
styleUrls: ['./event-metrics.component.scss'],
9-
imports: [EventMetricsLogComponent, TranslocoModule]
10+
imports: [EventMetricsLogComponent, TranslocoModule, MobileNotSupportedComponent]
1011
})
1112
export class EventMetricsComponent {}

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/_draft-jobs-theme.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
--sf-draft-jobs-project-link-color: #{mat.get-theme-color($theme, primary, if($is-dark, 70, 40))};
88
--sf-draft-jobs-book-count-color: #{mat.get-theme-color($theme, neutral, if($is-dark, 70, 40))};
99
--sf-draft-jobs-disabled-link-color: #{mat.get-theme-color($theme, neutral, if($is-dark, 50, 50))};
10+
--sf-serval-project-card-background: #{mat.get-theme-color($theme, neutral, if($is-dark, 30, 90))};
1011
}
1112

1213
@mixin theme($theme) {

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
</app-notice>
2525
}
2626

27-
<div class="table-wrapper">
27+
<!-- Show table when viewed on a larger screen. -->
28+
<div class="table-wrapper table-view">
2829
<table mat-table id="draft-jobs-table" [dataSource]="rows">
2930
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
3031
<tr mat-row *matRowDef="let row; columns: columnsToDisplay"></tr>
@@ -158,6 +159,131 @@
158159
</table>
159160
</div>
160161

162+
<!-- Narrower view with cards. -->
163+
<div class="card-list card-view">
164+
@for (row of rows; track row.job.buildId) {
165+
<div
166+
class="draft-job-card"
167+
[class.status-running]="row.job.status === 'running'"
168+
[class.status-success]="row.job.status === 'success'"
169+
[class.status-failed]="row.job.status === 'failed'"
170+
[class.status-cancelled]="row.job.status === 'cancelled'"
171+
[class.status-incomplete]="row.job.status === 'incomplete'"
172+
>
173+
<div class="card-header">
174+
@if (!row.projectDeleted) {
175+
<a [routerLink]="['/serval-administration', row.projectId]" class="card-project-name">{{
176+
row.projectName || "Unknown"
177+
}}</a>
178+
} @else {
179+
<div class="deleted-project">
180+
<mat-icon class="deleted-project-icon" matTooltip="Project has been deleted">delete</mat-icon>
181+
<span>{{ row.projectId }}</span>
182+
</div>
183+
}
184+
</div>
185+
<div class="card-body">
186+
<div class="card-author">
187+
<app-owner
188+
[ownerRef]="row.userId"
189+
[includeAvatar]="true"
190+
[dateTime]="row.startTimeStamp"
191+
[showTimeZone]="true"
192+
></app-owner>
193+
</div>
194+
<div class="card-build-id">Serval build id {{ row.job.buildId }}</div>
195+
<div class="card-duration-time">
196+
@if (row.job.status === "running") {
197+
<span class="running-text">In progress (from {{ row.startTimeStamp }})</span>
198+
} @else if (row.durationTooltip) {
199+
<span [matTooltip]="row.durationTooltip"
200+
>{{ row.duration || "Not available" }} (from {{ row.startTimeStamp }})</span
201+
>
202+
} @else {
203+
{{ row.duration || "Not available" }} (from {{ row.startTimeStamp }})
204+
}
205+
</div>
206+
<div class="card-books-section">
207+
<strong>Training:</strong>
208+
@for (projectBook of row.trainingBooks; track projectBook.projectId) {
209+
<span class="card-books-line">
210+
<a [routerLink]="['/serval-administration', projectBook.projectId]" class="project-link">{{
211+
getProjectShortName(projectBook.projectId)
212+
}}</a
213+
>:
214+
@if (projectBook.books.length <= 2) {
215+
{{ projectBook.books.join(", ") }}
216+
} @else {
217+
<span [matTooltip]="projectBook.books.join(', ')" class="book-count"
218+
>{{ projectBook.books.length }} books</span
219+
>
220+
}
221+
</span>
222+
} @empty {
223+
None
224+
}
225+
<br />
226+
<strong>Translation:</strong>
227+
@for (projectBook of row.translationBooks; track projectBook.projectId) {
228+
<span class="card-books-line">
229+
<a [routerLink]="['/serval-administration', projectBook.projectId]" class="project-link">{{
230+
getProjectShortName(projectBook.projectId)
231+
}}</a
232+
>:
233+
@if (projectBook.books.length <= 2) {
234+
{{ projectBook.books.join(", ") }}
235+
} @else {
236+
<span [matTooltip]="projectBook.books.join(', ')" class="book-count"
237+
>{{ projectBook.books.length }} books</span
238+
>
239+
}
240+
</span>
241+
} @empty {
242+
None
243+
}
244+
</div>
245+
</div>
246+
<div class="card-footer">
247+
<div class="card-status">
248+
<mat-icon
249+
class="status-icon"
250+
[class.successful]="row.job.status === 'success'"
251+
[class.failed]="row.job.status === 'failed'"
252+
[class.running]="row.job.status === 'running'"
253+
[class.cancelled]="row.job.status === 'cancelled'"
254+
[class.incomplete]="row.job.status === 'incomplete'"
255+
>
256+
@if (row.job.status === "success") {
257+
check_circle
258+
} @else if (row.job.status === "failed") {
259+
error
260+
} @else if (row.job.status === "running") {
261+
schedule
262+
} @else if (row.job.status === "cancelled") {
263+
cancel
264+
} @else if (row.job.status === "incomplete") {
265+
warning
266+
}
267+
</mat-icon>
268+
<span>{{ row.status }}</span>
269+
</div>
270+
<div class="card-actions">
271+
<button
272+
mat-stroked-button
273+
(click)="openJobDetailsDialog(row.job)"
274+
color="primary"
275+
class="details-button"
276+
[class.disabled]="!row.job.buildId"
277+
matTooltip="View complete job details including build information and events"
278+
>
279+
<mat-icon class="mirror-rtl">list</mat-icon> Events
280+
</button>
281+
</div>
282+
</div>
283+
</div>
284+
}
285+
</div>
286+
161287
@if (rows.length === 0) {
162288
<p>No draft jobs found for the selected time period.</p>
163289
}

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.scss

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,137 @@
11
@use 'src/variables' as sfColors;
2+
@use 'src/xforge-common/media-breakpoints/breakpoints' as *;
23

34
.table-wrapper {
45
overflow-x: auto;
56
display: block;
67
}
78

9+
// Show table on wider viewport, hide cards
10+
.table-view {
11+
display: block;
12+
}
13+
14+
.card-view {
15+
display: none;
16+
}
17+
18+
// Show cards on narrower viewport, hide table
19+
@include media-breakpoint('<', xxl) {
20+
.table-view {
21+
display: none;
22+
}
23+
24+
.card-view {
25+
display: block;
26+
}
27+
}
28+
829
#draft-jobs-table tr:nth-child(odd) {
930
background-color: var(--sf-draft-jobs-table-row-background);
1031
}
1132

33+
// Card layout styles for narrower viewport
34+
.draft-job-card {
35+
margin-bottom: 16px;
36+
border: none;
37+
border-radius: 8px;
38+
position: relative;
39+
40+
&.status-running {
41+
background-color: rgba(255, 152, 0, 0.16);
42+
}
43+
44+
&.status-success {
45+
background-color: rgba(76, 175, 80, 0.16);
46+
}
47+
48+
&.status-failed {
49+
background-color: rgba(244, 67, 54, 0.16);
50+
}
51+
52+
&.status-cancelled {
53+
background-color: rgba(158, 158, 158, 0.16);
54+
}
55+
56+
&.status-incomplete {
57+
background-color: rgba(158, 158, 158, 0.16);
58+
}
59+
60+
.card-header {
61+
padding: 16px 16px 8px 16px;
62+
63+
.card-project-name {
64+
font-size: 1.1rem;
65+
font-weight: 600;
66+
color: var(--sf-draft-jobs-project-link-color);
67+
text-decoration: none;
68+
69+
&:hover {
70+
text-decoration: underline;
71+
}
72+
}
73+
}
74+
75+
.card-body {
76+
padding: 12px 16px;
77+
display: flex;
78+
flex-direction: column;
79+
gap: 8px;
80+
81+
.card-author {
82+
font-weight: 500;
83+
}
84+
85+
.card-build-id {
86+
color: var(--mdc-theme-text-secondary-on-background);
87+
}
88+
89+
.card-duration-time {
90+
color: var(--mdc-theme-text-secondary-on-background);
91+
}
92+
93+
.card-books-section {
94+
margin-top: 4px;
95+
line-height: 1.6;
96+
97+
strong {
98+
margin-right: 4px;
99+
}
100+
101+
.card-books-line {
102+
display: inline;
103+
}
104+
}
105+
}
106+
107+
.card-footer {
108+
padding: 12px 16px;
109+
display: flex;
110+
justify-content: space-between;
111+
align-items: center;
112+
113+
.card-status {
114+
display: flex;
115+
align-items: center;
116+
gap: 8px;
117+
font-weight: 500;
118+
}
119+
120+
.card-actions {
121+
display: flex;
122+
align-items: center;
123+
gap: 12px;
124+
125+
.details-button {
126+
min-width: auto;
127+
padding: 0 12px;
128+
height: 36px;
129+
line-height: 36px;
130+
}
131+
}
132+
}
133+
}
134+
12135
app-owner {
13136
white-space: nowrap;
14137
}
@@ -103,6 +226,10 @@ app-owner {
103226
color: var(--sf-draft-jobs-book-count-color);
104227
}
105228

229+
app-notice {
230+
margin-bottom: 24px;
231+
}
232+
106233
.filter-notice-content {
107234
display: flex;
108235
justify-content: space-between;

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-administration.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<app-mobile-not-supported></app-mobile-not-supported>
21
<div class="body-content">
32
<h1>Serval Administration</h1>
43

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-project.component.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<app-mobile-not-supported></app-mobile-not-supported>
2-
31
@if (showProjectTitle) {
42
<h1>{{ projectName }}</h1>
53
}
@@ -99,9 +97,10 @@ <h2>Downloads</h2>
9997
color="primary"
10098
(click)="downloadProject(row['id'], row['fileName'])"
10199
[disabled]="!isOnline"
100+
class="download-button"
102101
>
103102
<mat-icon>download</mat-icon>
104-
Download
103+
<span class="button-text">Download</span>
105104
</button>
106105
</td>
107106
</ng-container>

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-project.component.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use 'src/xforge-common/media-breakpoints/breakpoints' as *;
2+
13
.admin-tool {
24
display: flex;
35
flex-direction: column;
@@ -18,3 +20,22 @@
1820
display: flex;
1921
align-items: center;
2022
}
23+
24+
.download-button {
25+
// Hide button text on narrow viewports, showing icon only
26+
@include media-breakpoint('<', md) {
27+
.button-text {
28+
display: none;
29+
}
30+
31+
// Remove icon margins when text is hidden to center it properly
32+
mat-icon {
33+
margin-right: 0;
34+
margin-left: 0;
35+
}
36+
37+
// Make button more compact with just icon
38+
min-width: 40px;
39+
padding: 0px;
40+
}
41+
}

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-project.component.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { ParatextService } from '../core/paratext.service';
1919
import { SFProjectService } from '../core/sf-project.service';
2020
import { BuildDto } from '../machine-api/build-dto';
2121
import { JsonViewerComponent } from '../shared/json-viewer/json-viewer.component';
22-
import { MobileNotSupportedComponent } from '../shared/mobile-not-supported/mobile-not-supported.component';
2322
import { NoticeComponent } from '../shared/notice/notice.component';
2423
import { SharedModule } from '../shared/shared.module';
2524
import { projectLabel } from '../shared/utils';
@@ -56,7 +55,6 @@ function projectType(project: TranslateSource | SFProjectProfile): string {
5655
SharedModule,
5756
UICommonModule,
5857
DraftInformationComponent,
59-
MobileNotSupportedComponent,
6058
WriteStatusComponent,
6159
JsonViewerComponent
6260
]

0 commit comments

Comments
 (0)