chore: release 1.11.1 with ui improvements and pxe autoselect
parent
952482e68b
commit
809b368e4a
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@
|
|||
|
||||
.content {
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,18 +104,33 @@ export class PxeBootFilesComponent implements OnInit {
|
|||
}
|
||||
|
||||
loadUnits() {
|
||||
this.http.get<any>(`${this.baseUrl}/organizational-units?type=classroom&page=1&itemsPerPage=50`).subscribe(
|
||||
this.http.get<any>(`${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<any>(`${this.baseUrl}/pxe-templates?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters })
|
||||
.subscribe(
|
||||
|
|
|
|||
|
|
@ -52,9 +52,10 @@ export class AddClientsToSubnetComponent implements OnInit {
|
|||
loadUnits() {
|
||||
this.http.get<any>(`${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<any>(`${this.baseUrl}/clients?organizationalUnit.id=${event.value.id}&exists[subnet]=false`).subscribe(
|
||||
response => {
|
||||
|
|
|
|||
|
|
@ -33,4 +33,6 @@ html, body {
|
|||
.content {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue