diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 9ec2f42..1245eeb 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -123,7 +123,7 @@ import { JoyrideModule } from 'ngx-joyride'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component'; - +import { MatSortModule } from '@angular/material/sort'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './locale/', '.json'); } @@ -234,6 +234,7 @@ export function HttpLoaderFactory(http: HttpClient) { MatDatepickerModule, MatNativeDateModule, MatSliderModule, + MatSortModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index 0c1b9fc..85c1ad6 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -40,7 +40,7 @@ Filtrar por tipo Todos - Grupos de aulas + Grupos de aulas Aulas Grupos de ordenadores diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index 0d64752..96cf096 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; @@ -8,8 +8,10 @@ import { ToastrService } from 'ngx-toastr'; import { JoyrideService } from 'ngx-joyride'; import { FlatTreeControl } from '@angular/cdk/tree'; import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; +import { Subscription } from 'rxjs'; + import { DataService } from './services/data.service'; -import { UnidadOrganizativa } from './model/model'; +import { UnidadOrganizativa, Client, TreeNode, FlatNode, Command, Filter } 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'; @@ -22,86 +24,67 @@ import { OrganizationalUnitTabViewComponent } from './components/organizational- import { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/delete-modal.component'; import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal'; -interface TreeNode { - clients?: any[]; - name: string; - type: string; - children?: TreeNode[]; - ip?: string; - '@id'?: string; - hasClients?: boolean; - status?: string; -} - -interface FlatNode { - name: string; - type: string; - level: number; - expandable: boolean; - ip?: string; - hasClients?: boolean; +enum NodeType { + OrganizationalUnit = 'organizational-unit', + ClassroomsGroup = 'classrooms-group', + Classroom = 'classroom', + ClientsGroup = 'clients-group', + Client = 'client', } @Component({ selector: 'app-groups', templateUrl: './groups.component.html', - styleUrls: ['./groups.component.css'] + styleUrls: ['./groups.component.css'], }) -export class GroupsComponent implements OnInit { +export class GroupsComponent implements OnInit, OnDestroy { baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; organizationalUnits: UnidadOrganizativa[] = []; selectedUnidad: UnidadOrganizativa | null = null; - selectedDetail: any | null = null; - loading: boolean = false; - loadingChildren: boolean = false; - searchTerm: string = ''; + selectedDetail: UnidadOrganizativa | null = null; + loading = false; + searchTerm = ''; treeControl: FlatTreeControl; treeFlattener: MatTreeFlattener; treeDataSource: MatTreeFlatDataSource; selectedNode: TreeNode | null = null; - commands: any[] = []; - commandsLoading: boolean = false; - selectedClients: any[] = []; - cols: number = 4; - selectedClientsOriginal: any[] = []; + commands: Command[] = []; + commandsLoading = false; + selectedClients: Client[] = []; + cols = 4; + selectedClientsOriginal: Client[] = []; currentView: 'card' | 'list' = 'list'; - isTreeViewActive: boolean = false; - savedFilterNames: any[] = []; - selectedTreeFilter: string = ''; - - syncStatus: boolean = false; - syncingClientId: number | null = null; + isTreeViewActive = false; + savedFilterNames: [string, string][] = []; + selectedTreeFilter = ''; + syncStatus = false; + syncingClientId: string | null = null; + private originalTreeData: TreeNode[] = []; @ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent; @ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent; + private subscriptions: Subscription = new Subscription(); + constructor( private http: HttpClient, private router: Router, private dataService: DataService, public dialog: MatDialog, - private _bottomSheet: MatBottomSheet, + private bottomSheet: MatBottomSheet, private joyrideService: JoyrideService, private toastr: ToastrService ) { this.treeFlattener = new MatTreeFlattener( - (node: TreeNode, level: number) => ({ - name: node.name, - type: node.type, - level, - expandable: !!node.children?.length, - hasClients: node.hasClients, - ip: node.ip, - ['@id']: node['@id'] - }), - node => node.level, - node => node.expandable, - node => node.children + this.transformer, + (node) => node.level, + (node) => node.expandable, + (node) => node.children ); this.treeControl = new FlatTreeControl( - node => node.level, - node => node.expandable + (node) => node.level, + (node) => node.expandable ); this.treeDataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); @@ -111,17 +94,32 @@ export class GroupsComponent implements OnInit { this.search(); this.getFilters(); this.updateGridCols(); - window.addEventListener('resize', () => this.updateGridCols()); + window.addEventListener('resize', this.updateGridCols); } - toggleView(view: 'card' | 'list') { + ngOnDestroy(): void { + window.removeEventListener('resize', this.updateGridCols); + this.subscriptions.unsubscribe(); + } + + private transformer = (node: TreeNode, level: number): FlatNode => ({ + name: node.name, + type: node.type, + level, + expandable: !!node.children?.length, + hasClients: node.hasClients, + ip: node.ip, + '@id': node['@id'], + }); + + toggleView(view: 'card' | 'list'): void { this.currentView = view; } - updateGridCols(): void { + updateGridCols = (): void => { const width = window.innerWidth; this.cols = width <= 600 ? 1 : width <= 960 ? 2 : width <= 1280 ? 3 : 4; - } + }; clearSelection(): void { this.selectedUnidad = null; @@ -139,112 +137,122 @@ export class GroupsComponent implements OnInit { } getFilters(): void { - this.dataService.getFilters().subscribe( - data => { - this.savedFilterNames = data.map((filter: any) => [filter.name, filter.uuid]); - }, - error => { - console.error('Error fetching filters:', error); - } + this.subscriptions.add( + this.dataService.getFilters().subscribe( + (data) => { + this.savedFilterNames = data.map((filter: { name: string; uuid: string; }) => [filter.name, filter.uuid]); + }, + (error) => { + console.error('Error fetching filters:', error); + } + ) ); } - loadSelectedFilter(savedFilter: any) { - const url = `${this.baseUrl}/views/` + savedFilter[1]; - this.dataService.getFilter(savedFilter[1]).subscribe(response => { - if (response) { - console.log('Filter1:', response.filters); - } - }, error => { - console.error('Error:', error); - }); + loadSelectedFilter(savedFilter: [string, string]): void { + this.subscriptions.add( + this.dataService.getFilter(savedFilter[1]).subscribe( + (response) => { + if (response) { + console.log('Filter:', response.filters); + } + }, + (error) => { + console.error('Error:', error); + } + ) + ); } search(): void { this.loading = true; - this.dataService.getOrganizationalUnits(this.searchTerm).subscribe( - data => { - this.organizationalUnits = data; - this.loading = false; - }, - error => { - console.error('Error fetching unidades organizativas', error); - this.loading = false; - } + this.subscriptions.add( + this.dataService.getOrganizationalUnits(this.searchTerm).subscribe( + (data) => { + this.organizationalUnits = data; + this.loading = false; + }, + (error) => { + console.error('Error fetching organizational units', error); + this.loading = false; + } + ) ); } - onSelectUnidad(unidad: any): void { + onSelectUnidad(unidad: UnidadOrganizativa): void { this.selectedUnidad = unidad; this.selectedDetail = unidad; this.selectedClients = this.collectAllClients(unidad); this.selectedClientsOriginal = [...this.selectedClients]; - this.loadChildrenAndClients(unidad.id).then(fullData => { + 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[] { + private collectAllClients(node: UnidadOrganizativa): Client[] { let clients = node.clients || []; - if (node.children && node.children.length > 0) { - node.children.forEach((child: any) => { + if (node.children) { + node.children.forEach((child) => { clients = clients.concat(this.collectAllClients(child)); }); } return clients; } - async loadChildrenAndClients(id: string): Promise { + private 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, - type: node.type, - '@id': node['@id'], + + const processHierarchy = (nodes: UnidadOrganizativa[]): UnidadOrganizativa[] => { + return nodes.map((node) => ({ + ...node, children: node.children ? processHierarchy(node.children) : [], - clients: node.clients || [] })); }; - + return { - ...this.selectedUnidad, - children: childrenData ? processHierarchy(childrenData) : [] + ...this.selectedUnidad!, + children: childrenData ? processHierarchy(childrenData) : [], }; } catch (error) { console.error('Error loading children:', error); - return this.selectedUnidad; + return this.selectedUnidad!; } } + - convertToTreeData(data: any): TreeNode[] { - const processNode = (node: UnidadOrganizativa): TreeNode => ({ - name: node.name, - type: node.type, - '@id': node['@id'], - children: node.children?.map(processNode) || [], - hasClients: node.clients && node.clients.length > 0, - }); - - return [processNode(data)]; - } + private convertToTreeData(data: UnidadOrganizativa): TreeNode[] { + const processNode = (node: UnidadOrganizativa): TreeNode => ({ + name: node.name, + type: node.type, + '@id': node['@id'], + children: node.children?.map(processNode) || [], + hasClients: (node.clients?.length ?? 0) > 0, + }); + return [processNode(data)]; +} + 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( - (data: any) => { - this.selectedClientsOriginal = [...data.clients]; - this.selectedClients = data.clients || []; - }, - (error) => { - console.error('Error fetching clients:', error); - } + this.fetchClientsForNode(node); + } + + private fetchClientsForNode(node: TreeNode): void { + if (node.hasClients && node['@id']) { + this.subscriptions.add( + this.http.get<{ clients: Client[] }>(`${this.baseUrl}${node['@id']}`).subscribe( + (data) => { + this.selectedClientsOriginal = [...data.clients]; + this.selectedClients = data.clients || []; + }, + (error) => { + console.error('Error fetching clients:', error); + } + ) ); } else { this.selectedClients = []; @@ -252,222 +260,207 @@ export class GroupsComponent implements OnInit { } } - getNodeIcon(node: any): string { + getNodeIcon(node: TreeNode): string { switch (node.type) { - case 'organizational-unit': return 'apartment'; - case 'classrooms-group': return 'doors'; - case 'classroom': return 'school'; - case 'clients-group': return 'lan'; - case 'client': return 'computer'; - default: return 'group'; + case NodeType.OrganizationalUnit: + return 'apartment'; + case NodeType.ClassroomsGroup: + return 'doors'; + case NodeType.Classroom: + return 'school'; + case NodeType.ClientsGroup: + return 'lan'; + case NodeType.Client: + return 'computer'; + default: + return 'group'; } } - addOU(event: MouseEvent, parent: any = null): void { + addOU(event: MouseEvent, parent: TreeNode | null = null): void { event.stopPropagation(); - const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, { data: { parent }, width: '900px' }); + const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, { + data: { parent }, + width: '900px', + }); dialogRef.afterClosed().subscribe(() => { - 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) - ); + this.refreshOrganizationalUnits(); }); } - addClient(event: MouseEvent, organizationalUnit: any = null): void { + addClient(event: MouseEvent, organizationalUnit: TreeNode | null = null): void { event.stopPropagation(); - const dialogRef = this.dialog.open(CreateClientComponent, { data: { organizationalUnit }, width: '900px' }); + const dialogRef = this.dialog.open(CreateClientComponent, { + data: { organizationalUnit }, + width: '900px', + }); dialogRef.afterClosed().subscribe(() => { + this.refreshOrganizationalUnits(); + if (organizationalUnit && organizationalUnit['@id']) { + this.refreshClientsForNode(organizationalUnit); + } + }); + } + + private refreshOrganizationalUnits(): void { + this.subscriptions.add( this.dataService.getOrganizationalUnits().subscribe( - data => { + (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) - ); - }); - } - - setSelectedNode(node: TreeNode): void { - this.selectedNode = node; - } - - onEditNode(event: MouseEvent, node: TreeNode | null): void { - if (!node) return; - - const uuid = node['@id'] ? node['@id'].split('/').pop() : ''; - const type = node.type; - - event.stopPropagation(); - - if (type !== 'client') { - this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' }); - } else { - this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' }); - } - } - - onDelete(node: TreeNode | null): void { - console.log('Deleting node:', node); - } - - onDeleteClick(event: MouseEvent, node: TreeNode | null, clientNode?: TreeNode | null): void { - 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( - () => { - this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => { + if (this.selectedUnidad) { + this.loadChildrenAndClients(this.selectedUnidad?.id || '').then((updatedData) => { + this.selectedUnidad = updatedData; const treeData = this.convertToTreeData(updatedData); - this.treeDataSource.data = treeData[0]?.children || []; - }); - - if (type === 'client' && 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 => { - console.error('Error deleting entity:', error); - this.toastr.error('Error al eliminar la entidad', error.message); + this.originalTreeData = treeData[0]?.children || []; + this.treeDataSource.data = [...this.originalTreeData]; + }); } - ); - } - }); - } - - private refreshClients(node: TreeNode): void { - if (!node || !node['@id']) { - this.selectedClients = []; - return; - } - - const url = `${this.baseUrl}${node['@id']}`; - this.http.get(url).subscribe( - (data: any) => { - if (data && Array.isArray(data.clients)) { - this.selectedClients = data.clients; - } else { - this.selectedClients = []; - } - }, - error => { - console.error('Error refreshing clients:', error); - const errorMessage = error.status === 404 - ? 'No se encontraron clientes para este nodo.' - : 'Error al comunicarse con el servidor.'; - this.toastr.error(errorMessage); - } + }, + (error) => console.error('Error fetching organizational units', error) + ) ); } - onEditClick(event: MouseEvent, type: any, uuid: string): void { + onEditNode(event: MouseEvent, node: TreeNode | null): void { event.stopPropagation(); - if (type != 'client') { + const uuid = node ? this.extractUuid(node['@id']) : null; + if (!uuid) return; + + if (node && node.type !== NodeType.Client) { this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' }); } else { this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' }); } } - onRoomMap(room: any): void { - this.http.get(`${this.baseUrl}`+ 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); + onDeleteClick(event: MouseEvent, node: TreeNode | null, clientNode?: TreeNode | null): void{ + event.stopPropagation(); + const uuid = node ? this.extractUuid(node['@id']) : null; + if (!uuid) return; + + if (!node) return; + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { name: node.name }, + }); + + dialogRef.afterClosed().subscribe((result) => { + if (result === true) { + this.deleteEntity(uuid, node.type, node); } + }); + } + + private deleteEntity(uuid: string, type: string, node: TreeNode): void { + this.subscriptions.add( + this.dataService.deleteElement(uuid, type).subscribe( + () => { + this.refreshOrganizationalUnits(); + if (type === NodeType.Client) { + this.refreshClientsForNode(node); + } + this.toastr.success('Entidad eliminada exitosamente'); + }, + (error) => { + console.error('Error deleting entity:', error); + this.toastr.error('Error al eliminar la entidad', error.message); + } + ) + ); + } + + private refreshClientsForNode(node: TreeNode): void { + if (!node['@id']) { + this.selectedClients = []; + return; + } + this.fetchClientsForNode(node); + } + + onEditClick(event: MouseEvent, type: string, uuid: string): void { + event.stopPropagation(); + if (type !== NodeType.Client) { + this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' }); + } else { + this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' }); + } + } + + onRoomMap(room: TreeNode | null): void { + if (!room || !room['@id']) return; + this.subscriptions.add( + this.http.get<{ clients: Client[] }>(`${this.baseUrl}${room['@id']}`).subscribe( + (response) => { + this.dialog.open(ClassroomViewDialogComponent, { + width: '90vw', + data: { clients: response.clients }, + }); + }, + (error) => { + console.error('Error fetching room data:', error); + } + ) ); } fetchCommands(): void { this.commandsLoading = true; - this.http.get(`${this.baseUrl}`+'/commands?page=1&itemsPerPage=30').subscribe( - (response: any) => { - this.commands = response['hydra:member']; - this.commandsLoading = false; - }, - (error) => { - console.error('Error fetching commands:', error); - this.commandsLoading = false; - } + this.subscriptions.add( + this.http.get<{ 'hydra:member': Command[] }>(`${this.baseUrl}/commands?page=1&itemsPerPage=30`).subscribe( + (response) => { + this.commands = response['hydra:member']; + this.commandsLoading = false; + }, + (error) => { + console.error('Error fetching commands:', error); + this.commandsLoading = false; + } + ) ); } - executeCommand(command: any, selectedNode: any): void { - this.toastr.success('Ejecutando comando: ' + command.name + " en " + selectedNode.name); + executeCommand(command: Command, selectedNode: TreeNode | null): void { + + if (!selectedNode) { + this.toastr.error('No hay un nodo seleccionado.'); + return; + } else { + 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 { + onShowClientDetail(event: MouseEvent, client: Client): void { event.stopPropagation(); this.router.navigate(['clients', client.uuid], { state: { clientData: client } }); } - onShowDetailsClick(event: MouseEvent, data: any): void { + onShowDetailsClick(event: MouseEvent, data: TreeNode | null): void { event.stopPropagation(); - if (data.type != 'client') { + if (data && data.type !== NodeType.Client) { this.dialog.open(ShowOrganizationalUnitComponent, { data: { data }, width: '700px' }); - } - if (data.type == 'client') { - this.router.navigate(['clients', data['@id'].split('/').pop()], { state: { clientData: data } }); + } else { + if (data) { + this.router.navigate(['clients', this.extractUuid(data['@id'])], { state: { clientData: data } }); + } } } - onTreeClick(event: MouseEvent, data: any): void { + onTreeClick(event: MouseEvent, data: TreeNode): void { event.stopPropagation(); - if (data.type != 'client') { + if (data.type !== NodeType.Client) { this.dialog.open(TreeViewComponent, { data: { data }, width: '800px' }); } } openBottomSheet(): void { - this._bottomSheet.open(LegendComponent); + this.bottomSheet.open(LegendComponent); } iniciarTour(): void { this.joyrideService.startTour({ steps: ['groupsTitleStepText', 'addStep', 'keyStep', 'unitStep', 'elementsStep', 'tabsStep'], showPrevButton: true, - themeColor: '#3f51b5' + themeColor: '#3f51b5', }); } @@ -475,23 +468,25 @@ export class GroupsComponent implements OnInit { isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable; filterTree(searchTerm: string, filterType: string): void { - const filterNodes = (nodes: any[]): any[] => { - return nodes - .map(node => { - const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesType = filterType ? node.type.toLowerCase() === filterType.toLowerCase() : true; - const filteredChildren = node.children ? filterNodes(node.children) : []; - if (matchesName && matchesType || filteredChildren.length > 0) { - return { ...node, children: filteredChildren }; - } - return null; - }) - .filter(node => node !== null); + const filterNodes = (nodes: TreeNode[]): TreeNode[] => { + const filteredNodes: TreeNode[] = []; + for (const node of nodes) { + const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesType = filterType ? node.type.toLowerCase() === filterType.toLowerCase() : true; + const filteredChildren = node.children ? filterNodes(node.children) : []; + + if ((matchesName && matchesType) || filteredChildren.length > 0) { + filteredNodes.push({ ...node, children: filteredChildren }); + } + } + return filteredNodes; }; - - const filteredData = filterNodes(this.treeDataSource.data); + + const filteredData = filterNodes(this.originalTreeData); this.treeDataSource.data = filteredData; } + + onTreeFilterInput(event: Event): void { const input = event.target as HTMLInputElement; @@ -513,32 +508,44 @@ export class GroupsComponent implements OnInit { const lowerTerm = searchTerm.toLowerCase(); - this.selectedClients = this.selectedClientsOriginal.filter(client => { - const matchesName = client.name.toLowerCase().includes(lowerTerm); - const matchesIP = client.ip?.toLowerCase().includes(lowerTerm) || false; - const matchesStatus = client.status?.toLowerCase().includes(lowerTerm) || false; - const matchesMac = client.mac?.toLowerCase().includes(lowerTerm) || false; - - return matchesName || matchesIP || matchesStatus || matchesMac; + this.selectedClients = this.selectedClientsOriginal.filter((client) => { + return ( + client.name.toLowerCase().includes(lowerTerm) || + client.ip?.toLowerCase().includes(lowerTerm) || + client.status?.toLowerCase().includes(lowerTerm) || + client.mac?.toLowerCase().includes(lowerTerm) + ); }); } - getStatus(client: any): void { + public setSelectedNode(node: TreeNode): void { + this.selectedNode = node; + } + + getStatus(client: Client): void { + if (!client.uuid || !client['@id']) return; + this.syncingClientId = client.uuid; this.syncStatus = true; - this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe( - response => { - this.toastr.success('Cliente actualizado correctamente'); - this.search(); - this.syncStatus = false; - this.syncingClientId = null; - }, - error => { - this.toastr.error('Error de conexión con el cliente'); - this.syncStatus = false; - this.syncingClientId = null; - } + this.subscriptions.add( + this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe( + () => { + this.toastr.success('Cliente actualizado correctamente'); + this.search(); + this.syncStatus = false; + this.syncingClientId = null; + }, + () => { + this.toastr.error('Error de conexión con el cliente'); + this.syncStatus = false; + this.syncingClientId = null; + } + ) ); } + + private extractUuid(idPath: string | undefined): string | null { + return idPath ? idPath.split('/').pop() || null : null; + } } diff --git a/ogWebconsole/src/app/components/groups/model/model.ts b/ogWebconsole/src/app/components/groups/model/model.ts index 289a796..7789157 100644 --- a/ogWebconsole/src/app/components/groups/model/model.ts +++ b/ogWebconsole/src/app/components/groups/model/model.ts @@ -9,14 +9,14 @@ export interface Aula { } export interface UnidadOrganizativa { - clients: any[]; - children: UnidadOrganizativa[]; - '@id'?: string; id: string; - name: string; uuid: string; + name: string; type: string; - parent: UnidadOrganizativa[]; + '@id': string; + clients?: Client[]; + children?: UnidadOrganizativa[]; + parent?: UnidadOrganizativa; } export interface OrganizationalUnit { @@ -29,6 +29,9 @@ export interface OrganizationalUnit { } export interface Client { + mac: any; + status: any; + ip: any; "@id": string; "@type": string; id: number; @@ -54,3 +57,35 @@ export interface ClientCollection { "@type": string; }; } + +export interface TreeNode { + name: string; + type: string; + '@id'?: string; + children?: TreeNode[]; + hasClients?: boolean; + clients?: Client[]; + ip?: string; +} + +export interface FlatNode { + name: string; + type: string; + level: number; + expandable: boolean; + hasClients?: boolean; + ip?: string; + '@id'?: string; +} + + +export interface Command { + name: string; + description?: string; +} + +export interface Filter { + name: string; + uuid: string; +} +