admin-service-checks-page.component.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { Component, ViewChild } from '@angular/core';
  2. import { ValidationErrors } from '@angular/forms';
  3. import { ActivatedRoute, Router } from '@angular/router';
  4. import { faAngleRight, faPlayCircle, faPlus, faSave, faServer, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
  5. import { NgbAccordion, NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap';
  6. import { ServiceCheckFormComponent } from 'src/app/components/service-check-form/service-check-form.component';
  7. import { ComponentService } from 'src/app/services/component.service';
  8. import { ServiceApiService } from 'src/app/services/service-api.service';
  9. import { ServerApiService } from 'src/app/services/server-api.service';
  10. import { ToastService } from 'src/app/services/toast.service';
  11. @Component({
  12. selector: 'app-admin-service-checks-page',
  13. templateUrl: './admin-service-checks-page.component.html',
  14. styleUrls: ['./admin-service-checks-page.component.scss'],
  15. host: { class: 'd-flex flex-column h-100' }
  16. })
  17. export class AdminServiceChecksPageComponent {
  18. public PANEL_ID_PFX = 'acc-svc-checks-';
  19. public PANEL_ID_REG = /^acc-svc-checks-(-?\d+)$/;
  20. @ViewChild(ServiceCheckFormComponent) formRef!: ServiceCheckFormComponent;
  21. @ViewChild('acc') accordeonRef!: NgbAccordion;
  22. public serverConfigs?: ServerConfig[];
  23. public serviceChecks: HttpCheckConfig[] = [];
  24. public loadingServiceChecks = false;
  25. public fa = { save: faSave, server: faServer, angleRight: faAngleRight, plus: faPlus, trash: faTrashAlt, play: faPlayCircle };
  26. public activeId = 0;
  27. public params?: { serverID?: number; checkID?: number; activeIds?: string[] };
  28. constructor(
  29. apiService: ServerApiService,
  30. private cmpService: ComponentService,
  31. route: ActivatedRoute,
  32. private router: Router,
  33. private serviceApi: ServiceApiService,
  34. private toastService: ToastService
  35. ) {
  36. apiService.serverConfigs$.subscribe(data => {
  37. this.serverConfigs = data;
  38. this.syncInit();
  39. });
  40. route.params.subscribe({
  41. next: params => {
  42. if (!params) return;
  43. this.params = {
  44. serverID: params['serverID'] ? Number(params['serverID']) : undefined,
  45. checkID: params['checkID'] ? Number(params['checkID']) : undefined
  46. };
  47. this.syncInit();
  48. }
  49. });
  50. }
  51. ngOnInit(): void {}
  52. async syncInit() {
  53. if (!this.params) return;
  54. if (!this.serverConfigs) return;
  55. if (this.serverConfigs.length > 0) {
  56. if (!this.params.serverID) {
  57. // redirect to first server's id
  58. this.router.navigateByUrl(`/admin/svc/${this.serverConfigs[0].id}`);
  59. return;
  60. }
  61. // set selected nav item and load service checks
  62. this.activeId = this.serverConfigs.findIndex(c => c.id === this.params?.serverID);
  63. await this.fetchServiceChecks(this.params.serverID);
  64. if (this.params.checkID) {
  65. // open accordeon panel
  66. if (this.params.checkID === -1) {
  67. this.addServiceCheck(this.params.serverID);
  68. } else {
  69. const activePanelId = `${this.PANEL_ID_PFX}${this.params.checkID}`;
  70. this.params.activeIds = [activePanelId];
  71. setTimeout(() => this.scrollIntoView(activePanelId));
  72. }
  73. }
  74. }
  75. }
  76. async fetchServiceChecks(serverId: number) {
  77. this.loadingServiceChecks = true;
  78. try {
  79. this.serviceChecks = await this.serviceApi.loadServiceChecks(serverId);
  80. } catch (error: any) {
  81. console.error(error);
  82. }
  83. this.loadingServiceChecks = false;
  84. }
  85. private scrollIntoView(panelId: string) {
  86. const contentPanel = document.getElementById(panelId);
  87. let parent: HTMLElement | null = null;
  88. while ((parent = contentPanel?.parentElement ?? null)) {
  89. if (parent.classList.contains('accordion-item')) break;
  90. }
  91. parent?.scrollIntoView();
  92. }
  93. onAccordeonChange(event: NgbPanelChangeEvent) {
  94. if (event.nextState) {
  95. const checkId = this.PANEL_ID_REG.exec(event.panelId)?.[1];
  96. if (this.params) this.params.checkID = Number(checkId);
  97. history.pushState(null, '', `/admin/svc/${this.params?.serverID}/${checkId}`);
  98. } else {
  99. if (this.params) this.params.checkID = undefined;
  100. history.pushState(null, '', `/admin/svc/${this.params?.serverID}`);
  101. }
  102. }
  103. async saveServiceCheck(event: Event) {
  104. event.stopPropagation();
  105. try {
  106. if (!this.params || !this.params.serverID) return;
  107. const savedCheck = await this.formRef?.save();
  108. const panelId = `${this.PANEL_ID_PFX}${savedCheck.id}`;
  109. if (this.params.checkID === -1) {
  110. await this.fetchServiceChecks(this.params.serverID);
  111. this.params.activeIds = [panelId];
  112. this.onAccordeonChange({ nextState: true, panelId, preventDefault: () => {} });
  113. } else {
  114. const idx = this.serviceChecks.findIndex(c => savedCheck.id === c.id);
  115. if (idx >= 0) this.serviceChecks[idx] = savedCheck;
  116. }
  117. } catch (error: any) {
  118. if (error.type === 'validation') {
  119. const invalid: ValidationErrors = error.errors;
  120. Object.entries(invalid).forEach(entry => {
  121. this.toastService.error(entry[1], entry[0], 180000);
  122. });
  123. }
  124. console.error(error);
  125. }
  126. }
  127. addServiceCheck(serverId: number) {
  128. const panelId = `${this.PANEL_ID_PFX}-1`;
  129. this.serviceChecks.unshift({ id: -1, serverId, checks: [] as string[] } as HttpCheckConfig);
  130. if (this.params) {
  131. this.params.activeIds = [panelId];
  132. }
  133. this.onAccordeonChange({ nextState: true, panelId, preventDefault: () => {} });
  134. }
  135. async removeServiceCheck(event: Event, checkId: number) {
  136. event.stopPropagation();
  137. try {
  138. if (!this.params?.serverID) return;
  139. if (checkId === -1) {
  140. const decision = await this.cmpService.openConfirmModal({
  141. modalContent: { message: `Really delete unsaved service check?` }
  142. });
  143. if (decision) this.serviceChecks.shift();
  144. } else {
  145. const decision = await this.cmpService.openConfirmModal({
  146. modalContent: { message: `Really delete service check #${checkId}?` }
  147. });
  148. if (!decision) return;
  149. await this.serviceApi.deleteServiceCheck(this.params.serverID, checkId);
  150. const idx = this.serviceChecks.findIndex(c => c.id === checkId);
  151. if (idx >= 0) this.serviceChecks.splice(idx, 1);
  152. }
  153. this.params.activeIds = [];
  154. this.onAccordeonChange({ nextState: false, panelId: '', preventDefault: () => {} });
  155. } catch (error) {
  156. console.error(error);
  157. }
  158. }
  159. public async testServiceCheck(event: Event) {
  160. event.stopPropagation();
  161. await this.formRef?.test();
  162. }
  163. }