[YUNIKORN-2491] Persist queue and partition selection and prompt user to pick queue on Applications page
(#180)
- When a user lands on the applications page, queue selection is automatically focused and waits for user to select the queue
- Selected queue,partition pair is stored in localStorage
Closes: #180
Signed-off-by: Yu-Lin Chen <chenyulin0719@apache.org>
diff --git a/src/app/components/apps-view/apps-view.component.html b/src/app/components/apps-view/apps-view.component.html
index 78f408a..8cf5896 100644
--- a/src/app/components/apps-view/apps-view.component.html
+++ b/src/app/components/apps-view/apps-view.component.html
@@ -31,7 +31,7 @@
<div class="dropdown-wrapper">
<label class="dropdown-label">Queue: </label>
<mat-form-field>
- <mat-select [(value)]="leafQueueSelected" (selectionChange)="onQueueSelectionChanged($event)">
+ <mat-select [(value)]="leafQueueSelected" (selectionChange)="onQueueSelectionChanged($event)" #queueSelect>
<mat-option *ngFor="let queue of leafQueueList" [value]="queue.value">
{{ queue.name }}
</mat-option>
diff --git a/src/app/components/apps-view/apps-view.component.ts b/src/app/components/apps-view/apps-view.component.ts
index 5bbcc96..edbf0bb 100644
--- a/src/app/components/apps-view/apps-view.component.ts
+++ b/src/app/components/apps-view/apps-view.component.ts
@@ -21,7 +21,7 @@
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
-import { MatSelectChange } from '@angular/material/select';
+import { MatSelectChange, MatSelect } from '@angular/material/select';
import { finalize, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { NgxSpinnerService } from 'ngx-spinner';
import { fromEvent } from 'rxjs';
@@ -46,6 +46,7 @@
@ViewChild('appSort', { static: true }) appSort!: MatSort;
@ViewChild('allocSort', { static: true }) allocSort!: MatSort;
@ViewChild('searchInput', { static: true }) searchInput!: ElementRef;
+ @ViewChild('queueSelect', { static: false }) queueSelect!: MatSelect;
appDataSource = new MatTableDataSource<AppInfo>([]);
appColumnDef: ColumnDef[] = [];
@@ -62,7 +63,7 @@
partitionSelected = '';
leafQueueList: DropdownItem[] = [];
leafQueueSelected = '';
-
+
detailToggle: boolean = false;
constructor(
@@ -135,7 +136,7 @@
this.partitionList.push(new PartitionInfo(part.name, part.name));
});
- this.partitionSelected = list[0].name;
+ this.partitionSelected = CommonUtil.getStoredPartition(list[0].name);
this.fetchQueuesForPartition(this.partitionSelected);
} else {
this.partitionList = [new PartitionInfo('-- Select --', '')];
@@ -143,6 +144,7 @@
this.leafQueueList = [new DropdownItem('-- Select --', '')];
this.leafQueueSelected = '';
this.appDataSource.data = [];
+ this.clearQueueSelection();
}
});
}
@@ -161,14 +163,37 @@
if (data && data.rootQueue) {
const leafQueueList = this.generateLeafQueueList(data.rootQueue);
this.leafQueueList = [new DropdownItem('-- Select --', ''), ...leafQueueList];
- this.leafQueueSelected = '';
this.fetchApplicationsUsingQueryParams();
+ this.setDefaultQueue(leafQueueList);
} else {
this.leafQueueList = [new DropdownItem('-- Select --', '')];
}
});
}
+ setDefaultQueue(queueList: DropdownItem[]): void {
+ const storedPartitionAndQueue = localStorage.getItem('selectedPartitionAndQueue');
+
+ if (!storedPartitionAndQueue || storedPartitionAndQueue.indexOf(':') < 0) {
+ setTimeout(() => this.openQueueSelection(), 0);
+ return;
+ }
+
+ const [storedPartition, storedQueue] = storedPartitionAndQueue.split(':');
+ if (this.partitionSelected !== storedPartition) return;
+
+ const storedQueueDropdownItem = queueList.find((queue) => queue.value === storedQueue);
+ if (storedQueueDropdownItem) {
+ this.leafQueueSelected = storedQueueDropdownItem.value;
+ this.fetchAppListForPartitionAndQueue(this.partitionSelected, this.leafQueueSelected);
+ return;
+ } else {
+ this.leafQueueSelected = '';
+ this.appDataSource.data = [];
+ setTimeout(() => this.openQueueSelection(), 0); // Allows render to finish and then opens the queue select dropdown
+ }
+ }
+
generateLeafQueueList(rootQueue: QueueInfo, list: DropdownItem[] = []): DropdownItem[] {
if (rootQueue && rootQueue.isLeaf) {
list.push(new DropdownItem(rootQueue.queueName, rootQueue.queueName));
@@ -205,6 +230,7 @@
this.partitionSelected = partitionName;
this.leafQueueSelected = queueName;
this.fetchAppListForPartitionAndQueue(partitionName, queueName);
+ CommonUtil.setStoredQueueAndPartition(partitionName, queueName);
}
this.router.navigate([], {
@@ -284,6 +310,7 @@
this.partitionSelected = selected.value;
this.appDataSource.data = [];
this.removeRowSelection();
+ this.clearQueueSelection();
this.fetchQueuesForPartition(this.partitionSelected);
} else {
this.searchText = '';
@@ -291,6 +318,7 @@
this.leafQueueSelected = '';
this.appDataSource.data = [];
this.removeRowSelection();
+ this.clearQueueSelection();
}
}
@@ -301,35 +329,51 @@
this.appDataSource.data = [];
this.removeRowSelection();
this.fetchAppListForPartitionAndQueue(this.partitionSelected, this.leafQueueSelected);
+ CommonUtil.setStoredQueueAndPartition(this.partitionSelected, this.leafQueueSelected);
} else {
this.searchText = '';
this.leafQueueSelected = '';
this.appDataSource.data = [];
this.removeRowSelection();
+ this.clearQueueSelection();
}
}
- formatResources(colValue:string):string[]{
- const arr:string[]=colValue.split("<br/>")
+ formatResources(colValue: string): string[] {
+ const arr: string[] = colValue.split('<br/>');
// Check if there are "cpu" or "Memory" elements in the array
- const hasCpu = arr.some((item) => item.toLowerCase().includes("cpu"));
- const hasMemory = arr.some((item) => item.toLowerCase().includes("memory"));
+ const hasCpu = arr.some((item) => item.toLowerCase().includes('cpu'));
+ const hasMemory = arr.some((item) => item.toLowerCase().includes('memory'));
if (!hasCpu) {
- arr.unshift("CPU: n/a");
+ arr.unshift('CPU: n/a');
}
if (!hasMemory) {
- arr.unshift("Memory: n/a");
+ arr.unshift('Memory: n/a');
}
// Concatenate the two arrays, with "cpu" and "Memory" elements first
- const cpuAndMemoryElements = arr.filter((item) => item.toLowerCase().includes("CPU") || item.toLowerCase().includes("Memory"));
- const otherElements = arr.filter((item) => !item.toLowerCase().includes("CPU") && !item.toLowerCase().includes("Memory"));
+ const cpuAndMemoryElements = arr.filter(
+ (item) => item.toLowerCase().includes('CPU') || item.toLowerCase().includes('Memory')
+ );
+ const otherElements = arr.filter(
+ (item) => !item.toLowerCase().includes('CPU') && !item.toLowerCase().includes('Memory')
+ );
const result = cpuAndMemoryElements.concat(otherElements);
return result;
}
- toggle(){
+ clearQueueSelection() {
+ CommonUtil.setStoredQueueAndPartition('');
+ this.leafQueueSelected = '';
+ this.openQueueSelection();
+ }
+
+ openQueueSelection() {
+ this.queueSelect.open();
+ }
+
+ toggle() {
this.detailToggle = !this.detailToggle;
}
}
diff --git a/src/app/components/nodes-view/nodes-view.component.ts b/src/app/components/nodes-view/nodes-view.component.ts
index 9bcf58a..e4c4f51 100644
--- a/src/app/components/nodes-view/nodes-view.component.ts
+++ b/src/app/components/nodes-view/nodes-view.component.ts
@@ -39,9 +39,8 @@
export class NodesViewComponent implements OnInit {
@ViewChild('nodesViewMatPaginator', { static: true }) nodePaginator!: MatPaginator;
@ViewChild('allocationMatPaginator', { static: true }) allocPaginator!: MatPaginator;
- @ViewChild('nodeSort', {static: true }) nodeSort!: MatSort;
- @ViewChild('allocSort', {static: true }) allocSort!: MatSort;
-
+ @ViewChild('nodeSort', { static: true }) nodeSort!: MatSort;
+ @ViewChild('allocSort', { static: true }) allocSort!: MatSort;
nodeDataSource = new MatTableDataSource<NodeInfo>([]);
nodeColumnDef: ColumnDef[] = [];
@@ -58,7 +57,10 @@
detailToggle: boolean = false;
filterValue: string = '';
- constructor(private scheduler: SchedulerService, private spinner: NgxSpinnerService) {}
+ constructor(
+ private scheduler: SchedulerService,
+ private spinner: NgxSpinnerService
+ ) {}
ngOnInit() {
this.nodeDataSource.paginator = this.nodePaginator;
@@ -118,12 +120,13 @@
this.partitionList.push(new PartitionInfo(part.name, part.name));
});
- this.partitionSelected = list[0].name;
+ this.partitionSelected = CommonUtil.getStoredPartition(list[0].name);
this.fetchNodeListForPartition(this.partitionSelected);
} else {
this.partitionList = [new PartitionInfo('-- Select --', '')];
this.partitionSelected = '';
this.nodeDataSource.data = [];
+ CommonUtil.setStoredQueueAndPartition('');
}
});
}
@@ -192,95 +195,103 @@
return this.allocDataSource.data && this.allocDataSource.data.length === 0;
}
- formatColumn(){
- if(this.nodeDataSource.data.length===0){
+ formatColumn() {
+ if (this.nodeDataSource.data.length === 0) {
return;
}
- this.nodeColumnIds.forEach((colId)=>{
- if (colId==='indicatorIcon'){
+ this.nodeColumnIds.forEach((colId) => {
+ if (colId === 'indicatorIcon') {
return;
}
// Verify whether all cells in the column are empty.
- let isEmpty:boolean = true;
+ let isEmpty: boolean = true;
Object.values(this.nodeDataSource.data).forEach((node) => {
- Object.entries(node).forEach(entry => {
+ Object.entries(node).forEach((entry) => {
const [key, value] = entry;
- if (key===colId && !(value==='' || value==='n/a')){
- isEmpty=false;
+ if (key === colId && !(value === '' || value === 'n/a')) {
+ isEmpty = false;
}
});
});
-
- if (isEmpty){
- this.nodeColumnIds = this.nodeColumnIds.filter(el => el!==colId);
- this.nodeColumnIds = this.nodeColumnIds.filter(colId => colId!=="attributes");
+
+ if (isEmpty) {
+ this.nodeColumnIds = this.nodeColumnIds.filter((el) => el !== colId);
+ this.nodeColumnIds = this.nodeColumnIds.filter((colId) => colId !== 'attributes');
}
- })
+ });
}
onPartitionSelectionChanged(selected: MatSelectChange) {
this.partitionSelected = selected.value;
this.clearRowSelection();
this.fetchNodeListForPartition(this.partitionSelected);
+ CommonUtil.setStoredQueueAndPartition(this.partitionSelected);
}
- formatResources(colValue:string):string[]{
- const arr:string[]=colValue.split("<br/>");
+ formatResources(colValue: string): string[] {
+ const arr: string[] = colValue.split('<br/>');
// Check if there are "cpu" or "Memory" elements in the array
- const hasCpu = arr.some((item) => item.toLowerCase().includes("cpu"));
- const hasMemory = arr.some((item) => item.toLowerCase().includes("memory"));
+ const hasCpu = arr.some((item) => item.toLowerCase().includes('cpu'));
+ const hasMemory = arr.some((item) => item.toLowerCase().includes('memory'));
if (!hasCpu) {
- arr.unshift("CPU: n/a");
+ arr.unshift('CPU: n/a');
}
if (!hasMemory) {
- arr.unshift("Memory: n/a");
+ arr.unshift('Memory: n/a');
}
// Concatenate the two arrays, with "cpu" and "Memory" elements first
- const cpuAndMemoryElements = arr.filter((item) => item.toLowerCase().includes("CPU") || item.toLowerCase().includes("Memory"));
- const otherElements = arr.filter((item) => !item.toLowerCase().includes("CPU") && !item.toLowerCase().includes("Memory"));
+ const cpuAndMemoryElements = arr.filter(
+ (item) => item.toLowerCase().includes('CPU') || item.toLowerCase().includes('Memory')
+ );
+ const otherElements = arr.filter(
+ (item) => !item.toLowerCase().includes('CPU') && !item.toLowerCase().includes('Memory')
+ );
const result = cpuAndMemoryElements.concat(otherElements);
return result;
}
- formatAttribute(attributes:any):string[]{
- let result:string[]=[];
- Object.entries(attributes).forEach(entry=>{
+ formatAttribute(attributes: any): string[] {
+ let result: string[] = [];
+ Object.entries(attributes).forEach((entry) => {
const [key, value] = entry;
- if (value==="" || key.includes("si")){
- return
+ if (value === '' || key.includes('si')) {
+ return;
}
- result.push(key+':'+value);
- })
+ result.push(key + ':' + value);
+ });
return result;
}
- toggle(){
+ toggle() {
this.detailToggle = !this.detailToggle;
this.displayAttribute(this.detailToggle);
}
- displayAttribute(toggle:boolean) {
- if (toggle){
+ displayAttribute(toggle: boolean) {
+ if (toggle) {
this.nodeColumnIds = [
...this.nodeColumnIds.slice(0, 1),
- "attributes",
- ...this.nodeColumnIds.slice(1)
- ];
- }else{
- this.nodeColumnIds=this.nodeColumnIds.filter(colId => colId!=="attributes");
+ 'attributes',
+ ...this.nodeColumnIds.slice(1),
+ ];
+ } else {
+ this.nodeColumnIds = this.nodeColumnIds.filter((colId) => colId !== 'attributes');
}
}
- filterPredicate: ((data: NodeInfo, filter: string) => boolean) = (data: NodeInfo, filter: string): boolean => {
+ filterPredicate: (data: NodeInfo, filter: string) => boolean = (
+ data: NodeInfo,
+ filter: string
+ ): boolean => {
// a deep copy of the NodeInfo with formatted attributes for filtering
const deepCopy = JSON.parse(JSON.stringify(data));
- Object.entries(deepCopy.attributes).forEach(entry=>{
+ Object.entries(deepCopy.attributes).forEach((entry) => {
const [key, value] = entry;
- deepCopy.attributes[key]= `${key}:${value}`
- })
+ deepCopy.attributes[key] = `${key}:${value}`;
+ });
const objectString = JSON.stringify(deepCopy).toLowerCase();
return objectString.includes(filter);
};
diff --git a/src/app/components/queues-view/queues-view.component.ts b/src/app/components/queues-view/queues-view.component.ts
index 389f42e..253b37f 100644
--- a/src/app/components/queues-view/queues-view.component.ts
+++ b/src/app/components/queues-view/queues-view.component.ts
@@ -92,12 +92,14 @@
this.partitionList.push(new PartitionInfo(part.name, part.name));
});
- this.partitionSelected = list[0].name;
+ this.partitionSelected = CommonUtil.getStoredPartition(list[0].name);
+
this.fetchSchedulerQueuesForPartition(this.partitionSelected);
} else {
this.partitionList = [new PartitionInfo('-- Select --', '')];
this.partitionSelected = '';
this.queueList = {};
+ CommonUtil.setStoredQueueAndPartition('');
}
});
}
@@ -208,9 +210,11 @@
this.partitionSelected = selected.value;
this.closeQueueDrawer();
this.fetchSchedulerQueuesForPartition(this.partitionSelected);
+ CommonUtil.setStoredQueueAndPartition(this.partitionSelected);
} else {
this.partitionSelected = '';
this.queueList = {};
+ CommonUtil.setStoredQueueAndPartition('');
}
}
diff --git a/src/app/utils/common.util.ts b/src/app/utils/common.util.ts
index 98f2a18..c810873 100644
--- a/src/app/utils/common.util.ts
+++ b/src/app/utils/common.util.ts
@@ -115,4 +115,17 @@
return a.localeCompare(b); // Other resources will be in lexicographic order
}
}
+
+ static getStoredPartition(defaultValue = ''): string {
+ const storedPartition = localStorage.getItem('selectedPartitionAndQueue');
+
+ if (storedPartition && storedPartition.indexOf(':') > 0) return storedPartition.split(':')[0];
+
+ return defaultValue;
+ }
+
+ static setStoredQueueAndPartition(partition: string, queue = '') {
+ if (partition) localStorage.setItem('selectedPartitionAndQueue', `${partition}:${queue}`);
+ else localStorage.removeItem('selectedPartitionAndQueue');
+ }
}