feat(ui): add image cloning column with multiple image support
parent
809b368e4a
commit
bd8ca8d36a
|
|
@ -1998,3 +1998,30 @@ mat-button-toggle-group {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.cloning-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image-name {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.image-date {
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.multi-image-badge {
|
||||
background-color: #673ab7;
|
||||
color: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -556,6 +556,37 @@
|
|||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="imageCloning">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<div class="column-header">
|
||||
<span>{{ 'imageCloning' | translate }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let client">
|
||||
@if (getImagesFromClient(client); as images) {
|
||||
@if (images.length === 1) {
|
||||
<div class="cloning-cell">
|
||||
<span class="image-name truncate-cell-medium" [matTooltip]="images[0].name">{{ images[0].name }}</span>
|
||||
@if (images[0].date) {
|
||||
<span class="image-date">{{ 'labelDeployed' | translate }}: {{ images[0].date | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||
}
|
||||
</div>
|
||||
} @else if (images.length > 1) {
|
||||
<div class="cloning-cell">
|
||||
<div style="display: flex; align-items: center; gap: 4px;">
|
||||
<span class="image-name truncate-cell-medium" [matTooltip]="images[0].name">{{ images[0].name }}</span>
|
||||
<span class="multi-image-badge" [matTooltip]="getMultiImageTooltip(images)">+{{images.length - 1}}</span>
|
||||
</div>
|
||||
@if (images[0].date) {
|
||||
<span class="image-date">{{ 'labelDeployed' | translate }}: {{ images[0].date | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
<span>-</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="oglive">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'ogLiveTitle' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let client">
|
||||
|
|
|
|||
|
|
@ -127,7 +127,50 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||
{ value: 'disconnected', name: 'Desconectado' }
|
||||
];
|
||||
|
||||
displayedColumns: string[] = ['select', 'status', 'ip', 'firmwareType', 'name', 'oglive', 'subnet', 'pxeTemplate', 'actions'];
|
||||
displayedColumns: string[] = ['select', 'status', 'ip', 'firmwareType', 'imageCloning', 'name', 'oglive', 'subnet', 'pxeTemplate', 'actions'];
|
||||
|
||||
getImagesFromClient(client: any): any[] {
|
||||
if (!client) return [];
|
||||
const images: any[] = [];
|
||||
const partitions = client.partitions ||
|
||||
(client.disks ? client.disks.flatMap((d: any) => d.partitions || []) : []);
|
||||
|
||||
const dateKeys = ['lastRestored', 'restoredAt', 'lastCloned', 'cloningDate', 'updatedAt', 'createdAt', 'date'];
|
||||
const extractDate = (obj: any): string | null => {
|
||||
if (!obj) return null;
|
||||
for (const key of dateKeys) {
|
||||
if (obj[key]) return obj[key];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
for (const part of partitions) {
|
||||
const date = extractDate(part.image) || extractDate(part) || extractDate(client);
|
||||
|
||||
if (part.image?.name) {
|
||||
images.push({ name: part.image.name, date });
|
||||
} else if (typeof part.image === 'string' && part.image.trim()) {
|
||||
images.push({ name: part.image, date });
|
||||
} else if (part.operatingSystem?.name) {
|
||||
images.push({ name: part.operatingSystem.name, date });
|
||||
} else if (part.os?.name) {
|
||||
images.push({ name: part.os.name, date });
|
||||
}
|
||||
}
|
||||
return images;
|
||||
}
|
||||
|
||||
getMultiImageTooltip(images: any[]): string {
|
||||
if (!images || images.length <= 1) return '';
|
||||
return images.slice(1).map(img => {
|
||||
let txt = img.name;
|
||||
if (img.date) {
|
||||
const d = new Date(img.date);
|
||||
txt += ` (Desplegada: ${d.toLocaleDateString()} ${d.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})})`;
|
||||
}
|
||||
return txt;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
private subscriptions: Subscription = new Subscription();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"imageCloning": "Imatge / Clonació",
|
||||
"labelDeployed": "Desplegada",
|
||||
"actionAddedSuccess": "Acció afegida correctament",
|
||||
"actions": "Accions",
|
||||
"actionsDescription": "Administra cada imatge amb opcions per veure, editar, eliminar i més.",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"imageCloning": "Image / Cloning",
|
||||
"labelDeployed": "Deployed",
|
||||
"actionAddedSuccess": "Action added successfully",
|
||||
"actions": "Actions",
|
||||
"actionsDescription": "Manage each image with options to view, edit, delete, and more.",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"imageCloning": "Imagen / Clonación",
|
||||
"labelDeployed": "Desplegada",
|
||||
"actionAddedSuccess": "Acción añadida con éxito",
|
||||
"actions": "Acciones",
|
||||
"actionsDescription": "Administra cada imagen con opciones para ver, editar, eliminar y más.",
|
||||
|
|
|
|||
Loading…
Reference in New Issue