From 0b5c7df2e4576ee324ba24a2026bc2fb1ca17cd6 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 17 Oct 2024 12:15:08 +0200 Subject: [PATCH] refs #968. UX Operative System, Software and SOftwarProfile --- ogWebconsole/src/app/app-routing.module.ts | 8 +- ogWebconsole/src/app/app.module.ts | 12 ++ .../components/calendar/calendar.component.ts | 1 - .../ogboot-status.component.html | 13 +- .../ogboot-status/ogboot-status.component.ts | 2 +- .../create-image/create-image.component.ts | 4 +- .../pxe-images/pxe-images.component.css | 4 +- .../pxe-images/pxe-images.component.html | 11 +- .../ogboot/pxe-images/pxe-images.component.ts | 14 +- .../status/status.component.html | 2 +- .../create-operative-system.component.css | 60 ++++++ .../create-operative-system.component.html | 15 ++ .../create-operative-system.component.spec.ts | 23 +++ .../create-operative-system.component.ts | 90 +++++++++ .../operative-system.component.css | 63 ++++++ .../operative-system.component.html | 44 +++++ .../operative-system.component.spec.ts | 23 +++ .../operative-system.component.ts | 127 ++++++++++++ .../create-software-profile.component.css | 60 ++++++ .../create-software-profile.component.html | 74 +++++++ .../create-software-profile.component.spec.ts | 23 +++ .../create-software-profile.component.ts | 182 ++++++++++++++++++ .../software-profile/data.service.ts | 49 +++++ .../software-profile.component.css | 63 ++++++ .../software-profile.component.html | 50 +++++ .../software-profile.component.spec.ts | 23 +++ .../software-profile.component.ts | 132 +++++++++++++ .../create-software.component.css | 60 ++++++ .../create-software.component.html | 29 +++ .../create-software.component.spec.ts | 23 +++ .../create-software.component.ts | 96 +++++++++ .../app/components/software/data.service.ts | 48 +++++ .../software/software.component.css | 63 ++++++ .../software/software.component.html | 58 ++++++ .../software/software.component.spec.ts | 23 +++ .../components/software/software.component.ts | 137 +++++++++++++ .../app/layout/sidebar/sidebar.component.html | 28 ++- .../app/layout/sidebar/sidebar.component.ts | 4 + 38 files changed, 1714 insertions(+), 27 deletions(-) create mode 100644 ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.css create mode 100644 ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.html create mode 100644 ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts create mode 100644 ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts create mode 100644 ogWebconsole/src/app/components/operative-system/operative-system.component.css create mode 100644 ogWebconsole/src/app/components/operative-system/operative-system.component.html create mode 100644 ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts create mode 100644 ogWebconsole/src/app/components/operative-system/operative-system.component.ts create mode 100644 ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.css create mode 100644 ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.html create mode 100644 ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts create mode 100644 ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts create mode 100644 ogWebconsole/src/app/components/software-profile/data.service.ts create mode 100644 ogWebconsole/src/app/components/software-profile/software-profile.component.css create mode 100644 ogWebconsole/src/app/components/software-profile/software-profile.component.html create mode 100644 ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts create mode 100644 ogWebconsole/src/app/components/software-profile/software-profile.component.ts create mode 100644 ogWebconsole/src/app/components/software/create-software/create-software.component.css create mode 100644 ogWebconsole/src/app/components/software/create-software/create-software.component.html create mode 100644 ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts create mode 100644 ogWebconsole/src/app/components/software/create-software/create-software.component.ts create mode 100644 ogWebconsole/src/app/components/software/data.service.ts create mode 100644 ogWebconsole/src/app/components/software/software.component.css create mode 100644 ogWebconsole/src/app/components/software/software.component.html create mode 100644 ogWebconsole/src/app/components/software/software.component.spec.ts create mode 100644 ogWebconsole/src/app/components/software/software.component.ts diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts index 6a9ab56..11b48f0 100644 --- a/ogWebconsole/src/app/app-routing.module.ts +++ b/ogWebconsole/src/app/app-routing.module.ts @@ -24,6 +24,9 @@ import { StatusComponent } from "./components/ogdhcp/og-dhcp-subnets/status/stat import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component'; import { ImagesComponent } from './components/images/images.component'; import { RestoreImageComponent } from './components/groups/components/client-main-view/restore-image/restore-image.component'; +import {SoftwareComponent} from "./components/software/software.component"; +import {SoftwareProfileComponent} from "./components/software-profile/software-profile.component"; +import {OperativeSystemComponent} from "./components/operative-system/operative-system.component"; const routes: Routes = [ { path: '', redirectTo: 'auth/login', pathMatch: 'full' }, { @@ -49,7 +52,10 @@ const routes: Routes = [ { path: 'calendars', component: CalendarComponent }, { path: 'client/:id', component: ClientMainViewComponent }, { path: 'images', component: ImagesComponent }, - { path: 'restore-image', component: RestoreImageComponent} + { path: 'restore-image', component: RestoreImageComponent}, + { path: 'software', component: SoftwareComponent }, + { path: 'software-profiles', component: SoftwareProfileComponent }, + { path: 'operative-systems', component: OperativeSystemComponent }, ], }, { diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index b7719be..c005cc9 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -107,6 +107,12 @@ import { ImagesComponent } from './components/images/images.component'; import { CreateImageComponent } from './components/images/create-image/create-image.component'; import { PartitionAssistantComponent } from './components/groups/components/client-main-view/partition-assistant/partition-assistant.component'; import { RestoreImageComponent } from './components/groups/components/client-main-view/restore-image/restore-image.component'; +import { SoftwareComponent } from './components/software/software.component'; +import { CreateSoftwareComponent } from './components/software/create-software/create-software.component'; +import { SoftwareProfileComponent } from './components/software-profile/software-profile.component'; +import { CreateSoftwareProfileComponent } from './components/software-profile/create-software-profile/create-software-profile.component'; +import { OperativeSystemComponent } from './components/operative-system/operative-system.component'; +import { CreateOperativeSystemComponent } from './components/operative-system/create-operative-system/create-operative-system.component'; @NgModule({ declarations: [ AppComponent, @@ -171,6 +177,12 @@ import { RestoreImageComponent } from './components/groups/components/client-mai CreateImageComponent, PartitionAssistantComponent, RestoreImageComponent, + SoftwareComponent, + CreateSoftwareComponent, + SoftwareProfileComponent, + CreateSoftwareProfileComponent, + OperativeSystemComponent, + CreateOperativeSystemComponent, ], bootstrap: [AppComponent], imports: [BrowserModule, diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.ts b/ogWebconsole/src/app/components/calendar/calendar.component.ts index 8ad1698..c9d147e 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.ts +++ b/ogWebconsole/src/app/components/calendar/calendar.component.ts @@ -5,7 +5,6 @@ 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/pxe-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"; diff --git a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.html b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.html index 6d1f17f..a4b385a 100644 --- a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.html +++ b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.html @@ -1,6 +1,6 @@

OgBoot server Status

- +

Uso de disco

@@ -20,7 +20,7 @@

Libre: {{ diskUsage.percentage }}

- +

Servicios

    @@ -32,15 +32,9 @@ {{ service.name }}: {{ service.status }}
- -
- - - -
- +

OGLives instalados

@@ -63,4 +57,3 @@
- \ No newline at end of file diff --git a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts index 340f621..424caea 100644 --- a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts +++ b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts @@ -13,7 +13,7 @@ export class OgbootStatusComponent implements OnInit { installedOglives: any[] = []; diskUsageChartData: any[] = []; - view: [number, number] = [1300, 500]; + view: [number, number] = [1100, 500]; // Opciones de la gráfica gradient: boolean = true; diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts index 7447d26..feaee7b 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts @@ -37,7 +37,7 @@ export class CreatePXEImageComponent implements OnInit { this.http.get(`${this.baseUrl}/og-lives/server/get-isos?page=1&itemsPerPage=30`) .subscribe({ next: (response: any) => { - this.downloads = response.data.downloads; + this.downloads = response.message.downloads.downloads; }, error: (error) => { console.error('Error fetching downloads:', error); @@ -53,7 +53,7 @@ export class CreatePXEImageComponent implements OnInit { submitForm(): void { const payload = { name: this.name, - downloadUrl: this.selectedDownload.URL + downloadUrl: this.selectedDownload.filename }; if (this.isEditMode && this.imageId) { diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css index 07e92bc..d744f99 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css @@ -93,12 +93,12 @@ table { margin-left: 8px; } -.example-button-row { +.button-row { display: table-cell; max-width: 600px; } -.example-button-row .mat-mdc-button-base { +.button-row .mat-mdc-button-base { margin: 8px 8px 8px 0; } diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html index a82efb4..25ad249 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html @@ -1,13 +1,14 @@ - Sincronización ogBoot + Información en servidor ogBoot -

Oglives creados en servidor ogBoot: {{ alertMessage }}

-

Oglives creados en servidor ogCore (base de datos): {{ length }}

-
- +
+ +
+
+
diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts index 13cdd73..6924478 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts @@ -9,6 +9,7 @@ import {ToastrService} from "ngx-toastr"; import { DatePipe } from "@angular/common"; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import {DataService} from "./data.service"; +import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component"; @Component({ selector: 'app-pxe-images', @@ -193,12 +194,23 @@ export class PXEimagesComponent implements OnInit { this.http.get(`${this.apiUrl}/server/get-collection`) .subscribe(response => { // @ts-ignore - this.alertMessage = response.installed_ogLives.length + this.alertMessage = response.message }, error => { console.error('Error al cargar la información del alert', error); }); } + openSubnetInfoDialog() { + this.loadAlert() + this.dialog.open(ServerInfoDialogComponent, { + width: '600px', + data: { + alertMessage: this.alertMessage, + length: this.length + } + }); + } + syncOgBoot(): void { this.http.post(`${this.apiUrl}/sync`, {}) .subscribe(response => { diff --git a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets/status/status.component.html b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets/status/status.component.html index 917bed9..98b1b17 100644 --- a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets/status/status.component.html +++ b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets/status/status.component.html @@ -1,5 +1,5 @@
-

OgBoot server Status

+

OgDhcp server Status

diff --git a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.css b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.css new file mode 100644 index 0000000..722ef4f --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.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/operative-system/create-operative-system/create-operative-system.component.html b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.html new file mode 100644 index 0000000..5263ebd --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.html @@ -0,0 +1,15 @@ +

{{ operativeSystemId ? 'Editar' : 'Crear' }} sistema operativo

+ +
+ + Nombre + + +
+
+ + + + + + diff --git a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts new file mode 100644 index 0000000..bcaea4a --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateOperativeSystemComponent } from './create-operative-system.component'; + +describe('CreateOperativeSystemComponent', () => { + let component: CreateOperativeSystemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateOperativeSystemComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateOperativeSystemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts new file mode 100644 index 0000000..f731306 --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts @@ -0,0 +1,90 @@ +import {Component, Inject} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from "@angular/forms"; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {ToastrService} from "ngx-toastr"; +import {DataService} from "../../software/data.service"; + +@Component({ + selector: 'app-create-operative-system', + templateUrl: './create-operative-system.component.html', + styleUrl: './create-operative-system.component.css' +}) +export class CreateOperativeSystemComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + formGroup: FormGroup; + private apiUrl = `${this.baseUrl}/software`; + operativeSystemId: string | null = null; + + constructor( + private fb: FormBuilder, + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private dataService: DataService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.formGroup = this.fb.group({ + name: ['', Validators.required], + }); + } + + ngOnInit(): void { + if (this.data) { + this.load() + } + } + + load(): void { + this.dataService.getSoftware(this.data).subscribe({ + next: (response) => { + console.log(response); + this.formGroup = this.fb.group({ + name: [response.name, Validators.required] + }); + this.operativeSystemId = response['@id']; + }, + error: (err) => { + console.error('Error fetching software:', err); + } + }); + } + + onCancel(event: Event): void { + event.preventDefault(); + this.dialogRef.close(); + } + + onSubmit(): void { + if (this.formGroup.valid) { + + const payload = { + name: this.formGroup.value.name + }; + + if (this.operativeSystemId) { + this.http.put(`${this.baseUrl}${this.operativeSystemId}`, payload).subscribe( + (response) => { + this.toastService.success('Sistema operativo editado correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al editar el sistema operativo', error); + } + ); + } else { + this.http.post(`${this.baseUrl}/operative-systems`, payload).subscribe( + (response) => { + this.toastService.success('Sistema operativo añadido correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al añadir sistema operativo', error); + } + ); + } + } + } +} diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.css b/ogWebconsole/src/app/components/operative-system/operative-system.component.css new file mode 100644 index 0000000..2fd1786 --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.css @@ -0,0 +1,63 @@ +.title { + font-size: 24px; +} + +.calendar-button-row { + display: flex; + justify-content: flex-start; + margin-top: 16px; +} + +.divider { + margin: 20px 0; +} + +.lists-container { + padding: 16px; +} + +.card.unidad-card { + height: 100%; + box-sizing: border-box; +} + +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; +} diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.html b/ogWebconsole/src/app/components/operative-system/operative-system.component.html new file mode 100644 index 0000000..60589b9 --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.html @@ -0,0 +1,44 @@ +
+

Administrar sistemas operativos

+
+ +
+
+ +
+ + Buscar nombre de sistema operativo + + search + Pulsar 'enter' para buscar + +
+ + + + + + + + + + + + + +
{{ column.header }} + + {{ column.cell(image) }} + + Acciones + + +
+
+ + +
diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts b/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts new file mode 100644 index 0000000..1f910e8 --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OperativeSystemComponent } from './operative-system.component'; + +describe('OperativeSystemComponent', () => { + let component: OperativeSystemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [OperativeSystemComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(OperativeSystemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.ts b/ogWebconsole/src/app/components/operative-system/operative-system.component.ts new file mode 100644 index 0000000..2d6c21a --- /dev/null +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.ts @@ -0,0 +1,127 @@ +import {Component, 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 "../software/data.service"; +import {ToastrService} from "ngx-toastr"; +import {CreateSoftwareComponent} from "../software/create-software/create-software.component"; +import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import {PageEvent} from "@angular/material/paginator"; +import {CreateOperativeSystemComponent} from "./create-operative-system/create-operative-system.component"; + +@Component({ + selector: 'app-operative-system', + templateUrl: './operative-system.component.html', + styleUrl: './operative-system.component.css' +}) +export class OperativeSystemComponent { + 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 = 0; + pageSizeOptions: number[] = [5, 10, 20, 40, 100]; + 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: (operativeSystem: any) => `${operativeSystem.id}`, + }, + { + columnDef: 'name', + header: 'Nombre', + cell: (operativeSystem: any) => `${operativeSystem.name}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (operativeSystem: any) => `${this.datePipe.transform(operativeSystem.createdAt, 'dd/MM/yyyy hh:mm:ss')}`, + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + private apiUrl = `${this.baseUrl}/operative-systems`; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private dataService: DataService, + private toastService: ToastrService + ) {} + + ngOnInit(): void { + this.search(); + } + + addSoftware(): void { + const dialogRef = this.dialog.open(CreateOperativeSystemComponent, { + width: '600px' + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + search(): void { + this.http.get(`${this.apiUrl}?page=${this.page + 1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + (data) => { + this.dataSource.data = data['hydra:member']; + this.length = data['hydra:totalItems']; + }, + (error) => { + console.error('Error fetching commands', error); + } + ); + } + + editSoftware(calendar: any): void { + const dialogRef = this.dialog.open(CreateOperativeSystemComponent, { + width: '600px', + data: calendar['@id'] + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + deleteSoftware(operativeSystem: any): void { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { name: operativeSystem.name } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + const apiUrl = `${this.baseUrl}${operativeSystem['@id']}`; + + this.http.delete(apiUrl).subscribe({ + next: () => { + this.search(); + this.toastService.success('Operative System deleted'); + }, + error: (error) => { + this.toastService.error('Error deleting operative system'); + } + }); + } else { + console.log('calendar deletion cancelled'); + } + }); + } + + onPageChange(event: PageEvent) { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.length = event.length; + this.search(); + } +} diff --git a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.css b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.css new file mode 100644 index 0000000..722ef4f --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.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/software-profile/create-software-profile/create-software-profile.component.html b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.html new file mode 100644 index 0000000..325d220 --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.html @@ -0,0 +1,74 @@ +

{{ softwareProfileId ? 'Editar' : 'Crear' }} perfil de software

+ + + + + +
+ + Descripción + + + + Comentarios + + + + Seleccionar unidad organizativa + + + {{ ou.name }} + + + + + Seleccionar sistema operativo + + + {{ op.name }} + + + +
+
+ + +
+ + + + + + + {{ software.name }} + + + + +
+ + + {{ software.name }} + + + +
+
+
+
+
+ + + + + diff --git a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts new file mode 100644 index 0000000..c3c335c --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateSoftwareProfileComponent } from './create-software-profile.component'; + +describe('CreateSoftwareProfileComponent', () => { + let component: CreateSoftwareProfileComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateSoftwareProfileComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateSoftwareProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts new file mode 100644 index 0000000..cc4c8ff --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts @@ -0,0 +1,182 @@ +import {Component, Inject} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {ToastrService} from "ngx-toastr"; +import {DataService as SoftwareService} from "../../software/data.service"; +import {DataService} from "../data.service"; +import {Observable, startWith} from "rxjs"; +import { debounceTime, switchMap, map } from 'rxjs/operators'; + +@Component({ + selector: 'app-create-software-profile', + templateUrl: './create-software-profile.component.html', + styleUrl: './create-software-profile.component.css' +}) +export class CreateSoftwareProfileComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + formGroup: FormGroup; + private apiUrl = `${this.baseUrl}/software-profiles`; + softwareCollection: any[] = []; + organizationalUnits: any[] = []; + operativeSystems: any[] = []; + softwareProfileId: string | null = null; + selectedSoftware: any; + selectedSoftwares: any[] = []; + softwareControl = new FormControl(); + filteredSoftware!: Observable; + + constructor( + private fb: FormBuilder, + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private softwareDataService: SoftwareService, + private dataService: DataService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.formGroup = this.fb.group({ + description: [''], + comments: [''], + organizationalUnit: [null, Validators.required], + operativeSystem: [null] + }); + } + + ngOnInit(): void { + if (this.data) { + this.load() + } + this.loadSoftware(); + this.loadOrganizationalUnits() + this.loadOperativeSystems() + + this.filteredSoftware = this.softwareControl.valueChanges.pipe( + startWith(''), + debounceTime(300), + switchMap(value => this._filterSoftware(value)) + ); + } + + loadSoftware() { + this.http.get( `${this.baseUrl}/software?&page=1&itemsPerPage=10`).subscribe( + response => { + this.softwareCollection = response['hydra:member']; + }, + error => { + console.error('Error fetching parent units:', error); + } + ); + } + + loadOrganizationalUnits() { + this.http.get( `${this.baseUrl}/organizational-units?&page=1&itemsPerPage=10000`).subscribe( + response => { + this.organizationalUnits = response['hydra:member']; + }, + error => { + console.error('Error fetching parent units:', error); + } + ); + } + + loadOperativeSystems() { + this.http.get( `${this.baseUrl}/operative-systems?&page=1&itemsPerPage=10000`).subscribe( + response => { + this.operativeSystems = response['hydra:member']; + }, + error => { + console.error('Error fetching parent units:', error); + } + ); + } + + private _filterSoftware(value: string): Observable { + + return this.softwareDataService.getSoftwareCollection({ 'name': value}).pipe( + map(response => response || []) + ); + } + + displayFnClient(client: any): string { + return client && client.name ? client.name : ''; + } + + onOptionClientSelected(software: any) { + if (!this.selectedSoftwares.find(s => s.id === software.id)) { + this.selectedSoftwares.push(software); + } + this.softwareControl.setValue(''); + } + + load(): void { + console.log(this.data); + this.dataService.getSoftwareProfile(this.data).subscribe({ + next: (response) => { + this.formGroup = this.fb.group({ + description: [response.description], + comments: [response.comments], + organizationalUnit: [response.organizationalUnit ? response.organizationalUnit['@id'] : null], + operativeSystem: [response.operativeSystem ? response.operativeSystem['@id'] : null], + }); + this.softwareProfileId = response['@id']; + }, + error: (err) => { + console.error('Error fetching software:', err); + } + }); + } + + addSoftware() { + const software = this.softwareCollection.find(s => s.id === this.selectedSoftware); + if (software && !this.selectedSoftwares.includes(software)) { + this.selectedSoftwares.push(software); + } + } + + removeSoftware(software: any) { + this.selectedSoftwares = this.selectedSoftwares.filter(s => s.id !== software.id); + } + + onCancel(event: Event): void { + event.preventDefault(); + this.dialogRef.close(); + } + + onSubmit(): void { + if (this.formGroup.valid) { + + const payload = { + description: this.formGroup.value.description, + comments: this.formGroup.value.comments, + softwareCollection: this.selectedSoftwares.map(s => s['@id']), + organizationalUnit: this.formGroup.value.organizationalUnit, + operativeSystem: this.formGroup.value.operativeSystem + }; + + if (this.softwareProfileId) { + this.http.put(`${this.baseUrl}${this.softwareProfileId}`, payload).subscribe( + (response) => { + this.toastService.success('Software editado correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al editar el comando', error); + } + ); + } else { + this.http.post(`${this.baseUrl}/software-profiles`, payload).subscribe( + (response) => { + this.toastService.success('Software añadido correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al añadir comando', error); + } + ); + } + } + } +} diff --git a/ogWebconsole/src/app/components/software-profile/data.service.ts b/ogWebconsole/src/app/components/software-profile/data.service.ts new file mode 100644 index 0000000..b631756 --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/data.service.ts @@ -0,0 +1,49 @@ +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}/software-profiles`; + + constructor(private http: HttpClient) {} + + getSoftwareProfiles(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); + }) + ); + } + + getSoftwareProfile(id: string): Observable { + console.log(id) + return this.http.get(`${this.baseUrl}${id}`).pipe( + map(response => { + if (response.description) { + return response; + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching calendar', error); + return throwError(error); + }) + ); + } +} diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.css b/ogWebconsole/src/app/components/software-profile/software-profile.component.css new file mode 100644 index 0000000..2fd1786 --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.css @@ -0,0 +1,63 @@ +.title { + font-size: 24px; +} + +.calendar-button-row { + display: flex; + justify-content: flex-start; + margin-top: 16px; +} + +.divider { + margin: 20px 0; +} + +.lists-container { + padding: 16px; +} + +.card.unidad-card { + height: 100%; + box-sizing: border-box; +} + +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; +} diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.html b/ogWebconsole/src/app/components/software-profile/software-profile.component.html new file mode 100644 index 0000000..baa247e --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.html @@ -0,0 +1,50 @@ +
+

Administrar perfiles software

+
+ +
+
+ +
+ + Buscar nombre de perfil + + search + Pulsar 'enter' para buscar + +
+ + + + + + + + + + + + + +
{{ column.header }} + + + {{ image[column.columnDef] ? 'check_circle' : 'cancel' }} + + + + + {{ column.cell(image) }} + + Acciones + + +
+
+ + +
diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts b/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts new file mode 100644 index 0000000..4131330 --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SoftwareProfileComponent } from './software-profile.component'; + +describe('SoftwareProfileComponent', () => { + let component: SoftwareProfileComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SoftwareProfileComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SoftwareProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.ts b/ogWebconsole/src/app/components/software-profile/software-profile.component.ts new file mode 100644 index 0000000..4034adb --- /dev/null +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.ts @@ -0,0 +1,132 @@ +import {Component, 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 "../software/data.service"; +import {ToastrService} from "ngx-toastr"; +import {CreateSoftwareComponent} from "../software/create-software/create-software.component"; +import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import {PageEvent} from "@angular/material/paginator"; +import {CreateSoftwareProfileComponent} from "./create-software-profile/create-software-profile.component"; + +@Component({ + selector: 'app-software-profile', + templateUrl: './software-profile.component.html', + styleUrl: './software-profile.component.css' +}) +export class SoftwareProfileComponent { + 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 = 0; + pageSizeOptions: number[] = [5, 10, 20, 40, 100]; + 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: (software: any) => `${software.id}`, + }, + { + columnDef: 'description', + header: 'Descripción', + cell: (software: any) => `${software.description}` + }, + { + columnDef: 'operativeSystem', + header: 'Sistema Operativo', + cell: (software: any) => `${software.operativeSystem?.name}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (software: any) => `${this.datePipe.transform(software.createdAt, 'dd/MM/yyyy hh:mm:ss')}`, + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + private apiUrl = `${this.baseUrl}/software-profiles`; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private dataService: DataService, + private toastService: ToastrService + ) {} + + ngOnInit(): void { + this.search(); + } + + addSoftware(): void { + const dialogRef = this.dialog.open(CreateSoftwareProfileComponent, { + width: '600px' + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + search(): void { + this.http.get(`${this.apiUrl}?page=${this.page + 1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + (data) => { + this.dataSource.data = data['hydra:member']; + this.length = data['hydra:totalItems']; + }, + (error) => { + console.error('Error fetching commands', error); + } + ); + } + + editSoftware(softwareProfile: any): void { + const dialogRef = this.dialog.open(CreateSoftwareProfileComponent, { + width: '600px', + data: softwareProfile['@id'] + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + deleteSoftware(calendar: any): void { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { name: calendar.name } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + const apiUrl = `${this.baseUrl}${calendar['@id']}`; + + this.http.delete(apiUrl).subscribe({ + next: () => { + this.search(); + this.toastService.success('Software deleted successfully'); + }, + error: (error) => { + this.toastService.error('Error deleting calendar'); + } + }); + } else { + console.log('calendar deletion cancelled'); + } + }); + } + + onPageChange(event: PageEvent) { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.length = event.length; + this.search(); + } +} diff --git a/ogWebconsole/src/app/components/software/create-software/create-software.component.css b/ogWebconsole/src/app/components/software/create-software/create-software.component.css new file mode 100644 index 0000000..722ef4f --- /dev/null +++ b/ogWebconsole/src/app/components/software/create-software/create-software.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/software/create-software/create-software.component.html b/ogWebconsole/src/app/components/software/create-software/create-software.component.html new file mode 100644 index 0000000..495f491 --- /dev/null +++ b/ogWebconsole/src/app/components/software/create-software/create-software.component.html @@ -0,0 +1,29 @@ +

{{ softwareId ? 'Editar' : 'Crear' }} software

+ +
+ + Nombre + + + + + Descripción + + + + + Tipo + + Aplicacion + Sistema operativo + Fichero + + +
+
+ + + + + + diff --git a/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts b/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts new file mode 100644 index 0000000..afd91d6 --- /dev/null +++ b/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateSoftwareComponent } from './create-software.component'; + +describe('CreateSoftwareComponent', () => { + let component: CreateSoftwareComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateSoftwareComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateSoftwareComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/software/create-software/create-software.component.ts b/ogWebconsole/src/app/components/software/create-software/create-software.component.ts new file mode 100644 index 0000000..ea8c629 --- /dev/null +++ b/ogWebconsole/src/app/components/software/create-software/create-software.component.ts @@ -0,0 +1,96 @@ +import {Component, Inject} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from "@angular/forms"; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {ToastrService} from "ngx-toastr"; +import {DataService} from "../data.service"; + +@Component({ + selector: 'app-create-software', + templateUrl: './create-software.component.html', + styleUrl: './create-software.component.css' +}) +export class CreateSoftwareComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + formGroup: FormGroup; + private apiUrl = `${this.baseUrl}/software`; + softwareId: string | null = null; + + constructor( + private fb: FormBuilder, + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private dataService: DataService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.formGroup = this.fb.group({ + name: ['', Validators.required], + description: [''], + type: [''], + }); + } + + ngOnInit(): void { + if (this.data) { + this.load() + } + } + + load(): void { + this.dataService.getSoftware(this.data).subscribe({ + next: (response) => { + console.log(response); + this.formGroup = this.fb.group({ + name: [response.name, Validators.required], + description: [response.description], + type: [response.type], + }); + this.softwareId = response['@id']; + }, + error: (err) => { + console.error('Error fetching software:', err); + } + }); + } + + onCancel(event: Event): void { + event.preventDefault(); + this.dialogRef.close(); + } + + onSubmit(): void { + if (this.formGroup.valid) { + + const payload = { + name: this.formGroup.value.name, + description: this.formGroup.value.description, + type: this.formGroup.value.type, + }; + + if (this.softwareId) { + this.http.put(`${this.baseUrl}${this.softwareId}`, payload).subscribe( + (response) => { + this.toastService.success('Software editado correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al editar el comando', error); + } + ); + } else { + this.http.post(`${this.baseUrl}/software`, payload).subscribe( + (response) => { + this.toastService.success('Software añadido correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al añadir comando', error); + } + ); + } + } + } +} diff --git a/ogWebconsole/src/app/components/software/data.service.ts b/ogWebconsole/src/app/components/software/data.service.ts new file mode 100644 index 0000000..68d4574 --- /dev/null +++ b/ogWebconsole/src/app/components/software/data.service.ts @@ -0,0 +1,48 @@ +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}/software`; + + constructor(private http: HttpClient) {} + + getSoftwareCollection(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); + }) + ); + } + + getSoftware(id: string): Observable { + return this.http.get(`${this.baseUrl}${id}`).pipe( + map(response => { + if (response.name) { + return response; + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching calendar', error); + return throwError(error); + }) + ); + } +} diff --git a/ogWebconsole/src/app/components/software/software.component.css b/ogWebconsole/src/app/components/software/software.component.css new file mode 100644 index 0000000..2fd1786 --- /dev/null +++ b/ogWebconsole/src/app/components/software/software.component.css @@ -0,0 +1,63 @@ +.title { + font-size: 24px; +} + +.calendar-button-row { + display: flex; + justify-content: flex-start; + margin-top: 16px; +} + +.divider { + margin: 20px 0; +} + +.lists-container { + padding: 16px; +} + +.card.unidad-card { + height: 100%; + box-sizing: border-box; +} + +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; +} diff --git a/ogWebconsole/src/app/components/software/software.component.html b/ogWebconsole/src/app/components/software/software.component.html new file mode 100644 index 0000000..1f4c568 --- /dev/null +++ b/ogWebconsole/src/app/components/software/software.component.html @@ -0,0 +1,58 @@ +
+

Administrar Software

+
+ +
+
+ +
+ + Buscar nombre de software + + search + Pulsar 'enter' para buscar + + + Buscar por tipo + + Aplicación + Sistema operativo + Sistema de ficheros + + +
+ + + + + + + + + + + + + +
{{ column.header }} + + + {{ image[column.columnDef] ? 'check_circle' : 'cancel' }} + + + + + {{ column.cell(image) }} + + Acciones + + +
+
+ + +
diff --git a/ogWebconsole/src/app/components/software/software.component.spec.ts b/ogWebconsole/src/app/components/software/software.component.spec.ts new file mode 100644 index 0000000..71d391e --- /dev/null +++ b/ogWebconsole/src/app/components/software/software.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SoftwareComponent } from './software.component'; + +describe('SoftwareComponent', () => { + let component: SoftwareComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SoftwareComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SoftwareComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/software/software.component.ts b/ogWebconsole/src/app/components/software/software.component.ts new file mode 100644 index 0000000..390dc49 --- /dev/null +++ b/ogWebconsole/src/app/components/software/software.component.ts @@ -0,0 +1,137 @@ +import {Component, 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 {CreateCalendarComponent} from "../calendar/create-calendar/create-calendar.component"; +import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import {PageEvent} from "@angular/material/paginator"; +import {CreateSoftwareComponent} from "./create-software/create-software.component"; + +@Component({ + selector: 'app-software', + templateUrl: './software.component.html', + styleUrl: './software.component.css' +}) +export class SoftwareComponent { + 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 = 0; + pageSizeOptions: number[] = [5, 10, 20, 40, 100]; + 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: (software: any) => `${software.id}`, + }, + { + columnDef: 'name', + header: 'Nombre', + cell: (software: any) => `${software.name}` + }, + { + columnDef: 'description', + header: 'Descripción', + cell: (software: any) => `${software.description}` + }, + { + columnDef: 'type', + header: 'Tipo', + cell: (software: any) => `${software.type}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (software: any) => `${this.datePipe.transform(software.createdAt, 'dd/MM/yyyy hh:mm:ss')}`, + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + private apiUrl = `${this.baseUrl}/software`; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private dataService: DataService, + private toastService: ToastrService + ) {} + + ngOnInit(): void { + this.search(); + } + + addSoftware(): void { + const dialogRef = this.dialog.open(CreateSoftwareComponent, { + width: '600px' + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + search(): void { + this.http.get(`${this.apiUrl}?page=${this.page + 1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + (data) => { + this.dataSource.data = data['hydra:member']; + this.length = data['hydra:totalItems']; + }, + (error) => { + console.error('Error fetching commands', error); + } + ); + } + + editSoftware(calendar: any): void { + const dialogRef = this.dialog.open(CreateSoftwareComponent, { + width: '600px', + data: calendar['@id'] + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + deleteSoftware(calendar: any): void { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { name: calendar.name } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + const apiUrl = `${this.baseUrl}${calendar['@id']}`; + + this.http.delete(apiUrl).subscribe({ + next: () => { + this.search(); + this.toastService.success('Software deleted successfully'); + }, + error: (error) => { + this.toastService.error('Error deleting calendar'); + } + }); + } else { + console.log('calendar deletion cancelled'); + } + }); + } + + onPageChange(event: PageEvent) { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.length = event.length; + this.search(); + } +} diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html index eec756b..14dd2fe 100644 --- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html +++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html @@ -119,13 +119,35 @@ - + - settings_input_component - Componentes + terminal + Software + + + + + list + Listado + + + + + folder_shared + Perfiles + + + + + terminal + S. Operativos + + + + warehouse diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts b/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts index 963703f..b051273 100644 --- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts +++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts @@ -14,6 +14,7 @@ export class SidebarComponent { showOgBootSub: boolean = false; showOgDhcpSub: boolean = false; showCommandSub: boolean = false; + showSoftwareSub: boolean = false; toggleOgBootSub() { this.showOgBootSub = !this.showOgBootSub; @@ -24,6 +25,9 @@ export class SidebarComponent { toggleCommandSub() { this.showCommandSub = !this.showCommandSub; } + toggleSoftwareSub() { + this.showSoftwareSub = !this.showSoftwareSub; + } constructor(public dialog: MatDialog) {}