feat(plugins): add generic partition-level status and badge support
parent
80e8cf95e6
commit
fb437fd8a9
|
|
@ -274,3 +274,67 @@ table {
|
|||
font-style: italic;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.partition-badges-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.plugin-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.2s ease;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.plugin-badge .plugin-badge-icon {
|
||||
font-size: 14px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-neutral {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
border: 1px solid #cbd5e1;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-pending {
|
||||
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||
color: #b45309;
|
||||
border: 1px solid #fcd34d;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-error {
|
||||
background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fca5a5;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-info {
|
||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||
color: #1d4ed8;
|
||||
border: 1px solid #93c5fd;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-success {
|
||||
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
||||
color: #047857;
|
||||
border: 1px solid #6ee7b7;
|
||||
}
|
||||
|
||||
.plugin-badge.badge-warning {
|
||||
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
|
||||
color: #b45309;
|
||||
border: 1px solid #fde68a;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,13 @@
|
|||
@if (image.deployDate) {
|
||||
<span class="image-date">Desplegada: {{ image.deployDate | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||
}
|
||||
<div class="partition-badges-row">
|
||||
@for (badge of getPartitionBadges(disk.diskNumber, image.partitionNumber); track badge.id + '-' + badge.type + '-' + badge.label) {
|
||||
<span class="plugin-badge badge-{{badge.type}}" [matTooltip]="badge.tooltip">
|
||||
<mat-icon class="plugin-badge-icon">{{ badge.icon }}</mat-icon> {{ badge.label }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { Router } from "@angular/router";
|
|||
import { ToastrService } from "ngx-toastr";
|
||||
import { ManageClientComponent } from "../../shared/clients/manage-client/manage-client.component";
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { PluginBadge, PluginService } from '@services/plugin.service';
|
||||
|
||||
interface ClientInfo {
|
||||
property: string;
|
||||
|
|
@ -23,6 +24,7 @@ interface ClientInfo {
|
|||
})
|
||||
export class ClientDetailsComponent {
|
||||
public translate = inject(TranslateService);
|
||||
private pluginService = inject(PluginService);
|
||||
baseUrl: string;
|
||||
@ViewChild('assistantContainer') assistantContainer!: ElementRef;
|
||||
clientUuid: string;
|
||||
|
|
@ -209,6 +211,10 @@ export class ClientDetailsComponent {
|
|||
});
|
||||
}
|
||||
|
||||
getPartitionBadges(diskNumber: number, partitionNumber: number): PluginBadge[] {
|
||||
return this.pluginService.getPartitionBadges(this.clientData, diskNumber, partitionNumber);
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,14 +108,26 @@ export class PluginService {
|
|||
return `${pluginName}:${idOrUuid}`;
|
||||
}
|
||||
|
||||
private getPartitionStatusKey(pluginName: string, idOrUuid: string | number, disk: number, partition: number): string {
|
||||
return `${pluginName}:${idOrUuid}:${disk}:${partition}`;
|
||||
}
|
||||
|
||||
public setPluginStatus(pluginName: string, idOrUuid: string | number, status: string): void {
|
||||
this.clientPluginStatusMap.set(this.getStatusKey(pluginName, idOrUuid), status);
|
||||
}
|
||||
|
||||
public setPartitionPluginStatus(pluginName: string, idOrUuid: string | number, disk: number, partition: number, status: string): void {
|
||||
this.clientPluginStatusMap.set(this.getPartitionStatusKey(pluginName, idOrUuid, disk, partition), status);
|
||||
}
|
||||
|
||||
public getPluginStatus(pluginName: string, idOrUuid: string | number): string | undefined {
|
||||
return this.clientPluginStatusMap.get(this.getStatusKey(pluginName, idOrUuid));
|
||||
}
|
||||
|
||||
public getPartitionPluginStatus(pluginName: string, idOrUuid: string | number, disk: number, partition: number): string | undefined {
|
||||
return this.clientPluginStatusMap.get(this.getPartitionStatusKey(pluginName, idOrUuid, disk, partition));
|
||||
}
|
||||
|
||||
private initMercureSubscription(): void {
|
||||
const mercureUrl = this.configService.mercureUrl;
|
||||
if (!mercureUrl || this.mercureEventSource) {
|
||||
|
|
@ -128,11 +140,20 @@ export class PluginService {
|
|||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data && data.plugin && data.status) {
|
||||
const disk = data.disk ?? data.disk_number ?? data.diskNumber;
|
||||
const part = data.partition ?? data.partition_number ?? data.partitionNumber;
|
||||
|
||||
if (data.clientId !== undefined && data.clientId !== null) {
|
||||
this.setPluginStatus(data.plugin, String(data.clientId), data.status);
|
||||
if (disk !== undefined && disk !== null && part !== undefined && part !== null) {
|
||||
this.setPartitionPluginStatus(data.plugin, String(data.clientId), Number(disk), Number(part), data.status);
|
||||
}
|
||||
}
|
||||
if (data.clientUuid) {
|
||||
this.setPluginStatus(data.plugin, data.clientUuid, data.status);
|
||||
if (disk !== undefined && disk !== null && part !== undefined && part !== null) {
|
||||
this.setPartitionPluginStatus(data.plugin, data.clientUuid, Number(disk), Number(part), data.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -243,12 +264,21 @@ export class PluginService {
|
|||
const status = row.status;
|
||||
const id = row.client_id ?? row.clientId ?? row.id;
|
||||
const uuid = row.client_uuid ?? row.clientUuid ?? row.uuid;
|
||||
const disk = row.disk_number ?? row.diskNumber ?? row.disk;
|
||||
const part = row.partition_number ?? row.partitionNumber ?? row.partition;
|
||||
|
||||
if (status) {
|
||||
if (id !== undefined && id !== null) {
|
||||
this.setPluginStatus(plugin.name, String(id), status);
|
||||
if (disk !== undefined && disk !== null && part !== undefined && part !== null) {
|
||||
this.setPartitionPluginStatus(plugin.name, String(id), Number(disk), Number(part), status);
|
||||
}
|
||||
}
|
||||
if (uuid) {
|
||||
this.setPluginStatus(plugin.name, uuid, status);
|
||||
if (disk !== undefined && disk !== null && part !== undefined && part !== null) {
|
||||
this.setPartitionPluginStatus(plugin.name, uuid, Number(disk), Number(part), status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -357,9 +387,75 @@ export class PluginService {
|
|||
continue;
|
||||
}
|
||||
|
||||
const currentStatus = (idKey ? this.getPluginStatus(plugin.name, idKey) : null)
|
||||
// 1. Estado global del cliente
|
||||
let currentStatus = (idKey ? this.getPluginStatus(plugin.name, idKey) : null)
|
||||
|| (uuidKey ? this.getPluginStatus(plugin.name, uuidKey) : null);
|
||||
|
||||
// 2. Si no hay estado global explícito, buscar si alguna partición del equipo tiene un estado activo
|
||||
if (!currentStatus) {
|
||||
for (const [key, val] of this.clientPluginStatusMap.entries()) {
|
||||
const isMatch = (idKey && key.startsWith(`${plugin.name}:${idKey}:`)) ||
|
||||
(uuidKey && key.startsWith(`${plugin.name}:${uuidKey}:`));
|
||||
if (isMatch && val && val !== 'NONE' && val !== 'UNFROZEN') {
|
||||
currentStatus = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentStatus) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchingBadge = plugin.badges.find(
|
||||
b => b.status.toUpperCase() === currentStatus.toUpperCase()
|
||||
);
|
||||
|
||||
if (matchingBadge) {
|
||||
let label = matchingBadge.label;
|
||||
if (matchingBadge.labelKey) {
|
||||
const trans = this.translateService.instant(matchingBadge.labelKey);
|
||||
if (trans && trans !== matchingBadge.labelKey) {
|
||||
label = trans;
|
||||
}
|
||||
}
|
||||
|
||||
let tooltip = matchingBadge.tooltip;
|
||||
if (matchingBadge.tooltipKey) {
|
||||
const trans = this.translateService.instant(matchingBadge.tooltipKey);
|
||||
if (trans && trans !== matchingBadge.tooltipKey) {
|
||||
tooltip = trans;
|
||||
}
|
||||
}
|
||||
|
||||
badges.push({
|
||||
id: plugin.name,
|
||||
type: matchingBadge.type,
|
||||
icon: matchingBadge.icon,
|
||||
label,
|
||||
tooltip
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
||||
getPartitionBadges(client: any, diskNumber: number, partitionNumber: number): PluginBadge[] {
|
||||
const badges: PluginBadge[] = [];
|
||||
if (!client || this.activePlugins.length === 0) return badges;
|
||||
|
||||
const idKey = client.id ? String(client.id) : null;
|
||||
const uuidKey = client.uuid || client.name;
|
||||
|
||||
for (const plugin of this.activePlugins) {
|
||||
if (!plugin.badges || plugin.badges.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentStatus = (idKey ? this.getPartitionPluginStatus(plugin.name, idKey, diskNumber, partitionNumber) : null)
|
||||
|| (uuidKey ? this.getPartitionPluginStatus(plugin.name, uuidKey, diskNumber, partitionNumber) : null);
|
||||
|
||||
if (!currentStatus) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue