mirror of
https://github.com/bec-project/bec_atlas.git
synced 2025-07-13 22:51:49 +02:00
feat: add column settings dialog, no changing column order
This commit is contained in:
@ -1,24 +1,42 @@
|
||||
export interface ScanDataResponse {
|
||||
scan_id?: string;
|
||||
scan_number?: number;
|
||||
status?: "open" | "paused" | "aborted" | "halted" | "closed";
|
||||
session_id?: string;
|
||||
num_points?: number;
|
||||
scan_name?: string;
|
||||
scan_type?: "step" | "fly";
|
||||
dataset_number?: number;
|
||||
scan_report_devices?: string[];
|
||||
user_metadata?: { [key: string]: any };
|
||||
readout_priority?: { [key: "monitored" | "baseline" | "async" | "continuous" | "on_request" | string]: string[] };
|
||||
scan_parameters?: { [key: "exp_time" | "frames_per_trigger" | "settling_time" | "readout_time" | string]: any };
|
||||
request_inputs?: { [key: "arg_bundle" | "inputs" | "kwargs" | string]: any };
|
||||
info?: { [key: string]: any };
|
||||
timestamp?: number;
|
||||
user_data?: { [key: string]: any };
|
||||
name?: string;
|
||||
user_rating?: number;
|
||||
system_rating?: number;
|
||||
user_comments?: string;
|
||||
system_comments?: string;
|
||||
scan_id?: string;
|
||||
scan_number?: number;
|
||||
status?: 'open' | 'paused' | 'aborted' | 'halted' | 'closed';
|
||||
session_id?: string;
|
||||
num_points?: number;
|
||||
scan_name?: string;
|
||||
scan_type?: 'step' | 'fly';
|
||||
dataset_number?: number;
|
||||
scan_report_devices?: string[];
|
||||
user_metadata?: { [key: string]: any };
|
||||
readout_priority?: {
|
||||
[
|
||||
key:
|
||||
| 'monitored'
|
||||
| 'baseline'
|
||||
| 'async'
|
||||
| 'continuous'
|
||||
| 'on_request'
|
||||
| string
|
||||
]: string[];
|
||||
};
|
||||
scan_parameters?: {
|
||||
[
|
||||
key:
|
||||
| 'exp_time'
|
||||
| 'frames_per_trigger'
|
||||
| 'settling_time'
|
||||
| 'readout_time'
|
||||
| string
|
||||
]: any;
|
||||
};
|
||||
request_inputs?: { [key: 'arg_bundle' | 'inputs' | 'kwargs' | string]: any };
|
||||
info?: { [key: string]: any };
|
||||
timestamp?: number;
|
||||
user_data?: { [key: string]: any };
|
||||
name?: string;
|
||||
user_rating?: number;
|
||||
system_rating?: number;
|
||||
user_comments?: string;
|
||||
system_comments?: string;
|
||||
}
|
||||
|
7
frontend/bec_atlas/src/app/core/model/scan-user-data.ts
Normal file
7
frontend/bec_atlas/src/app/core/model/scan-user-data.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface ScanUserData {
|
||||
name: string;
|
||||
user_rating: number;
|
||||
system_rating: number;
|
||||
user_comments: string;
|
||||
system_comments: string;
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Inject, inject, Injectable } from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ServerSettingsService } from '../server-settings.service';
|
||||
import { ScanDataResponse } from './model/scan-data';
|
||||
import { Realm } from './model/realm';
|
||||
import { Deployment } from './model/deployment';
|
||||
import { ScanCountResponse } from './model/scan-count';
|
||||
import { ScanUserData } from './model/scan-user-data';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -42,7 +43,7 @@ export class RemoteDataService {
|
||||
*/
|
||||
protected get<T>(
|
||||
path: string,
|
||||
params: { [key: string]: string | number },
|
||||
params: { [key: string]: string | number | Array<string> },
|
||||
headers: HttpHeaders
|
||||
) {
|
||||
return this.httpClient.get<T>(
|
||||
@ -110,7 +111,7 @@ export class ScanDataService extends RemoteDataService {
|
||||
session_id: sessionId,
|
||||
offset: offset.toString(),
|
||||
limit: limit.toString(),
|
||||
fields: fields ? fields.join(',') : '',
|
||||
fields: fields ? fields : '',
|
||||
sort: sort ? JSON.stringify(sort) : '',
|
||||
includeUserData: includeUserData.toString(),
|
||||
},
|
||||
|
@ -0,0 +1,17 @@
|
||||
<h2 mat-dialog-title>Select Columns</h2>
|
||||
<mat-dialog-content class="column-selection-dialog">
|
||||
|
||||
@for (column of columns; track column; let i = $index) {
|
||||
<mat-checkbox class="checkbox" (change)="handleCheckboxChecked($event, i)" [checked]="column.selected" [name]="column.name" >{{ column.name }}</mat-checkbox><br>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-icon-button (click)="onCancelClick()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="onApplyClick()">
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ColumnSelectionDialogComponent } from './column-selection-dialog.component';
|
||||
|
||||
describe('ColumnSelectionDialogComponent', () => {
|
||||
let component: ColumnSelectionDialogComponent;
|
||||
let fixture: ComponentFixture<ColumnSelectionDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ColumnSelectionDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ColumnSelectionDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatCheckbox, MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import {
|
||||
MatDialogModule,
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
} from '@angular/material/dialog';
|
||||
|
||||
// Models
|
||||
export interface ColumnModel {
|
||||
name: string;
|
||||
selected: boolean;
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-column-selection-dialog',
|
||||
imports: [MatCheckbox, MatDialogModule, MatIconModule, MatButtonModule],
|
||||
templateUrl: './column-selection-dialog.component.html',
|
||||
styleUrl: './column-selection-dialog.component.scss',
|
||||
})
|
||||
export class ColumnSelectionDialogComponent {
|
||||
columns = inject<ColumnModel[]>(MAT_DIALOG_DATA);
|
||||
initialSelection: string[] = [];
|
||||
readonly dialogRef = inject(MatDialogRef<ColumnSelectionDialogComponent>);
|
||||
|
||||
handleCheckboxChecked(event: MatCheckboxChange, index: number): void {
|
||||
this.columns[index].selected = event.checked;
|
||||
}
|
||||
|
||||
onCancelClick(): void {
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
|
||||
onApplyClick(): void {
|
||||
let data = this.columns
|
||||
.filter((column) => column.selected)
|
||||
.map((column) => column.name);
|
||||
this.dialogRef.close(data);
|
||||
}
|
||||
}
|
@ -6,18 +6,24 @@
|
||||
<button mat-icon-button (click)="ngOnInit()">
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="ngOnInit()">
|
||||
<button mat-icon-button (click)="handleRefresh()">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="ngOnInit()">
|
||||
<button mat-icon-button (click)="openDialog()">
|
||||
<mat-icon>settings</mat-icon>
|
||||
</button>
|
||||
<!--
|
||||
<mat-menu #menu="matMenu">
|
||||
@for (column of availableColumns(); track column) {
|
||||
<mat-checkbox class="checkbox" (change)="handleColumnSelection(column)">{{ column }}</mat-checkbox>
|
||||
}
|
||||
</mat-menu> -->
|
||||
</mat-toolbar>
|
||||
<table mat-table *ngIf="tableData() as data" [dataSource]="data" class="mat-elevation-z8">
|
||||
|
||||
<!-- Create table data dynamically -->
|
||||
<ng-container *ngFor="let column of displayedColumns" [matColumnDef]="column">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>
|
||||
<ng-container *ngFor="let column of displayedColumns()" [matColumnDef]="column">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ column | titlecase }} </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (column === 'timestamp') {
|
||||
{{ element[column] * 1000 | date :'HH:mm:ss'}}
|
||||
@ -33,9 +39,9 @@
|
||||
</ng-container>
|
||||
|
||||
<!-- Header Row -->
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns(); sticky: true"></tr>
|
||||
<!-- Data Rows -->
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns()"></tr>
|
||||
</table>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
@ -1,12 +1,11 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
WritableSignal,
|
||||
signal,
|
||||
ViewChild,
|
||||
computed,
|
||||
resource,
|
||||
Signal,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ScanCountService, ScanDataService } from '../core/remote-data.service';
|
||||
import { ScanDataResponse } from '../core/model/scan-data';
|
||||
@ -26,6 +25,10 @@ import { StarRatingModule } from 'angular-star-rating';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
import { ScanCountResponse } from '../core/model/scan-count';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ColumnSelectionDialogComponent } from './column-selection-dialog/column-selection-dialog.component';
|
||||
|
||||
export interface ResourceStatus {
|
||||
status: any;
|
||||
@ -50,10 +53,11 @@ export interface ResourceLoaderParams {
|
||||
MatPaginatorModule,
|
||||
StarRatingModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
],
|
||||
templateUrl: './scan-table.component.html',
|
||||
styleUrl: './scan-table.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ScanTableComponent {
|
||||
tableData: Signal<ScanDataResponse[]>;
|
||||
@ -61,11 +65,10 @@ export class ScanTableComponent {
|
||||
limit = signal<number>(10);
|
||||
offset = signal<number>(0);
|
||||
sessionId = signal<string>('');
|
||||
|
||||
// page: number = this.offset / this.limit;
|
||||
dialog = inject(MatDialog);
|
||||
pageEvent: PageEvent = new PageEvent();
|
||||
sorting: number = -1;
|
||||
displayedColumns: string[] = [
|
||||
displayedColumns = signal<string[]>([
|
||||
'scan_number',
|
||||
'status',
|
||||
'num_points',
|
||||
@ -74,6 +77,29 @@ export class ScanTableComponent {
|
||||
'dataset_number',
|
||||
'timestamp',
|
||||
'user_rating',
|
||||
]);
|
||||
allColumns: string[] = [
|
||||
'scan_id',
|
||||
'scan_number',
|
||||
'status',
|
||||
'session_id',
|
||||
'num_points',
|
||||
'scan_name',
|
||||
'scan_type',
|
||||
'dataset_number',
|
||||
'scan_report_devices',
|
||||
'user_metadata',
|
||||
'readout_priority',
|
||||
'scan_parameters',
|
||||
'request_inputs',
|
||||
'info',
|
||||
'timestamp',
|
||||
'user_data',
|
||||
'name',
|
||||
'user_rating',
|
||||
'system_rating',
|
||||
'user_comments',
|
||||
'system_comments',
|
||||
];
|
||||
ignoredEntries: string[] = [
|
||||
'scan_report_devices',
|
||||
@ -84,21 +110,38 @@ export class ScanTableComponent {
|
||||
'info',
|
||||
];
|
||||
|
||||
availableColumns = computed(() =>
|
||||
this.allColumns.filter((element) => !this.ignoredEntries.includes(element))
|
||||
);
|
||||
|
||||
reloadCriteria = computed(() => ({
|
||||
sessionId: this.sessionId(),
|
||||
offset: this.offset(),
|
||||
limit: this.limit(),
|
||||
column: this.displayedColumns(),
|
||||
}));
|
||||
|
||||
loadScanDataResource = resource({
|
||||
request: () => this.reloadCriteria(),
|
||||
loader: ({ request, abortSignal }): Promise<ScanDataResponse[]> => {
|
||||
let columns = request.column.map((element) =>
|
||||
[
|
||||
'name',
|
||||
'user_rating',
|
||||
'system_rating',
|
||||
'user_comments',
|
||||
'system_comments',
|
||||
].includes(element)
|
||||
? 'user_data'
|
||||
: element
|
||||
);
|
||||
console.log('Columns', columns);
|
||||
return firstValueFrom(
|
||||
this.scanData.getScanData(
|
||||
request.sessionId,
|
||||
request.offset,
|
||||
request.limit,
|
||||
this.displayedColumns,
|
||||
columns,
|
||||
false,
|
||||
{ scan_number: this.sorting }
|
||||
)
|
||||
@ -115,8 +158,18 @@ export class ScanTableComponent {
|
||||
|
||||
handleScanData(data: ScanDataResponse[] | []) {
|
||||
for (const entry of data) {
|
||||
if (entry.user_data && entry.user_data['user_rating']) {
|
||||
if (entry?.user_data !== undefined) {
|
||||
entry.name = entry.user_data['name'];
|
||||
entry.system_rating = entry.user_data['system_rating'];
|
||||
entry.user_rating = entry.user_data['user_rating'];
|
||||
entry.user_comments = entry.user_data['user_comments'];
|
||||
entry.system_comments = entry.user_data['system_comments'];
|
||||
} else {
|
||||
entry.name = '';
|
||||
entry.system_rating = 0;
|
||||
entry.user_rating = 0;
|
||||
entry.user_comments = '';
|
||||
entry.system_comments = '';
|
||||
}
|
||||
}
|
||||
return data;
|
||||
@ -154,4 +207,27 @@ export class ScanTableComponent {
|
||||
this.offset.set(event.pageIndex * event.pageSize);
|
||||
this.limit.set(event.pageSize);
|
||||
}
|
||||
|
||||
handleRefresh() {
|
||||
this.loadScanCountResource.reload();
|
||||
this.loadScanDataResource.reload();
|
||||
}
|
||||
|
||||
openDialog(): void {
|
||||
const dialogRef = this.dialog.open(ColumnSelectionDialogComponent, {
|
||||
data: this.availableColumns().map((element) => ({
|
||||
name: element,
|
||||
selected: this.displayedColumns().includes(element),
|
||||
})),
|
||||
disableClose: true,
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((result: string[] | null) => {
|
||||
if (result !== null) {
|
||||
this.displayedColumns.set(result);
|
||||
// this.handleRefresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleColumnSelection(event: any) {}
|
||||
}
|
||||
|
Reference in New Issue
Block a user