Skip to content

Commit 8d1f306

Browse files
LL-LINAias00
andauthored
[feature] implement labels-based monitors filtering in bulletin creation flow (apache#3161)
Co-authored-by: aias00 <[email protected]>
1 parent e98af95 commit 8d1f306

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

home/docs/help/bulletin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ keywords: [bulletin, custom]
99
1010
### Add Bulletin Item
1111

12-
1. Click `Add New Bulletin Item`, enter the `Bulletin Name`, scroll down to select the `Monitoring Type`, select the associated `Monitoring Task Name`, and then select the `Monitoring Metrics` you want to show in the shuttle box.
12+
1. Click `Add New Bulletin Item`, enter the `Bulletin Name`, scroll down to select the `Monitoring Type`, select the associated `Monitoring Task Name`, You can filter `Monitor Task Name` by `Label`, and then select the `Monitoring Metrics` you want to show in the shuttle box.
1313

1414
2. Click `OK` button to finish creating the customized bulletin.
1515

home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/bulletin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ keywords: [看板, 自定义]
99
1010
### 新增看板项
1111

12-
1. 点击新增看板项, 输入`看板名称`, 下拉选择`监控类型`, 再选择关联的`监控任务名称`, 最后在穿梭框中选择需要展示的`监控指标`
12+
1. 点击新增看板项, 输入`看板名称`, 下拉选择`监控类型`, 可根据`标签``监控任务名称`进行筛选,再选择关联的`监控任务名称`, 最后在穿梭框中选择需要展示的`监控指标`
1313

1414
2. 点击`确定`按钮, 即可完成自定义看板的创建。
1515

922 Bytes
Loading

web-app/src/app/routes/bulletin/bulletin.component.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,21 @@
181181
</nz-form-control>
182182
</nz-form-item>
183183

184+
<nz-form-item>
185+
<nz-form-label nzSpan="7" [nzFor]="'filterLabels'">{{ 'monitor.search.label' | i18n }} </nz-form-label>
186+
<nz-form-control nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
187+
<app-form-field
188+
[item]="{
189+
field: 'labels',
190+
type: 'labels'
191+
}"
192+
[name]="'labels'"
193+
[(ngModel)]="filterLabels"
194+
(ngModelChange)="onFilterInputChange($event)"
195+
/>
196+
</nz-form-control>
197+
</nz-form-item>
198+
184199
<nz-form-item>
185200
<nz-form-label [nzSpan]="7" nzFor="dropdown" nzRequired="true">{{ 'bulletin.monitor.name' | i18n }}</nz-form-label>
186201
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
@@ -193,7 +208,7 @@
193208
nzMode="multiple"
194209
required
195210
>
196-
<ng-container *ngFor="let monitor of monitors">
211+
<ng-container *ngFor="let monitor of filteredMonitors">
197212
<nz-option *ngIf="isMonitorListLoading" [nzValue]="monitor.id" [nzLabel]="monitor.name"></nz-option>
198213
</ng-container>
199214
</nz-select>

web-app/src/app/routes/bulletin/bulletin.component.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import { NzNotificationService } from 'ng-zorro-antd/notification';
2525
import { NzTableQueryParams } from 'ng-zorro-antd/table';
2626
import { TransferChange } from 'ng-zorro-antd/transfer';
2727
import { NzFormatEmitEvent, NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd/tree';
28-
import { finalize } from 'rxjs/operators';
28+
import { Subject } from 'rxjs';
29+
import { finalize, debounceTime, distinctUntilChanged } from 'rxjs/operators';
2930

3031
import { BulletinDefine } from '../../pojo/BulletinDefine';
3132
import { Fields } from '../../pojo/Fields';
@@ -73,22 +74,35 @@ export class BulletinComponent implements OnInit, OnDestroy {
7374
refreshInterval: any;
7475
deadline = 30;
7576
countDownTime: number = 0;
77+
filterLabels: Record<string, string> = {};
78+
filteredMonitors: Monitor[] = [];
79+
private filterSubject = new Subject<string>(); //filter logic debouncing
7680

7781
ngOnInit() {
7882
this.loadTabs();
7983
this.refreshInterval = setInterval(() => {
8084
this.countDown();
8185
}, 1000); // every 30 seconds refresh the tabs
86+
this.filterSubject
87+
.pipe(
88+
debounceTime(300), // triggered after the user stops typing for 300ms
89+
distinctUntilChanged()
90+
)
91+
.subscribe(value => {
92+
this.filterMonitors();
93+
});
8294
}
8395

8496
ngOnDestroy() {
8597
if (this.refreshInterval) {
8698
clearInterval(this.refreshInterval);
8799
}
100+
this.filterSubject.complete();
88101
}
89102

90103
sync() {
91104
this.loadCurrentBulletinData();
105+
this.clearFilters();
92106
}
93107

94108
configRefreshDeadline(deadlineTime: number) {
@@ -158,6 +172,7 @@ export class BulletinComponent implements OnInit, OnDestroy {
158172
this.isManageModalVisible = false;
159173
// clear fields
160174
this.fields = {};
175+
this.clearFilters();
161176
}
162177

163178
resetManageModalData() {
@@ -254,6 +269,7 @@ export class BulletinComponent implements OnInit, OnDestroy {
254269
message => {
255270
if (message.code === 0) {
256271
this.monitors = message.data;
272+
this.filterMonitors();
257273
if (this.monitors != null) {
258274
this.isMonitorListLoading = true;
259275
}
@@ -568,4 +584,47 @@ export class BulletinComponent implements OnInit, OnDestroy {
568584
}
569585
return result;
570586
}
587+
588+
onFilterInputChange(value: string): void {
589+
this.filterSubject.next(value); // push the input value to the debounce Subject
590+
}
591+
592+
filterMonitors(): void {
593+
const validLabels = this.cleanFilterLabels(this.filterLabels);
594+
const activeFilters = Object.entries(validLabels).filter(([k, v]) => k !== '' && k !== 'null'); // Second filtering ensures key validity
595+
596+
if (activeFilters.length === 0) {
597+
this.filteredMonitors = [...this.monitors];
598+
return;
599+
}
600+
601+
this.filteredMonitors = this.monitors.filter(monitor => {
602+
if (!monitor.labels || typeof monitor.labels !== 'object') return false;
603+
604+
return activeFilters.every(([filterKey, filterValue]) => {
605+
const keyExists = Object.prototype.hasOwnProperty.call(monitor.labels, filterKey);
606+
const actualValue = (monitor.labels[filterKey] ?? '').toLowerCase();
607+
return filterValue === '' ? keyExists : actualValue.includes(filterValue.toLowerCase());
608+
});
609+
});
610+
}
611+
612+
private cleanFilterLabels(labels: any): Record<string, string> {
613+
const cleaned: Record<string, string> = {};
614+
Object.entries(labels ?? {}).forEach(([k, v]) => {
615+
if (typeof k === 'string') {
616+
const trimmedKey = k.trim();
617+
if (trimmedKey !== '' && trimmedKey !== 'null') {
618+
cleaned[trimmedKey] = (v ?? '').toString().trim();
619+
}
620+
}
621+
});
622+
return cleaned;
623+
}
624+
625+
clearFilters(): void {
626+
this.filterLabels = {};
627+
this.filterMonitors();
628+
this.cdr.detectChanges();
629+
}
571630
}

0 commit comments

Comments
 (0)