Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
testing/ogGui-multibranch/pipeline/head This commit looks good Details

pull/18/head
Manuel Aranda Rosales 2025-03-10 16:21:43 +01:00
commit 70319d718f
5 changed files with 148 additions and 108 deletions

View File

@ -258,11 +258,11 @@ mat-tree mat-tree-node.disabled:hover {
.client-name { .client-name {
display: block; display: block;
font-size: 16px; font-weight: 500;
font-weight: 600;
color: #333;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 5px; margin-top: 5px;
padding-left: 1rem;
padding-right: 1rem;
} }
.filters-container { .filters-container {
@ -319,11 +319,26 @@ mat-tree mat-tree-node.disabled:hover {
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
} }
.client-image { .client-details {
width: 35px; flex-grow: 1;
height: 35px; display: flex;
flex-direction: column;
justify-content: space-between;
margin-top: 4px;
}
.action-icons {
display: flex;
justify-content: center;
align-items: center;
gap: 1px;
margin-top: 10px; margin-top: 10px;
margin-bottom: 8px; }
.client-status-container {
display: flex;
align-items: center;
gap: 5px;
} }
.client-ip { .client-ip {
@ -338,11 +353,9 @@ mat-tree mat-tree-node.disabled:hover {
gap: 4px; gap: 4px;
} }
.action-icons { .sync-spinner {
display: flex; margin-left: 1em;
justify-content: center; margin-right: 1em;
gap: 1px;
margin-top: 10px;
} }
.mat-elevation-z8 { .mat-elevation-z8 {
@ -360,10 +373,6 @@ mat-tree mat-tree-node.disabled:hover {
position: relative; position: relative;
} }
.client-details {
margin-top: 4px;
}
@media (max-width: 1560px) { @media (max-width: 1560px) {
.clients-view-header { .clients-view-header {
display: flex; display: flex;

View File

@ -60,12 +60,14 @@
</button> </button>
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field search-select" appearance="outline"> <mat-form-field class="form-field search-select" appearance="outline">
<mat-select placeholder="Buscar por estado..." #clientSearchStatusInput (selectionChange)="onClientFilterStatusInput($event.value)"> <mat-select placeholder="Buscar por estado..." #clientSearchStatusInput
(selectionChange)="onClientFilterStatusInput($event.value)">
<mat-option *ngFor="let option of status" [value]="option.value"> <mat-option *ngFor="let option of status" [value]="option.value">
{{ option.name }} {{ option.name }}
</mat-option> </mat-option>
</mat-select> </mat-select>
<button *ngIf="clientSearchStatusInput.value" mat-icon-button matSuffix aria-label="Clear tree search" (click)="clearStatusFilter($event, clientSearchStatusInput)"> <button *ngIf="clientSearchStatusInput.value" mat-icon-button matSuffix aria-label="Clear tree search"
(click)="clearStatusFilter($event, clientSearchStatusInput)">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</mat-form-field> </mat-form-field>
@ -226,7 +228,7 @@
<mat-checkbox (click)="$event.stopPropagation()" (change)="toggleRow(client)" <mat-checkbox (click)="$event.stopPropagation()" (change)="toggleRow(client)"
[checked]="selection.isSelected(client)" [disabled]="client.status === 'busy'"> [checked]="selection.isSelected(client)" [disabled]="client.status === 'busy'">
</mat-checkbox> </mat-checkbox>
<img [src]="'assets/images/ordenador_' + client.status + '.png'" alt="Client Icon" <img style="margin-top: 0.5em;" [src]="'assets/images/ordenador_' + client.status + '.png'" alt="Client Icon"
class="client-image" /> class="client-image" />
<div class="client-details"> <div class="client-details">
@ -234,14 +236,6 @@
<span class="client-ip">{{ client.ip }}</span> <span class="client-ip">{{ client.ip }}</span>
<span class="client-ip">{{ client.mac }}</span> <span class="client-ip">{{ client.mac }}</span>
<div class="action-icons"> <div class="action-icons">
<button *ngIf="(!syncStatus || syncingClientId !== client.uuid)" mat-icon-button
color="primary" (click)="getStatus(client, selectedNode)">
<mat-icon>sync</mat-icon>
</button>
<button *ngIf="syncStatus && syncingClientId === client.uuid" mat-icon-button color="primary">
<mat-spinner diameter="24"></mat-spinner>
</button>
<app-execute-command [clientData]="[client]" [buttonType]="'icon'" [icon]="'terminal'" <app-execute-command [clientData]="[client]" [buttonType]="'icon'" [icon]="'terminal'"
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"></app-execute-command> [disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"></app-execute-command>
@ -252,6 +246,10 @@
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
<span class="sync-spinner" *ngIf="syncStatus && syncingClientId === client.uuid">
<mat-spinner diameter="24"></mat-spinner>
</span>
<mat-menu #clientMenu="matMenu"> <mat-menu #clientMenu="matMenu">
<button mat-menu-item (click)="onEditClick($event, client.type, client.uuid)"> <button mat-menu-item (click)="onEditClick($event, client.type, client.uuid)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
@ -261,6 +259,11 @@
<mat-icon>visibility</mat-icon> <mat-icon>visibility</mat-icon>
<span>{{ 'viewDetails' | translate }}</span> <span>{{ 'viewDetails' | translate }}</span>
</button> </button>
<button mat-menu-item *ngIf="(!syncStatus || syncingClientId !== client.uuid)"
(click)="getStatus(client, selectedNode)">
<mat-icon>sync</mat-icon>
<span>{{ 'sync' | translate }}</span>
</button>
<button mat-menu-item (click)="onDeleteClick($event, client)"> <button mat-menu-item (click)="onDeleteClick($event, client)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'delete' | translate }}</span> <span>{{ 'delete' | translate }}</span>
@ -300,8 +303,13 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'status' | translate }} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'status' | translate }} </th>
<td mat-cell *matCellDef="let client" matTooltip="{{ getClientPath(client) }}" <td mat-cell *matCellDef="let client" matTooltip="{{ getClientPath(client) }}"
matTooltipPosition="left" matTooltipShowDelay="500"> matTooltipPosition="left" matTooltipShowDelay="500">
<div class="client-status-container">
<img [src]="'assets/images/ordenador_' + client.status + '.png'" alt="Client Icon" <img [src]="'assets/images/ordenador_' + client.status + '.png'" alt="Client Icon"
class="client-image" /> class="client-image" />
<span *ngIf="syncStatus && syncingClientId === client.uuid">
<mat-spinner diameter="24"></mat-spinner>
</span>
</div>
</td> </td>
</ng-container> </ng-container>
@ -372,7 +380,8 @@
</mat-menu> </mat-menu>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row style="background-color: #f3f3f3;" *matHeaderRowDef="displayedColumns; sticky: true"></tr> <tr mat-header-row style="background-color: #f3f3f3;"
*matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</section> </section>

View File

@ -1,8 +1,25 @@
.dialog-content { .create-image-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; padding: 1rem;
/* Espacio entre los elementos del formulario */ }
.loading-spinner {
display: block;
margin: 0 auto;
}
.mat-dialog-content.loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.mat-dialog-content {
padding-left: 1.5em;
padding-right: 1.5em;
padding-top: 1em;
} }
.image-form { .image-form {

View File

@ -1,13 +1,13 @@
<app-loading [isLoading]="loading"></app-loading> <div class="create-image-container">
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Crear' }} imagen</h2>
<h2 mat-dialog-title>{{ imageId ? 'Editar' : 'Crear' }} imagen</h2> <div class="mat-dialog-content" [ngClass]="{'loading': loading}">
<mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
<mat-dialog-content class="dialog-content"> <form *ngIf="!loading" [formGroup]="imageForm" (ngSubmit)="saveImage()" class="image-form">
<form [formGroup]="imageForm" (ngSubmit)="saveImage()" class="image-form">
<mat-card *ngIf="showWarning" class="warning-card"> <mat-card *ngIf="showWarning" class="warning-card">
<mat-card-content> <mat-card-content>
<mat-icon color="warn">warning</mat-icon> <mat-icon color="warn">warning</mat-icon>
Ha marcado la casilla <strong>"Imagen Global"</strong>. Se transferirá la imagen al resto de repositorios en el Ha marcado la casilla <strong>"Imagen Global"</strong>. Se transferirá la imagen al resto de repositorios en
el
caso de que no exista previamente. caso de que no exista previamente.
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
@ -63,10 +63,10 @@
<p>Código de partición: {{ partitionInfo['partitionCode'] }}</p> <p>Código de partición: {{ partitionInfo['partitionCode'] }}</p>
</div> </div>
</form> </form>
</div>
</mat-dialog-content> <mat-dialog-actions class="action-container">
<mat-dialog-actions class="action-container">
<button class="ordinary-button" (click)="close()">{{ 'cancelButton' | translate }}</button> <button class="ordinary-button" (click)="close()">{{ 'cancelButton' | translate }}</button>
<button class="submit-button" (click)="saveImage()">{{ 'saveButton' | translate }}</button> <button class="submit-button" (click)="saveImage()" [disabled]="loading">{{ 'saveButton' | translate }}</button>
</mat-dialog-actions> </mat-dialog-actions>
</div>

View File

@ -17,6 +17,7 @@ export class CreateImageComponent implements OnInit {
softwareProfiles: any[] = []; softwareProfiles: any[] = [];
repositories: any[] = []; repositories: any[] = [];
loading: boolean = false; loading: boolean = false;
isEditMode: boolean = false;
partitionInfo: { [key: string]: string } = {}; partitionInfo: { [key: string]: string } = {};
showWarning: boolean = false; showWarning: boolean = false;
@ -42,30 +43,34 @@ export class CreateImageComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.loading = true; this.loading = true;
if (this.data) { if (this.data) {
this.load() this.isEditMode = true;
this.load();
} else {
this.loading = false;
} }
this.fetchSoftwareProfiles(); this.fetchSoftwareProfiles();
this.fetchRepositories(); this.fetchRepositories();
this.loading = false;
} }
load(): void { load(): void {
this.dataService.getImage(this.data).subscribe({ this.dataService.getImage(this.data).subscribe({
next: (response) => { next: (response) => {
this.imageForm = this.fb.group({ this.imageForm.patchValue({
name: [response.name, Validators.required], name: response.name,
description: [response.description], description: response.description,
comments: [response.comments], comments: response.comments,
remotePc: [response.remotePc], remotePc: response.remotePc,
isGlobal: [response.isGlobal], isGlobal: response.isGlobal,
softwareProfile: [response.softwareProfile ? response.softwareProfile['@id'] : null, Validators.required], softwareProfile: response.softwareProfile ? response.softwareProfile['@id'] : null,
imageRepositories: [response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [], Validators.required], imageRepositories: response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [],
}); });
this.imageId = response['@id']; this.imageId = response['@id'];
this.partitionInfo = response.partitionInfo; this.partitionInfo = response.partitionInfo;
this.loading = false;
}, },
error: (err) => { error: (err) => {
console.error('Error fetching remote calendar:', err); console.error('Error fetching remote calendar:', err);
this.loading = false;
} }
}); });
} }