ref 2994. Hardware profile updated logic in childrens

pull/97/head
Manuel Aranda Rosales 2026-07-13 10:41:52 +02:00
parent aa52c89c52
commit 24517a8649
6 changed files with 69 additions and 47 deletions

View File

@ -92,27 +92,13 @@ mat-option .unit-path {
padding: 6px 0 12px 0;
}
.hardware-profile-icon {
color: #3f51b5;
}
.hardware-profile-value {
font-weight: 500;
color: #2c3e50;
}
.hardware-profile-empty {
color: #6c757d;
font-style: italic;
.hardware-profile-box .form-field {
flex: 1;
min-width: 0;
width: auto;
}
.hardware-profile-link {
margin-left: auto;
background: none;
border: none;
color: #3f51b5;
font-weight: 500;
cursor: pointer;
text-decoration: underline;
padding: 0;
}

View File

@ -143,14 +143,17 @@
<fieldset class="fieldset-group">
<legend class="fieldset-legend">{{ 'labelHardwareProfile' | translate }}</legend>
<div class="hardware-profile-box">
@if (hardwareProfile) {
<mat-icon class="hardware-profile-icon">memory</mat-icon>
<span class="hardware-profile-value">{{ hardwareProfile.description }}</span>
} @else {
<span class="hardware-profile-empty">{{ 'noHardwareProfileAssigned' | translate }}</span>
}
<button type="button" class="hardware-profile-link" (click)="goToHardwareProfiles()">
{{ 'manageHardwareProfiles' | translate }}
<mat-form-field class="form-field">
<mat-select formControlName="hardwareProfile">
<mat-option [value]="null">{{ 'noHardwareProfileAssigned' | translate }}</mat-option>
@for (profile of hardwareProfiles; track profile['@id']) {
<mat-option [value]="profile['@id']">{{ profile.description }}</mat-option>
}
</mat-select>
</mat-form-field>
<button type="button" mat-icon-button class="hardware-profile-link" (click)="goToHardwareProfiles()"
[matTooltip]="'manageHardwareProfiles' | translate">
<mat-icon>settings</mat-icon>
</button>
</div>
</fieldset>

View File

@ -25,7 +25,7 @@ export class ManageClientComponent implements OnInit {
templates: any[] = [];
repositories: any[] = [];
bootServers: any[] = [];
hardwareProfile: any = null;
hardwareProfiles: any[] = [];
loading: boolean = false;
displayedColumns: string[] = ['name', 'ip'];
dialogTitle: string;
@ -58,7 +58,8 @@ export class ManageClientComponent implements OnInit {
this.loadParentUnits(),
this.loadBootServers(),
this.loadRepositories(),
this.loadMenus()
this.loadMenus(),
this.loadHardwareProfiles()
];
Promise.all(observables).then(() => {
@ -94,6 +95,7 @@ export class ManageClientComponent implements OnInit {
ogLive: [null],
repository: [null],
menu: [null],
hardwareProfile: [null],
maintenance: [false],
resolution: [null],
notes: ['']
@ -225,6 +227,24 @@ export class ManageClientComponent implements OnInit {
});
}
loadHardwareProfiles(): Promise<void> {
return new Promise((resolve, reject) => {
const url = `${this.baseUrl}/hardware-profiles?page=1&itemsPerPage=10000`;
this.http.get<any>(url).subscribe(
response => {
this.hardwareProfiles = response['hydra:member'];
resolve();
},
error => {
console.error(this.translate.instant('errorFetchingHardwareProfiles'), error);
this.hardwareProfiles = [];
resolve();
}
);
});
}
loadRepositories(): Promise<void> {
return new Promise((resolve, reject) => {
const url = `${this.baseUrl}/image-repositories?page=1&itemsPerPage=10000`;
@ -284,7 +304,6 @@ export class ManageClientComponent implements OnInit {
const bootServerIri: string | null = data.bootServer ? data.bootServer['@id'] : null;
const savedOgLive: string | null = data.ogLive ? data.ogLive['@id'] : null;
const savedPxeTemplate: string | null = data.pxeTemplate ? data.pxeTemplate['@id'] : null;
this.hardwareProfile = data.hardwareProfile || null;
// Patch fields that don't depend on server first
this.clientForm.patchValue({
@ -298,6 +317,7 @@ export class ManageClientComponent implements OnInit {
bootServer: bootServerIri,
repository: data.repository ? data.repository['@id'] : null,
menu: data.menu ? data.menu['@id'] : null,
hardwareProfile: data.hardwareProfile ? data.hardwareProfile['@id'] : null,
maintenance: data.maintenance,
resolution: data.resolution,
notes: data.notes || ''

View File

@ -62,11 +62,22 @@
.client-info {
display: flex;
flex-direction: column;
max-width: 220px;
}
.client-name {
font-weight: 500;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.client-more {
font-weight: 400;
font-style: italic;
color: #666;
cursor: default;
}
.client-details {
@ -74,8 +85,4 @@
flex-direction: column;
font-size: 0.75em;
color: #666;
}
.client-ip, .client-mac {
font-family: monospace;
}

View File

@ -47,11 +47,12 @@
</mat-icon>
</ng-container>
<ng-container *ngIf="column.columnDef === 'client'">
<ng-container *ngIf="column.columnDef === 'clients'">
<div *ngIf="column.cell(image) !== 'N/A'; else noClientData" class="client-info">
<div class="client-name">{{ column.cell(image).name }}</div>
<div class="client-details">
<span class="client-ip">{{ column.cell(image).ip }}</span>
<div class="client-name" *ngFor="let assignedClient of getVisibleClients(column.cell(image))">{{ assignedClient.name }}</div>
<div class="client-name client-more" *ngIf="column.cell(image).length > maxVisibleClients"
[matTooltip]="getHiddenClientNames(column.cell(image))">
{{ 'andMoreClients' | translate: { count: column.cell(image).length - maxVisibleClients } }}
</div>
</div>
<ng-template #noClientData>
@ -60,7 +61,7 @@
</ng-container>
<ng-container
*ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl' && column.columnDef !== 'client'">
*ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl' && column.columnDef !== 'clients'">
{{ column.cell(image) }}
</ng-container>
</td>

View File

@ -37,6 +37,7 @@ export class HardwareProfileComponent {
filteredClients!: Observable<any[]>;
clientControl = new FormControl();
clients: any[] = [];
maxVisibleClients: number = 3;
columns = [
{
columnDef: 'id',
@ -49,13 +50,9 @@ export class HardwareProfileComponent {
cell: (hardware: any) => hardware.description
},
{
columnDef: 'client',
header: 'client',
cell: (hardware: any) => hardware.client ? {
name: hardware.client.name || 'N/A',
ip: hardware.client.ip || 'N/A',
mac: hardware.client.mac || 'N/A'
} : 'N/A'
columnDef: 'clients',
header: 'clients',
cell: (hardware: any) => hardware.clients?.length ? hardware.clients : 'N/A'
},
{
columnDef: 'createdAt',
@ -104,16 +101,24 @@ export class HardwareProfileComponent {
}
onOptionClientSelected(selectedClient: any): void {
this.filters['client.id'] = selectedClient.id;
this.filters['clients.id'] = selectedClient.id;
this.search();
}
clearClientFilter(event: Event, clientSearchClientInput: any): void {
clientSearchClientInput.value = '';
this.filters['client.id'] = '';
this.filters['clients.id'] = '';
this.search();
}
getVisibleClients(clients: any): any[] {
return (clients || []).slice(0, this.maxVisibleClients);
}
getHiddenClientNames(clients: any): string {
return (clients || []).slice(this.maxVisibleClients).map((client: any) => client.name).join(', ');
}
private _filterClients(value: string): any[] {
const filterValue = value.toLowerCase();