diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts index 925f4e6..bca74a7 100644 --- a/ogWebconsole/src/app/app-routing.module.ts +++ b/ogWebconsole/src/app/app-routing.module.ts @@ -30,6 +30,7 @@ import {OperativeSystemComponent} from "./components/operative-system/operative- import { PartitionAssistantComponent } from "./components/groups/components/client-main-view/partition-assistant/partition-assistant.component"; +import {RepositoriesComponent} from "./components/repositories/repositories.component"; const routes: Routes = [ { path: '', redirectTo: 'auth/login', pathMatch: 'full' }, { @@ -56,6 +57,7 @@ const routes: Routes = [ { path: 'client/:id', component: ClientMainViewComponent }, { path: 'client/:id/partition-assistant', component: PartitionAssistantComponent }, { path: 'images', component: ImagesComponent }, + { path: 'repositories', component: RepositoriesComponent }, { path: 'restore-image', component: RestoreImageComponent}, { path: 'software', component: SoftwareComponent }, { path: 'software-profiles', component: SoftwareProfileComponent }, diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index d1f5901..02bb02b 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -116,6 +116,8 @@ import { CreateOperativeSystemComponent } from './components/operative-system/cr import { ShowTemplateContentComponent } from './components/ogboot/pxe/show-template-content/show-template-content.component'; import { AddClientsToPxeComponent } from './components/ogboot/pxe/add-clients-to-pxe/add-clients-to-pxe.component'; import { ClientsComponent } from './components/ogboot/pxe/clients/clients.component'; +import { RepositoriesComponent } from './components/repositories/repositories.component'; +import { CreateRepositoryComponent } from './components/repositories/create-repository/create-repository.component'; @NgModule({ declarations: [ AppComponent, @@ -189,6 +191,8 @@ import { ClientsComponent } from './components/ogboot/pxe/clients/clients.compon ShowTemplateContentComponent, AddClientsToPxeComponent, ClientsComponent, + RepositoriesComponent, + CreateRepositoryComponent, ], bootstrap: [AppComponent], imports: [BrowserModule, diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.css b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.css new file mode 100644 index 0000000..57c71a9 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.css @@ -0,0 +1,43 @@ +.dialog-content { + display: flex; + flex-direction: column; + gap: 16px; +} + +.repository-form { + width: 100%; + display: flex; + flex-direction: column; +} + +.form-field { + width: 100%; + margin-bottom: 16px; +} + +.dialog-actions { + display: flex; + justify-content: flex-end; + margin-top: 24px; +} + +button { + margin-left: 8px; +} + +@media (max-width: 600px) { + .form-field { + width: 100%; + } + + .dialog-actions { + flex-direction: column; + align-items: stretch; + } + + button { + width: 100%; + margin-left: 0; + margin-bottom: 8px; + } +} diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html new file mode 100644 index 0000000..501d1fa --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html @@ -0,0 +1,25 @@ +

{{ repositoryId ? 'Editar' : 'Añadir' }} repositorio

+ + +
+ + Nombre del repositorio + + + + + Ip + + + + + Comentarios + + +
+
+ + + + + diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts new file mode 100644 index 0000000..fbdb87b --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateRepositoryComponent } from './create-repository.component'; + +describe('CreateRepositoryComponent', () => { + let component: CreateRepositoryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateRepositoryComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateRepositoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts new file mode 100644 index 0000000..634dd4f --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts @@ -0,0 +1,91 @@ +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 "../../images/data.service"; + +@Component({ + selector: 'app-create-repository', + templateUrl: './create-repository.component.html', + styleUrl: './create-repository.component.css' +}) +export class CreateRepositoryComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + imageForm: FormGroup; + repositoryId: string | null = null; + softwareProfiles: any[] = []; + + constructor( + private fb: FormBuilder, + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private dataService: DataService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.imageForm = this.fb.group({ + name: ['', Validators.required], + ip: [''], + comments: [''], + }); + } + + ngOnInit() { + if (this.data) { + this.load() + } + } + + load(): void { + this.dataService.getImage(this.data).subscribe({ + next: (response) => { + this.imageForm = this.fb.group({ + name: [response.name, Validators.required], + ip: [response.ip], + comments: [response.comments], + }); + this.repositoryId = response['@id']; + }, + error: (err) => { + console.error('Error fetching remote calendar:', err); + } + }); + } + + save(): void { + const payload = { + name: this.imageForm.value.name, + ip: this.imageForm.value.ip, + comments: this.imageForm.value.comments, + }; + + if (this.repositoryId) { + this.http.put(`${this.baseUrl}${this.repositoryId}`, payload).subscribe( + (response) => { + this.toastService.success('Imagen editada correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al editar la imagen', error); + } + ); + } else { + this.http.post(`${this.baseUrl}/image-repositories`, payload).subscribe( + (response) => { + this.toastService.success('Imagen añadida correctamente'); + this.dialogRef.close(); + }, + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al añadir la imagen', error); + } + ); + } + } + + close(): void { + this.dialogRef.close(); + } +} diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.css b/ogWebconsole/src/app/components/repositories/repositories.component.css new file mode 100644 index 0000000..16117cb --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/repositories.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/repositories/repositories.component.html b/ogWebconsole/src/app/components/repositories/repositories.component.html new file mode 100644 index 0000000..a2e2a1e --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/repositories.component.html @@ -0,0 +1,48 @@ +
+

Administrar repositorios

+
+ +
+
+ + +
+ + Buscar nombre de imagen + + search + Pulsar 'enter' para buscar + +
+ + + + + + + + + + + + + +
{{ column.header }} + + {{ column.cell(repository) }} + + Acciones + + +
+ +
+ + +
diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts b/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts new file mode 100644 index 0000000..7169583 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RepositoriesComponent } from './repositories.component'; + +describe('RepositoriesComponent', () => { + let component: RepositoriesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [RepositoriesComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RepositoriesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.ts b/ogWebconsole/src/app/components/repositories/repositories.component.ts new file mode 100644 index 0000000..6c0a04b --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/repositories.component.ts @@ -0,0 +1,120 @@ +import { Component } 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 {ToastrService} from "ngx-toastr"; +import {CreateImageComponent} from "../images/create-image/create-image.component"; +import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import {CreateRepositoryComponent} from "./create-repository/create-repository.component"; + +@Component({ + selector: 'app-repositories', + templateUrl: './repositories.component.html', + styleUrl: './repositories.component.css' +}) +export class RepositoriesComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + dataSource = new MatTableDataSource(); + length: number = 0; + itemsPerPage: number = 10; + page: number = 0; + loading: boolean = false; + filters: { [key: string]: string } = {}; + datePipe: DatePipe = new DatePipe('es-ES'); + columns = [ + { + columnDef: 'id', + header: 'Id', + cell: (repository: any) => `${repository.id}` + }, + { + columnDef: 'name', + header: 'Nombre de repositorio', + cell: (repository: any) => `${repository.name}` + }, + { + columnDef: 'ip', + header: 'Ip', + cell: (repository: any) => `${repository.ip}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (repository: any) => `${this.datePipe.transform(repository.createdAt, 'dd/MM/yyyy hh:mm:ss')}` + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + private apiUrl = `${this.baseUrl}/image-repositories`; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private toastService: ToastrService + ) {} + + ngOnInit(): void { + this.search(); + } + + addImage(): void { + const dialogRef = this.dialog.open(CreateRepositoryComponent, { + width: '600px' + }); + + dialogRef.afterClosed().subscribe(() => { + this.search(); + }); + } + + search(): void { + this.loading = true; + 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']; + this.loading = false; + }, + error => { + console.error('Error fetching images', error); + this.loading = false; + } + ); + } + + editRepository(event: MouseEvent, repository: any): void { + event.stopPropagation(); + this.dialog.open(CreateRepositoryComponent, { + width: '600px', + data: repository['@id'] + }).afterClosed().subscribe(() => this.search()); + } + + deleteRepository(event: MouseEvent,command: any): void { + event.stopPropagation(); + this.dialog.open(DeleteModalComponent, { + width: '300px', + data: { name: command.name }, + }).afterClosed().subscribe((result) => { + if (result) { + this.http.delete(`${this.apiUrl}/${command.uuid}`).subscribe({ + next: () => { + this.toastService.success('Imagen eliminada con éxito'); + this.search(); + }, + error: (error) => { + console.error('Error al eliminar la imagen:', error); + } + }); + } + }); + } + + onPageChange(event: any): void { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.length = event.length; + this.search(); + } +}