or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

angular-dashboard.mddto.mdhistory-server.mdindex.mdjar-management.mdweb-server.md

angular-dashboard.mddocs/

0

# Angular Web Dashboard

1

2

Modern TypeScript dashboard providing real-time monitoring, job visualization, resource management, and developer tools for Apache Flink clusters. Built with Angular 18 and Ng-Zorro (Ant Design) components.

3

4

## Package Information

5

6

- **Framework**: Angular 18

7

- **Language**: TypeScript

8

- **UI Library**: Ng-Zorro (Ant Design)

9

- **Build Tool**: Angular CLI

10

- **Visualization**: D3.js, G2, Dagre for charts and graphs

11

12

## Core Imports

13

14

```typescript

15

// Angular core

16

import { Component, Injectable, OnInit } from '@angular/core';

17

import { Router, ActivatedRoute } from '@angular/router';

18

import { HttpClient } from '@angular/common/http';

19

20

// Ng-Zorro components

21

import { NzTableModule } from 'ng-zorro-antd/table';

22

import { NzCardModule } from 'ng-zorro-antd/card';

23

import { NzButtonModule } from 'ng-zorro-antd/button';

24

25

// Dashboard services

26

import { JobService } from './services/job.service';

27

import { TaskManagerService } from './services/task-manager.service';

28

import { StatusService } from './services/status.service';

29

```

30

31

## Basic Usage

32

33

```typescript

34

// Component setup

35

@Component({

36

selector: 'flink-job-list',

37

template: `

38

<nz-table [nzData]="jobs" [nzLoading]="loading">

39

<thead>

40

<tr>

41

<th>Job Name</th>

42

<th>Status</th>

43

<th>Start Time</th>

44

<th>Actions</th>

45

</tr>

46

</thead>

47

<tbody>

48

<tr *ngFor="let job of jobs">

49

<td>{{ job.name }}</td>

50

<td><flink-job-badge [status]="job.state"></flink-job-badge></td>

51

<td>{{ job['start-time'] | humanizeDate }}</td>

52

<td>

53

<button nz-button (click)="viewJob(job.jid)">View</button>

54

</td>

55

</tr>

56

</tbody>

57

</nz-table>

58

`

59

})

60

export class JobListComponent implements OnInit {

61

jobs: JobOverview[] = [];

62

loading = false;

63

64

constructor(private jobService: JobService, private router: Router) {}

65

66

ngOnInit() {

67

this.loadJobs();

68

}

69

70

loadJobs() {

71

this.loading = true;

72

this.jobService.getJobs().subscribe(jobs => {

73

this.jobs = jobs;

74

this.loading = false;

75

});

76

}

77

78

viewJob(jobId: string) {

79

this.router.navigate(['/job', jobId]);

80

}

81

}

82

```

83

84

## Architecture

85

86

The Angular dashboard follows a modular architecture with:

87

88

- **Standalone Components**: Modern Angular approach with individual component modules

89

- **Service Layer**: Injectable services for API communication and state management

90

- **Route-based Lazy Loading**: Feature modules loaded on demand for performance

91

- **Type-safe Models**: Complete TypeScript interfaces for all data structures

92

- **Reactive Programming**: RxJS observables for async data handling

93

94

## Application Structure

95

96

### Main Routes

97

98

```typescript

99

// Route configuration

100

export const routes: Routes = [

101

{ path: '', redirectTo: '/overview', pathMatch: 'full' },

102

{ path: 'overview', loadComponent: () => import('./pages/overview/overview.component') },

103

{ path: 'submit', loadComponent: () => import('./pages/submit/submit.component') },

104

{ path: 'job-manager', loadComponent: () => import('./pages/job-manager/job-manager.component') },

105

{ path: 'task-manager', loadComponent: () => import('./pages/task-manager/task-manager.component') },

106

{ path: 'job', loadChildren: () => import('./pages/job/job.routes') }

107

];

108

```

109

110

### Core Layout

111

112

```typescript { .api }

113

/**

114

* Root application component with navigation sidebar

115

*/

116

@Component({

117

selector: 'flink-root',

118

template: `

119

<nz-layout class="main-layout">

120

<nz-sider [nzCollapsed]="isCollapsed" [nzTrigger]="null">

121

<div class="sidebar-logo">

122

<img src="assets/flink-logo.svg" alt="Apache Flink">

123

</div>

124

<ul nz-menu nzMode="inline" [nzInlineCollapsed]="isCollapsed">

125

<li nz-menu-item routerLink="/overview">

126

<i nz-icon nzType="dashboard"></i>

127

<span>Overview</span>

128

</li>

129

<li nz-menu-item routerLink="/submit">

130

<i nz-icon nzType="upload"></i>

131

<span>Submit Job</span>

132

</li>

133

<!-- Additional menu items -->

134

</ul>

135

</nz-sider>

136

<nz-layout>

137

<nz-header>

138

<i nz-icon [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"

139

(click)="isCollapsed = !isCollapsed"></i>

140

</nz-header>

141

<nz-content>

142

<router-outlet></router-outlet>

143

</nz-content>

144

</nz-layout>

145

</nz-layout>

146

`

147

})

148

export class AppComponent {

149

isCollapsed = false;

150

}

151

```

152

153

## Services and API Communication

154

155

### Core Services

156

157

#### JobService

158

159

```typescript { .api }

160

/**

161

* Comprehensive job operations and monitoring service

162

*/

163

@Injectable({

164

providedIn: 'root'

165

})

166

export class JobService {

167

constructor(private http: HttpClient) {}

168

169

/**

170

* Get list of all jobs (running and completed)

171

*/

172

getJobs(): Observable<JobOverview[]>;

173

174

/**

175

* Get detailed information for a specific job

176

*/

177

getJobDetail(jobId: string): Observable<JobDetail>;

178

179

/**

180

* Get job configuration

181

*/

182

getJobConfig(jobId: string): Observable<JobConfig>;

183

184

/**

185

* Get job execution plan/graph

186

*/

187

getJobPlan(jobId: string): Observable<Plan>;

188

189

/**

190

* Get job metrics and performance data

191

*/

192

getJobMetrics(jobId: string): Observable<JobMetrics>;

193

194

/**

195

* Get job checkpoints information

196

*/

197

getJobCheckpoints(jobId: string): Observable<JobCheckpoint>;

198

199

/**

200

* Get job exceptions and error information

201

*/

202

getJobExceptions(jobId: string): Observable<JobException>;

203

204

/**

205

* Get backpressure information for job

206

*/

207

getJobBackpressure(jobId: string): Observable<JobBackpressure>;

208

209

/**

210

* Get flame graph data for performance analysis

211

*/

212

getJobFlameGraph(jobId: string, vertexId: string): Observable<JobFlameGraph>;

213

214

/**

215

* Cancel a running job

216

*/

217

cancelJob(jobId: string, mode?: 'cancel' | 'stop'): Observable<void>;

218

219

/**

220

* Trigger savepoint for job

221

*/

222

triggerSavepoint(jobId: string, targetDirectory?: string): Observable<SavepointInfo>;

223

}

224

```

225

226

#### TaskManagerService

227

228

```typescript { .api }

229

/**

230

* Task Manager monitoring and management service

231

*/

232

@Injectable({

233

providedIn: 'root'

234

})

235

export class TaskManagerService {

236

constructor(private http: HttpClient) {}

237

238

/**

239

* Get list of all task managers

240

*/

241

getTaskManagers(): Observable<TaskManager[]>;

242

243

/**

244

* Get detailed task manager information

245

*/

246

getTaskManagerDetail(taskManagerId: string): Observable<TaskManagerDetail>;

247

248

/**

249

* Get task manager metrics

250

*/

251

getTaskManagerMetrics(taskManagerId: string): Observable<TaskManagerMetrics>;

252

253

/**

254

* Get task manager logs

255

*/

256

getTaskManagerLogs(taskManagerId: string, logType: 'out' | 'log'): Observable<string>;

257

258

/**

259

* Get thread dump for task manager

260

*/

261

getTaskManagerThreadDump(taskManagerId: string): Observable<ThreadDump>;

262

263

/**

264

* Get profiling information

265

*/

266

getTaskManagerProfile(taskManagerId: string): Observable<ProfilingResult>;

267

}

268

```

269

270

#### OverviewService

271

272

```typescript { .api }

273

/**

274

* Cluster overview and statistics service

275

*/

276

@Injectable({

277

providedIn: 'root'

278

})

279

export class OverviewService {

280

constructor(private http: HttpClient) {}

281

282

/**

283

* Get cluster overview with job and resource statistics

284

*/

285

getOverview(): Observable<Overview>;

286

287

/**

288

* Get cluster configuration information

289

*/

290

getConfiguration(): Observable<Configuration>;

291

}

292

```

293

294

#### StatusService

295

296

```typescript { .api }

297

/**

298

* Application status, configuration, and error messaging service

299

*/

300

@Injectable({

301

providedIn: 'root'

302

})

303

export class StatusService {

304

constructor(private http: HttpClient) {}

305

306

/**

307

* Get application configuration and feature flags

308

*/

309

getConfig(): Observable<DashboardConfiguration>;

310

311

/**

312

* Handle and display error messages

313

*/

314

handleError(error: any): void;

315

316

/**

317

* Show success notification

318

*/

319

showSuccess(message: string): void;

320

321

/**

322

* Show error notification

323

*/

324

showError(message: string): void;

325

}

326

```

327

328

### Data Models and Interfaces

329

330

#### Job Models

331

332

```typescript { .api }

333

/**

334

* Job overview information for listing

335

*/

336

export interface JobOverview {

337

jid: string;

338

name: string;

339

state: JobState;

340

'start-time': number;

341

'end-time': number;

342

duration: number;

343

'last-modification': number;

344

tasks: TaskStatusCounts;

345

}

346

347

/**

348

* Detailed job information

349

*/

350

export interface JobDetail extends JobOverview {

351

vertices: JobVertex[];

352

'status-counts': TaskStatusCounts;

353

plan: Plan;

354

timestamps: JobTimestamps;

355

}

356

357

/**

358

* Job vertex information for execution graph

359

*/

360

export interface JobVertex {

361

id: string;

362

name: string;

363

parallelism: number;

364

status: JobVertexState;

365

'start-time': number;

366

'end-time': number;

367

duration: number;

368

tasks: TaskStatusCounts;

369

metrics: VertexMetrics;

370

}

371

372

/**

373

* Job execution plan/graph

374

*/

375

export interface Plan {

376

jid: string;

377

name: string;

378

nodes: PlanNode[];

379

edges: PlanEdge[];

380

}

381

382

/**

383

* Job configuration

384

*/

385

export interface JobConfig {

386

jid: string;

387

name: string;

388

'execution-config': ExecutionConfig;

389

}

390

```

391

392

#### Monitoring Models

393

394

```typescript { .api }

395

/**

396

* Job metrics and performance data

397

*/

398

export interface JobMetrics {

399

vertices: VertexMetrics[];

400

watermarks: WatermarkInfo[];

401

throughput: ThroughputInfo[];

402

}

403

404

/**

405

* Job checkpoint information

406

*/

407

export interface JobCheckpoint {

408

counts: CheckpointCounts;

409

summary: CheckpointSummary;

410

history: CheckpointStatistics[];

411

latest: {

412

completed: CheckpointStatistics | null;

413

savepoint: CheckpointStatistics | null;

414

failed: CheckpointStatistics | null;

415

restored: CheckpointStatistics | null;

416

};

417

}

418

419

/**

420

* Job backpressure monitoring data

421

*/

422

export interface JobBackpressure {

423

status: 'OK' | 'LOW' | 'HIGH';

424

'backpressure-level': 'OK' | 'LOW' | 'HIGH';

425

'end-timestamp': number;

426

subtasks: SubtaskBackpressure[];

427

}

428

429

/**

430

* Flame graph data for performance analysis

431

*/

432

export interface JobFlameGraph {

433

data: FlameGraphNode[];

434

endTime: number;

435

maxStackDepth: number;

436

}

437

```

438

439

#### Resource Models

440

441

```typescript { .api }

442

/**

443

* Task Manager information and status

444

*/

445

export interface TaskManager {

446

id: string;

447

path: string;

448

dataPort: number;

449

jmxPort: number;

450

timeSinceLastHeartbeat: number;

451

slotsNumber: number;

452

freeSlots: number;

453

totalResource: ResourceProfile;

454

freeResource: ResourceProfile;

455

hardware: HardwareDescription;

456

memoryConfiguration: MemoryConfiguration;

457

}

458

459

/**

460

* Cluster overview statistics

461

*/

462

export interface Overview {

463

taskmanagers: number;

464

'slots-total': number;

465

'slots-available': number;

466

'jobs-running': number;

467

'jobs-finished': number;

468

'jobs-cancelled': number;

469

'jobs-failed': number;

470

'flink-version': string;

471

'flink-commit': string;

472

}

473

```

474

475

## Major Dashboard Features

476

477

### 1. Cluster Overview Dashboard

478

479

Real-time cluster monitoring with key metrics and navigation.

480

481

```typescript { .api }

482

/**

483

* Overview dashboard component showing cluster statistics

484

*/

485

@Component({

486

selector: 'flink-overview',

487

template: `

488

<div class="overview-cards">

489

<nz-card nzTitle="Task Managers">

490

<nz-statistic [nzValue]="overview?.taskmanagers" nzSuffix="running"></nz-statistic>

491

</nz-card>

492

<nz-card nzTitle="Available Slots">

493

<nz-statistic

494

[nzValue]="overview?.['slots-available']"

495

[nzTotal]="overview?.['slots-total']"

496

nzSuffix="/ {{ overview?.['slots-total'] }}">

497

</nz-statistic>

498

</nz-card>

499

<nz-card nzTitle="Running Jobs">

500

<nz-statistic

501

[nzValue]="overview?.['jobs-running']"

502

[nzValueStyle]="{ color: '#52c41a' }">

503

</nz-statistic>

504

</nz-card>

505

</div>

506

507

<flink-job-list [jobs]="runningJobs"></flink-job-list>

508

`

509

})

510

export class OverviewComponent implements OnInit {

511

overview: Overview | null = null;

512

runningJobs: JobOverview[] = [];

513

514

constructor(

515

private overviewService: OverviewService,

516

private jobService: JobService

517

) {}

518

519

ngOnInit() {

520

this.loadOverview();

521

this.loadRunningJobs();

522

}

523

}

524

```

525

526

### 2. Job Visualization and Monitoring

527

528

Interactive job execution graph with detailed metrics and monitoring.

529

530

```typescript { .api }

531

/**

532

* Interactive job graph visualization component using D3/Dagre

533

*/

534

@Component({

535

selector: 'flink-dagre',

536

template: `

537

<div class="dagre-container" #dagreContainer>

538

<svg #dagreSvg></svg>

539

<div class="dagre-controls">

540

<button nz-button (click)="zoomIn()">Zoom In</button>

541

<button nz-button (click)="zoomOut()">Zoom Out</button>

542

<button nz-button (click)="resetZoom()">Reset</button>

543

</div>

544

</div>

545

`

546

})

547

export class DagreComponent implements OnInit, OnDestroy {

548

@Input() plan: Plan | null = null;

549

@Input() jobDetail: JobDetail | null = null;

550

551

private svg: d3.Selection<SVGElement, unknown, HTMLElement, any>;

552

private zoom: d3.ZoomBehavior<SVGElement, unknown>;

553

554

ngOnInit() {

555

this.initializeSvg();

556

this.setupZoom();

557

if (this.plan) {

558

this.renderGraph();

559

}

560

}

561

562

private renderGraph() {

563

// D3/Dagre graph rendering logic

564

const g = new dagre.graphlib.Graph();

565

g.setGraph({});

566

g.setDefaultEdgeLabel(() => ({}));

567

568

// Add nodes and edges from plan

569

this.plan?.nodes.forEach(node => {

570

g.setNode(node.id, {

571

label: node.description,

572

width: 200,

573

height: 50

574

});

575

});

576

577

this.plan?.edges.forEach(edge => {

578

g.setEdge(edge.source, edge.target);

579

});

580

581

dagre.layout(g);

582

this.drawGraph(g);

583

}

584

}

585

```

586

587

### 3. Performance Monitoring

588

589

Comprehensive performance analysis with flame graphs and metrics visualization.

590

591

```typescript { .api }

592

/**

593

* Flame graph component for performance analysis

594

*/

595

@Component({

596

selector: 'flink-flame-graph',

597

template: `

598

<div class="flame-graph-container" #flameGraphContainer>

599

<div class="flame-graph-controls">

600

<nz-select [(ngModel)]="selectedVertex" (ngModelChange)="loadFlameGraph()">

601

<nz-option *ngFor="let vertex of vertices"

602

[nzValue]="vertex.id"

603

[nzLabel]="vertex.name">

604

</nz-option>

605

</nz-select>

606

<button nz-button (click)="refreshFlameGraph()">Refresh</button>

607

</div>

608

<div #flameGraph class="flame-graph"></div>

609

</div>

610

`

611

})

612

export class FlameGraphComponent implements OnInit {

613

@Input() jobId: string = '';

614

@Input() vertices: JobVertex[] = [];

615

616

selectedVertex: string = '';

617

flameGraphData: JobFlameGraph | null = null;

618

619

constructor(private jobService: JobService) {}

620

621

ngOnInit() {

622

if (this.vertices.length > 0) {

623

this.selectedVertex = this.vertices[0].id;

624

this.loadFlameGraph();

625

}

626

}

627

628

loadFlameGraph() {

629

if (this.jobId && this.selectedVertex) {

630

this.jobService.getJobFlameGraph(this.jobId, this.selectedVertex)

631

.subscribe(data => {

632

this.flameGraphData = data;

633

this.renderFlameGraph();

634

});

635

}

636

}

637

638

private renderFlameGraph() {

639

// D3 flame graph rendering using d3-flame-graph library

640

const flameGraph = flamegraph()

641

.width(960)

642

.cellHeight(18)

643

.transitionDuration(750)

644

.minFrameSize(5)

645

.transitionEase(d3.easeCubic)

646

.sort(true)

647

.title('')

648

.onClick((d: any) => console.info('clicked:', d));

649

650

d3.select(this.flameGraphContainer.nativeElement)

651

.select('.flame-graph')

652

.datum(this.flameGraphData?.data)

653

.call(flameGraph);

654

}

655

}

656

```

657

658

### 4. Resource Management

659

660

Task Manager and Job Manager monitoring with detailed metrics and logs.

661

662

```typescript { .api }

663

/**

664

* Task Manager detail component with metrics and management

665

*/

666

@Component({

667

selector: 'flink-task-manager-detail',

668

template: `

669

<nz-card nzTitle="Task Manager Details">

670

<nz-descriptions nzBordered>

671

<nz-descriptions-item nzTitle="ID">{{ taskManager?.id }}</nz-descriptions-item>

672

<nz-descriptions-item nzTitle="Address">{{ taskManager?.path }}</nz-descriptions-item>

673

<nz-descriptions-item nzTitle="Slots">

674

{{ taskManager?.freeSlots }} / {{ taskManager?.slotsNumber }}

675

</nz-descriptions-item>

676

<nz-descriptions-item nzTitle="Memory">

677

{{ taskManager?.hardware?.managedMemory | humanizeBytes }}

678

</nz-descriptions-item>

679

</nz-descriptions>

680

</nz-card>

681

682

<nz-tabset>

683

<nz-tab nzTitle="Metrics">

684

<flink-task-manager-metrics [taskManagerId]="taskManagerId"></flink-task-manager-metrics>

685

</nz-tab>

686

<nz-tab nzTitle="Logs">

687

<flink-log-viewer

688

[logUrl]="getLogUrl('log')"

689

[taskManagerId]="taskManagerId">

690

</flink-log-viewer>

691

</nz-tab>

692

<nz-tab nzTitle="Thread Dump">

693

<flink-thread-dump [taskManagerId]="taskManagerId"></flink-thread-dump>

694

</nz-tab>

695

</nz-tabset>

696

`

697

})

698

export class TaskManagerDetailComponent implements OnInit {

699

@Input() taskManagerId: string = '';

700

701

taskManager: TaskManagerDetail | null = null;

702

703

constructor(private taskManagerService: TaskManagerService) {}

704

705

ngOnInit() {

706

this.loadTaskManagerDetail();

707

}

708

709

loadTaskManagerDetail() {

710

this.taskManagerService.getTaskManagerDetail(this.taskManagerId)

711

.subscribe(tm => this.taskManager = tm);

712

}

713

714

getLogUrl(type: 'log' | 'out'): string {

715

return `/taskmanagers/${this.taskManagerId}/logs/${type}`;

716

}

717

}

718

```

719

720

## Shared Utilities and Pipes

721

722

### Custom Pipes for Data Formatting

723

724

```typescript { .api }

725

/**

726

* Humanize date pipe for readable timestamps

727

*/

728

@Pipe({ name: 'humanizeDate' })

729

export class HumanizeDatePipe implements PipeTransform {

730

transform(value: number): string {

731

if (!value) return '-';

732

return new Date(value).toLocaleString();

733

}

734

}

735

736

/**

737

* Humanize duration pipe for time intervals

738

*/

739

@Pipe({ name: 'humanizeDuration' })

740

export class HumanizeDurationPipe implements PipeTransform {

741

transform(value: number): string {

742

if (!value) return '-';

743

const seconds = Math.floor(value / 1000);

744

const minutes = Math.floor(seconds / 60);

745

const hours = Math.floor(minutes / 60);

746

747

if (hours > 0) return `${hours}h ${minutes % 60}m`;

748

if (minutes > 0) return `${minutes}m ${seconds % 60}s`;

749

return `${seconds}s`;

750

}

751

}

752

753

/**

754

* Humanize bytes pipe for memory and storage sizes

755

*/

756

@Pipe({ name: 'humanizeBytes' })

757

export class HumanizeBytesPipe implements PipeTransform {

758

transform(bytes: number): string {

759

if (!bytes) return '0 B';

760

const k = 1024;

761

const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];

762

const i = Math.floor(Math.log(bytes) / Math.log(k));

763

return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];

764

}

765

}

766

```

767

768

### Badge Components for Status Indicators

769

770

```typescript { .api }

771

/**

772

* Job status badge component

773

*/

774

@Component({

775

selector: 'flink-job-badge',

776

template: `

777

<nz-badge

778

[nzStatus]="getBadgeStatus()"

779

[nzText]="status">

780

</nz-badge>

781

`

782

})

783

export class JobBadgeComponent {

784

@Input() status: JobState = 'INITIALIZING';

785

786

getBadgeStatus(): 'success' | 'processing' | 'error' | 'warning' | 'default' {

787

switch (this.status) {

788

case 'RUNNING': return 'processing';

789

case 'FINISHED': return 'success';

790

case 'FAILED': return 'error';

791

case 'CANCELED': return 'warning';

792

default: return 'default';

793

}

794

}

795

}

796

```

797

798

The Angular dashboard provides a comprehensive, modern web interface for Apache Flink monitoring and management with real-time updates, interactive visualizations, and extensive developer tools for troubleshooting and performance analysis.