From bd8ca8d36aca654d58aab18f26d510cf786020d8 Mon Sep 17 00:00:00 2001 From: jmbardallo Date: Thu, 24 Sep 2026 14:09:37 +0200 Subject: [PATCH] feat(ui): add image cloning column with multiple image support --- .../components/groups/groups.component.css | 27 +++++++++++ .../components/groups/groups.component.html | 31 +++++++++++++ .../app/components/groups/groups.component.ts | 45 ++++++++++++++++++- ogWebconsole/src/locale/cat.json | 2 + ogWebconsole/src/locale/en.json | 2 + ogWebconsole/src/locale/es.json | 2 + 6 files changed, 108 insertions(+), 1 deletion(-) diff --git a/ogWebconsole/src/app/components/groups/groups.component.css b/ogWebconsole/src/app/components/groups/groups.component.css index 294a102..16800aa 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.css +++ b/ogWebconsole/src/app/components/groups/groups.component.css @@ -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; +} + diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index aab9067..d745240 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -556,6 +556,37 @@ } + + +
+ {{ 'imageCloning' | translate }} +
+ + + @if (getImagesFromClient(client); as images) { + @if (images.length === 1) { +
+ {{ images[0].name }} + @if (images[0].date) { + {{ 'labelDeployed' | translate }}: {{ images[0].date | date:'dd/MM/yyyy HH:mm' }} + } +
+ } @else if (images.length > 1) { +
+
+ {{ images[0].name }} + +{{images.length - 1}} +
+ @if (images[0].date) { + {{ 'labelDeployed' | translate }}: {{ images[0].date | date:'dd/MM/yyyy HH:mm' }} + } +
+ } @else { + - + } + } + +
{{ 'ogLiveTitle' | translate }} diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index 7b0eb55..c493dcb 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -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(); diff --git a/ogWebconsole/src/locale/cat.json b/ogWebconsole/src/locale/cat.json index 24e425d..0209f5a 100644 --- a/ogWebconsole/src/locale/cat.json +++ b/ogWebconsole/src/locale/cat.json @@ -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.", diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json index 398c9c6..7e42500 100644 --- a/ogWebconsole/src/locale/en.json +++ b/ogWebconsole/src/locale/en.json @@ -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.", diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json index 1e62ee8..01573ea 100644 --- a/ogWebconsole/src/locale/es.json +++ b/ogWebconsole/src/locale/es.json @@ -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.",