From 809b368e4a8a58a13410a92e68d3575298693b07 Mon Sep 17 00:00:00 2001 From: jmbardallo Date: Thu, 24 Sep 2026 13:53:14 +0200 Subject: [PATCH] chore: release 1.11.1 with ui improvements and pxe autoselect --- CHANGELOG.md | 7 ++ ogWebconsole/src/app/app.component.css | 1 + .../deploy-image/deploy-image.component.ts | 34 +++++- .../components/groups/groups.component.css | 107 ++++++++++++++++++ .../pxe-boot-files.component.ts | 25 +++- .../add-clients-to-subnet.component.ts | 17 ++- .../main-layout/main-layout.component.css | 2 + 7 files changed, 185 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edfdcc6..4de3bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.11.1] - 2026-09-24 + +### Fixed +- **UI Layout**: Corregido el problema de desbordamiento de barras de desplazamiento (scrollbars) globales forzando un diseño con flexbox en el layout principal de los clientes. +- **Jerarquía de Unidades Organizativas**: Se ha reemplazado la visualización del "padre inmediato" por una ruta recursiva completa (ej: Sede / Edificio / Planta / Aula) en los desplegables de aulas de ogDHCP y ogBoot. +- **Auto-selección de PXE**: Al elegir una partición destino en la ventana de "Despliegue de Imagen" y seleccionar "Cambiar Plantilla PXE", el sistema auto-seleccionará automáticamente la plantilla correspondiente al disco y partición (ej: `firstDisk_firstPartition`). + ## [1.11.0] - 2026-09-23 ### Added diff --git a/ogWebconsole/src/app/app.component.css b/ogWebconsole/src/app/app.component.css index 958301e..8c264de 100644 --- a/ogWebconsole/src/app/app.component.css +++ b/ogWebconsole/src/app/app.component.css @@ -7,5 +7,6 @@ .content { padding: 16px; + box-sizing: border-box; } diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts index 3b0d55f..d95d62c 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts @@ -65,7 +65,13 @@ export class DeployImageComponent implements OnInit { selectedModelClient: any = null; filteredPartitions: any[] = []; - postDeployAction: string = 'none'; + private _postDeployAction: string = 'none'; + get postDeployAction(): string { return this._postDeployAction; } + set postDeployAction(val: string) { + this._postDeployAction = val; + this.autoSelectPxeTemplate(); + } + selectedPxeTemplate: any = null; pxePermanent: boolean = false; pxeTemplates: any[] = []; @@ -848,6 +854,32 @@ export class DeployImageComponent implements OnInit { set selectedPartition(value: any) { this._selectedPartition = value; + this.autoSelectPxeTemplate(); + } + + private getOrdinalName(n: number): string { + const ordinals = ['first', 'second', 'third', 'fourth']; + return ordinals[n - 1] || `${n}th`; + } + + private autoSelectPxeTemplate(): void { + if (this._postDeployAction !== 'pxeTemplate' || !this._selectedPartition || !this.pxeTemplates) { + return; + } + + const disk = this._selectedPartition.diskNumber; + const part = this._selectedPartition.partitionNumber; + + if (!disk || !part) return; + + const diskOrdinal = this.getOrdinalName(disk); + const partOrdinal = this.getOrdinalName(part); + const expectedName = `${diskOrdinal}Disk_${partOrdinal}Partition`; + + const match = this.pxeTemplates.find((t: any) => t.name === expectedName); + if (match) { + this.selectedPxeTemplate = match; + } } loadGitRepositories() { diff --git a/ogWebconsole/src/app/components/groups/groups.component.css b/ogWebconsole/src/app/components/groups/groups.component.css index cb4b5a4..294a102 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.css +++ b/ogWebconsole/src/app/components/groups/groups.component.css @@ -1,3 +1,10 @@ +:host { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; +} + .groups-container { display: flex; flex-direction: column; @@ -1891,3 +1898,103 @@ mat-button-toggle-group { height: 18px; } + +/* ========================================================================== + SOLUCIÓN FINAL: EXPANSIÓN VERTICAL Y ANCHO DINÁMICO (PC Y MÓVIL) + ========================================================================== */ + +/* 1. ESCRITORIO / PC (> 992px): Expansión vertical 100% + Ancho dinámico */ +@media (min-width: 993px) { + .main-container { + flex: 1 1 auto !important; + height: 100% !important; + display: flex !important; + flex-direction: row !important; + align-items: stretch !important; + overflow: hidden !important; + } + + .filters-and-tree-container { + display: flex !important; + flex-direction: column !important; + height: 100% !important; + min-height: 0 !important; + flex: 0 0 auto !important; + width: fit-content !important; + min-width: 340px !important; + max-width: 45vw !important; + } + + .filters-panel { + flex: 0 0 auto !important; + } + + .tree-container { + display: flex !important; + flex-direction: column !important; + flex: 1 1 auto !important; /* Estira el panel del árbol hasta abajo */ + height: 100% !important; + min-height: 0 !important; + max-height: none !important; + overflow: hidden !important; + } + + /* REGLA CLAVE PARA RECUPERAR LA VERTICALIDAD DEL ÁRBOL EN PC */ + .modern-tree { + flex: 1 1 auto !important; /* Obliga al mat-tree a llenar el espacio vertical */ + height: 100% !important; + max-height: none !important; /* Elimina cualquier límite previo de altura */ + overflow-y: auto !important; /* Scrollbar vertical limpia si hay muchas aulas */ + } + + .clients-container { + flex: 1 1 auto !important; + min-width: 0 !important; + height: 100% !important; + display: flex !important; + flex-direction: column !important; + overflow: hidden !important; + } + + /* Forzar a los hijos de clients-container a respetar el límite de altura */ + .clients-view, .clients-view > div, .list-view { + display: flex !important; + flex-direction: column !important; + flex: 1 1 auto !important; + min-height: 0 !important; + height: 100% !important; + } +} + +/* 2. MÓVILES Y TABLETS (<= 992px): Disposición apilada verticalmente */ +@media (max-width: 992px) { + .main-container { + display: flex !important; + flex-direction: column !important; + width: 100% !important; + height: auto !important; + overflow-x: hidden !important; + overflow-y: auto !important; + } + + .filters-and-tree-container { + width: 100% !important; + max-width: 100% !important; + min-width: 0 !important; + flex: none !important; + height: auto !important; + max-height: 350px !important; + } + + .node-content, .node-main { + white-space: normal !important; + } + + .clients-container { + width: 100% !important; + margin-left: 0 !important; + flex: none !important; + height: auto !important; + } +} + diff --git a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts index 3e06504..d8de551 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts @@ -104,18 +104,33 @@ export class PxeBootFilesComponent implements OnInit { } loadUnits() { - this.http.get(`${this.baseUrl}/organizational-units?type=classroom&page=1&itemsPerPage=50`).subscribe( + this.http.get(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=500`).subscribe( response => { - this.units = response['hydra:member'].map((unit: any) => ({ - ...unit, - displayName: unit.parent && unit.parent.name ? `${unit.parent.name} / ${unit.name}` : unit.name - })); + const rawUnits = response['hydra:member']; + this.units = rawUnits + .filter((unit: any) => unit.type === 'classroom') + .map((unit: any) => ({ + ...unit, + displayName: this.buildFullDisplayName(unit, rawUnits) + })); this.units.sort((a, b) => a.displayName.localeCompare(b.displayName)); }, error => console.error(this.translate.instant('errorFetchingOrganizationalUnits1'), error) ); } + private buildFullDisplayName(unit: any, allUnits: any[]): string { + let hierarchy = unit.name; + let currentParent = unit.parent; + + while (currentParent) { + hierarchy = `${currentParent.name} / ${hierarchy}`; + const fullParent = allUnits.find((u: any) => u['@id'] === currentParent['@id']); + currentParent = fullParent ? fullParent.parent : null; + } + return hierarchy; + } + fetchPxeTemplates(): void { this.http.get(`${this.baseUrl}/pxe-templates?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }) .subscribe( diff --git a/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts b/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts index c48c57f..19d83fb 100644 --- a/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts @@ -52,9 +52,10 @@ export class AddClientsToSubnetComponent implements OnInit { loadUnits() { this.http.get(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=500`).subscribe( response => { - this.units = response['hydra:member'].map((unit: any) => ({ + const rawUnits = response['hydra:member']; + this.units = rawUnits.map((unit: any) => ({ ...unit, - displayName: unit.parent && unit.parent.name ? `${unit.parent.name} / ${unit.name}` : unit.name + displayName: this.buildFullDisplayName(unit, rawUnits) })); this.units.sort((a, b) => a.displayName.localeCompare(b.displayName)); this.loading = false; @@ -63,6 +64,18 @@ export class AddClientsToSubnetComponent implements OnInit { ); } + private buildFullDisplayName(unit: any, allUnits: any[]): string { + let hierarchy = unit.name; + let currentParent = unit.parent; + + while (currentParent) { + hierarchy = `${currentParent.name} / ${hierarchy}`; + const fullParent = allUnits.find((u: any) => u['@id'] === currentParent['@id']); + currentParent = fullParent ? fullParent.parent : null; + } + return hierarchy; + } + loadChildUnits(event: any) { this.http.get(`${this.baseUrl}/clients?organizationalUnit.id=${event.value.id}&exists[subnet]=false`).subscribe( response => { diff --git a/ogWebconsole/src/app/layout/main-layout/main-layout.component.css b/ogWebconsole/src/app/layout/main-layout/main-layout.component.css index 152ecc7..c4bfcb1 100644 --- a/ogWebconsole/src/app/layout/main-layout/main-layout.component.css +++ b/ogWebconsole/src/app/layout/main-layout/main-layout.component.css @@ -33,4 +33,6 @@ html, body { .content { height: 100%; overflow: auto; + display: flex; + flex-direction: column; }