From 9241a80be758a02a6ff5ed1cea4a30d62a4d4e18 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 25 Sep 2024 17:27:17 +0200 Subject: [PATCH 1/3] refs #729. RemoteCalendar and RemoteCalendarRule. First commit, 90% --- ogWebconsole/src/app/app-routing.module.ts | 2 + ogWebconsole/src/app/app.module.ts | 11 +- .../calendar/calendar.component.css | 102 ++++++++++++ .../calendar/calendar.component.html | 58 +++++++ .../calendar/calendar.component.spec.ts | 23 +++ .../components/calendar/calendar.component.ts | 149 ++++++++++++++++++ .../create-calendar-rule.component.css | 32 ++++ .../create-calendar-rule.component.html | 53 +++++++ .../create-calendar-rule.component.spec.ts | 23 +++ .../create-calendar-rule.component.ts | 115 ++++++++++++++ .../create-calendar.component.css | 60 +++++++ .../create-calendar.component.html | 39 +++++ .../create-calendar.component.spec.ts | 23 +++ .../create-calendar.component.ts | 86 ++++++++++ .../app/components/calendar/data.service.ts | 32 ++++ .../app/layout/sidebar/sidebar.component.html | 2 +- 16 files changed, 807 insertions(+), 3 deletions(-) create mode 100644 ogWebconsole/src/app/components/calendar/calendar.component.css create mode 100644 ogWebconsole/src/app/components/calendar/calendar.component.html create mode 100644 ogWebconsole/src/app/components/calendar/calendar.component.spec.ts create mode 100644 ogWebconsole/src/app/components/calendar/calendar.component.ts create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.spec.ts create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.css create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.spec.ts create mode 100644 ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts create mode 100644 ogWebconsole/src/app/components/calendar/data.service.ts diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts index c2e2ca4..ae46aaa 100644 --- a/ogWebconsole/src/app/app-routing.module.ts +++ b/ogWebconsole/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ import {OgbootStatusComponent} from "./components/ogboot/ogboot-status/ogboot-st import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component'; import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component'; import { CommandsComponent } from './components/commands/commands.component'; +import { CalendarComponent } from "./components/calendar/calendar.component"; const routes: Routes = [ { path: '', redirectTo: 'auth/login', pathMatch: 'full' }, { @@ -34,6 +35,7 @@ const routes: Routes = [ { path: 'dhcp', component: OgdhcpComponent }, { path: 'dhcp-subnets', component: OgDhcpSubnetsComponent }, { path: 'commands', component: CommandsComponent }, + { path: 'calendars', component: CalendarComponent }, ], }, { diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 7f82a24..ac45ef0 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -88,6 +88,10 @@ import { CommandDetailComponent } from './components/commands/command-detail/com import { CreateCommandComponent } from './components/commands/create-command/create-command.component'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatNativeDateModule } from '@angular/material/core'; +import { CalendarComponent } from './components/calendar/calendar.component'; +import { CreateCalendarComponent } from './components/calendar/create-calendar/create-calendar.component'; +import {MatRadioButton, MatRadioGroup} from "@angular/material/radio"; +import { CreateCalendarRuleComponent } from './components/calendar/create-calendar-rule/create-calendar-rule.component'; @NgModule({ declarations: [ AppComponent, @@ -132,7 +136,10 @@ import { MatNativeDateModule } from '@angular/material/core'; AddClientsToSubnetComponent, CommandsComponent, CommandDetailComponent, - CreateCommandComponent + CreateCommandComponent, + CalendarComponent, + CreateCalendarComponent, + CreateCalendarRuleComponent ], bootstrap: [AppComponent], imports: [BrowserModule, @@ -169,7 +176,7 @@ import { MatNativeDateModule } from '@angular/material/core'; progressAnimation: 'increasing', closeButton: true } - ), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription + ), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription, MatRadioGroup, MatRadioButton ], schemas: [ CUSTOM_ELEMENTS_SCHEMA, diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.css b/ogWebconsole/src/app/components/calendar/calendar.component.css new file mode 100644 index 0000000..16117cb --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/calendar.component.css @@ -0,0 +1,102 @@ +.title { + font-size: 24px; +} + +.images-button-row { + display: flex; + justify-content: flex-start; + margin-top: 16px; +} + +.divider { + margin: 20px 0; +} + +.lists-container { + padding: 16px; +} + +.imagesLists-container { + flex: 1; +} + +.card.unidad-card { + height: 100%; + box-sizing: border-box; +} + +.image-container { + display: flex; + align-items: center; + margin-bottom: 16px; + border-bottom: 1px solid rgba(122, 122, 122, 0.555); +} + +.image-container h4 { + margin: 0; + flex: 1; +} + +.image-name{ + cursor: pointer; +} + +table { + width: 100%; + margin-top: 50px; +} + +.search-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 0 5px; + box-sizing: border-box; +} + +.search-string { + flex: 2; + padding: 5px; +} + +.search-boolean { + flex: 1; + padding: 5px; +} + +.header-container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; +} + +.mat-elevation-z8 { + box-shadow: 0px 0px 0px rgba(0,0,0,0.2); +} + +.paginator-container { + display: flex; + justify-content: end; + margin-bottom: 30px; +} + +.example-headers-align .mat-expansion-panel-header-description { + justify-content: space-between; + align-items: center; +} + +.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field { + margin-left: 8px; +} + +.example-button-row { + display: table-cell; + max-width: 600px; +} + +.example-button-row .mat-mdc-button-base { + margin: 8px 8px 8px 0; +} + diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.html b/ogWebconsole/src/app/components/calendar/calendar.component.html new file mode 100644 index 0000000..d735aa8 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/calendar.component.html @@ -0,0 +1,58 @@ +
+

Administrar calendarios

+
+ +
+
+ +
+ + Buscar nombre de calendario + + search + Pulsar 'enter' para buscar + +
+ + + + + + + + + + + + + +
{{ column.header }} + + + {{ image[column.columnDef] ? 'check_circle' : 'cancel' }} + + + + + + {{ image.downloadUrl ? image.downloadUrl.substring(0, 20) + '...' : '' }} + + + + + {{ column.cell(image) }} + + + Acciones + + + +
+
+ + +
diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts b/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts new file mode 100644 index 0000000..43d63ca --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CalendarComponent } from './calendar.component'; + +describe('CalendarComponent', () => { + let component: CalendarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CalendarComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CalendarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.ts b/ogWebconsole/src/app/components/calendar/calendar.component.ts new file mode 100644 index 0000000..756fcec --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/calendar.component.ts @@ -0,0 +1,149 @@ +import {Component, OnInit, signal} from '@angular/core'; +import {MatTableDataSource} from "@angular/material/table"; +import {DatePipe} from "@angular/common"; +import {MatDialog} from "@angular/material/dialog"; +import {HttpClient} from "@angular/common/http"; +import {DataService} from "./data.service"; +import {ToastrService} from "ngx-toastr"; +import {InfoImageComponent} from "../ogboot/images/info-image/info-image/info-image.component"; +import {PageEvent} from "@angular/material/paginator"; +import {CreateCalendarComponent} from "./create-calendar/create-calendar.component"; +import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; + +@Component({ + selector: 'app-calendar', + templateUrl: './calendar.component.html', + styleUrl: './calendar.component.css' +}) +export class CalendarComponent implements OnInit { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + images: { downloadUrl: string; name: string; uuid: string }[] = []; + dataSource = new MatTableDataSource(); + length: number = 0; + itemsPerPage: number = 10; + page: number = 1; + pageSizeOptions: number[] = [5, 10, 20, 40, 100]; + selectedElements: string[] = []; + loading:boolean = false; + filters: { [key: string]: string } = {}; + alertMessage: string | null = null; + readonly panelOpenState = signal(false); + datePipe: DatePipe = new DatePipe('es-ES'); + columns = [ + { + columnDef: 'id', + header: 'ID', + cell: (user: any) => `${user.id}`, + }, + { + columnDef: 'name', + header: 'Nombre', + cell: (user: any) => `${user.name}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (user: any) => `${this.datePipe.transform(user.createdAt, 'dd/MM/yyyy hh:mm:ss')}`, + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + private apiUrl = `${this.baseUrl}/remote-calendars`; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private dataService: DataService, + private toastService: ToastrService + ) {} + + ngOnInit(): void { + this.search(); + } + + addImage(): void { + const dialogRef = this.dialog.open(CreateCalendarComponent, { + width: '400px' + }); + + dialogRef.afterClosed().subscribe(result => { + console.log('The dialog was closed'); + this.search(); + }); + } + + search(): void { + this.loading = true; + this.dataService.getRemoteCalendars(this.filters).subscribe( + data => { + this.dataSource.data = data; + this.loading = false; + }, + error => { + console.error('Error fetching og lives', error); + this.loading = false; + } + ); + } + + sync(calendar: any): void { + console.log('Syncing calendars'); + } + + editImage(calendar: any): void { + const dialogRef = this.dialog.open(CreateCalendarComponent, { + width: '700px', + data: calendar + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.search(); + } + }); + } + + deleteCalendar(role: any): void { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '300px', + data: { name: role.name } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + const apiUrl = `${this.baseUrl}/remote-calendars/${role.uuid}`; + + this.http.delete(apiUrl).subscribe({ + next: () => { + console.log('Role deleted successfully'); + this.search(); + this.toastService.success('Role deleted successfully'); + }, + error: (error) => { + console.error('Error deleting role:', error); + } + }); + } else { + console.log('Role deletion cancelled'); + } + }); + } + + applyFilter() { + this.http.get(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({ + next: (response) => { + this.dataSource.data = response['hydra:member']; + this.length = response['hydra:totalItems']; + }, + error: (error) => { + console.error('Error al cargar las imágenes:', error); + } + }); + } + + onPageChange(event: PageEvent) { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.applyFilter(); + } +} diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css new file mode 100644 index 0000000..8c6148f --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css @@ -0,0 +1,32 @@ +.full-width { + width: 100%; +} +.form-container { + padding: 40px; +} + +.form-group { + margin-top: 20px; + margin-bottom: 26px; +} + +.full-width { + width: 100%; + margin-bottom: 16px; +} + +.checkbox-group { + display: flex; + flex-direction: column; + margin: 15px 0; + align-items: flex-start; +} + +.time-fields { + display: flex; + gap: 15px; /* Espacio entre los campos */ +} + +.time-field { + flex: 1; +} diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html new file mode 100644 index 0000000..14fa504 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html @@ -0,0 +1,53 @@ +

{{ isEditMode ? 'Editar' : 'Añadir' }} calendario

+ + ¿Disponibilidad remoto? + +
+ Selecciona los días de la semana +
+ + {{ day }} + +
+ +
+ + Hora de inicio + + + + + Hora de fin + + +
+
+ +
+ + Razón + + +
+ + Fecha de inicio + + MM/DD/YYYY + + + + + Fecha de fin + + MM/DD/YYYY + + + +
+
+
+ + + + + diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.spec.ts b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.spec.ts new file mode 100644 index 0000000..607fb50 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateCalendarRuleComponent } from './create-calendar-rule.component'; + +describe('CreateCalendarRuleComponent', () => { + let component: CreateCalendarRuleComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateCalendarRuleComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateCalendarRuleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts new file mode 100644 index 0000000..e9ccbb8 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts @@ -0,0 +1,115 @@ +import {Component, Inject} from '@angular/core'; +import {ToastrService} from "ngx-toastr"; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; + +@Component({ + selector: 'app-create-calendar-rule', + templateUrl: './create-calendar-rule.component.html', + styleUrl: './create-calendar-rule.component.css' +}) +export class CreateCalendarRuleComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + name: string = ''; + remoteCalendarRules: any[] = []; + weekDays: string[] = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']; + busyWeekDays: { [key: string]: boolean } = {}; + busyFromHour: any = null; + busyToHour: any = null; + availableFromDate: any = null; + availableToDate: any = null; + isRemoteAvailable: boolean = false; + showAdditionalForm: boolean = false; + availableReason: any = null; + isEditMode: boolean = false; + ruleId: string | null = null; + calendarId: string | null = null; + selectedDaysIndices: number[] = []; + + constructor( + private toastService: ToastrService, + private http: HttpClient, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any +) { } + + ngOnInit(): void { + this.calendarId = this.data.calendar + if (this.data) { + this.isEditMode = true; + this.availableFromDate = this.data.rule.availableFromDate; + this.availableToDate = this.data.rule.availableToDate; + this.isRemoteAvailable = this.data.rule.isRemoteAvailable; + this.availableReason = this.data.rule.availableReason; + this.busyFromHour = this.data.rule.busyFromHour; + this.busyToHour = this.data.rule.busyToHour; + if (this.data.rule.busyWeekDays) { + this.busyWeekDays = this.data.rule.busyWeekDays.reduce((acc: { + [x: string]: boolean; + }, day: string | number) => { + // @ts-ignore + acc[this.weekDays[day]] = true; + return acc; + }, {}); + } + this.ruleId = this.data.rule['@id'] + } + } + + onNoClick(): void { + this.dialogRef.close(); + } + + toggleAdditionalForm(): void { + this.showAdditionalForm = !this.showAdditionalForm; + } + + getSelectedDaysIndices() { + this.selectedDaysIndices = this.weekDays + .map((day, index) => this.busyWeekDays[day] ? index : -1) + .filter(index => index !== -1); + } + + submitRule(): void { + this.getSelectedDaysIndices() + const selectedDaysArray = Object.keys(this.busyWeekDays).map((day, index) => this.busyWeekDays[index]); + + const formData = { + remoteCalendar: this.calendarId, + busyWeekDays: this.selectedDaysIndices, + busyFromHour: this.busyFromHour, + busyToHour: this.busyFromHour, + availableFromDate: this.availableFromDate, + availableToDate: this.availableToDate, + isRemoteAvailable: this.isRemoteAvailable, + availableReason: this.availableReason + }; + + if (this.isEditMode && this.ruleId) { + this.http.put(`${this.baseUrl}${this.ruleId}`, formData) + .subscribe({ + next: (response) => { + this.toastService.success('Calendar updated successfully'); + this.dialogRef.close(true); + }, + error: (error) => { + console.error('Error:', error); + this.toastService.error(error.error['hydra:description']); + } + }); + } else { + this.http.post(`${this.baseUrl}/remote-calendar-rules`, formData) + .subscribe({ + next: (response) => { + this.toastService.success('Calendar created successfully'); + this.dialogRef.close(true); + }, + error: (error) => { + console.error('Error:', error); + this.toastService.error(error.error['hydra:description']); + } + }); + } + console.log(formData); + } +} diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.css b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.css new file mode 100644 index 0000000..722ef4f --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.css @@ -0,0 +1,60 @@ +.full-width { + width: 100%; +} +.form-container { + padding: 40px; +} + +.form-group { + margin-top: 20px; + margin-bottom: 26px; +} + +.full-width { + width: 100%; + margin-bottom: 16px; +} + +.additional-form { + margin-top: 20px; +} + +.checkbox-group { + display: flex; + flex-direction: column; + margin: 15px 0; + align-items: flex-start; +} + +.time-fields { + display: flex; + gap: 15px; /* Espacio entre los campos */ +} + +.time-field { + flex: 1; +} + +.list-item-content { + display: flex; + align-items: flex-start; /* Alinea el contenido al inicio */ + justify-content: space-between; /* Espacio entre los textos y los íconos */ + width: 100%; /* Asegúrate de que el contenido ocupe todo el ancho */ +} + +.text-content { + flex-grow: 1; /* Permite que este contenedor ocupe el espacio disponible */ + margin-right: 16px; /* Espaciado a la derecha para separar de los íconos */ + margin-left: 10px; +} + +.icon-container { + display: flex; + align-items: center; /* Alinea los íconos verticalmente */ +} + +.right-icon { + margin-left: 8px; /* Espaciado entre los íconos */ + cursor: pointer; +} + diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html new file mode 100644 index 0000000..b351cfa --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html @@ -0,0 +1,39 @@ +

{{ isEditMode ? 'Editar' : 'Añadir' }} calendario

+ + + + + + Nombre + + + +
Reglas
+ + + +
+ event_available +
+
{{ rule.isRemoteAvailable ? 'Disponible' : 'No disponible ( periodo presencial )' }}
+
{{ rule.busyFromHour }} - {{ rule.busyToHour }}
+
{{ rule.availableReason }} | {{ rule.busyFromHour }} - {{ rule.busyToHour }}
+
+
+ + +
+
+
+
+
+
+ + + + + diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.spec.ts b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.spec.ts new file mode 100644 index 0000000..ce6a69c --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateCalendarComponent } from './create-calendar.component'; + +describe('CreateCalendarComponent', () => { + let component: CreateCalendarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateCalendarComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateCalendarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts new file mode 100644 index 0000000..7af9681 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts @@ -0,0 +1,86 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {ToastrService} from "ngx-toastr"; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; +import {CreateCalendarRuleComponent} from "../create-calendar-rule/create-calendar-rule.component"; + +@Component({ + selector: 'app-create-calendar', + templateUrl: './create-calendar.component.html', + styleUrl: './create-calendar.component.css' +}) +export class CreateCalendarComponent implements OnInit { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + name: string = ''; + remoteCalendarRules: any[] = []; + isEditMode: boolean = false; + calendarId: string | null = null; + + constructor( + private toastService: ToastrService, + private http: HttpClient, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + public dialog: MatDialog, + ) { } + + ngOnInit(): void { + if (this.data) { + this.isEditMode = true; + this.name = this.data.name; + this.remoteCalendarRules = this.data.remoteCalendarRules; + this.calendarId = this.data['@id'] + } + } + + onNoClick(): void { + this.dialogRef.close(); + } + + submitForm(): void { + const payload = { + name: this.name + }; + + if (this.isEditMode && this.calendarId) { + this.http.patch(`${this.baseUrl}${this.calendarId}`, payload) + .subscribe({ + next: (response) => { + this.toastService.success('Calendar updated successfully'); + this.dialogRef.close(true); + }, + error: (error) => { + console.error('Error:', error); + this.toastService.error(error.error['hydra:description']); + } + }); + } else { + this.http.post(`${this.baseUrl}/remote-calendars`, payload) + .subscribe({ + next: (response) => { + this.toastService.success('Calendar created successfully'); + this.dialogRef.close(true); + }, + error: (error) => { + console.error('Error:', error); + this.toastService.error(error.error['hydra:description']); + } + }); + } + } + + createRule(rule: any = null): void { + const dialogRef = this.dialog.open(CreateCalendarRuleComponent, { + width: '700px', + data: { + calendar: this.calendarId, + rule: rule + }, + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + } + }); + } +} diff --git a/ogWebconsole/src/app/components/calendar/data.service.ts b/ogWebconsole/src/app/components/calendar/data.service.ts new file mode 100644 index 0000000..ee82d23 --- /dev/null +++ b/ogWebconsole/src/app/components/calendar/data.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import {HttpClient, HttpParams} from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class DataService { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + private apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`; + + constructor(private http: HttpClient) {} + + getRemoteCalendars(filters: { [key: string]: string }): Observable { + const params = new HttpParams({ fromObject: filters }); + + return this.http.get(this.apiUrl, { params }).pipe( + map(response => { + if (response['hydra:member'] && Array.isArray(response['hydra:member'])) { + return response['hydra:member']; + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching remote calendars', error); + return throwError(error); + }) + ); + } +} diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html index 8b5eebc..534f632 100644 --- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html +++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html @@ -104,7 +104,7 @@ - + calendar_month Calendarios From 390f454ac2fcd76a5167e0113724887d4b6a357b Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 12:03:20 +0200 Subject: [PATCH 2/3] refs #729. RemoteCalendar and RemoteCalendarRule. Finish --- ogWebconsole/src/app/app-routing.module.ts | 4 -- .../calendar/calendar.component.html | 2 +- .../components/calendar/calendar.component.ts | 20 ++++---- .../create-calendar-rule.component.css | 2 - .../create-calendar-rule.component.html | 16 ++++-- .../create-calendar-rule.component.ts | 4 +- .../create-calendar.component.html | 20 +++++--- .../create-calendar.component.ts | 50 +++++++++++++++++-- .../app/components/calendar/data.service.ts | 16 ++++++ 9 files changed, 100 insertions(+), 34 deletions(-) diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts index 97dbbef..e1f456c 100644 --- a/ogWebconsole/src/app/app-routing.module.ts +++ b/ogWebconsole/src/app/app-routing.module.ts @@ -15,14 +15,10 @@ import { PxeBootFilesComponent } from './components/ogboot/pxe-boot-files/pxe-bo import {OgbootStatusComponent} from "./components/ogboot/ogboot-status/ogboot-status.component"; import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component'; import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component'; -<<<<<<< HEAD -import { CommandsComponent } from './components/commands/commands.component'; import { CalendarComponent } from "./components/calendar/calendar.component"; -======= import { CommandsComponent } from './components/commands/main-commands/commands.component'; import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component'; import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component'; ->>>>>>> 30cd43ff0fcccafbf6d2b7534dd31ba0b3c2c257 const routes: Routes = [ { path: '', redirectTo: 'auth/login', pathMatch: 'full' }, { diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.html b/ogWebconsole/src/app/components/calendar/calendar.component.html index d735aa8..990232f 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.html +++ b/ogWebconsole/src/app/components/calendar/calendar.component.html @@ -39,7 +39,7 @@ Acciones - + diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.ts b/ogWebconsole/src/app/components/calendar/calendar.component.ts index 756fcec..0a1671d 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.ts +++ b/ogWebconsole/src/app/components/calendar/calendar.component.ts @@ -90,10 +90,10 @@ export class CalendarComponent implements OnInit { console.log('Syncing calendars'); } - editImage(calendar: any): void { + editCalendar(calendar: any): void { const dialogRef = this.dialog.open(CreateCalendarComponent, { width: '700px', - data: calendar + data: calendar['@id'] }); dialogRef.afterClosed().subscribe(result => { @@ -103,28 +103,28 @@ export class CalendarComponent implements OnInit { }); } - deleteCalendar(role: any): void { + deleteCalendar(calendar: any): void { const dialogRef = this.dialog.open(DeleteModalComponent, { - width: '300px', - data: { name: role.name } + width: '400px', + data: { name: calendar.name } }); dialogRef.afterClosed().subscribe(result => { if (result) { - const apiUrl = `${this.baseUrl}/remote-calendars/${role.uuid}`; + const apiUrl = `${this.baseUrl}${calendar['@id']}`; this.http.delete(apiUrl).subscribe({ next: () => { - console.log('Role deleted successfully'); + console.log('Calendar deleted successfully'); this.search(); - this.toastService.success('Role deleted successfully'); + this.toastService.success('Calendar deleted successfully'); }, error: (error) => { - console.error('Error deleting role:', error); + this.toastService.error('Error deleting calendar'); } }); } else { - console.log('Role deletion cancelled'); + console.log('calendar deletion cancelled'); } }); } diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css index 8c6148f..13e2689 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.css @@ -16,8 +16,6 @@ } .checkbox-group { - display: flex; - flex-direction: column; margin: 15px 0; align-items: flex-start; } diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html index 14fa504..8249a20 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.html @@ -4,12 +4,20 @@
Selecciona los días de la semana -
- - {{ day }} - +
+
+ + {{ day }} + +
+
+ + {{ day }} + +
+
Hora de inicio diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts index e9ccbb8..6711779 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts @@ -30,7 +30,7 @@ export class CreateCalendarRuleComponent { private toastService: ToastrService, private http: HttpClient, public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any + @Inject(MAT_DIALOG_DATA) public data: any, ) { } ngOnInit(): void { @@ -78,7 +78,7 @@ export class CreateCalendarRuleComponent { remoteCalendar: this.calendarId, busyWeekDays: this.selectedDaysIndices, busyFromHour: this.busyFromHour, - busyToHour: this.busyFromHour, + busyToHour: this.busyToHour, availableFromDate: this.availableFromDate, availableToDate: this.availableToDate, isRemoteAvailable: this.isRemoteAvailable, diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html index b351cfa..c90baeb 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.html @@ -1,15 +1,21 @@

{{ isEditMode ? 'Editar' : 'Añadir' }} calendario

- - Nombre - + + mode_edit +
+
Reglas
+ +
-
Reglas
- + + +
@@ -23,7 +29,7 @@ -
@@ -35,5 +41,5 @@ - + diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts index 7af9681..5e7254e 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts @@ -3,6 +3,8 @@ import {ToastrService} from "ngx-toastr"; import {HttpClient} from "@angular/common/http"; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; import {CreateCalendarRuleComponent} from "../create-calendar-rule/create-calendar-rule.component"; +import {DataService} from "../data.service"; +import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; @Component({ selector: 'app-create-calendar', @@ -22,17 +24,29 @@ export class CreateCalendarComponent implements OnInit { public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, public dialog: MatDialog, + private dataService: DataService, ) { } ngOnInit(): void { if (this.data) { - this.isEditMode = true; - this.name = this.data.name; - this.remoteCalendarRules = this.data.remoteCalendarRules; - this.calendarId = this.data['@id'] + this.load() } } + load(): void { + this.dataService.getRemoteCalendar(this.data).subscribe({ + next: (response) => { + this.isEditMode = true; + this.name = response.name; + this.remoteCalendarRules = response.remoteCalendarRules; + this.calendarId = this.data; + }, + error: (err) => { + console.error('Error fetching remote calendar:', err); + } + }); + } + onNoClick(): void { this.dialogRef.close(); } @@ -48,6 +62,7 @@ export class CreateCalendarComponent implements OnInit { next: (response) => { this.toastService.success('Calendar updated successfully'); this.dialogRef.close(true); + this.load() }, error: (error) => { console.error('Error:', error); @@ -60,6 +75,7 @@ export class CreateCalendarComponent implements OnInit { next: (response) => { this.toastService.success('Calendar created successfully'); this.dialogRef.close(true); + this.load() }, error: (error) => { console.error('Error:', error); @@ -80,6 +96,32 @@ export class CreateCalendarComponent implements OnInit { dialogRef.afterClosed().subscribe(result => { if (result) { + this.load() + } + }); + } + + deleteCalendarRule(rule: any): void { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + const apiUrl = `${this.baseUrl}${rule['@id']}`; + + this.http.delete(apiUrl).subscribe({ + next: () => { + console.log('Calendar deleted successfully'); + this.load(); + this.toastService.success('Calendar deleted successfully'); + }, + error: (error) => { + this.toastService.error('Error deleting calendar'); + } + }); + } else { + console.log('calendar deletion cancelled'); } }); } diff --git a/ogWebconsole/src/app/components/calendar/data.service.ts b/ogWebconsole/src/app/components/calendar/data.service.ts index ee82d23..ba9b568 100644 --- a/ogWebconsole/src/app/components/calendar/data.service.ts +++ b/ogWebconsole/src/app/components/calendar/data.service.ts @@ -29,4 +29,20 @@ export class DataService { }) ); } + + getRemoteCalendar(id: string): Observable { + return this.http.get(`${this.baseUrl}${id}`).pipe( + map(response => { + if (response.name && response.remoteCalendarRules) { + return response; + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching calendar', error); + return throwError(error); + }) + ); + } } From a7da86c804eb8182bdadcfe6a618532995882a63 Mon Sep 17 00:00:00 2001 From: apuente Date: Mon, 30 Sep 2024 16:37:03 +0200 Subject: [PATCH 3/3] refs #732 Added organizational unit calendar reference --- .../client-view/client-view.component.html | 4 +- .../app/components/groups/groups.component.ts | 1 - .../create-organizational-unit.component.html | 23 ++-- .../create-organizational-unit.component.ts | 110 +++++++++++------- .../show-organizational-unit.component.html | 10 -- 5 files changed, 84 insertions(+), 64 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/client-view/client-view.component.html b/ogWebconsole/src/app/components/groups/client-view/client-view.component.html index d60e173..7cbdd4d 100644 --- a/ogWebconsole/src/app/components/groups/client-view/client-view.component.html +++ b/ogWebconsole/src/app/components/groups/client-view/client-view.component.html @@ -44,7 +44,7 @@ - + diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index b8be0e8..25646cb 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -124,7 +124,6 @@ export class GroupsComponent implements OnInit { this.loadingChildren = true this.dataService.getChildren(id).subscribe( childrenData => { - console.log('Children data:', childrenData); this.dataService.getClients(id).subscribe( clientsData => { this.clientsData = clientsData; diff --git a/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.html b/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.html index 55ea6fc..19cce7f 100644 --- a/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.html +++ b/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.html @@ -49,6 +49,17 @@ Aforo
+ + + + Calendario Asociado + + + {{ calendar.name }} + + + +
@@ -83,7 +94,7 @@ bootFileName - + Url servidor Proxy @@ -107,9 +118,7 @@ Modo P2P - + {{ option.name }} @@ -133,9 +142,7 @@ Modo Multicast - + {{ option.name }} @@ -147,7 +154,7 @@ Perfil de Hardware - {{ unit.description }} + {{ unit.description }} Formato de URL inválido. diff --git a/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.ts b/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.ts index 2e7f363..4d55383 100644 --- a/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.ts +++ b/ogWebconsole/src/app/components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component.ts @@ -1,9 +1,9 @@ -import {Component, OnInit, Output, EventEmitter, Inject} from '@angular/core'; +import { Component, OnInit, Output, EventEmitter, Inject } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { HttpClient, HttpHeaders } from '@angular/common/http'; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../../data.service"; +import { ToastrService } from 'ngx-toastr'; +import { DataService } from '../../data.service'; @Component({ selector: 'app-create-organizational-unit', @@ -24,17 +24,21 @@ export class CreateOrganizationalUnitComponent implements OnInit { 'classroom': 'Aula', 'clients-group': 'Grupo de clientes' }; + protected p2pModeOptions = [ + { name: 'Leecher', value: 'p2p-mode-leecher' }, + { name: 'Peer', value: 'p2p-mode-peer' }, + { name: 'Seeder', value: 'p2p-mode-seeder' }, + ]; + multicastModeOptions: { name: string, value: string }[] = [ + { name: 'Modo 1', value: 'mode1' }, + { name: 'Modo 2', value: 'mode2' }, + { name: 'Modo 3', value: 'mode3' }, + ]; parentUnits: any[] = []; hardwareProfiles: any[] = []; - protected p2pModeOptions = [ - {"name": 'Leecher', "value": "p2p-mode-leecher"}, - {"name": 'Peer', "value": "p2p-mode-peer"}, - {"name": 'Seeder', "value": "p2p-mode-seeder"}, - ]; - protected multicastModeOptions = [ - {"name": 'Half duplex', "value": "half-duplex"}, - {"name": 'Full duplex', "value": "full-duplex"}, - ]; + calendars: any[] = []; + selectedCalendarUuid: string | null = null; + @Output() unitAdded = new EventEmitter(); constructor( @@ -70,7 +74,8 @@ export class CreateOrganizationalUnitComponent implements OnInit { mcastMode: [''], menu: [''], hardwareProfile: [''], - validation: [false] + validation: [false], + remoteCalendar: [''] }); this.classroomInfoFormGroup = this._formBuilder.group({ location: [''], @@ -83,28 +88,31 @@ export class CreateOrganizationalUnitComponent implements OnInit { ngOnInit() { this.loadParentUnits(); this.loadHardwareProfiles(); + this.loadCalendars(); } loadParentUnits() { const url = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=1000`; - this.http.get(url).subscribe( - response => { - this.parentUnits = response['hydra:member']; - }, - error => { - console.error('Error fetching parent units:', error); - } + response => this.parentUnits = response['hydra:member'], + error => console.error('Error fetching parent units:', error) ); } loadHardwareProfiles(): void { this.dataService.getHardwareProfiles().subscribe( - (data: any[]) => { - this.hardwareProfiles = data; - }, - (error: any) => { - console.error('Error fetching hardware profiles', error); + (data: any[]) => this.hardwareProfiles = data, + error => console.error('Error fetching hardware profiles', error) + ); + } + + loadCalendars() { + const apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=30`; + this.http.get(apiUrl).subscribe( + response => this.calendars = response['hydra:member'], + error => { + console.error('Error loading calendars', error); + this.openSnackBar(true, 'Error loading calendars'); } ); } @@ -121,26 +129,13 @@ export class CreateOrganizationalUnitComponent implements OnInit { } onSubmit() { - if (this.generalFormGroup.valid && this.additionalInfoFormGroup.valid && this.networkSettingsFormGroup.valid && (this.generalFormGroup.value.type !== 'classroom' || this.classroomInfoFormGroup.valid)) { - const generalFormValues = this.cleanFormValues(this.generalFormGroup); - const additionalInfoFormValues = this.cleanFormValues(this.additionalInfoFormGroup); - const networkSettingsFormValues = this.cleanFormValues(this.networkSettingsFormGroup); - const classroomInfoFormValues = this.cleanFormValues(this.classroomInfoFormGroup); - - const formData: any = { - ...generalFormValues, - ...classroomInfoFormValues, - comments: additionalInfoFormValues.comments, - networkSettings: { ...networkSettingsFormValues } - }; - - console.log('POST data:', formData); + if (this.isFormValid()) { + const formData: any = this.buildPayload(); const postUrl = `${this.baseUrl}/organizational-units`; const headers = new HttpHeaders({ 'Content-Type': 'application/json' }); this.http.post(postUrl, formData, { headers }).subscribe( response => { - console.log('POST successful:', response); this.unitAdded.emit(); this.dialogRef.close(); this.openSnackBar(false, 'Cliente creado exitosamente'); @@ -153,14 +148,43 @@ export class CreateOrganizationalUnitComponent implements OnInit { } } + private isFormValid(): boolean { + return this.generalFormGroup.valid && + this.additionalInfoFormGroup.valid && + this.networkSettingsFormGroup.valid && + (this.generalFormGroup.value.type !== 'classroom' || this.classroomInfoFormGroup.valid); + } + + private buildPayload(): any { + const generalFormValues = this.cleanFormValues(this.generalFormGroup); + const additionalInfoFormValues = this.cleanFormValues(this.additionalInfoFormGroup); + const networkSettingsFormValues = this.cleanFormValues(this.networkSettingsFormGroup); + const classroomInfoFormValues = this.cleanFormValues(this.classroomInfoFormGroup); + + return { + ...generalFormValues, + ...classroomInfoFormValues, + comments: additionalInfoFormValues.comments, + networkSettings: { ...networkSettingsFormValues }, + remoteCalendar: this.selectedCalendarUuid ? `/remote-calendars/${this.selectedCalendarUuid}` : null, + menu: networkSettingsFormValues.menu || null, + hardwareProfile: networkSettingsFormValues.hardwareProfile || null, + }; + } + + onCalendarChange(event: any) { + this.selectedCalendarUuid = event.value; + } + onNoClick(): void { this.dialogRef.close(); } openSnackBar(isError: boolean, message: string) { if (isError) { - this.toastService.error(' Error al crear el cliente: ' + message, 'Error'); - } else + this.toastService.error('Error al crear el cliente: ' + message, 'Error'); + } else { this.toastService.success('Cliente creado exitosamente', 'Éxito'); + } } } diff --git a/ogWebconsole/src/app/components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component.html b/ogWebconsole/src/app/components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component.html index 93406cd..8cf3f5e 100644 --- a/ogWebconsole/src/app/components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component.html +++ b/ogWebconsole/src/app/components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component.html @@ -29,15 +29,5 @@ - -
- - - - - - -
-