Merge branch 'main' into develop
commit
99f6b61265
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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'));
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue