Merge branch 'main' into develop
testing/ogGui-multibranch/pipeline/pr-main Build queued... Details
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details

pull/89/head
Manuel Aranda Rosales 2026-05-06 23:42:12 +02:00
commit 99f6b61265
6 changed files with 131 additions and 117 deletions

View File

@ -1,5 +1,9 @@
# Changelog
## [1.5.7] - 2026-05-05
### Fixed
- Modificada vista para eliminar imagen de cache, se muestra la lista de ficheros de imagen contenida en la cache y se borra el fichero
## [1.5.6] - 2026-05-05
### Fixed
- Corregida edicion de menus

View File

@ -1,103 +1,90 @@
<h2 mat-dialog-title> {{ 'selectImageToDeleteFromCache' | translate }}</h2>
<mat-dialog-content class="dialog-content">
<mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
@if (loading) {
<mat-spinner class="loading-spinner"></mat-spinner>
}
<div *ngIf="!loading" class="select-container">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title> {{ 'labelClients' | translate }}</mat-panel-title>
<mat-panel-description>
{{ 'clientListBootOs' | translate }}<mat-icon>desktop_windows</mat-icon>
</mat-panel-description>
</mat-expansion-panel-header>
@if (!loading) {
<div class="select-container">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title> {{ 'labelClients' | translate }}</mat-panel-title>
<mat-panel-description>
{{ 'clientListBootOs' | translate }}<mat-icon>desktop_windows</mat-icon>
</mat-panel-description>
</mat-expansion-panel-header>
<div class="button-row">
<button class="action-button" (click)="toggleSelectAll()">
{{ allSelected ? 'Desmarcar todos' : 'Marcar todos' }}
</button>
</div>
<div class="clients-grid">
<div *ngFor="let client of data.clients" class="client-item">
<div class="client-card"
(click)="client.status === 'og-live' && toggleClientSelection(client)"
[ngClass]="{'selected-client': client.selected, 'disabled-client': client.status !== 'og-live'}" >
<img
[src]="'assets/images/computer_' + client.status + '.svg'"
alt="Client Icon"
class="client-image" />
<div class="client-details">
<span class="client-name">{{ client.name | slice:0:20 }}</span>
<span class="client-ip">{{ client.ip }}</span>
<span class="client-ip">{{ client.mac }}</span>
</div>
<mat-divider></mat-divider>
<mat-radio-group [(ngModel)]="selectedModelClient" (change)="loadPartitions(selectedModelClient)">
<mat-radio-button [value]="client"
color="primary"
[disabled]="!client.selected"
(click)="$event.stopPropagation()">
{{ 'labelModel' | translate }}</mat-radio-button>
</mat-radio-group>
</div>
<div class="button-row">
<button class="action-button" (click)="toggleSelectAll()">
{{ allSelected ? 'Desmarcar todos' : 'Marcar todos' }}
</button>
</div>
</div>
</mat-expansion-panel>
</div>
<div class="clients-grid">
@for (client of data.clients; track client['@id']) {
<div class="client-item">
<div class="client-card"
(click)="client.status === 'og-live' && toggleClientSelection(client)"
[ngClass]="{'selected-client': client.selected, 'disabled-client': client.status !== 'og-live'}" >
<mat-divider *ngIf="!loading" style="margin-top: 20px;"></mat-divider>
<img
[src]="'assets/images/computer_' + client.status + '.svg'"
alt="Client Icon"
class="client-image" />
<div *ngIf="!loading" class="partition-table-container">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: start">{{ 'buttonSelectImage' | translate }}</th>
<td mat-cell *matCellDef="let row">
<mat-radio-group
[(ngModel)]="selectedPartition"
[disabled]="!row.operativeSystem"
>
<mat-radio-button [value]="row">
</mat-radio-button>
</mat-radio-group>
</td>
</ng-container>
<div class="client-details">
<span class="client-name">{{ client.name | slice:0:20 }}</span>
<span class="client-ip">{{ client.ip }}</span>
<span class="client-ip">{{ client.mac }}</span>
</div>
<mat-divider></mat-divider>
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header | translate }} </th>
<td mat-cell *matCellDef="let image">
<ng-container *ngIf="column.columnDef !== 'size' && column.columnDef !== 'operativeSystem'">
{{ column.cell(image) }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'size'">
<div style="display: flex; flex-direction: column;">
<span> {{ image.size }} MB</span>
<span style="font-size: 0.75rem; color: gray;">{{ image.size / 1024 }} GB</span>
<mat-radio-group [(ngModel)]="selectedModelClient" (change)="loadPartitions(selectedModelClient)">
<mat-radio-button [value]="client"
color="primary"
[disabled]="!client.selected"
(click)="$event.stopPropagation()">
{{ 'labelModel' | translate }}</mat-radio-button>
</mat-radio-group>
</div>
</div>
}
</div>
</mat-expansion-panel>
</div>
<mat-divider style="margin-top: 20px;"></mat-divider>
<div class="partition-table-container">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: start">{{ 'buttonSelectImage' | translate }}</th>
<td mat-cell *matCellDef="let row">
<mat-radio-group [(ngModel)]="selectedImage">
<mat-radio-button [value]="row"></mat-radio-button>
</mat-radio-group>
</td>
</ng-container>
@for (column of columns; track column.columnDef) {
<ng-container [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header | translate }} </th>
<td mat-cell *matCellDef="let image">
{{ column.cell(image) }}
</td>
</ng-container>
}
<ng-container *ngIf="column.columnDef === 'operativeSystem'">
<div style="display: flex; flex-direction: column;">
<span> {{ image.operativeSystem?.name }} </span>
<span style="font-size: 0.75rem; color: gray;">{{ image.image?.name}} </span>
</div>
</ng-container>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
}
</mat-dialog-content>
<div mat-dialog-actions class="action-container">
<button class="ordinary-button" (click)="close()">{{ 'buttonCancel' | translate }}</button>
<button class="submit-button" (click)="execute()" [disabled]="!selectedPartition">{{ 'buttonExecute' | translate }}</button>
<button class="submit-button" (click)="execute()" [disabled]="!selectedImage">{{ 'buttonExecute' | translate }}</button>
</div>

View File

@ -15,7 +15,7 @@ import {ToastrService} from "ngx-toastr";
export class RemoveCacheImageComponent {
public translate = inject(TranslateService);
baseUrl: string;
selectedPartition: any = null;
selectedImage: any = null;
dataSource = new MatTableDataSource<any>();
clientId: string | null = null;
selectedClients: any[] = [];
@ -26,34 +26,14 @@ export class RemoveCacheImageComponent {
loading: boolean = false;
columns = [
{
columnDef: 'diskNumber',
header: 'labelDisk',
cell: (partition: any) => partition.diskNumber
},
{
columnDef: 'partitionNumber',
header: 'partition',
cell: (partition: any) => partition.partitionNumber
columnDef: 'name',
header: 'name',
cell: (image: any) => image.name
},
{
columnDef: 'size',
header: 'labelDiskSize',
cell: (partition: any) => `${partition.size} MB`
},
{
columnDef: 'partitionCode',
header: 'partitionType',
cell: (partition: any) => partition.partitionCode
},
{
columnDef: 'filesystem',
header: 'fileSystem',
cell: (partition: any) => partition.filesystem
},
{
columnDef: 'operativeSystem',
header: 'sW',
cell: (partition: any) => partition.operativeSystem?.name
header: 'size',
cell: (image: any) => this.formatBytes(image.size)
}
];
@ -97,9 +77,43 @@ export class RemoveCacheImageComponent {
this.http.get(url).subscribe(
(response: any) => {
if (response.partitions) {
this.dataSource.data = response.partitions.filter((partition: any) => {
return partition.partitionNumber !== 0 && partition.image;
const cachePartitions = response.partitions.filter((partition: any) => {
return partition.partitionNumber !== 0 && partition.filesystem === 'CACHE' && partition.cacheContent !== "";
});
let imagesMap: { [key: string]: number } = {};
cachePartitions.forEach((p: any) => {
if (p.cacheContent) {
const items = p.cacheContent.split(',');
items.forEach((item: string) => {
const parts = item.split(':');
if (parts.length >= 2) {
const filename = parts[0];
let size = 0;
let baseName = filename;
if (baseName.endsWith('.img.full.sum')) baseName = baseName.replace('.img.full.sum', '');
else if (baseName.endsWith('.img.sum')) baseName = baseName.replace('.img.sum', '');
// El tamaño de la imagen que usamos es sólo del fichero .img
else if (baseName.endsWith('.img')) {
baseName = baseName.replace('.img', '');
size = parseInt(parts[1], 10) || 0;
}
if (!imagesMap[baseName]) {
imagesMap[baseName] = 0;
}
imagesMap[baseName] += size;
}
});
}
});
this.dataSource.data = Object.keys(imagesMap).map(name => ({
name: name,
size: imagesMap[name]
}));
}
},
(error) => {
@ -108,6 +122,15 @@ export class RemoveCacheImageComponent {
);
}
formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
toggleClientSelection(client: any) {
client.selected = !client.selected;
this.updateSelectedClients();
@ -141,7 +164,7 @@ export class RemoveCacheImageComponent {
this.loading = true;
this.http.post(`${this.baseUrl}/clients/server/remove-cache-image`, {
clients: this.selectedClients.map((client: any) => client.uuid),
partition: this.selectedPartition['@id']
imageName: this.selectedImage.name
}).subscribe(
response => {
this.toastService.success(this.translate.instant('clientUpdatedSuccessfully'));

View File

@ -575,7 +575,7 @@
"login": "Iniciar sessió",
"createImage": "Crear imatge",
"deployImage": "Desplegar imatge",
"deleteImageCache": "Esborrar cache d'imatge",
"deleteImageCache": "Esborrar imatge de cache",
"partition": "Particionar i formatar",
"softwareInventory": "Inventari de software",
"hardwareInventory": "Inventari de hardware",

View File

@ -577,7 +577,7 @@
"login": "Login",
"createImage": "Create Image",
"deployImage": "Deploy Image",
"deleteImageCache": "Delete Image Cache",
"deleteImageCache": "Delete Image from Cache",
"partition": "Partition and Format",
"softwareInventory": "Software Inventory",
"hardwareInventory": "Hardware Inventory",

View File

@ -577,7 +577,7 @@
"login": "Iniciar sesión",
"createImage": "Crear imagen",
"deployImage": "Desplegar imagen",
"deleteImageCache": "Borrar caché de imagen",
"deleteImageCache": "Borrar imagen de cache",
"partition": "Particionar y formatear",
"softwareInventory": "Inventario de software",
"hardwareInventory": "Inventario de hardware",