Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** Colors associated with different health states. */
export const HEALTH_COLORS: Record<string, string> = {
'idle': '#66BB6A',
'busy': '#42A5F5',
'lameduck': '#FFA726',
'init': '#FFEE58',
'dying': '#EF5350',
'dirty': '#AB47BC',
'prepping': '#78909C',
'missing': '#8E44AD',
'failed': '#F44336',
'others': '#BDBDBD',
};
/** Colors for health summary (In Service vs Out of Service). */
export const HEALTH_SUMMARY_COLORS = ['#34A853', '#EA4335'];

/** Colors associated with different test results. */
export const TEST_COLORS: Record<string, string> = {
'pass': '#34A853',
'fail': '#EA4335',
'error': '#FDBB05',
'timeout': '#A142F4',
'other': '#9E9E9E',
};
/** Chart colors for test results. */
export const TEST_CHART_COLORS = [
TEST_COLORS['pass'],
TEST_COLORS['fail'],
TEST_COLORS['error'],
TEST_COLORS['timeout'],
TEST_COLORS['other'],
];

/** Colors associated with different recovery outcomes. */
export const RECOVERY_COLORS: Record<string, string> = {
'success': '#34A853',
'fail': '#EA4335',
};
/** Chart colors for recovery outcomes. */
export const RECOVERY_CHART_COLORS = [
RECOVERY_COLORS['success'],
RECOVERY_COLORS['fail'],
];
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<app-overview-page [navList]="navList">
<app-master-detail-layout [navList]="navList">
@if (device) {
<!-- Health & Activity Card -->
<app-info-card
#overviewSection
id="overview-health"
title="Health & Activity"
[collapsible]="false">
<div class="overview-card-content">
[collapsible]="false"
>
<div class="info-card-content">
@let healthUI = getHealthStateUI(device.healthAndActivity.state);
<div class="health-summary">
<div class="summary-icon-bg {{ healthUI.iconBgColorClass }}">
Expand Down Expand Up @@ -124,8 +125,9 @@ <h3 class="health-title {{ healthUI.iconColorClass }}">
#overviewSection
id="overview-system"
title="Basic Information"
[collapsible]="false">
<div class="overview-card-content">
[collapsible]="false"
>
<div class="info-card-content">
<div class="basic-info-grid-wrapper">
<dl class="info-grid-tight">
<dt title="Model">Model</dt>
Expand Down Expand Up @@ -205,7 +207,7 @@ <h3 class="health-title {{ healthUI.iconColorClass }}">
<span class="operation-text">View Testbed YAML</span>
</a>
</div>
<div class="overview-card-content no-padding">
<div class="info-card-content no-padding">
<div class="sub-device-cards-wrapper">
@for (sub of device.subDevices; track sub.id) {
<app-info-card [title]="sub.id" [collapsible]="isSubDeviceCollapsible()" class="sub-device-card">
Expand Down Expand Up @@ -272,8 +274,9 @@ <h3 class="health-title {{ healthUI.iconColorClass }}">
#overviewSection
id="overview-permissions"
title="Permissions"
[collapsible]="false">
<div class="overview-card-content permissions-content">
[collapsible]="false"
>
<div class="info-card-content permissions-content">
<div>
<h4 class="permission-type-title">Owners</h4>
<div class="chip-grid-container">
Expand Down Expand Up @@ -303,7 +306,7 @@ <h4 class="permission-type-title">Executors</h4>

<!-- Capabilities Card -->
<app-info-card #overviewSection id="overview-capabilities" title="Capabilities">
<div id="capabilities-content" class="overview-card-content capabilities-content">
<div id="capabilities-content" class="info-card-content capabilities-content">
<!-- Supported Drivers Section -->
<div class="capability-section">
<h4 class="capabilities-title">
Expand Down Expand Up @@ -382,7 +385,7 @@ <h4 class="capabilities-title">

<!-- Dimensions Card -->
<app-info-card #overviewSection id="overview-dimensions" title="Dimensions" [expanded]="true">
<div id="dimensions-content" class="overview-card-content text-sm">
<div id="dimensions-content" class="info-card-content text-sm">
@if (device.dimensions) {
<mat-form-field
appearance="outline"
Expand Down Expand Up @@ -473,7 +476,7 @@ <h5 class="dimension-source-title"> {{ source }} ({{ items.length }}) </h5>

<!-- Properties Card -->
<app-info-card #overviewSection id="overview-properties" title="Properties">
<div id="properties-content" class="overview-card-content">
<div id="properties-content" class="info-card-content">
@if (device.properties && objectUtils.objectKeys(device.properties).length > 0) {
<div class="data-block">
<dl class="data-block-grid">
Expand All @@ -493,4 +496,4 @@ <h5 class="dimension-source-title"> {{ source }} ({{ items.length }}) </h5>
} @else {
<p>Device data not available.</p>
}
</app-overview-page>
</app-master-detail-layout>
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ import {
} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
import {MatDialog, MatDialogModule} from '@angular/material/dialog';
import {MatDialog} from '@angular/material/dialog';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatIconModule} from '@angular/material/icon';
import {MatInputModule} from '@angular/material/input';
import {Subject} from 'rxjs';
import {debounceTime, distinctUntilChanged, takeUntil} from 'rxjs/operators';
import type {DeviceOverview} from '../../../../core/models/device_overview';
import {
DeviceDimension,
DimensionSourceGroup,
HealthState,
type DeviceDimension,
type DimensionSourceGroup,
type SubDeviceInfo,
SubDeviceInfo,
} from '../../../../core/models/device_overview';
import {DEVICE_SERVICE} from '../../../../core/services/device/device_service';
import {InfoCard} from '../../../../shared/components/info_card/info_card';
import {
MasterDetailLayout,
NavItem,
OverviewPage,
} from '../../../../shared/components/overview_page/overview_page';
} from '../../../../shared/components/master_detail_layout/master_detail_layout';
import {dateUtils} from '../../../../shared/utils/date_utils';
import {objectUtils} from '../../../../shared/utils/object_utils';
import {TestbedConfigViewer} from '../testbed_config_viewer/testbed_config_viewer';
Expand Down Expand Up @@ -63,13 +63,12 @@ interface DimensionItem {
standalone: true,
imports: [
CommonModule,
FormsModule,
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatFormFieldModule,
MatDialogModule,
OverviewPage,
FormsModule,
MasterDetailLayout,
InfoCard,
],
templateUrl: './device_overview_tab.ng.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<app-master-detail-layout [navList]="navList">
<app-info-card
#statisticSection
id="health-statistic"
title="Device Healthiness Statistics"
[collapsible]="false">
<ng-container operations>
<app-date-range-picker (dateRangeChange)="onDateRangeChange('health', $event)"></app-date-range-picker>
</ng-container>
@if (loadingHealth()) {
<mat-progress-bar mode="indeterminate" class="progress-bar"></mat-progress-bar>
}
<div class="info-card-content" [style.opacity]="loadingHealth() ? 0.5 : 1">
<div class="chart-section">
<h4 class="chart-title">Daily Healthiness Trend</h4>
<div #healthChartSummary class="chart-container"></div>
</div>
<div class="chart-section">
<h4 class="chart-title">Daily Healthiness Breakdown</h4>
<div #healthChartDetail class="chart-container"></div>
</div>
<app-statistic-breakdown
title="Total Healthiness Breakdown"
[selectedPeriodStr]="healthDateRangeString"
[tableData]="healthDataSource"
[tableColumns]="['label', 'value']"
[charts]="healthBreakdownCharts"
></app-statistic-breakdown>
</div>
</app-info-card>
<app-info-card
#statisticSection
id="test-result-statistic"
title="Test Result Statistics"
[collapsible]="false">
<ng-container operations>
<app-date-range-picker (dateRangeChange)="onDateRangeChange('test', $event)"></app-date-range-picker>
</ng-container>
@if (loadingTest()) {
<mat-progress-bar mode="indeterminate" class="progress-bar"></mat-progress-bar>
}
<div class="info-card-content" [style.opacity]="loadingTest() ? 0.5 : 1">
<div class="chart-section">
<h4 class="chart-title">Daily Test Results Trend</h4>
<div #testChart class="chart-container"></div>
</div>
<app-statistic-breakdown
title="Total Test Result Breakdown"
[selectedPeriodStr]="testDateRangeString"
[tableData]="testDataSource"
[tableColumns]="['label', 'count', 'percentage']"
[charts]="testBreakdownCharts"
></app-statistic-breakdown>
</div>
</app-info-card>
<app-info-card
#statisticSection
id="recovery-task-statistic"
title="Recovery Task Statistics"
[collapsible]="false">
<ng-container operations>
<app-date-range-picker (dateRangeChange)="onDateRangeChange('recovery', $event)"></app-date-range-picker>
</ng-container>
@if (loadingRecovery()) {
<mat-progress-bar mode="indeterminate" class="progress-bar"></mat-progress-bar>
}
<div class="info-card-content" [style.opacity]="loadingRecovery() ? 0.5 : 1">
<div class="chart-section">
<h4 class="chart-title">Daily Recovery Task Trend</h4>
<div #recoveryChart class="chart-container"></div>
</div>
<app-statistic-breakdown
title="Total Recovery Task Breakdown"
[selectedPeriodStr]="recoveryDateRangeString"
[tableData]="recoveryDataSource"
[tableColumns]="['label', 'count', 'percentage']"
[charts]="recoveryBreakdownCharts"
></app-statistic-breakdown>
</div>
</app-info-card>
</app-master-detail-layout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
@use '@angular/material' as mat;

.progress-bar {
@include mat.progress-bar-overrides((
active-indicator-color: #1967d2,
track-color: #e8f0fe,
));
}

.chart-section {
margin-bottom: 2rem;
}

.chart-title {
font-size: 1rem;
font-weight: 500;
color: #374151;
text-align: center;
margin-bottom: 0.5rem;
}

.chart-container {
width: 100%;
height: 350px;
}




.table-container {
overflow-x: auto;

@include mat.table-overrides((
row-item-container-height: 46px,
row-item-label-text-size: 1rem,
row-item-label-text-color: #374151,
row-item-outline-width: 1px,
row-item-outline-color: #e5e7eb,
header-headline-size: 0.75rem,
header-headline-color: #6b7280,
header-headline-weight: 500,
header-container-height: 40px,
));
}

.breakdown-table {
width: 100%;
background-color: transparent;
}

.label-cell {
padding: 0;
}

.value-cell {
text-align: right;
justify-content: flex-end;
}

.pl-2 {
padding-left: 0.5rem;
}

.pl-4 {
padding-left: 1rem;
}

.pl-6 {
padding-left: 1.5rem;
}

.pl-8 {
padding-left: 2rem;
}

.pl-10 {
padding-left: 2.5rem;
}

.breakdown-table td.mat-cell.pl-2 {
padding-left: 0.5rem;
}

.breakdown-table td.mat-cell.pl-4 {
padding-left: 1rem;
}

.breakdown-table td.mat-cell.pl-8 {
padding-left: 2rem;
}

.color-dot {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
margin-right: 0.5rem;
display: inline-block;
}

.total-row td.mat-cell {
border-bottom: 2px solid #d1d5db;
font-weight: 700;
}

.health-table {
background-color: transparent;
}

.health-table .mat-row:nth-of-type(even) {
background-color: #f8f9fa;
}

.health-table .summary-text {
font-weight: 500;
}

.health-table .summary-value {
font-weight: 700;
}

.charts-container {
display: flex;
flex-direction: column;
gap: 1.5rem;
}

.chart-subtitle {
font-size: 0.875rem;
font-weight: 500;
color: #4b5563;
text-align: center;
margin-bottom: 0.5rem;
margin-top: 0;
}

.pie-chart-container {
width: 100%;
height: 250px;
}


Loading