Parcourir la source

admin-panel: implemented url based sub paths; fixed UX while creating new

Christian Kahlau il y a 2 ans
Parent
commit
c37a9be439

+ 2 - 1
ng/src/app/app-routing.module.ts

@@ -12,7 +12,8 @@ const routes: Routes = [
   { path: 'svc/:serverID/:serviceID', component: ServiceCheckDetailPageComponent },
   { path: 'admin', component: AdminPanelComponent },
   { path: 'admin/svc', component: AdminServiceChecksPageComponent },
-  { path: 'admin/svc/:serverID/:serviceID', component: AdminServiceChecksPageComponent }
+  { path: 'admin/svc/:serverID', component: AdminServiceChecksPageComponent },
+  { path: 'admin/svc/:serverID/:checkID', component: AdminServiceChecksPageComponent }
 ];
 
 @NgModule({

+ 16 - 17
ng/src/app/components/service-check-form/service-check-form.component.ts

@@ -1,5 +1,5 @@
 import { Component, Input, OnInit, ViewChild } from '@angular/core';
-import { FormControl, FormGroup, FormArray, FormBuilder } from '@angular/forms';
+import { FormGroup, FormBuilder } from '@angular/forms';
 import { faMinusSquare, faPlusSquare } from '@fortawesome/free-solid-svg-icons';
 
 import { deepCopy } from '../../../../../common/util/object-utils';
@@ -19,12 +19,12 @@ export class ServiceCheckFormComponent implements OnInit {
   public fa = { plus: faPlusSquare, delete: faMinusSquare };
 
   serviceCheckForm: FormGroup = this.formBuilder.group({
-    id: -1, //constant for defaultValues
-    title: '', //constant for defaultValues
-    active: undefined, //constant for defaultValues
-    url: '', //constant for defaultValues
-    interval: 10, //constant for defaultValues
-    timeout: 10, //constant for defaultValuess
+    id: -1,
+    title: '',
+    active: true,
+    url: '',
+    interval: 300,
+    timeout: 10000,
     notify: true,
     notifyThreshold: 3
   });
@@ -36,15 +36,14 @@ export class ServiceCheckFormComponent implements OnInit {
   }
 
   async save() {
-    try {
-      this.serviceCheckForm.updateValueAndValidity();
-      const copy = deepCopy(this.serviceCheckForm.value as HttpCheckConfig);
-      copy.checks = this.checkEditor.collect();
-      const savedCheck = await this.serviceApi.saveServiceCheck(this.serviceCheck.serverId as number, copy);
-      this.serviceCheckForm.patchValue(savedCheck);
-      this.serviceCheck = savedCheck;
-    } catch (error: any) {
-      console.error(error);
-    }
+    this.serviceCheckForm.updateValueAndValidity();
+    const copy = deepCopy(this.serviceCheckForm.value as HttpCheckConfig);
+    copy.checks = this.checkEditor.collect();
+
+    const savedCheck = await this.serviceApi.saveServiceCheck(this.serviceCheck.serverId as number, copy);
+    this.serviceCheckForm.patchValue(savedCheck);
+    this.serviceCheck = savedCheck;
+
+    return savedCheck;
   }
 }

+ 13 - 5
ng/src/app/pages/admin-panel/admin-service-checks-page/admin-service-checks-page.component.html

@@ -1,9 +1,9 @@
 <h3>Service Checks</h3>
 
-<div class="border d-flex flex-fill overflow-hidden">
+<div *ngIf="serverConfigs?.length; else loadingServerConfs" class="border d-flex flex-fill overflow-hidden">
   <ul ngbNav #nav="ngbNav" class="border-end nav-pills h-100" style="width: 250px" orientation="vertical" [(activeId)]="activeId">
     <li *ngFor="let serverConfig of serverConfigs; index as s" [ngbNavItem]="s" class="bg-light text-primary">
-      <a ngbNavLink (click)="fetchServiceChecks(serverConfig.id)" class="d-flex btn btn-toolbar">
+      <a ngbNavLink [routerLink]="'/admin/svc/' + serverConfig.id" class="d-flex btn btn-toolbar">
         <fa-icon class="pe-2" [icon]="fa.server"></fa-icon>
         <span class="flex-fill text-start">{{ serverConfig.title }}</span>
         <fa-icon class="ps-2" [icon]="fa.angleRight"></fa-icon>
@@ -11,12 +11,17 @@
       <ng-template ngbNavContent>
         <div class="d-flex flex-column">
           <div class="btn-group d-block">
-            <button class="btn btn-primary float-end m-1" (click)="addServiceCheck(serverConfig.id)">
+            <button class="btn btn-primary float-end m-1" [disabled]="params?.checkID === -1" (click)="addServiceCheck(serverConfig.id)">
               <fa-icon class="pe-2" [icon]="fa.plus"></fa-icon>Add Service Check
             </button>
           </div>
-          <ngb-accordion #acc="ngbAccordion" className="flex-fill">
-            <ngb-panel *ngFor="let serviceCheck of serviceChecks">
+          <ngb-accordion
+            #acc="ngbAccordion"
+            [closeOthers]="true"
+            className="flex-fill"
+            [activeIds]="params?.activeIds ?? []"
+            (panelChange)="onAccordeonChange($event)">
+            <ngb-panel *ngFor="let serviceCheck of serviceChecks" [id]="PANEL_ID_PFX + serviceCheck.id">
               <ng-template ngbPanelHeader let-opened="opened">
                 <button class="accordion-button" ngbPanelToggle [class.collapsed]="!opened" [ngClass]="{ 'bg-primary text-white': opened }">
                   <p class="flex-fill m-0">{{ serviceCheck.title }}</p>
@@ -35,3 +40,6 @@
   </ul>
   <div [ngbNavOutlet]="nav" class="flex-fill h-100 overflow-auto"></div>
 </div>
+<ng-template #loadingServerConfs>
+  <app-status-timeline-widget></app-status-timeline-widget>
+</ng-template>

+ 90 - 7
ng/src/app/pages/admin-panel/admin-service-checks-page/admin-service-checks-page.component.ts

@@ -1,5 +1,7 @@
 import { Component, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
 import { faAngleRight, faPlus, faSave, faServer } from '@fortawesome/free-solid-svg-icons';
+import { NgbAccordion, NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap';
 
 import { ServiceCheckFormComponent } from 'src/app/components/service-check-form/service-check-form.component';
 import { ServiceApiService } from 'src/app/services/service-api.service';
@@ -12,24 +14,65 @@ import { ServerApiService } from 'src/app/services/server-api.service';
   host: { class: 'd-flex flex-column h-100' }
 })
 export class AdminServiceChecksPageComponent {
+  public PANEL_ID_PFX = 'acc-svc-checks-';
+  public PANEL_ID_REG = /^acc-svc-checks-(-?\d+)$/;
+
   @ViewChild(ServiceCheckFormComponent) formRef!: ServiceCheckFormComponent;
-  public serverConfigs: ServerConfig[] = [];
+  @ViewChild('acc') accordeonRef!: NgbAccordion;
+  public serverConfigs?: ServerConfig[];
   public serviceChecks: HttpCheckConfig[] = [];
   public fa = { save: faSave, server: faServer, angleRight: faAngleRight, plus: faPlus };
 
   public activeId = 0;
+  public params?: { serverID?: number; checkID?: number; activeIds?: string[] };
 
-  constructor(private serviceApi: ServiceApiService, apiService: ServerApiService) {
+  constructor(apiService: ServerApiService, route: ActivatedRoute, private router: Router, private serviceApi: ServiceApiService) {
     apiService.serverConfigs$.subscribe(data => {
       this.serverConfigs = data;
-      if (data.length > 0) {
-        this.fetchServiceChecks(data[this.activeId].id);
+      this.syncInit();
+    });
+    route.params.subscribe({
+      next: params => {
+        if (!params) return;
+        this.params = {
+          serverID: params['serverID'] ? Number(params['serverID']) : undefined,
+          checkID: params['checkID'] ? Number(params['checkID']) : undefined
+        };
+        this.syncInit();
       }
     });
   }
 
   ngOnInit(): void {}
 
+  async syncInit() {
+    if (!this.params) return;
+    if (!this.serverConfigs) return;
+
+    if (this.serverConfigs.length > 0) {
+      if (!this.params.serverID) {
+        // redirect to first server's id
+        this.router.navigateByUrl(`/admin/svc/${this.serverConfigs[0].id}`);
+        return;
+      }
+
+      // set selected nav item and load service checks
+      this.activeId = this.serverConfigs.findIndex(c => c.id === this.params?.serverID);
+      await this.fetchServiceChecks(this.params.serverID);
+
+      if (this.params.checkID) {
+        // open accordeon panel
+        if (this.params.checkID === -1) {
+          this.addServiceCheck(this.params.serverID);
+        } else {
+          const activePanelId = `${this.PANEL_ID_PFX}${this.params.checkID}`;
+          this.params.activeIds = [activePanelId];
+          setTimeout(() => this.scrollIntoView(activePanelId));
+        }
+      }
+    }
+  }
+
   async fetchServiceChecks(serverId: number) {
     try {
       this.serviceChecks = await this.serviceApi.loadServiceChecks(serverId);
@@ -38,12 +81,52 @@ export class AdminServiceChecksPageComponent {
     }
   }
 
-  saveServiceCheck(event: Event) {
+  private scrollIntoView(panelId: string) {
+    const contentPanel = document.getElementById(panelId);
+    let parent: HTMLElement | null = null;
+    while ((parent = contentPanel?.parentElement ?? null)) {
+      if (parent.classList.contains('accordion-item')) break;
+    }
+    parent?.scrollIntoView();
+  }
+
+  onAccordeonChange(event: NgbPanelChangeEvent) {
+    if (event.nextState) {
+      const checkId = this.PANEL_ID_REG.exec(event.panelId)?.[1];
+      if (this.params) this.params.checkID = Number(checkId);
+      history.pushState(null, '', `/admin/svc/${this.params?.serverID}/${checkId}`);
+    } else {
+      if (this.params) this.params.checkID = undefined;
+      history.pushState(null, '', `/admin/svc/${this.params?.serverID}`);
+    }
+  }
+
+  async saveServiceCheck(event: Event) {
     event.stopPropagation();
-    this.formRef?.save();
+    try {
+      if (!this.params || !this.params.serverID) return;
+      const savedCheck = await this.formRef?.save();
+      const panelId = `${this.PANEL_ID_PFX}${savedCheck.id}`;
+
+      if (this.params.checkID === -1) {
+        await this.fetchServiceChecks(this.params.serverID);
+        this.params.activeIds = [panelId];
+        this.onAccordeonChange({ nextState: true, panelId, preventDefault: () => {} });
+      } else {
+        const idx = this.serviceChecks.findIndex(c => savedCheck.id === c.id);
+        if (idx >= 0) this.serviceChecks[idx] = savedCheck;
+      }
+    } catch (error) {
+      console.error(error);
+    }
   }
 
   addServiceCheck(serverId: number) {
-    this.serviceChecks.unshift({ serverId, checks: [] as string[] } as HttpCheckConfig);
+    const panelId = `${this.PANEL_ID_PFX}-1`;
+    this.serviceChecks.unshift({ id: -1, serverId, checks: [] as string[] } as HttpCheckConfig);
+    if (this.params) {
+      this.params.activeIds = [panelId];
+    }
+    this.onAccordeonChange({ nextState: true, panelId, preventDefault: () => {} });
   }
 }