diff --git a/ogWebconsole/src/app/components/groups/groups.component.css b/ogWebconsole/src/app/components/groups/groups.component.css index f40ad86..60fad89 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.css +++ b/ogWebconsole/src/app/components/groups/groups.component.css @@ -502,3 +502,94 @@ button[mat-raised-button] { display: flex; 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; + 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; +} + +.header-actions-container { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.back-button { + flex-shrink: 0; /* Asegura que el botón Back no se reduzca */ +} + +.view-toggle-container { + display: flex; + gap: 1rem; + align-items: center; +} diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index a4c529c..b672684 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -17,11 +17,12 @@ - + @@ -77,10 +78,24 @@ - +
+ + + + +
+ + +
+
+
@@ -114,7 +129,7 @@ + @@ -162,6 +177,10 @@ visibility {{ 'viewUnitMenu' | translate }} + - + +

Clientes del {{ selectedNode?.name }}

-
+ + +
Client Icon @@ -189,28 +212,124 @@ - + -
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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 + + + + + + +
+ + + +
+
- - diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index 31f9535..b9bf844 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -1,13 +1,17 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { DataService } from './services/data.service'; -import { UnidadOrganizativa } from './model/model'; +import { HttpClient } from '@angular/common/http'; +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'; import { CreateClientComponent } from './shared/clients/create-client/create-client.component'; import { EditOrganizationalUnitComponent } from './shared/organizational-units/edit-organizational-unit/edit-organizational-unit.component'; @@ -17,11 +21,8 @@ import { TreeViewComponent } from './shared/tree-view/tree-view.component'; import { LegendComponent } from './shared/legend/legend.component'; import { ClientTabViewComponent } from './components/client-tab-view/client-tab-view.component'; import { OrganizationalUnitTabViewComponent } from './components/organizational-unit-tab-view/organizational-unit-tab-view.component'; -import { MatMenuTrigger } from '@angular/material/menu'; -import { HttpClient } from '@angular/common/http'; -import { Router } from '@angular/router'; -import { ToastrService } from 'ngx-toastr'; import { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/delete-modal.component'; +import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal'; interface TreeNode { name: string; @@ -30,6 +31,7 @@ interface TreeNode { ip?: string; '@id'?: string; hasClients?: boolean; + status?: string ; } interface FlatNode { @@ -62,6 +64,8 @@ export class GroupsComponent implements OnInit { commandsLoading: boolean = false; selectedClients: any[] = []; cols: number = 4; + currentView: 'card' | 'list' = 'list'; + @ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent; @ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent; @@ -104,35 +108,26 @@ export class GroupsComponent implements OnInit { this.updateGridCols(); window.addEventListener('resize', () => this.updateGridCols()); } + toggleView(view: 'card' | 'list') { + this.currentView = view; + } updateGridCols(): void { const width = window.innerWidth; - if (width <= 600) { - this.cols = 1; - } else if (width <= 960) { - this.cols = 2; - } else if (width <= 1280) { - this.cols = 3; - } else { - this.cols = 4; - } + this.cols = width <= 600 ? 1 : width <= 960 ? 2 : width <= 1280 ? 3 : 4; } clearSelection(): void { this.selectedUnidad = null; this.selectedDetail = null; + this.selectedClients = []; } onTabChange(event: MatTabChangeEvent): void { - switch (event.index) { - case 2: - this.clientTabComponent.search(); - break; - case 3: - this.organizationalUnitTabComponent.search(); - break; - default: - break; + if (event.index === 2) { + this.clientTabComponent.search(); + } else if (event.index === 3) { + this.organizationalUnitTabComponent.search(); } } @@ -160,8 +155,7 @@ export class GroupsComponent implements OnInit { onSelectUnidad(unidad: UnidadOrganizativa): void { this.selectedUnidad = unidad; this.selectedDetail = unidad; - console.log('Selected unidad:', unidad); - + this.loadChildrenAndClients(unidad.id).then(fullData => { const treeData = this.convertToTreeData(fullData); this.treeDataSource.data = treeData[0]?.children || []; @@ -171,7 +165,6 @@ export class GroupsComponent implements OnInit { async loadChildrenAndClients(id: string): Promise { try { const childrenData = await this.dataService.getChildren(id).toPromise(); - const processHierarchy = (nodes: UnidadOrganizativa[]): TreeNode[] => { return nodes.map(node => ({ name: node.name, @@ -181,8 +174,7 @@ export class GroupsComponent implements OnInit { clients: node.clients || [] })); }; - - + return { ...this.selectedUnidad, children: childrenData ? processHierarchy(childrenData) : [] @@ -201,21 +193,18 @@ export class GroupsComponent implements OnInit { children: node.children?.map(processNode) || [], hasClients: node.clients && node.clients.length > 0, }); - + return [processNode(data)]; } onNodeClick(node: TreeNode): void { - console.log('Node clicked:', node); this.selectedNode = node; - + if (node.hasClients) { const url = `${this.baseUrl}${node['@id']}`; this.http.get(url).subscribe( (data: any) => { - console.log('Clients fetched:', data); this.selectedClients = data.clients || []; - }, (error) => { console.error('Error fetching clients:', error); @@ -289,41 +278,41 @@ export class GroupsComponent implements OnInit { onDeleteClick(event: MouseEvent, node: TreeNode | null, clientNode?: TreeNode | null): void { console.log('Deleting node or client:', node); - + const uuid = node && node['@id'] ? node['@id'].split('/').pop() || '' : ''; const name = node?.name || 'Elemento desconocido'; const type = node?.type || ''; - + event.stopPropagation(); - + const dialogRef = this.dialog.open(DeleteModalComponent, { width: '400px', data: { name } }); - + dialogRef.afterClosed().subscribe(result => { if (result === true) { this.dataService.deleteElement(uuid, type).subscribe( () => { console.log('Entity deleted successfully:', uuid); - + this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => { const treeData = this.convertToTreeData(updatedData); this.treeDataSource.data = treeData[0]?.children || []; }); - + if (type === 'client' && clientNode) { console.log('Refreshing clients for node:', clientNode); this.refreshClients(clientNode); } - + this.dataService.getOrganizationalUnits().subscribe( data => { this.organizationalUnits = data; }, error => console.error('Error fetching unidades organizativas:', error) ); - + this.toastr.success('Entidad eliminada exitosamente'); }, error => { @@ -341,10 +330,10 @@ export class GroupsComponent implements OnInit { this.selectedClients = []; return; } - + const url = `${this.baseUrl}${node['@id']}`; console.log('Fetching clients for node with URL:', url); - + this.http.get(url).subscribe( (data: any) => { console.log('Response data:', data); @@ -375,6 +364,20 @@ export class GroupsComponent implements OnInit { } } + onRoomMap(room: any): void { + this.http.get('https://127.0.0.1:8443' + room['@id']).subscribe( + (response: any) => { + this.dialog.open(ClassroomViewDialogComponent, { + width: '90vw', + data: { clients: response.clients } + }); + }, + (error: any) => { + console.error('Error en la solicitud HTTP:', error); + } + ); + } + fetchCommands(): void { this.commandsLoading = true; this.http.get('https://127.0.0.1:8443/commands?page=1&itemsPerPage=30').subscribe( @@ -390,14 +393,18 @@ export class GroupsComponent implements OnInit { } executeCommand(command: any, selectedNode: any): void { - console.log('Executing command:', command.name); - this.toastr.success('Ejecutando comando: ' + command.name+ " en "+ selectedNode.name ) ; + this.toastr.success('Ejecutando comando: ' + command.name + " en " + selectedNode.name); } onClientActions(client: any): void { console.log('Client actions:', client); } + onShowClientDetail(event: MouseEvent, client: any): void { + event.stopPropagation(); + this.router.navigate(['clients', client.uuid], { state: { clientData: client } }); + } + onShowDetailsClick(event: MouseEvent, data: any): void { event.stopPropagation(); if (data.type != 'client') { @@ -429,4 +436,5 @@ export class GroupsComponent implements OnInit { hasChild = (_: number, node: FlatNode): boolean => node.expandable; isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable; + } diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css index b978520..b7bc183 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css @@ -1,13 +1,12 @@ -/* Contenedor principal */ .create-client-container { display: flex; flex-direction: column; padding: 16px; font-family: Arial, sans-serif; font-size: 14px; + align-items: center; } -/* Títulos */ h1, h3, h4 { margin: 0 0 16px; color: #333; @@ -27,20 +26,21 @@ h4 { margin-top: 16px; } -/* Espaciado entre contenedores */ .inputs-container { display: flex; gap: 24px; margin-top: 16px; } -/* Contenedor de cada sección */ .mat-dialog-content { flex: 1; background-color: #f9f9f9; border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + min-width: 600px; + max-width: 90vw; + width: 800px; } .create-multiple-client-container { @@ -51,7 +51,6 @@ h4 { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } -/* Campos del formulario */ .client-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); @@ -66,7 +65,6 @@ h4 { width: 100%; } -/* Tabla */ .scrollable-table { max-height: 200px; overflow-y: auto; @@ -95,7 +93,6 @@ tr:hover { background-color: #f9f9f9; } -/* Botones */ button { margin-right: 8px; } @@ -115,13 +112,11 @@ button.mat-raised-button { font-weight: 600; } -/* Carga */ .loading-spinner { margin: 16px auto; display: block; } -/* Alternar formularios */ .toggle-button { background: none; border: none; @@ -135,7 +130,6 @@ button.mat-raised-button { text-decoration: none; } -/* Mejoras generales */ .mat-divider { margin: 0 16px; } @@ -151,7 +145,6 @@ input[type="file"] { display: none; } -/* Responsivo */ @media (max-width: 768px) { .inputs-container { flex-direction: column; 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 707c348..fd924bc 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 @@ -1,4 +1,5 @@ -
+
+

{{ 'addClientTitle' | translate }}s

@@ -8,7 +9,6 @@
{{ unit.name }}
-
{{ unit.path }}
@@ -20,8 +20,10 @@

o añadelos manualmente:

+
+

Clientes importados:

diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts index 5c781ea..d79f63f 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts @@ -23,6 +23,7 @@ export class CreateClientComponent implements OnInit { loading: boolean = false; displayedColumns: string[] = ['name', 'ip']; isSingleClientForm: boolean = false; + showTextarea: boolean = true; protected netifaceTypes = [ { name: 'Eth0', value: 'eth0' }, { name: 'Eth1', value: 'eth1' }, @@ -147,8 +148,10 @@ export class CreateClientComponent implements OnInit { if (clients.length > 0) { this.uploadedClients = clients; this.toastService.success('Archivo cargado correctamente, los datos están listos para enviarse.', 'Éxito'); + this.showTextarea = false; } else { this.toastService.error('No se encontraron datos válidos', 'Error'); + this.showTextarea = true; } }; @@ -172,8 +175,10 @@ export class CreateClientComponent implements OnInit { if (clients.length > 0) { this.uploadedClients = clients; this.toastService.success('Datos cargados correctamente, los datos están listos para enviarse.', 'Éxito'); + this.showTextarea = false; } else { this.toastService.error('No se encontraron datos válidos', 'Error'); + this.showTextarea = true; } }