mirror of
https://github.com/bec-project/bec_atlas.git
synced 2025-07-13 22:51:49 +02:00
feat: added deployment service and component
This commit is contained in:
8
frontend/bec_atlas/src/app/core/model/deployment.ts
Normal file
8
frontend/bec_atlas/src/app/core/model/deployment.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface Deployment {
|
||||
_id: string;
|
||||
realm_id: string;
|
||||
name: string;
|
||||
owner_groups: string[];
|
||||
access_groups: string[];
|
||||
config_templates: string[];
|
||||
}
|
10
frontend/bec_atlas/src/app/core/model/realm.ts
Normal file
10
frontend/bec_atlas/src/app/core/model/realm.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Deployment } from './deployment';
|
||||
|
||||
export interface Realm {
|
||||
_id: string;
|
||||
realm_id: string;
|
||||
name: string;
|
||||
owner_groups: Array<string>;
|
||||
access_groups: Array<string>;
|
||||
deployments: Array<Deployment>;
|
||||
}
|
@ -10,6 +10,8 @@
|
||||
/>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<!-- Username expansion panel -->
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-icon>account_circle</mat-icon>
|
||||
@ -20,7 +22,10 @@
|
||||
<button mat-button>Settings</button>
|
||||
<button mat-button>Logout</button>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<!-- Scan Table -->
|
||||
<button
|
||||
mat-button
|
||||
class="menu-item"
|
||||
@ -29,11 +34,17 @@
|
||||
<mat-icon>home</mat-icon>
|
||||
<span class="menu-text">Data Browser</span>
|
||||
</button>
|
||||
<mat-expansion-panel>
|
||||
|
||||
<!-- Experiment Control Expansion -->
|
||||
<mat-expansion-panel
|
||||
(opened)="panelOpened()"
|
||||
[hideToggle]="hideExperimentPanel"
|
||||
>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-icon>science</mat-icon>
|
||||
<span class="menu-text">Experiment Control</span>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<button
|
||||
mat-button
|
||||
class="menu-item"
|
||||
@ -48,19 +59,29 @@
|
||||
>
|
||||
<span class="menu-text">Admin</span>
|
||||
</button>
|
||||
|
||||
<button mat-button class="menu-item" (click)="openDeploymentDialog()">
|
||||
<span class="menu-text">{{ selectOrSwitchButtonTitle }}</span>
|
||||
</button>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<!-- Help -->
|
||||
<button mat-button class="menu-item">
|
||||
<mat-icon>help</mat-icon>
|
||||
<span class="menu-text">Help</span>
|
||||
</button>
|
||||
<div class="spacer"></div>
|
||||
@if ((realm_name) && (deployment_name)) {
|
||||
<div class="footer">
|
||||
<mat-divider></mat-divider>
|
||||
<div class="menu-text">{{ realm_name }}</div>
|
||||
<div class="menu-text">{{ deployment_name }}</div>
|
||||
</div>
|
||||
}
|
||||
</mat-sidenav>
|
||||
|
||||
<mat-sidenav-content>
|
||||
<div class="content">
|
||||
<!-- <app-device-box [device]="'samx'" [signal_name]="'samx'"></app-device-box>
|
||||
<app-device-box [device]="'samy'" [signal_name]="'samy'"></app-device-box>
|
||||
<app-queue-table></app-queue-table> -->
|
||||
<!-- <app-scan-table></app-scan-table> -->
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</mat-sidenav-content>
|
||||
|
@ -15,6 +15,12 @@ mat-sidenav {
|
||||
@include mat.elevation(3);
|
||||
}
|
||||
|
||||
::ng-deep .mat-drawer-inner-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%; /* Ensure it takes full height */
|
||||
}
|
||||
|
||||
.sidenav.collapsed {
|
||||
width: 60px;
|
||||
}
|
||||
@ -88,3 +94,12 @@ mat-sidenav-content {
|
||||
background-color: var(--mat-sys-surface-dim);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex-grow: 1; /* Pushes everything below it down */
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-bottom: 10px;
|
||||
color: var(--mat-sys-primary);
|
||||
}
|
||||
|
@ -1,43 +1,92 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DeviceBoxComponent } from '../device-box/device-box.component';
|
||||
import { QueueTableComponent } from '../queue-table/queue-table.component';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ScanTableComponent } from '../scan-table/scan-table.component';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
|
||||
import { DeploymentService } from '../deployment.service';
|
||||
import {
|
||||
MatDialog,
|
||||
MatDialogModule,
|
||||
MatDialogConfig,
|
||||
} from '@angular/material/dialog';
|
||||
import { DeploymentSelectionComponent } from '../deployment-selection/deployment-selection.component';
|
||||
import { RedisConnectorService } from '../core/redis-connector.service';
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
imports: [
|
||||
DeviceBoxComponent,
|
||||
CommonModule,
|
||||
QueueTableComponent,
|
||||
MatExpansionModule,
|
||||
MatDividerModule,
|
||||
MatSidenavModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
ScanTableComponent,
|
||||
MatDialogModule,
|
||||
RouterModule,
|
||||
],
|
||||
providers: [DeploymentService, RedisConnectorService],
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrl: './dashboard.component.scss',
|
||||
})
|
||||
export class DashboardComponent {
|
||||
// isScreenSmall = false;
|
||||
readonly dialog = inject(MatDialog);
|
||||
deployment_name: string = '';
|
||||
realm_name: string = '';
|
||||
hideExperimentPanel = true;
|
||||
selectOrSwitchButtonTitle = 'Select Deployment';
|
||||
|
||||
constructor(private breakpointObserver: BreakpointObserver) {}
|
||||
constructor(
|
||||
private breakpointObserver: BreakpointObserver,
|
||||
private deploymentService: DeploymentService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
// check if the user has already selected a deployment
|
||||
// if not, open the deployment selection dialog
|
||||
this.deploymentService.selectedDeployment.subscribe((deployment) => {
|
||||
if (deployment) {
|
||||
console.log('Updating deployment name to: ', deployment.name);
|
||||
this.deployment_name = deployment.name;
|
||||
this.realm_name = deployment.realm_id;
|
||||
this.hideExperimentPanel = false;
|
||||
this.selectOrSwitchButtonTitle = 'Switch Deployment';
|
||||
} else {
|
||||
this.deployment_name = '';
|
||||
this.realm_name = '';
|
||||
this.hideExperimentPanel = true;
|
||||
this.selectOrSwitchButtonTitle = 'Select Deployment';
|
||||
}
|
||||
});
|
||||
|
||||
// this.breakpointObserver
|
||||
// .observe([Breakpoints.Small, Breakpoints.XSmall])
|
||||
// .subscribe((result) => {
|
||||
// this.isScreenSmall = result.matches;
|
||||
// });
|
||||
}
|
||||
|
||||
openDeploymentDialog() {
|
||||
// open deployment dialog
|
||||
let dialogConfig: MatDialogConfig = {
|
||||
disableClose: true,
|
||||
width: '80%',
|
||||
};
|
||||
let dialogRef = this.dialog.open(
|
||||
DeploymentSelectionComponent,
|
||||
dialogConfig
|
||||
);
|
||||
dialogRef.afterClosed().subscribe((result) => {
|
||||
this.deploymentService.selectDeployment(result);
|
||||
});
|
||||
}
|
||||
|
||||
panelOpened() {
|
||||
if (!this.deploymentService.selectedDeployment.value) {
|
||||
this.openDeploymentDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
<mat-dialog-content>
|
||||
<h2>Deployments</h2>
|
||||
<form [formGroup]="form">
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Select Beamline</mat-label>
|
||||
<mat-select formControlName="beamline">
|
||||
<mat-option *ngFor="let beamline of beamlines" [value]="beamline._id">
|
||||
{{ beamline.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Select Deployment</mat-label>
|
||||
<mat-select
|
||||
formControlName="deployment"
|
||||
[disabled]="!form.get('beamline')?.value"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let deployment of deployments"
|
||||
[value]="deployment._id"
|
||||
>
|
||||
{{ deployment.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button mat-button (click)="applySelection()" cdkFocusInitial>Apply</button>
|
||||
</mat-dialog-actions>
|
||||
</mat-dialog-content>
|
@ -0,0 +1,8 @@
|
||||
.deployment-selection-card {
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
form {
|
||||
padding-top: 10px;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DeploymentSelectionComponent } from './deployment-selection.component';
|
||||
|
||||
describe('DeploymentSelectionComponent', () => {
|
||||
let component: DeploymentSelectionComponent;
|
||||
let fixture: ComponentFixture<DeploymentSelectionComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DeploymentSelectionComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DeploymentSelectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,76 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { RealmDataService } from '../core/remote-data.service';
|
||||
import { Realm } from '../core/model/realm';
|
||||
import { Deployment } from '../core/model/deployment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-deployment-selection',
|
||||
imports: [
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
ReactiveFormsModule,
|
||||
CommonModule,
|
||||
],
|
||||
templateUrl: './deployment-selection.component.html',
|
||||
styleUrl: './deployment-selection.component.scss',
|
||||
})
|
||||
export class DeploymentSelectionComponent {
|
||||
form: FormGroup;
|
||||
beamlines: Realm[] = [];
|
||||
deployments: Deployment[] = [];
|
||||
selectedDeployment: Deployment | null = null;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private realmDataService: RealmDataService,
|
||||
private dialogRef: MatDialogRef<DeploymentSelectionComponent>
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
beamline: [''],
|
||||
deployment: [''],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// fetch deployments
|
||||
this.realmDataService
|
||||
.getRealmsWithDeploymentAccess(true)
|
||||
.subscribe((realms) => {
|
||||
console.log(realms);
|
||||
this.beamlines = realms;
|
||||
// this.deployments = deployments;
|
||||
});
|
||||
this.form.get('beamline')?.valueChanges.subscribe((beamlineId) => {
|
||||
if (beamlineId) {
|
||||
let beamline = this.beamlines.find((realm) => realm._id === beamlineId);
|
||||
this.deployments = beamline?.deployments ?? [];
|
||||
}
|
||||
});
|
||||
this.form.get('deployment')?.valueChanges.subscribe((deploymentId) => {
|
||||
if (deploymentId) {
|
||||
let selectedDeployment = this.deployments.find(
|
||||
(deployment) => deployment._id === deploymentId
|
||||
);
|
||||
if (selectedDeployment) {
|
||||
this.selectedDeployment = selectedDeployment;
|
||||
} else {
|
||||
this.selectedDeployment = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
applySelection() {
|
||||
this.dialogRef.close(this.selectedDeployment);
|
||||
}
|
||||
}
|
16
frontend/bec_atlas/src/app/deployment.service.spec.ts
Normal file
16
frontend/bec_atlas/src/app/deployment.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DeploymentService } from './deployment.service';
|
||||
|
||||
describe('DeploymentService', () => {
|
||||
let service: DeploymentService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(DeploymentService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
37
frontend/bec_atlas/src/app/deployment.service.ts
Normal file
37
frontend/bec_atlas/src/app/deployment.service.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { DeploymentDataService } from './core/remote-data.service';
|
||||
import { Deployment } from './core/model/deployment';
|
||||
|
||||
@Injectable()
|
||||
export class DeploymentService {
|
||||
selectedDeployment = new BehaviorSubject<Deployment | null>(null);
|
||||
|
||||
constructor(private deploymentDataService: DeploymentDataService) {
|
||||
// check the local storage for a selected deployment
|
||||
const deployment = sessionStorage.getItem('selected_deployment');
|
||||
if (!deployment) {
|
||||
return;
|
||||
}
|
||||
this.deploymentDataService
|
||||
.getDeployment(deployment)
|
||||
.subscribe((deployment) => {
|
||||
if (deployment) {
|
||||
this.selectedDeployment.next(deployment);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectDeployment(deployment: Deployment | null): void {
|
||||
// save the selected deployment to local storage
|
||||
if (!deployment) {
|
||||
sessionStorage.removeItem('selected_deployment');
|
||||
this.selectedDeployment.next(null);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.setItem('selected_deployment', deployment._id);
|
||||
|
||||
this.selectedDeployment.next(deployment);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user