Merge pull request 'Rediseño de ventana de detalles de equipo, la partición cache muestra los ficheros que contiene' (#60) from cache-content into main
Reviewed-on: #60pull/61/head 1.2.6
commit
208d89d589
|
|
@ -1,4 +1,8 @@
|
|||
# Changelog
|
||||
## [1.2.6] - 2026-03-30
|
||||
### Added
|
||||
- Rediseño de la pantalla detalles de un equipo, en la partición CACHE, se muestra el listado de ficheros que contiene con el tamaño de cada uno.
|
||||
|
||||
## [1.2.5] - 2026-03-27
|
||||
### Fixed
|
||||
- Corregido error 401 Unauthorized en la edición de menús con nombres que contienen la palabra "login". Se ha refinado el interceptor para aplicar el prefijo "Bearer" de forma selectiva mediante validación exacta del path `/auth/login`.
|
||||
|
|
|
|||
|
|
@ -130,3 +130,114 @@ table {
|
|||
gap: 1em;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
/* ngx-charts custom wrapped legend */
|
||||
::ng-deep .charts-container .legend-labels {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
::ng-deep .charts-container .legend-label {
|
||||
width: auto !important;
|
||||
margin-right: 16px !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
::ng-deep .charts-container .legend-label-text {
|
||||
max-width: none !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
}
|
||||
::ng-deep .charts-container .legend-wrap {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* Extracted Card Redesign Classes */
|
||||
.disk-card-wrapper {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.disk-mat-card {
|
||||
border-radius: 8px;
|
||||
overflow: visible !important;
|
||||
padding: 0;
|
||||
}
|
||||
.disk-card-header {
|
||||
background-color: #f5f5f5;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
.disk-card-title {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
color: #3f51b5;
|
||||
font-weight: bold;
|
||||
}
|
||||
.disk-card-metrics-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.disk-card-metrics-chip {
|
||||
background-color: white !important;
|
||||
border: 1px solid #ccc;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.disk-card-layout {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
background: white;
|
||||
gap: 0;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
.disk-table-container {
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.disk-table {
|
||||
width: 100%;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.disk-chart-container {
|
||||
flex: 0 0 400px;
|
||||
border-left: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 30px 20px 50px 20px;
|
||||
box-sizing: border-box;
|
||||
background: #fafafa;
|
||||
min-height: 350px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
.disk-chart-wrapper {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* OS Column Classes */
|
||||
.os-col-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.cache-title {
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
}
|
||||
.cache-item {
|
||||
font-size: 0.75rem;
|
||||
color: #666;
|
||||
}
|
||||
.image-name {
|
||||
font-size: 0.75rem;
|
||||
color: gray;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<mat-dialog-content class="modal-content">
|
||||
<app-loading [isLoading]="loading"></app-loading>
|
||||
|
||||
<div class="client-container" *ngIf="!loading">
|
||||
@if (!loading) {
|
||||
<div class="client-container">
|
||||
<div class="header-container">
|
||||
<h2 class="title">{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}</h2>
|
||||
</div>
|
||||
|
|
@ -9,75 +10,112 @@
|
|||
<div class="client-info-card">
|
||||
<div class="info-columns">
|
||||
<div class="info-column">
|
||||
<div class="info-pair" *ngFor="let data of generalData">
|
||||
@for (data of generalData; track data) {
|
||||
<div class="info-pair">
|
||||
<div class="label">{{ data?.property }}</div>
|
||||
<div class="value">{{ data?.value || '--' }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<div class="info-pair" *ngFor="let data of networkData">
|
||||
@for (data of networkData; track data) {
|
||||
<div class="info-pair">
|
||||
<div class="label">{{ data?.property }}</div>
|
||||
<div class="value">{{ data?.value || '--' }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header-container">
|
||||
<h2 i18n="@@adminImagesTitle">Discos/Particiones</h2>
|
||||
<mat-chip *ngIf="clientData.firmwareType"> {{ clientData.firmwareType }}</mat-chip>
|
||||
@if (clientData.firmwareType) {
|
||||
<mat-chip> {{ clientData.firmwareType }}</mat-chip>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="disk-container">
|
||||
<div class="disk-layout">
|
||||
<div class="table-container">
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </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'">
|
||||
<mat-chip color="primary">
|
||||
{{ (image.size / 1024).toFixed(2) }} GB
|
||||
</mat-chip>
|
||||
</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>
|
||||
|
||||
<ng-container>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.partitionNumber === 0"></tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="charts-container" [ngClass]="{'single-disk': chartDisk.length === 1}">
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div *ngFor="let disk of chartDisk" class="disk-usage">
|
||||
<ngx-charts-pie-chart class="chart" [view]="view" [results]="disk.chartData" [doughnut]="true">
|
||||
</ngx-charts-pie-chart>
|
||||
<h3>Disco {{ disk.diskNumber }}</h3>
|
||||
<p>Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)</p>
|
||||
<p>Total: {{ disk.total }} GB</p>
|
||||
@for (disk of diskUsageData; track disk.diskNumber) {
|
||||
<div class="disk-card-wrapper">
|
||||
<mat-card class="disk-mat-card mat-elevation-z4">
|
||||
<div class="disk-card-header">
|
||||
<h3 class="disk-card-title">Disco {{ disk.diskNumber }}</h3>
|
||||
<div class="disk-card-metrics-wrapper">
|
||||
<mat-chip class="disk-card-metrics-chip">
|
||||
Total: {{ disk.total.toFixed(2) }} GB | Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)
|
||||
</mat-chip>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="disk-card-layout">
|
||||
<div class="disk-table-container">
|
||||
<table mat-table [dataSource]="disk.partitions" class="disk-table">
|
||||
@for (column of columns; track column.columnDef) {
|
||||
<ng-container [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
@if (column.columnDef !== 'size' && column.columnDef !== 'operativeSystem') {
|
||||
{{ column.cell(image) }}
|
||||
}
|
||||
@if (column.columnDef === 'size') {
|
||||
<mat-chip color="primary">
|
||||
{{ (image.size / 1024).toFixed(2) }} GB
|
||||
</mat-chip>
|
||||
}
|
||||
|
||||
@if (column.columnDef === 'operativeSystem') {
|
||||
<div class="os-col-wrapper">
|
||||
@if (image.filesystem === 'CACHE' && image.cacheContent) {
|
||||
<span class="cache-title">Contenido:</span>
|
||||
@for (item of parseCacheContent(image.cacheContent); track item.name) {
|
||||
<div class="cache-item">
|
||||
• {{ item.name }} ({{ item.size }})
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (image.filesystem !== 'CACHE' || !image.cacheContent) {
|
||||
<span>{{image.operativeSystem?.name }}</span>
|
||||
<span class="image-name">{{ image.image?.name }}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
|
||||
<ng-container>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.partitionNumber === 0"></tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="charts-container disk-chart-container">
|
||||
<div class="disk-chart-wrapper">
|
||||
<ngx-charts-pie-chart
|
||||
class="chart"
|
||||
[view]="[360, 230]"
|
||||
[results]="disk.chartData"
|
||||
[doughnut]="true"
|
||||
[legend]="true"
|
||||
[legendPosition]="legendPosition"
|
||||
[legendTitle]="''">
|
||||
</ngx-charts-pie-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions class="action-container">
|
||||
<button class="ordinary-button" (click)="onNoClick()">{{ 'closeButton' | translate }}</button>
|
||||
</mat-dialog-actions>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -37,18 +37,14 @@ export class ClientDetailsComponent {
|
|||
chartDisk: any[] = [];
|
||||
view: [number, number] = [260, 160];
|
||||
showLegend: boolean = true;
|
||||
legendPosition: any = 'below';
|
||||
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'diskNumber',
|
||||
header: 'Disco',
|
||||
cell: (partition: any) => partition.diskNumber,
|
||||
},
|
||||
{
|
||||
columnDef: 'partitionNumber',
|
||||
header: 'Particion',
|
||||
cell: (partition: any) => partition.partitionNumber
|
||||
columnDef: 'partition',
|
||||
header: 'Partición',
|
||||
cell: (partition: any) => `${partition.partitionNumber}`
|
||||
},
|
||||
{
|
||||
columnDef: 'partitionCode',
|
||||
|
|
@ -168,7 +164,8 @@ export class ClientDetailsComponent {
|
|||
chartData: partitionData,
|
||||
total,
|
||||
used,
|
||||
percentage: total > 0 ? Math.round((used / total) * 100) : 0
|
||||
percentage: total > 0 ? Math.round((used / total) * 100) : 0,
|
||||
partitions
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -194,6 +191,15 @@ export class ClientDetailsComponent {
|
|||
});
|
||||
}
|
||||
|
||||
parseCacheContent(content: string): { name: string, size: string }[] {
|
||||
if (!content) return [];
|
||||
return content.split(',').map(item => {
|
||||
const [name, bytes] = item.split(':');
|
||||
const sizeMB = (parseInt(bytes) / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
return { name, size: sizeMB };
|
||||
});
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue