Skip to content

Commit 0ba548a

Browse files
OmniLab Teamcopybara-github
authored andcommitted
[MHFE_V6UI] Add refresh button to Host Detail page.
PiperOrigin-RevId: 951370023
1 parent 86a2bd9 commit 0ba548a

11 files changed

Lines changed: 243 additions & 100 deletions

File tree

src/devtools/mobileharness/fe/v6/angular/app/app.scss

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,19 @@ mat-spinner.loading-spinner {
280280
display: flex;
281281
height: 100%;
282282
justify-content: center;
283-
position: absolute;
283+
position: fixed;
284+
top: 0;
285+
left: 0;
284286
width: 100%;
285-
z-index: 101;
287+
height: 100%;
288+
background-color: rgba(255, 255, 255, 0.5); // Dims the content behind
289+
z-index: 9999; // Ensure it's on top of everything
290+
}
291+
292+
// If the initial pre-bootstrap loader is present, hide the global overlay
293+
// to prevent double spinners and overlapping masks.
294+
#initial-loading-overlay ~ app-root .loading-spinner-overlay {
295+
display: none !important;
286296
}
287297

288298
.page {

src/devtools/mobileharness/fe/v6/angular/app/core/services/device/fake_device_service.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import {
3838
providedIn: 'root',
3939
})
4040
export class FakeDeviceService extends DeviceService {
41+
private getDeviceOverviewCallCount = 0;
42+
4143
constructor() {
4244
super();
4345
}
@@ -54,8 +56,19 @@ export class FakeDeviceService extends DeviceService {
5456
): Observable<DeviceOverviewPageData> {
5557
const scenario = MOCK_DEVICE_SCENARIOS.find((s) => s.id === request.id);
5658
if (scenario) {
59+
console.log(
60+
`[FakeDeviceService] getDeviceOverview called for ${request.id}, count: ${++this.getDeviceOverviewCallCount}`,
61+
);
62+
const overview = {
63+
...scenario.overview,
64+
properties: {
65+
...scenario.overview.properties,
66+
'Refresh Count': String(this.getDeviceOverviewCallCount),
67+
'Last Refreshed At': new Date().toLocaleTimeString(),
68+
},
69+
};
5770
return of({
58-
overview: scenario.overview,
71+
overview,
5972
headerInfo: this.getMockDeviceHeaderInfo(scenario),
6073
}).pipe(delay(1000));
6174
} else {

src/devtools/mobileharness/fe/v6/angular/app/core/services/host/fake_host_service.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,17 @@ export class FakeHostService extends HostService {
7171
const scenario = MOCK_HOST_SCENARIOS.find((s) => s.hostName === hostName);
7272
if (scenario && scenario.overview) {
7373
const actions = scenario.actions || createHostActions('RUNNING', false);
74-
const overview = {...scenario.overview};
74+
console.log(
75+
`[FakeHostService] getHostOverview called for ${hostName}, count: ${++this.getHostOverviewCallCount}`,
76+
);
77+
const overview = {
78+
...scenario.overview,
79+
properties: {
80+
...scenario.overview.properties,
81+
'Refresh Count': String(this.getHostOverviewCallCount),
82+
'Last Refreshed At': new Date().toLocaleTimeString(),
83+
},
84+
};
7585

7686
if (!overview.labServer.actions) {
7787
const connectivityState =
@@ -348,6 +358,7 @@ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0`,
348358
private preflightStopCallCount = 0;
349359
private preflightStartCallCount = 0;
350360
private preflightRestartCallCount = 0;
361+
private getHostOverviewCallCount = 0;
351362

352363
override preflightLabServerLifecycle(
353364
hostName: string,

src/devtools/mobileharness/fe/v6/angular/app/features/device_detail/device_detail_page.ng.html

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,23 @@
5555
<div class="title-text-wrapper">
5656
<div class="title-main">
5757
<h1 class="device-title" title="{{ device.id }}">{{ device.id }}</h1>
58-
<button
59-
(click)="copyToClipboard(device.id)"
60-
class="copy-button"
61-
title="Copy device ID"
62-
>
63-
<mat-icon class="copy-icon">content_copy</mat-icon>
64-
</button>
58+
<div class="title-action-container">
59+
<button
60+
(click)="copyToClipboard(device.id)"
61+
class="title-action-btn"
62+
title="Copy device ID"
63+
>
64+
<mat-icon class="title-action-icon">content_copy</mat-icon>
65+
</button>
66+
<button
67+
class="title-action-btn"
68+
title="Refresh data"
69+
(click)="triggerRefresh()"
70+
[disabled]="loadingService.isLoading()"
71+
>
72+
<mat-icon class="title-action-icon" [class.spin-animation]="loadingService.isLoading()">refresh</mat-icon>
73+
</button>
74+
</div>
6575
</div>
6676
<div class="title-subtitle">
6777
<span>Connected to Host:</span>

src/devtools/mobileharness/fe/v6/angular/app/features/device_detail/device_detail_page.scss

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,14 @@ $labconsole-font: Roboto, sans-serif;
1313
background-color: #f8f9fa; // bg-gray-50
1414
}
1515

16-
@keyframes spin {
17-
from {
18-
transform: rotate(0deg);
19-
}
20-
to {
21-
transform: rotate(360deg);
22-
}
23-
}
16+
2417

2518
.main-card {
2619
background-color: white;
2720
border-radius: 1rem; // rounded-2xl
2821
box-shadow:
2922
0 10px 15px -3px rgba(0, 0, 0, 0.1),
3023
0 4px 6px -2px rgba(0, 0, 0, 0.05); // shadow-lg
31-
// overflow: hidden;
3224
}
3325

3426
.card-header {
@@ -148,6 +140,8 @@ $labconsole-font: Roboto, sans-serif;
148140
align-items: center;
149141
gap: 0.5rem;
150142

143+
144+
151145
.device-title {
152146
font-size: 1.5rem; // text-2xl
153147
font-weight: 500;
@@ -157,30 +151,7 @@ $labconsole-font: Roboto, sans-serif;
157151
text-overflow: ellipsis;
158152
}
159153

160-
.copy-button {
161-
width: 2.5rem; // w-10
162-
height: 2.5rem; // h-10
163-
border-radius: 50%;
164-
background: none;
165-
border: none;
166-
cursor: pointer;
167-
display: flex;
168-
align-items: center;
169-
justify-content: center;
170-
color: #6b7280; // text-gray-500
171-
flex-shrink: 0;
172154

173-
&:hover {
174-
background-color: #f3f4f6; // hover:bg-gray-100
175-
color: #1f2937; // hover:text-gray-800
176-
}
177-
178-
.copy-icon {
179-
font-size: 20px;
180-
height: 20px;
181-
width: 20px;
182-
}
183-
}
184155
}
185156

186157
.title-subtitle {
@@ -324,3 +295,7 @@ $labconsole-font: Roboto, sans-serif;
324295
}
325296
}
326297
}
298+
299+
300+
301+

src/devtools/mobileharness/fe/v6/angular/app/features/device_detail/device_detail_page.ts

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ import {
1111
} from '@angular/core';
1212
import {MatIconModule} from '@angular/material/icon';
1313
import {MatMenuModule} from '@angular/material/menu';
14+
import {MatSnackBarRef} from '@angular/material/snack-bar';
1415
import {MatTooltipModule} from '@angular/material/tooltip';
1516
import {Title} from '@angular/platform-browser';
1617
import {ActivatedRoute, RouterModule} from '@angular/router';
1718
import {LoadingService} from '@deviceinfra/app/shared/services/loading_service';
18-
import {combineLatest, Observable, of, ReplaySubject} from 'rxjs';
19+
import {
20+
BehaviorSubject,
21+
combineLatest,
22+
Observable,
23+
of,
24+
ReplaySubject,
25+
} from 'rxjs';
1926
import {catchError, map, switchMap, takeUntil, tap} from 'rxjs/operators';
2027

2128
import {dateUtils} from '@deviceinfra/app/shared/utils/date_utils';
@@ -74,11 +81,21 @@ export class DeviceDetailPage implements OnInit, OnDestroy {
7481
private readonly destroyed = new ReplaySubject<void>(1);
7582
private readonly appData = inject(APP_DATA);
7683
private readonly clipboardService = inject(ClipboardService);
77-
private readonly loadingService = inject(LoadingService);
84+
protected readonly loadingService = inject(LoadingService);
7885
private readonly envUniverseService = inject(EnvUniverseService);
7986

8087
readonly legacyFeUrl = getLegacyFeUrl(this.appData.applicationId ?? '');
8188

89+
private readonly refreshTrigger$ = new BehaviorSubject<void>(undefined);
90+
private refreshSnackBarRef?: MatSnackBarRef<unknown>;
91+
92+
triggerRefresh(): void {
93+
this.refreshSnackBarRef = this.snackBar.showInProgress(
94+
'Refreshing device data...',
95+
);
96+
this.refreshTrigger$.next();
97+
}
98+
8299
activeTab = signal<'overview' | 'test-history' | 'health' | 'record'>(
83100
'overview',
84101
);
@@ -111,40 +128,55 @@ export class DeviceDetailPage implements OnInit, OnDestroy {
111128
this.titleService.setTitle(`OmniLab Console`);
112129
}
113130

114-
readonly devicePageData$: Observable<DevicePageData> =
115-
this.route.paramMap.pipe(
116-
map((params) => params.get('id')),
117-
tap(() => {
118-
this.loadingService.show();
119-
}),
120-
switchMap((id) => {
121-
if (!id) {
122-
this.loadingService.hide();
123-
return of<DevicePageData>({
124-
pageData: null,
125-
error: 'No device ID provided in the route.',
126-
});
131+
readonly devicePageData$: Observable<DevicePageData> = combineLatest([
132+
this.route.paramMap.pipe(map((params) => params.get('id'))),
133+
this.refreshTrigger$,
134+
]).pipe(
135+
map(([id, _]) => id),
136+
tap(() => {
137+
this.loadingService.show();
138+
}),
139+
switchMap((id) => {
140+
if (!id) {
141+
this.loadingService.hide();
142+
if (this.refreshSnackBarRef) {
143+
this.refreshSnackBarRef.dismiss();
144+
this.refreshSnackBarRef = undefined;
127145
}
128-
return this.deviceService
129-
.getDeviceOverview({id, forceRefresh: true})
130-
.pipe(
131-
map((pageData) => ({
132-
pageData,
133-
error: null,
134-
})),
135-
catchError((err) => {
136-
console.error(`Error fetching device ${id}:`, err);
137-
return of<DevicePageData>({
138-
pageData: null,
139-
error: `Failed to load device data for ID: ${id}. ${getErrorMessage(err)}`,
140-
});
141-
}),
142-
tap(() => {
143-
this.loadingService.hide();
144-
}),
145-
);
146-
}),
147-
);
146+
return of<DevicePageData>({
147+
pageData: null,
148+
error: 'No device ID provided in the route.',
149+
});
150+
}
151+
return this.deviceService
152+
.getDeviceOverview({id, forceRefresh: true})
153+
.pipe(
154+
map((pageData) => ({
155+
pageData,
156+
error: null,
157+
})),
158+
catchError((err) => {
159+
console.error(`Error fetching device ${id}:`, err);
160+
if (this.refreshSnackBarRef) {
161+
this.refreshSnackBarRef.dismiss();
162+
this.refreshSnackBarRef = undefined;
163+
}
164+
return of<DevicePageData>({
165+
pageData: null,
166+
error: `Failed to load device data for ID: ${id}. ${getErrorMessage(err)}`,
167+
});
168+
}),
169+
tap(() => {
170+
this.loadingService.hide();
171+
if (this.refreshSnackBarRef) {
172+
this.refreshSnackBarRef.dismiss();
173+
this.refreshSnackBarRef = undefined;
174+
this.snackBar.showSuccess('Device data refreshed!');
175+
}
176+
}),
177+
);
178+
}),
179+
);
148180

149181
setActiveTab(tab: 'overview' | 'test-history' | 'health' | 'record'): void {
150182
this.activeTab.set(tab);

src/devtools/mobileharness/fe/v6/angular/app/features/host_detail/host_detail.ng.html

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ <h1 class="host-title" title="{{ host.hostName }}">
2929
<span class="host-text-bold">Host: </span>
3030
<span class="host-text">{{ host.hostName }}</span>
3131
</h1>
32-
<button class="copy-button" title="Copy host name to clipboard" (click)="copyToClipboard(host.hostName)">
33-
<mat-icon class="copy-icon">content_copy</mat-icon>
34-
</button>
35-
<div class="title-actions">
36-
<a></a>
32+
<div class="title-action-container">
33+
<button class="title-action-btn" title="Copy host name to clipboard" (click)="copyToClipboard(host.hostName)">
34+
<mat-icon class="title-action-icon">content_copy</mat-icon>
35+
</button>
36+
<button class="title-action-btn" title="Refresh data" (click)="triggerRefresh()" [disabled]="loadingService.isLoading()">
37+
<mat-icon class="title-action-icon" [class.spin-animation]="loadingService.isLoading()">refresh</mat-icon>
38+
</button>
3739
</div>
3840
</div>
3941
<div class="host-overview-content">

src/devtools/mobileharness/fe/v6/angular/app/features/host_detail/host_detail.scss

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use '../../shared/styles/action_bar';
2+
13
$labconsole-font: Roboto, sans-serif;
24

35
:host {
@@ -26,7 +28,6 @@ $labconsole-font: Roboto, sans-serif;
2628
box-shadow:
2729
0 10px 15px -3px rgba(0, 0, 0, 0.1),
2830
0 4px 6px -2px rgba(0, 0, 0, 0.05); // shadow-lg
29-
// overflow: hidden;
3031
}
3132

3233
.card-header {
@@ -208,16 +209,8 @@ $labconsole-font: Roboto, sans-serif;
208209
}
209210
}
210211

211-
.copy-button {
212-
display: flex;
213-
align-items: center;
214-
justify-content: center;
215-
flex-shrink: 0;
216-
cursor: pointer;
217-
width: 2.5rem;
218-
height: 2.5rem;
219-
color: #6b7280;
220-
background-color: transparent;
221-
border: none;
222-
}
212+
223213
}
214+
215+
216+

0 commit comments

Comments
 (0)