diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index a079e76..2d13964 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -127,6 +127,8 @@ import { CreateMultipleClientComponent } from './components/groups/shared/client
import { ExportImageComponent } from './components/images/export-image/export-image.component';
import {ImportImageComponent} from "./components/repositories/import-image/import-image.component";
import { LoadingComponent } from './shared/loading/loading.component';
+import { RepositoryImagesComponent } from './components/repositories/repository-images/repository-images.component';
+import { InputDialogComponent } from './components/commands/commands-task/task-logs/input-dialog/input-dialog.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
}
@@ -212,6 +214,8 @@ export function HttpLoaderFactory(http: HttpClient) {
ExportImageComponent,
ImportImageComponent,
LoadingComponent,
+ RepositoryImagesComponent,
+ InputDialogComponent,
],
bootstrap: [AppComponent],
imports: [BrowserModule,
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.css b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.html b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.html
new file mode 100644
index 0000000..e12c176
--- /dev/null
+++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.html
@@ -0,0 +1,7 @@
+
{{ 'inputDetails' | translate }}
+
+
{{ data.input | json }}
+
+
+
+
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.spec.ts b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.spec.ts
new file mode 100644
index 0000000..7168136
--- /dev/null
+++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.spec.ts
@@ -0,0 +1,47 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { InputDialogComponent } from './input-dialog.component';
+import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog";
+import {FormBuilder} from "@angular/forms";
+import {ToastrService} from "ngx-toastr";
+import {provideHttpClient} from "@angular/common/http";
+import {provideHttpClientTesting} from "@angular/common/http/testing";
+import {TranslateModule} from "@ngx-translate/core";
+
+describe('InputDialogComponent', () => {
+ let component: InputDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [InputDialogComponent],
+ imports: [
+ MatDialogModule,
+ TranslateModule.forRoot(),
+ ],
+ providers: [
+ FormBuilder,
+ ToastrService,
+ provideHttpClient(),
+ provideHttpClientTesting(),
+ {
+ provide: MatDialogRef,
+ useValue: {}
+ },
+ {
+ provide: MAT_DIALOG_DATA,
+ useValue: {}
+ }
+ ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(InputDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.ts b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.ts
new file mode 100644
index 0000000..744b415
--- /dev/null
+++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/input-dialog/input-dialog.component.ts
@@ -0,0 +1,18 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+ selector: 'app-input-dialog',
+ templateUrl: './input-dialog.component.html',
+ styleUrl: './input-dialog.component.css'
+})
+export class InputDialogComponent {
+ constructor(
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: { input: any }
+ ) {}
+
+ close(): void {
+ this.dialogRef.close();
+ }
+}
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html
index f26735f..cded0da 100644
--- a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html
+++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html
@@ -61,30 +61,42 @@
{{ column.header }} |
-
-
- {{
- trace.status === 'failed' ? 'Fallido' :
- trace.status === 'success' ? 'Finalizado con éxito' :
- trace.status === 'pending' ? 'Pendiente de ejecutar' :
- trace.status === 'in-progress' ? 'Ejecutando' :
- trace.status
- }}
-
-
+
+
+
+
+ {{
+ trace.status === 'failed' ? 'Fallido' :
+ trace.status === 'success' ? 'Finalizado con éxito' :
+ trace.status === 'pending' ? 'Pendiente de ejecutar' :
+ trace.status === 'in-progress' ? 'Ejecutando' :
+ trace.status
+ }}
+
+
-
- {{ column.cell(trace) }}
-
+
+
+
+
+
+
+
+ {{ column.cell(trace) }}
+
+
|
+
@@ -94,4 +106,4 @@
-
\ No newline at end of file
+
diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts
index d605f41..e78cb96 100644
--- a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts
+++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts
@@ -5,6 +5,8 @@ import { FormControl } from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { DatePipe } from '@angular/common';
import { JoyrideService } from 'ngx-joyride';
+import {MatDialog} from "@angular/material/dialog";
+import {InputDialogComponent} from "./input-dialog/input-dialog.component";
@Component({
selector: 'app-task-logs',
@@ -50,6 +52,11 @@ export class TaskLogsComponent implements OnInit {
header: 'Hilo de trabajo',
cell: (trace: any) => `${trace.jobId}`
},
+ {
+ columnDef: 'input',
+ header: 'Input',
+ cell: (trace: any) => `${trace.input}`
+ },
{
columnDef: 'output',
header: 'Logs',
@@ -75,7 +82,9 @@ export class TaskLogsComponent implements OnInit {
commandControl = new FormControl();
constructor(private http: HttpClient,
- private joyrideService: JoyrideService) {}
+ private joyrideService: JoyrideService,
+ private dialog: MatDialog
+ ) {}
ngOnInit(): void {
this.loadTraces();
@@ -121,6 +130,13 @@ export class TaskLogsComponent implements OnInit {
this.loadTraces();
}
+ openInputModal(inputData: any): void {
+ this.dialog.open(InputDialogComponent, {
+ width: '700px',
+ data: { input: inputData }
+ });
+ }
+
loadTraces(): void {
this.loading = true;
const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`;
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html
index a68c21d..0fd1c85 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html
+++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html
@@ -52,8 +52,9 @@
Seleccione imagen
- {{ image.name }}
+ {{ image.image?.name }}
+ Imágenes alojadas en {{ clientData[0].repository?.name }}
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts
index caaa02d..841f960 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts
+++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts
@@ -19,7 +19,7 @@ export class DeployImageComponent {
partitions: any[] = [];
images: any[] = [];
clientName: string = '';
- selectedImage: string | null = null;
+ selectedImage: any = null;
selectedOption: string | null = 'deploy-image';
selectedMethod: string | null = null;
selectedPartition: any = null;
@@ -139,10 +139,25 @@ export class DeployImageComponent {
}
loadImages() {
- const url = `${this.baseUrl}/images?status=success&page=1&itemsPerPage=1000`;
+ if (!this.clientData || this.clientData.length === 0 || !this.clientData[0]) {
+ console.error('Error: clientData es nulo, indefinido o vacío.');
+ return;
+ }
+
+ const repositoryId =
+ this.clientData[0]?.repository?.id ??
+ this.clientData[0]?.organizationalUnit?.networkSettings?.repository?.id;
+
+ if (!repositoryId) {
+ console.error('Error: No se encontró repositoryId en clientData.');
+ return;
+ }
+
+ const url = `${this.baseUrl}/image-image-repositories?status=success&repository.id=${repositoryId}&page=1&itemsPerPage=1000`;
+
this.http.get(url).subscribe(
(response: any) => {
- this.images = response['hydra:member'];
+ this.images = response?.['hydra:member'] || [];
},
(error) => {
console.error('Error al cargar las imágenes:', error);
@@ -189,7 +204,7 @@ export class DeployImageComponent {
maxClients: this.mcastMaxClients,
};
- this.http.post(`${this.baseUrl}${this.selectedImage}/deploy-image`, payload)
+ this.http.post(`${this.baseUrl}/image-image-repositories/${this.selectedImage.uuid}/deploy-image`, payload)
.subscribe({
next: (response) => {
this.toastService.success('Petición de despliegue enviada correctamente');
diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.html b/ogWebconsole/src/app/components/images/create-image/create-image.component.html
index baea718..2f3a3a2 100644
--- a/ogWebconsole/src/app/components/images/create-image/create-image.component.html
+++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.html
@@ -10,7 +10,7 @@
{{ 'repositoryLabel' | translate }}
-
+
{{ imageRepository.name }}
diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts
index 00ed9cb..793ec40 100644
--- a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts
+++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts
@@ -54,7 +54,7 @@ export class CreateImageComponent implements OnInit {
remotePc: [response.remotePc],
isGlobal: [response.isGlobal],
softwareProfile: [response.softwareProfile ? response.softwareProfile['@id'] : null, Validators.required],
- imageRepositories: [response.imageRepositories ? response.imageRepositories.map((r: any) => r['@id']) : [], Validators.required],
+ imageRepositories: [response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [], Validators.required],
});
this.imageId = response['@id'];
this.partitionInfo = response.partitionInfo;
diff --git a/ogWebconsole/src/app/components/images/images.component.html b/ogWebconsole/src/app/components/images/images.component.html
index ec22f52..989b53a 100644
--- a/ogWebconsole/src/app/components/images/images.component.html
+++ b/ogWebconsole/src/app/components/images/images.component.html
@@ -25,18 +25,6 @@
search
{{ 'searchHint' | translate }}
-
- Estado
-
- Fallido
- Pendiente
- Transfiriendo
- Creado con éxito
- En progreso
- Papelera
- Creando archivos auxiliares
-
-
Ver repositorios
@@ -62,19 +50,7 @@
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
-
-
- {{ getStatusLabel(image[column.columnDef]) }}
-
-
-
+
{{ column.cell(image) }}
@@ -83,24 +59,15 @@
Acciones |
-
-
- |
@@ -112,4 +79,4 @@
-
\ No newline at end of file
+
diff --git a/ogWebconsole/src/app/components/images/images.component.ts b/ogWebconsole/src/app/components/images/images.component.ts
index 6380e6c..aa3cfb3 100644
--- a/ogWebconsole/src/app/components/images/images.component.ts
+++ b/ogWebconsole/src/app/components/images/images.component.ts
@@ -52,11 +52,6 @@ export class ImagesComponent implements OnInit {
header: 'Imagen Global',
cell: (image: any) => `${image.isGlobal}`
},
- {
- columnDef: 'status',
- header: 'Estado',
- cell: (image: any) => `${image.status}`
- },
{
columnDef: 'createdAt',
header: 'Fecha de creación',
@@ -96,27 +91,6 @@ export class ImagesComponent implements OnInit {
)
}
- getStatusLabel(status: string): string {
- switch (status) {
- case 'pending':
- return 'Pendiente';
- case 'in-progress':
- return 'En progreso';
- case 'aux-file-pending':
- return 'Archivos auxiliares pendientes';
- case 'success':
- return 'Creado con éxito';
- case 'trash':
- return 'Papelera temporal';
- case 'failed':
- return 'Fallido';
- case 'trasnferring':
- return 'Transfiriendo';
- default:
- return 'Estado desconocido';
- }
- }
-
addImage(): void {
const dialogRef = this.dialog.open(CreateImageComponent, {
width: '800px'
@@ -157,78 +131,10 @@ export class ImagesComponent implements OnInit {
this.search();
}
- loadImageAlert(image: any): Observable {
- return this.http.get(`${this.apiUrl}/server/${image.uuid}/get`, {});
- }
-
- showImageInfo(event: MouseEvent, image:any) {
- event.stopPropagation();
- this.loading = true;
- this.loadImageAlert(image).subscribe(
- response => {
- this.alertMessage = response;
-
- this.dialog.open(ServerInfoDialogComponent, {
- width: '800px',
- data: {
- message: this.alertMessage
- }
- });
- this.loading = false;
- },
- error => {
- this.toastService.error(error.error['hydra:description']);
- this.loading = false;
- }
- );
- }
-
toggleAction(image: any, action:string): void {
switch (action) {
- case 'get-aux':
- this.http.post(`${this.baseUrl}/images/server/${image.uuid}/create-aux-files`, {}).subscribe({
- next: () => {
- this.toastService.success('Petición de creación de archivos auxiliares enviada');
- this.search()
- },
- error: (error) => {
- this.toastService.error(error.error['hydra:description']);
- }
- });
- break;
case 'delete-trash':
- if (!image.imageFullsum) {
- const dialogRef = this.dialog.open(DeleteModalComponent, {
- width: '400px',
- data: { name: image.name },
- });
-
- dialogRef.afterClosed().subscribe((result) => {
- this.http.delete(`${this.baseUrl}${image['@id']}`).subscribe({
- next: () => {
- this.toastService.success('Image deleted successfully');
- this.search()
- },
- error: (error) => {
- this.toastService.error('Error deleting image');
- }
- });
- });
- } else {
- this.http.post(`${this.baseUrl}/images/server/${image.uuid}/delete-trash`, {}).subscribe({
- next: () => {
- this.toastService.success('Petición de eliminación de la papelera temporal enviada');
- this.search()
- },
- error: (error) => {
- this.toastService.error(error.error['hydra:description']);
- }
- });
- }
-
- break;
- case 'delete-permanent':
- this.http.post(`${this.baseUrl}/images/server/${image.uuid}/delete-permanent`, {}).subscribe({
+ this.http.post(`${this.baseUrl}/images/server/${image.uuid}/delete-trash`, {}).subscribe({
next: () => {
this.toastService.success('Petición de eliminación de la papelera temporal enviada');
this.search()
@@ -238,10 +144,10 @@ export class ImagesComponent implements OnInit {
}
});
break;
- case 'recover':
- this.http.post(`${this.baseUrl}/images/server/${image.uuid}/recover`, {}).subscribe({
+ case 'delete-permanent':
+ this.http.post(`${this.baseUrl}/images/server/${image.uuid}/delete-permanent`, {}).subscribe({
next: () => {
- this.toastService.success('Petición de recuperación de la imagen enviada');
+ this.toastService.success('Petición de eliminación de la papelera temporal enviada');
this.search()
},
error: (error) => {
diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html
index e27dbc9..62a05fc 100644
--- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html
+++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html
@@ -113,6 +113,6 @@
-
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts
index 15f3c75..b756e28 100644
--- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts
+++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts
@@ -119,7 +119,6 @@ export class MainRepositoryViewComponent implements OnInit {
comments: [response.comments],
});
this.loading = false;
- this.searchImages();
},
(error) => {
console.error('Error al cargar los datos del cliente:', error);
@@ -207,21 +206,6 @@ export class MainRepositoryViewComponent implements OnInit {
return this.processesStatus ? this.processesStatus : [];
}
- searchImages(): void {
- this.loading = true;
- this.http.get(`${this.apiUrl}?repositoryId=${this.repositoryData.id}&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;
- }
- );
- }
-
loadAlert(): Observable {
return this.http.get(`${this.baseUrl}/image-repositories/server/get-collection`);
}
diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.css b/ogWebconsole/src/app/components/repositories/repositories.component.css
index 053704a..d7f88b1 100644
--- a/ogWebconsole/src/app/components/repositories/repositories.component.css
+++ b/ogWebconsole/src/app/components/repositories/repositories.component.css
@@ -95,4 +95,21 @@ table {
.example-button-row .mat-mdc-button-base {
margin: 8px 8px 8px 0;
-}
\ No newline at end of file
+
+}
+
+.header-container-title {
+ flex-grow: 1;
+ text-align: left;
+ margin-left: 1em;
+}
+
+.chip-success {
+ background-color: #63d165 !important;
+ color: white ;
+}
+
+.chip-failed {
+ background-color: #ff6b63 !important;
+ color: white;
+}
diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.html b/ogWebconsole/src/app/components/repositories/repositories.component.html
index 4433542..1a51e54 100644
--- a/ogWebconsole/src/app/components/repositories/repositories.component.html
+++ b/ogWebconsole/src/app/components/repositories/repositories.component.html
@@ -37,8 +37,19 @@
{{ column.header }} |
-
- {{ column.cell(repository) }}
+
+
+
+ {{ repository.status === true ? 'Conectado' : 'Fallido' }}
+
+
+
+
+ {{ column.cell(repository) }}
+
|
@@ -61,4 +72,4 @@
-
\ No newline at end of file
+
diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.ts b/ogWebconsole/src/app/components/repositories/repositories.component.ts
index 58e85f3..eedb828 100644
--- a/ogWebconsole/src/app/components/repositories/repositories.component.ts
+++ b/ogWebconsole/src/app/components/repositories/repositories.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {MatTableDataSource} from "@angular/material/table";
import {DatePipe} from "@angular/common";
import {MatDialog} from "@angular/material/dialog";
@@ -8,14 +8,13 @@ import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delet
import { JoyrideService } from 'ngx-joyride';
import {CreateRepositoryComponent} from "./create-repository/create-repository.component";
import { Router } from '@angular/router';
-import {ImportImageComponent} from "./import-image/import-image.component";
@Component({
selector: 'app-repositories',
templateUrl: './repositories.component.html',
styleUrl: './repositories.component.css'
})
-export class RepositoriesComponent {
+export class RepositoriesComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
dataSource = new MatTableDataSource();
length: number = 0;
@@ -40,6 +39,11 @@ export class RepositoriesComponent {
header: 'Ip',
cell: (repository: any) => `${repository.ip}`
},
+ {
+ columnDef: 'status',
+ header: 'Estado',
+ cell: (repository: any) => `${repository.status}`
+ },
{
columnDef: 'createdAt',
header: 'Fecha de creación',
diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css
new file mode 100644
index 0000000..af5ea91
--- /dev/null
+++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css
@@ -0,0 +1,104 @@
+.title {
+ font-size: 24px;
+}
+
+.images-button-row {
+ display: flex;
+ justify-content: flex-start;
+ margin-top: 16px;
+}
+
+.divider {
+ margin: 20px 0;
+}
+
+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;
+}
+
+.button-row {
+ display: table-cell;
+ max-width: 600px;
+}
+
+.button-row .mat-mdc-button-base {
+ margin: 8px 8px 8px 0;
+}
+
+.chip-failed {
+ background-color: #e87979 !important;
+ color: white;
+}
+
+.chip-success {
+ background-color: #46c446 !important;
+ color: white;
+}
+
+.chip-pending {
+ background-color: lightgrey !important;
+ color: black;
+}
+
+.chip-in-progress {
+ background-color: #f5a623 !important;
+ color: white;
+}
+
+.chip-transferring {
+ background-color: #f5a623 !important;
+ color: white;
+}
+
+.header-container-title {
+ flex-grow: 1;
+ text-align: left;
+ margin-left: 1em;
+}
+
diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html
new file mode 100644
index 0000000..8347221
--- /dev/null
+++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+ {{ 'searchLabel' | translate }}
+
+ search
+ {{ 'searchHint' | translate }}
+
+
+ Estado
+
+ Fallido
+ Pendiente
+ Transfiriendo
+ Creado con éxito
+ En progreso
+ Papelera
+ Creando archivos auxiliares
+
+
+
+
+
+
+ {{ column.header }} |
+
+
+
+ {{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
+
+
+
+
+ {{ getStatusLabel(image[column.columnDef]) }}
+
+
+
+ {{ column.cell(image) }}
+
+ |
+
+
+
+ Acciones |
+
+ visibility
+
+ delete
+
+
+ menu
+
+
+ Obtener ficheros auxiliares
+ Eliminar imagen
+ Recuperar imagen de la papelera
+ Transferir imagen
+
+ |
+
+
+
+
+
+
+
+
+
+
diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts
new file mode 100644
index 0000000..c18a2a3
--- /dev/null
+++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts
@@ -0,0 +1,78 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { provideHttpClient } from '@angular/common/http';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+import { ReactiveFormsModule, FormsModule, FormBuilder } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatTableModule } from '@angular/material/table';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatIconModule } from '@angular/material/icon';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { TranslateModule } from '@ngx-translate/core';
+import { ToastrModule, ToastrService } from 'ngx-toastr';
+import { JoyrideModule } from 'ngx-joyride';
+import { CommonModule } from '@angular/common';
+import {MatProgressSpinner} from "@angular/material/progress-spinner";
+import {RepositoryImagesComponent} from "./repository-images.component";
+import {LoadingComponent} from "../../../shared/loading/loading.component";
+import {MatOptionModule} from "@angular/material/core";
+import {MatSelectModule} from "@angular/material/select";
+
+describe('RepositoriesComponent', () => {
+ let component: RepositoryImagesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RepositoryImagesComponent, LoadingComponent],
+ imports: [
+ ReactiveFormsModule,
+ FormsModule,
+ MatDialogModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatCheckboxModule,
+ MatButtonModule,
+ MatTableModule,
+ MatPaginatorModule,
+ MatDividerModule,
+ MatIconModule,
+ MatOptionModule,
+ BrowserAnimationsModule,
+ MatProgressSpinner,
+ MatSelectModule ,
+ ToastrModule.forRoot(),
+ TranslateModule.forRoot(),
+ JoyrideModule.forRoot(),
+ CommonModule
+ ],
+ providers: [
+ FormBuilder,
+ ToastrService,
+ provideHttpClient(),
+ provideHttpClientTesting(),
+ {
+ provide: MatDialogRef,
+ useValue: {}
+ },
+ {
+ provide: MAT_DIALOG_DATA,
+ useValue: {}
+ }
+ ]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RepositoryImagesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts
new file mode 100644
index 0000000..0362379
--- /dev/null
+++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts
@@ -0,0 +1,257 @@
+import {Component, Input, OnInit} 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 {JoyrideService} from "ngx-joyride";
+import {CreateImageComponent} from "../../images/create-image/create-image.component";
+import {Observable} from "rxjs";
+import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
+import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component";
+import {ExportImageComponent} from "../../images/export-image/export-image.component";
+
+@Component({
+ selector: 'app-repository-images',
+ templateUrl: './repository-images.component.html',
+ styleUrl: './repository-images.component.css'
+})
+export class RepositoryImagesComponent implements OnInit {
+ 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 } = {};
+ alertMessage: string | null = null;
+ repository: any = {};
+ datePipe: DatePipe = new DatePipe('es-ES');
+ columns = [
+ {
+ columnDef: 'id',
+ header: 'Id',
+ cell: (image: any) => `${image.id}`
+ },
+ {
+ columnDef: 'image.image.name',
+ header: 'Nombre de imagen',
+ cell: (image: any) => `${image.image.name}`
+ },
+ {
+ columnDef: 'remotePc',
+ header: 'Remote Pc',
+ cell: (image: any) => `${image.remotePc}`
+ },
+ {
+ columnDef: 'status',
+ header: 'Estado',
+ cell: (image: any) => `${image.status}`
+ },
+ {
+ columnDef: 'createdAt',
+ header: 'Fecha de creación',
+ cell: (image: any) => `${this.datePipe.transform(image.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
+ }
+ ];
+ displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
+
+ private apiUrl = `${this.baseUrl}/image-image-repositories`;
+ @Input() repositoryUuid: any
+ private repositoryId: any;
+
+ constructor(
+ public dialog: MatDialog,
+ private http: HttpClient,
+ private toastService: ToastrService,
+ private joyrideService: JoyrideService,
+ ) {}
+
+ ngOnInit(): void {
+ if (this.repositoryUuid) {
+ this.loadRepository()
+ } else {
+ this.search();
+ }
+ }
+
+ loadRepository(): void {
+ this.http.get(`${this.baseUrl}/image-repositories/${this.repositoryUuid}`, {}).subscribe(
+ data => {
+ this.repositoryId = data.id;
+ this.repository = data
+ this.search();
+ },
+ error => {
+ console.error('Error fetching image repositories', error);
+ }
+ )
+ }
+
+ getStatusLabel(status: string): string {
+ switch (status) {
+ case 'pending':
+ return 'Pendiente';
+ case 'in-progress':
+ return 'En progreso';
+ case 'aux-files-pending':
+ return 'Archivos auxiliares pendientes';
+ case 'success':
+ return 'Creado con éxito';
+ case 'trash':
+ return 'Papelera temporal';
+ case 'failed':
+ return 'Fallido';
+ case 'transferring':
+ return 'Transfiriendo';
+ default:
+ return 'Estado desconocido';
+ }
+ }
+
+ search(): void {
+ this.loading = true;
+ this.http.get(`${this.apiUrl}?page=${this.page +1 }&itemsPerPage=${this.itemsPerPage}&repositoryId=${this.repositoryId}`, { 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;
+ }
+ );
+ }
+
+ onPageChange(event: any): void {
+ this.page = event.pageIndex;
+ this.itemsPerPage = event.pageSize;
+ this.length = event.length;
+ this.search();
+ }
+
+ loadImageAlert(image: any): Observable {
+ return this.http.get(`${this.apiUrl}/server/${image.uuid}/get`, {});
+ }
+
+ showImageInfo(event: MouseEvent, image:any) {
+ event.stopPropagation();
+ this.loading = true;
+ this.loadImageAlert(image).subscribe(
+ response => {
+ this.alertMessage = response;
+
+ this.dialog.open(ServerInfoDialogComponent, {
+ width: '800px',
+ data: {
+ message: this.alertMessage
+ }
+ });
+ this.loading = false;
+ },
+ error => {
+ this.toastService.error(error.error['hydra:description']);
+ this.loading = false;
+ }
+ );
+ }
+
+ toggleAction(image: any, action:string): void {
+ switch (action) {
+ case 'get-aux':
+ this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/create-aux-files`, {}).subscribe({
+ next: () => {
+ this.toastService.success('Petición de creación de archivos auxiliares enviada');
+ this.search()
+ },
+ error: (error) => {
+ this.toastService.error(error.error['hydra:description']);
+ }
+ });
+ break;
+ case 'delete-trash':
+ if (!image.imageFullsum) {
+ const dialogRef = this.dialog.open(DeleteModalComponent, {
+ width: '400px',
+ data: { name: image.name },
+ });
+
+ dialogRef.afterClosed().subscribe((result) => {
+ this.http.delete(`${this.baseUrl}${image['@id']}`).subscribe({
+ next: () => {
+ this.toastService.success('Image deleted successfully');
+ this.search()
+ },
+ error: (error) => {
+ this.toastService.error('Error deleting image');
+ }
+ });
+ });
+ } else {
+ this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-trash`,
+ { repository: `/image-repositories/${this.repositoryUuid}` })
+ .subscribe({
+ next: () => {
+ this.toastService.success('Petición de eliminación de la papelera temporal enviada');
+ this.search()
+ },
+ error: (error) => {
+ this.toastService.error(error.error['hydra:description']);
+ }
+ });
+ }
+
+ break;
+ case 'delete-permanent':
+ this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-permanent`, {}).subscribe({
+ next: () => {
+ this.toastService.success('Petición de eliminación de la papelera temporal enviada');
+ this.search()
+ },
+ error: (error) => {
+ this.toastService.error(error.error['hydra:description']);
+ }
+ });
+ break;
+ case 'recover':
+ this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/recover`, {}).subscribe({
+ next: () => {
+ this.toastService.success('Petición de recuperación de la imagen enviada');
+ this.search()
+ },
+ error: (error) => {
+ this.toastService.error(error.error['hydra:description']);
+ }
+ });
+ break;
+ case 'transfer':
+ this.dialog.open(ExportImageComponent, {
+ width: '600px',
+ data: {
+ image: image
+ }
+ });
+ break;
+ default:
+ console.error('Acción no soportada:', action);
+ break;
+ }
+ }
+ iniciarTour(): void {
+ this.joyrideService.startTour({
+ steps: [
+ 'imagesTitleStep',
+ 'addImageButton',
+ 'searchImagesField',
+ 'imagesTable',
+ 'actionsHeader',
+ 'editImageButton',
+ 'deleteImageButton',
+ 'imagesPagination'
+ ],
+ showPrevButton: true,
+ themeColor: '#3f51b5'
+ });
+ }
+}
diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json
index 08ac54e..d3a179c 100644
--- a/ogWebconsole/src/locale/en.json
+++ b/ogWebconsole/src/locale/en.json
@@ -181,6 +181,7 @@
"viewTreeTitle": "View organizational unit tree",
"toggleNodeAriaLabel": "Toggle node",
"closeButton": "Close",
+ "inputDetails": "Input details",
"orgUnitPropertiesTitle": "Organizational unit properties",
"generalDataTab": "General data",
"propertyHeader": "Property",
diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json
index dd1f26a..6a58707 100644
--- a/ogWebconsole/src/locale/es.json
+++ b/ogWebconsole/src/locale/es.json
@@ -240,6 +240,7 @@
"repositoryLabel": "Repositorios",
"serialNumberLabel": "Número de Serie",
"netifaceLabel": "Interfaz de red",
+ "inputDetails": "Detalles",
"netDriverLabel": "Controlador de red",
"macLabel": "MAC",
"macError": "Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55",