feat(ui): add image cloning column with multiple image support
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details
oggui-debian-package/pipeline/head This commit looks good Details

main
Juan Manuel Bardallo 2026-09-24 14:09:37 +02:00
parent 809b368e4a
commit bd8ca8d36a
6 changed files with 108 additions and 1 deletions

View File

@ -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;
}

View File

@ -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">

View File

@ -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();

View File

@ -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.",

View File

@ -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.",

View File

@ -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.",