From 252f961b73d2f453f10b807ecde2c9c7eaba0777 Mon Sep 17 00:00:00 2001 From: apuente Date: Tue, 3 Dec 2024 10:14:15 +0100 Subject: [PATCH] Refactor groups view and update styles --- .../components/groups/groups.component.css | 115 +-- .../components/groups/groups.component.html | 683 ++++++++---------- .../app/components/groups/groups.component.ts | 56 +- .../classroom-view.component.css | 15 +- .../classroom-view.component.html | 9 +- .../create-client.component.html | 4 +- .../shared/legend/legend.component.html | 10 + 7 files changed, 390 insertions(+), 502 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/groups.component.css b/ogWebconsole/src/app/components/groups/groups.component.css index 811fba0..01d8902 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.css +++ b/ogWebconsole/src/app/components/groups/groups.component.css @@ -133,22 +133,17 @@ mat-card-actions { .details-wrapper { width: 95%; padding: 20px; - /* Asegúrate de que no haya propiedades que centren el contenido */ display: block; } .details-placeholder { width: 100%; - /* Elimina max-width si existe */ - /* max-width: none; */ - /* Otros estilos existentes */ } button[mat-raised-button] { align-self: flex-start; } - @media (max-width: 1024px) { .card-container { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); @@ -195,9 +190,10 @@ mat-tree { padding: 10px; } -button{ +button { margin: 5px; } + mat-tree mat-tree-node { display: flex; align-items: center; @@ -247,29 +243,27 @@ mat-tree mat-tree-node:hover mat-icon { color: black; } -/* Iconos por tipo */ mat-tree mat-tree-node mat-icon.node-icon { color: #757575; margin-right: 10px; } mat-tree mat-tree-node mat-icon.node-icon.organizational-unit { - color: #1976d2; /* Azul para unidades organizativas */ + color: #1976d2; } mat-tree mat-tree-node mat-icon.node-icon.classroom { - color: #757575; /* Verde para aulas */ + color: #757575; } mat-tree mat-tree-node mat-icon.node-icon.client { - color: #757575; /* Naranja para clientes */ + color: #757575; } mat-tree mat-tree-node mat-icon.node-icon.group { - color: #757575; /* Rojo para grupos */ + color: #757575; } - mat-tree mat-tree-node button.mat-icon-button { margin-right: 10px; } @@ -281,10 +275,9 @@ mat-tree mat-tree-node button.mat-icon-button.disabled-toggle { } mat-tree mat-tree-node button.mat-icon-button.disabled-toggle:hover { - background-color: transparent; /* Desactiva hover */ + background-color: transparent; } - mat-tree mat-tree-node:hover { background-color: #e3f2fd; cursor: pointer; @@ -295,7 +288,13 @@ mat-tree mat-tree-node.disabled { } mat-tree mat-tree-node.disabled:hover { - background-color: transparent; /* Desactiva hover */ + background-color: transparent; +} + +.selected-node { + background-color: #e0f7fa; + border-left: 4px solid #3F51B5; + padding-left: calc(16px - 4px); } .mat-menu-item .mat-menu-item-submenu-icon { @@ -313,38 +312,33 @@ mat-tree mat-tree-node.disabled:hover { flex: 1 1 100%; max-width: 300px; } + .filter-container { margin-bottom: 16px; } .pc-og-live { - /* Estilo para clientes con status 'og-live' */ - color: #4caf50; /* Verde */ + color: #4caf50; } .pc-busy { - /* Estilo para clientes ocupados */ - color: #ff9800; /* Naranja */ + color: #ff9800; } .pc-windows { - /* Estilo para clientes con Windows */ - color: #0078d7; /* Azul Windows */ + color: #0078d7; } .pc-linux { - /* Estilo para clientes con Linux */ - color: #f0ad4e; /* Amarillo */ + color: #f0ad4e; } .pc-macos { - /* Estilo para clientes con macOS */ - color: #999999; /* Gris */ + color: #999999; } .pc-off { - /* Estilo para clientes apagados */ - color: #f44336; /* Rojo */ + color: #f44336; } .clients-card-container { @@ -356,7 +350,7 @@ mat-tree mat-tree-node.disabled:hover { } .classroom-item { - flex: 1 1 calc(25% - 16px); /* Ajusta este valor para controlar cuántas tarjetas caben en una fila */ + flex: 1 1 calc(25% - 16px); max-width: calc(25% - 16px); box-sizing: border-box; } @@ -399,20 +393,19 @@ mat-tree mat-tree-node.disabled:hover { } .tree-container { - width: 25%; /* El árbol ocupa el 25% del ancho */ + width: 25%; padding: 16px; overflow-x: hidden; - overflow-y: auto; /* Scroll si hay muchos nodos */ + overflow-y: auto; } .clients-container { - width: 75%; /* Los clientes ocupan el 75% del ancho */ + width: 75%; padding: 16px; box-sizing: border-box; - overflow-y: auto; /* Scroll si hay muchos clientes */ + overflow-y: auto; } - .clients-container h3 { margin-bottom: 15px; font-size: 1.5em; @@ -450,7 +443,6 @@ mat-tree mat-tree-node.disabled:hover { margin-bottom: 15px; } - .client-details { margin-top: 10px; } @@ -463,7 +455,6 @@ mat-tree mat-tree-node.disabled:hover { margin-bottom: 5px; } - .client-ip { display: block; font-size: 0.9em; @@ -474,9 +465,6 @@ button[mat-raised-button] { margin-top: 15px; } - - - .clients-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); @@ -503,43 +491,6 @@ button[mat-raised-button] { flex-direction: column; } - -.view-toggle-container { - display: flex; - gap: 1rem; - margin-bottom: 1rem; -} - -.clients-grid { - display: flex; - flex-wrap: wrap; - gap: 1rem; -} - -.clients-list .list-item-content { - display: flex; - justify-content: space-between; - width: 100%; - align-items: center; -} - -.client-card, .list-item-content { - border: 1px solid #ccc; - padding: 1rem; - border-radius: 5px; -} - -.client-card { - display: flex; - flex-direction: column; - align-items: center; -} - -.client-image { - width: 50px; - height: 50px; - margin-bottom: 0.5rem; -} .view-toggle-container { display: flex; gap: 1rem; @@ -585,7 +536,7 @@ button[mat-raised-button] { } .back-button { - flex-shrink: 0; /* Asegura que el botón Back no se reduzca */ + flex-shrink: 0; } .view-toggle-container { @@ -596,13 +547,13 @@ button[mat-raised-button] { .filters-container { display: flex; - flex-wrap: wrap; /* Permite que los elementos pasen a la siguiente línea si no caben */ - gap: 16px; /* Espaciado entre los elementos */ - margin: 16px 0; /* Separación con otros elementos */ - padding: 0 16px; /* Opcional, para añadir un poco de espacio interno */ + flex-wrap: wrap; + gap: 16px; + margin: 16px 0; + padding: 0 16px; } .filters-container mat-form-field { - flex: 1 1 300px; /* Toma todo el ancho disponible hasta 300px por elemento */ - max-width: 300px; /* Limita el ancho máximo */ + flex: 1 1 300px; + max-width: 300px; } diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index 308e714..e25deee 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -1,390 +1,309 @@ - +
+ +

+ {{ 'adminGroupsTitle' | translate }} +

+
+ + + +
+
- -
- +
+ + + + + + + + + + + + + +
+ +
+ + -

- {{ 'adminGroupsTitle' | translate }} -

-
- - - -
- - - - -
- - - - Filtros - - - - -
- - - - {{ savedFilter[0] }} - - - - - - Buscar en el árbol - - - - - Filtrar por tipo - - Todos - Unidades Organizativas - Aulas - Clientes - Grupos - - - - - Buscar cliente - - -
-
- - - - - -
- - - - apartment {{ unidad.name }} - - - -
- -
- - - - - - - - - -
-
- - - - -
- - - - -
-
+
+
+

{{ selectedUnidad?.name }}

+ + + - -
-
- -
- - -
- -

{{ selectedUnidad?.name }}

- - - - - {{ - node.type === 'organizational-unit' ? 'apartment' - : node.type === 'classrooms-group' ? 'meeting_room' - : node.type === 'classroom' ? 'school' - : node.type === 'clients-group' ? 'lan' - : node.type === 'client' ? 'computer' - : 'group' - }} - - {{ node.name }} - - - - - - - {{ - node.type === 'organizational-unit' ? 'apartment' - : node.type === 'classrooms-group' ? 'meeting_room' - : node.type === 'classroom' ? 'school' - : node.type === 'clients-group' ? 'lan' - : node.type === 'client' ? 'computer' - : 'group' + + {{ + node.type === 'organizational-unit' ? 'apartment' + : node.type === 'classrooms-group' ? 'meeting_room' + : node.type === 'classroom' ? 'school' + : node.type === 'clients-group' ? 'lan' + : node.type === 'client' ? 'computer' + : 'group' }} - - - {{ node.name }} - - - IP: {{ node.ip }} - - + + + + + {{ + node.type === 'organizational-unit' ? 'apartment' + : node.type === 'classrooms-group' ? 'meeting_room' + : node.type === 'classroom' ? 'school' + : node.type === 'clients-group' ? 'lan' + : node.type === 'client' ? 'computer' + : 'group' + }} + + {{ node.name }} + + - IP: {{ node.ip }} + + + + +
+ + + + + + + + + + + + +
+

Clientes {{ selectedNode?.name ? 'del ' + selectedNode?.name : '' }}

+
+
+
+ Client Icon +
+ {{ client.name }} + {{ client.ip }} + + {{ client.status || 'off' }} + + + + + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nombre {{ client.name }} IP {{ client.ip }} MAC {{ client.mac }} OG Live {{ client.oglive }} Estado + + {{ client.status || 'off' }} + + Mantenimiento {{ client.mantenimiento }} Subred {{ client.subnet }} Plantilla PXE {{ client.pxeTemplate }} Acciones + - - - - - - - - - - - - - - - - - - - - - -
-

Clientes del {{ selectedNode?.name }}

- - -
-
-
- Client Icon -
- {{ client.name }} - {{ client.ip }} - - - - - - - -
-
-
- - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Nombre {{ client.name }} IP {{ client.ip }} MAC {{ client.mac }} OG Live {{ client.oglive }} Estado - - {{ client.status || 'off' }} - - Subred {{ client.subnet }} Plantilla PXE {{ client.pxeTemplate }} Acciones - - - - - - -
- - - -
- -
+ + + + + +
+
- - - \ No newline at end of file +
+
+ diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index f0fa53b..972f0c8 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -4,12 +4,10 @@ import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { MatBottomSheet } from '@angular/material/bottom-sheet'; import { MatTabChangeEvent } from '@angular/material/tabs'; -import { MatMenuTrigger } from '@angular/material/menu'; import { ToastrService } from 'ngx-toastr'; import { JoyrideService } from 'ngx-joyride'; import { FlatTreeControl } from '@angular/cdk/tree'; import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; - import { DataService } from './services/data.service'; import { UnidadOrganizativa } from './model/model'; import { CreateOrganizationalUnitComponent } from './shared/organizational-units/create-organizational-unit/create-organizational-unit.component'; @@ -25,6 +23,7 @@ import { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/del import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal'; interface TreeNode { + clients?: any[]; name: string; type: string; children?: TreeNode[]; @@ -68,7 +67,7 @@ export class GroupsComponent implements OnInit { currentView: 'card' | 'list' = 'list'; isTreeViewActive: boolean = false; savedFilterNames: any[] = []; - + selectedTreeFilter: string = ''; @ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent; @ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent; @@ -174,16 +173,32 @@ export class GroupsComponent implements OnInit { ); } - onSelectUnidad(unidad: UnidadOrganizativa): void { + onSelectUnidad(unidad: any): void { this.selectedUnidad = unidad; this.selectedDetail = unidad; - + + this.selectedClients = this.collectAllClients(unidad); + this.selectedClientsOriginal = [...this.selectedClients]; + this.loadChildrenAndClients(unidad.id).then(fullData => { const treeData = this.convertToTreeData(fullData); this.treeDataSource.data = treeData[0]?.children || []; }); + this.isTreeViewActive = true; } + + + private collectAllClients(node: any): any[] { + let clients = node.clients || []; + if (node.children && node.children.length > 0) { + node.children.forEach((child: any) => { + clients = clients.concat(this.collectAllClients(child)); + }); + } + return clients; + } + async loadChildrenAndClients(id: string): Promise { try { @@ -222,7 +237,8 @@ export class GroupsComponent implements OnInit { onNodeClick(node: TreeNode): void { this.selectedNode = node; - + this.selectedClients = node.clients || []; + this.selectedClientsOriginal = [...this.selectedClients]; if (node.hasClients) { const url = `${this.baseUrl}${node['@id']}`; this.http.get(url).subscribe( @@ -259,6 +275,10 @@ export class GroupsComponent implements OnInit { this.dataService.getOrganizationalUnits().subscribe( data => { this.organizationalUnits = data; + this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => { + const treeData = this.convertToTreeData(updatedData); + this.treeDataSource.data = treeData[0]?.children || []; + }); }, error => console.error('Error fetching unidades organizativas', error) ); @@ -272,8 +292,13 @@ export class GroupsComponent implements OnInit { this.dataService.getOrganizationalUnits().subscribe( data => { this.organizationalUnits = data; + this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => { + const treeData = this.convertToTreeData(updatedData); + this.treeDataSource.data = treeData[0]?.children || []; + }); if (organizationalUnit && organizationalUnit.id) { this.loadChildrenAndClients(organizationalUnit.id); + this.refreshClients(organizationalUnit); } }, error => console.error('Error fetching unidades organizativas', error) @@ -393,7 +418,7 @@ export class GroupsComponent implements OnInit { } onRoomMap(room: any): void { - this.http.get('https://127.0.0.1:8443' + room['@id']).subscribe( + this.http.get(`${this.baseUrl}`+ room['@id']).subscribe( (response: any) => { this.dialog.open(ClassroomViewDialogComponent, { width: '90vw', @@ -408,7 +433,7 @@ export class GroupsComponent implements OnInit { fetchCommands(): void { this.commandsLoading = true; - this.http.get('https://127.0.0.1:8443/commands?page=1&itemsPerPage=30').subscribe( + this.http.get(`${this.baseUrl}`+'/commands?page=1&itemsPerPage=30').subscribe( (response: any) => { this.commands = response['hydra:member']; this.commandsLoading = false; @@ -465,8 +490,6 @@ export class GroupsComponent implements OnInit { hasChild = (_: number, node: FlatNode): boolean => node.expandable; isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable; - /* filtros */ - selectedTreeFilter: string = ''; filterTree(searchTerm: string, filterType: string): void { const filterNodes = (nodes: any[]): any[] => { return nodes @@ -474,41 +497,36 @@ export class GroupsComponent implements OnInit { const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesType = filterType ? node.type.toLowerCase() === filterType.toLowerCase() : true; - // Filtrar hijos recursivamente const filteredChildren = node.children ? filterNodes(node.children) : []; - // Si el nodo o algún hijo coincide, incluirlo if (matchesName && matchesType || filteredChildren.length > 0) { return { ...node, children: filteredChildren }; } - return null; // Excluir nodos que no coinciden + return null; }) - .filter(node => node !== null); // Eliminar nodos excluidos + .filter(node => node !== null); }; - // Aplicar filtro sobre el árbol completo const filteredData = filterNodes(this.treeDataSource.data); - // Actualizar el origen de datos del árbol this.treeDataSource.data = filteredData; } onTreeFilterInput(event: Event): void { const input = event.target as HTMLInputElement; - const searchTerm = input?.value || ''; // Maneja el caso de nulo o indefinido + const searchTerm = input?.value || ''; this.filterTree(searchTerm, this.selectedTreeFilter); } onClientFilterInput(event: Event): void { const input = event.target as HTMLInputElement; - const searchTerm = input?.value || ''; // Maneja valores nulos o indefinidos + const searchTerm = input?.value || ''; this.filterClients(searchTerm); } filterClients(searchTerm: string): void { if (!searchTerm) { - // Restaurar los datos originales si no hay filtro this.selectedClients = [...this.selectedClientsOriginal]; return; } diff --git a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.css b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.css index 371b00b..bcf134e 100644 --- a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.css +++ b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.css @@ -31,18 +31,11 @@ mat-card { } .client-info { - position: absolute; - top: 20px; - left: 5px; - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - background-color: rgba(0, 0, 0, 0); - color: black; text-align: center; - padding: 15px; - box-sizing: border-box; + margin-top: 5px; + font-size: medium; + color: gray; + align-items: center; } .client-name { diff --git a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.html b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.html index 5085467..5485a3d 100644 --- a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.html +++ b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.html @@ -10,12 +10,9 @@
{{ 'clientAlt' | translate }} -
-
{{ client.name }}
-
- {{ client.ip }} -
-
+
+
+ {{ client.name }}
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html index fd924bc..56ca328 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html @@ -130,7 +130,7 @@ - - + +
diff --git a/ogWebconsole/src/app/components/groups/shared/legend/legend.component.html b/ogWebconsole/src/app/components/groups/shared/legend/legend.component.html index c60c785..6cd66de 100644 --- a/ogWebconsole/src/app/components/groups/shared/legend/legend.component.html +++ b/ogWebconsole/src/app/components/groups/shared/legend/legend.component.html @@ -19,4 +19,14 @@ computer
{{ 'clientTitle' | translate }}
+ + + school +
Disponible acceso remoto
+
+ + school +
No disponible acceso remoto
+
+