This is an automated email from the ASF dual-hosted git repository.

zhuzh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 3ff5bb83f42914b16b01014937dd0cfa5de19d11
Author: Yi Zhang <[email protected]>
AuthorDate: Wed Dec 10 11:25:07 2025 +0800

    [FLINK-38778][ui] Update homepage statistics
---
 .../web-dashboard/src/app/interfaces/overview.ts   | 14 +++++++++
 .../src/app/pages/overview/overview.component.html |  2 +-
 .../src/app/pages/overview/overview.component.ts   | 35 ++++++++++++++++++----
 .../statistic/overview-statistic.component.html    | 10 +++----
 .../statistic/overview-statistic.component.ts      | 27 +++++++----------
 5 files changed, 60 insertions(+), 28 deletions(-)

diff --git a/flink-runtime-web/web-dashboard/src/app/interfaces/overview.ts 
b/flink-runtime-web/web-dashboard/src/app/interfaces/overview.ts
index df8da63df5a..be4a3c72fba 100644
--- a/flink-runtime-web/web-dashboard/src/app/interfaces/overview.ts
+++ b/flink-runtime-web/web-dashboard/src/app/interfaces/overview.ts
@@ -29,3 +29,17 @@ export interface Overview {
   'flink-version': string;
   'flink-commit': string;
 }
+
+export interface OverviewWithApplicationStatistics {
+  taskmanagers: number;
+  'taskmanagers-blocked'?: number; // only exist if non-zero
+  'slots-total': number;
+  'slots-available': number;
+  'slots-free-and-blocked'?: number; // only exist if non-zero
+  'applications-running': number;
+  'applications-finished': number;
+  'applications-cancelled': number;
+  'applications-failed': number;
+  'flink-version': string;
+  'flink-commit': string;
+}
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.html
 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.html
index 0371feb6b64..8f26cdcc528 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.html
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.html
@@ -16,7 +16,7 @@
   ~ limitations under the License.
   -->
 
-<flink-overview-statistic></flink-overview-statistic>
+<flink-overview-statistic 
[statisticData$]="statisticData$"></flink-overview-statistic>
 <flink-application-list
   [applicationData$]="applicationData$"
   [title]="'Running Application List'"
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.ts 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.ts
index 9bad37bbe7c..82f52177be3 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.ts
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/overview.component.ts
@@ -18,13 +18,13 @@
 
 import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from 
'@angular/core';
 import { Router } from '@angular/router';
-import { Observable, Subject } from 'rxjs';
-import { mergeMap, share, takeUntil } from 'rxjs/operators';
+import { combineLatest, Observable, Subject } from 'rxjs';
+import { map, mergeMap, share, takeUntil } from 'rxjs/operators';
 
 import { ApplicationListComponent } from 
'@flink-runtime-web/components/application-list/application-list.component';
-import { ApplicationItem } from '@flink-runtime-web/interfaces';
+import { ApplicationItem, OverviewWithApplicationStatistics } from 
'@flink-runtime-web/interfaces';
 import { OverviewStatisticComponent } from 
'@flink-runtime-web/pages/overview/statistic/overview-statistic.component';
-import { ApplicationService, StatusService } from 
'@flink-runtime-web/services';
+import { ApplicationService, OverviewService, StatusService } from 
'@flink-runtime-web/services';
 
 @Component({
   selector: 'flink-overview',
@@ -35,21 +35,46 @@ import { ApplicationService, StatusService } from 
'@flink-runtime-web/services';
 })
 export class OverviewComponent implements OnInit, OnDestroy {
   public applicationData$: Observable<ApplicationItem[]>;
+  public statisticData$: Observable<OverviewWithApplicationStatistics>;
 
   private readonly destroy$ = new Subject<void>();
 
   constructor(
     private readonly statusService: StatusService,
     private readonly applicationService: ApplicationService,
+    private readonly overviewService: OverviewService,
     private router: Router
   ) {}
 
   public ngOnInit(): void {
-    this.applicationData$ = this.statusService.refresh$.pipe(
+    const applications$ = this.statusService.refresh$.pipe(
       takeUntil(this.destroy$),
       mergeMap(() => this.applicationService.loadApplications()),
       share()
     );
+
+    this.applicationData$ = applications$;
+    this.statisticData$ = this.statusService.refresh$.pipe(
+      takeUntil(this.destroy$),
+      mergeMap(() => combineLatest([this.overviewService.loadOverview(), 
applications$])),
+      map(([clusterOverview, applications]) => {
+        const applicationStats: OverviewWithApplicationStatistics = {
+          'applications-running': applications.filter(app => app.status === 
'RUNNING').length,
+          'applications-finished': applications.filter(app => app.status === 
'FINISHED').length,
+          'applications-cancelled': applications.filter(app => app.status === 
'CANCELED').length,
+          'applications-failed': applications.filter(app => app.status === 
'FAILED').length,
+          taskmanagers: clusterOverview['taskmanagers'],
+          'taskmanagers-blocked': clusterOverview['taskmanagers-blocked'],
+          'slots-total': clusterOverview['slots-total'],
+          'slots-available': clusterOverview['slots-available'],
+          'slots-free-and-blocked': clusterOverview['slots-free-and-blocked'],
+          'flink-version': clusterOverview['flink-version'],
+          'flink-commit': clusterOverview['flink-commit']
+        };
+        return applicationStats;
+      }),
+      share()
+    );
   }
 
   public ngOnDestroy(): void {
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.html
 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.html
index b63d32af903..aafca607dd0 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.html
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.html
@@ -54,23 +54,23 @@
     </nz-card>
   </div>
   <div nz-col [nzXs]="24" [nzSm]="24" [nzMd]="12" [nzLg]="12" [nzXl]="12" 
class="col">
-    <nz-card [nzBordered]="false" nzTitle="Running Jobs" 
[nzLoading]="!statistic">
+    <nz-card [nzBordered]="false" nzTitle="Running Applications" 
[nzLoading]="!statistic">
       <ng-container *ngIf="statistic">
-        <div class="total">{{ statistic['jobs-running'] | number: '1.0-0' 
}}</div>
+        <div class="total">{{ statistic['applications-running'] | number: 
'1.0-0' }}</div>
         <div class="footer">
           <div class="field">
             <span>Finished</span>
-            <span>{{ statistic['jobs-finished'] | number: '1.0-0' }}</span>
+            <span>{{ statistic['applications-finished'] | number: '1.0-0' 
}}</span>
           </div>
           <nz-divider nzType="vertical"></nz-divider>
           <div class="field">
             <span>Canceled</span>
-            <span>{{ statistic['jobs-cancelled'] | number: '1.0-0' }}</span>
+            <span>{{ statistic['applications-cancelled'] | number: '1.0-0' 
}}</span>
           </div>
           <nz-divider nzType="vertical"></nz-divider>
           <div class="field">
             <span>Failed</span>
-            <span>{{ statistic['jobs-failed'] | number: '1.0-0' }}</span>
+            <span>{{ statistic['applications-failed'] | number: '1.0-0' 
}}</span>
           </div>
         </div>
       </ng-container>
diff --git 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.ts
 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.ts
index 9fef29d81ee..fa9425795aa 100644
--- 
a/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.ts
+++ 
b/flink-runtime-web/web-dashboard/src/app/pages/overview/statistic/overview-statistic.component.ts
@@ -17,12 +17,11 @@
  */
 
 import { DecimalPipe, NgIf } from '@angular/common';
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, 
OnInit } from '@angular/core';
-import { Subject } from 'rxjs';
-import { mergeMap, takeUntil } from 'rxjs/operators';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, 
OnDestroy, OnInit } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
 
-import { Overview } from '@flink-runtime-web/interfaces';
-import { OverviewService, StatusService } from '@flink-runtime-web/services';
+import { OverviewWithApplicationStatistics } from 
'@flink-runtime-web/interfaces';
 import { NzCardModule } from 'ng-zorro-antd/card';
 import { NzDividerModule } from 'ng-zorro-antd/divider';
 import { NzGridModule } from 'ng-zorro-antd/grid';
@@ -35,26 +34,20 @@ import { NzGridModule } from 'ng-zorro-antd/grid';
   imports: [NzGridModule, NgIf, NzCardModule, NzDividerModule, DecimalPipe]
 })
 export class OverviewStatisticComponent implements OnInit, OnDestroy {
-  public statistic: Overview | null;
+  @Input() statisticData$: Observable<OverviewWithApplicationStatistics> | 
null = null;
+  public statistic: OverviewWithApplicationStatistics | null = null;
 
   private readonly destroy$ = new Subject<void>();
 
-  constructor(
-    private readonly statusService: StatusService,
-    private readonly overviewService: OverviewService,
-    private readonly cdr: ChangeDetectorRef
-  ) {}
+  constructor(private readonly cdr: ChangeDetectorRef) {}
 
   public ngOnInit(): void {
-    this.statusService.refresh$
-      .pipe(
-        takeUntil(this.destroy$),
-        mergeMap(() => this.overviewService.loadOverview())
-      )
-      .subscribe(data => {
+    if (this.statisticData$) {
+      this.statisticData$.pipe(takeUntil(this.destroy$)).subscribe(data => {
         this.statistic = data;
         this.cdr.markForCheck();
       });
+    }
   }
 
   public ngOnDestroy(): void {

Reply via email to