Refactor groups component to update filter option value
testing/ogGui-multibranch/pipeline/head This commit is unstable Details

pull/10/head
Alvaro Puente Mella 2024-12-04 15:27:43 +01:00
parent aa9b82cda5
commit aecc16c332
4 changed files with 363 additions and 320 deletions

View File

@ -123,7 +123,7 @@ import { JoyrideModule } from 'ngx-joyride';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component'; import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component';
import { MatSortModule } from '@angular/material/sort';
export function HttpLoaderFactory(http: HttpClient) { export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json'); return new TranslateHttpLoader(http, './locale/', '.json');
} }
@ -234,6 +234,7 @@ export function HttpLoaderFactory(http: HttpClient) {
MatDatepickerModule, MatDatepickerModule,
MatNativeDateModule, MatNativeDateModule,
MatSliderModule, MatSliderModule,
MatSortModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,

View File

@ -40,7 +40,7 @@
<mat-label>Filtrar por tipo</mat-label> <mat-label>Filtrar por tipo</mat-label>
<mat-select [(value)]="selectedTreeFilter" (selectionChange)="filterTree(searchTerm, $event.value)"> <mat-select [(value)]="selectedTreeFilter" (selectionChange)="filterTree(searchTerm, $event.value)">
<mat-option [value]="">Todos</mat-option> <mat-option [value]="">Todos</mat-option>
<mat-option value="organizational-unit">Grupos de aulas</mat-option> <mat-option value="classrooms-group">Grupos de aulas</mat-option>
<mat-option value="classroom">Aulas</mat-option> <mat-option value="classroom">Aulas</mat-option>
<mat-option value="group">Grupos de ordenadores</mat-option> <mat-option value="group">Grupos de ordenadores</mat-option>
</mat-select> </mat-select>

View File

@ -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 { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
@ -8,8 +8,10 @@ import { ToastrService } from 'ngx-toastr';
import { JoyrideService } from 'ngx-joyride'; import { JoyrideService } from 'ngx-joyride';
import { FlatTreeControl } from '@angular/cdk/tree'; import { FlatTreeControl } from '@angular/cdk/tree';
import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree';
import { Subscription } from 'rxjs';
import { DataService } from './services/data.service'; 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 { CreateOrganizationalUnitComponent } from './shared/organizational-units/create-organizational-unit/create-organizational-unit.component';
import { CreateClientComponent } from './shared/clients/create-client/create-client.component'; import { CreateClientComponent } from './shared/clients/create-client/create-client.component';
import { EditOrganizationalUnitComponent } from './shared/organizational-units/edit-organizational-unit/edit-organizational-unit.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 { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/delete-modal.component';
import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal'; import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal';
interface TreeNode { enum NodeType {
clients?: any[]; OrganizationalUnit = 'organizational-unit',
name: string; ClassroomsGroup = 'classrooms-group',
type: string; Classroom = 'classroom',
children?: TreeNode[]; ClientsGroup = 'clients-group',
ip?: string; Client = 'client',
'@id'?: string;
hasClients?: boolean;
status?: string;
}
interface FlatNode {
name: string;
type: string;
level: number;
expandable: boolean;
ip?: string;
hasClients?: boolean;
} }
@Component({ @Component({
selector: 'app-groups', selector: 'app-groups',
templateUrl: './groups.component.html', 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; baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
organizationalUnits: UnidadOrganizativa[] = []; organizationalUnits: UnidadOrganizativa[] = [];
selectedUnidad: UnidadOrganizativa | null = null; selectedUnidad: UnidadOrganizativa | null = null;
selectedDetail: any | null = null; selectedDetail: UnidadOrganizativa | null = null;
loading: boolean = false; loading = false;
loadingChildren: boolean = false; searchTerm = '';
searchTerm: string = '';
treeControl: FlatTreeControl<FlatNode>; treeControl: FlatTreeControl<FlatNode>;
treeFlattener: MatTreeFlattener<TreeNode, FlatNode>; treeFlattener: MatTreeFlattener<TreeNode, FlatNode>;
treeDataSource: MatTreeFlatDataSource<TreeNode, FlatNode>; treeDataSource: MatTreeFlatDataSource<TreeNode, FlatNode>;
selectedNode: TreeNode | null = null; selectedNode: TreeNode | null = null;
commands: any[] = []; commands: Command[] = [];
commandsLoading: boolean = false; commandsLoading = false;
selectedClients: any[] = []; selectedClients: Client[] = [];
cols: number = 4; cols = 4;
selectedClientsOriginal: any[] = []; selectedClientsOriginal: Client[] = [];
currentView: 'card' | 'list' = 'list'; currentView: 'card' | 'list' = 'list';
isTreeViewActive: boolean = false; isTreeViewActive = false;
savedFilterNames: any[] = []; savedFilterNames: [string, string][] = [];
selectedTreeFilter: string = ''; selectedTreeFilter = '';
syncStatus = false;
syncStatus: boolean = false; syncingClientId: string | null = null;
syncingClientId: number | null = null; private originalTreeData: TreeNode[] = [];
@ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent; @ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent;
@ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent; @ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent;
private subscriptions: Subscription = new Subscription();
constructor( constructor(
private http: HttpClient, private http: HttpClient,
private router: Router, private router: Router,
private dataService: DataService, private dataService: DataService,
public dialog: MatDialog, public dialog: MatDialog,
private _bottomSheet: MatBottomSheet, private bottomSheet: MatBottomSheet,
private joyrideService: JoyrideService, private joyrideService: JoyrideService,
private toastr: ToastrService private toastr: ToastrService
) { ) {
this.treeFlattener = new MatTreeFlattener<TreeNode, FlatNode>( this.treeFlattener = new MatTreeFlattener<TreeNode, FlatNode>(
(node: TreeNode, level: number) => ({ this.transformer,
name: node.name, (node) => node.level,
type: node.type, (node) => node.expandable,
level, (node) => node.children
expandable: !!node.children?.length,
hasClients: node.hasClients,
ip: node.ip,
['@id']: node['@id']
}),
node => node.level,
node => node.expandable,
node => node.children
); );
this.treeControl = new FlatTreeControl<FlatNode>( this.treeControl = new FlatTreeControl<FlatNode>(
node => node.level, (node) => node.level,
node => node.expandable (node) => node.expandable
); );
this.treeDataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); this.treeDataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
@ -111,17 +94,32 @@ export class GroupsComponent implements OnInit {
this.search(); this.search();
this.getFilters(); this.getFilters();
this.updateGridCols(); 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; this.currentView = view;
} }
updateGridCols(): void { updateGridCols = (): void => {
const width = window.innerWidth; const width = window.innerWidth;
this.cols = width <= 600 ? 1 : width <= 960 ? 2 : width <= 1280 ? 3 : 4; this.cols = width <= 600 ? 1 : width <= 960 ? 2 : width <= 1280 ? 3 : 4;
} };
clearSelection(): void { clearSelection(): void {
this.selectedUnidad = null; this.selectedUnidad = null;
@ -139,112 +137,122 @@ export class GroupsComponent implements OnInit {
} }
getFilters(): void { getFilters(): void {
this.subscriptions.add(
this.dataService.getFilters().subscribe( this.dataService.getFilters().subscribe(
data => { (data) => {
this.savedFilterNames = data.map((filter: any) => [filter.name, filter.uuid]); this.savedFilterNames = data.map((filter: { name: string; uuid: string; }) => [filter.name, filter.uuid]);
}, },
error => { (error) => {
console.error('Error fetching filters:', error); console.error('Error fetching filters:', error);
} }
)
); );
} }
loadSelectedFilter(savedFilter: any) { loadSelectedFilter(savedFilter: [string, string]): void {
const url = `${this.baseUrl}/views/` + savedFilter[1]; this.subscriptions.add(
this.dataService.getFilter(savedFilter[1]).subscribe(response => { this.dataService.getFilter(savedFilter[1]).subscribe(
(response) => {
if (response) { if (response) {
console.log('Filter1:', response.filters); console.log('Filter:', response.filters);
} }
}, error => { },
(error) => {
console.error('Error:', error); console.error('Error:', error);
}); }
)
);
} }
search(): void { search(): void {
this.loading = true; this.loading = true;
this.subscriptions.add(
this.dataService.getOrganizationalUnits(this.searchTerm).subscribe( this.dataService.getOrganizationalUnits(this.searchTerm).subscribe(
data => { (data) => {
this.organizationalUnits = data; this.organizationalUnits = data;
this.loading = false; this.loading = false;
}, },
error => { (error) => {
console.error('Error fetching unidades organizativas', error); console.error('Error fetching organizational units', error);
this.loading = false; this.loading = false;
} }
)
); );
} }
onSelectUnidad(unidad: any): void { onSelectUnidad(unidad: UnidadOrganizativa): void {
this.selectedUnidad = unidad; this.selectedUnidad = unidad;
this.selectedDetail = unidad; this.selectedDetail = unidad;
this.selectedClients = this.collectAllClients(unidad); this.selectedClients = this.collectAllClients(unidad);
this.selectedClientsOriginal = [...this.selectedClients]; this.selectedClientsOriginal = [...this.selectedClients];
this.loadChildrenAndClients(unidad.id).then(fullData => { this.loadChildrenAndClients(unidad.id).then((fullData) => {
const treeData = this.convertToTreeData(fullData); const treeData = this.convertToTreeData(fullData);
this.treeDataSource.data = treeData[0]?.children || []; this.treeDataSource.data = treeData[0]?.children || [];
}); });
this.isTreeViewActive = true; this.isTreeViewActive = true;
} }
private collectAllClients(node: any): any[] { private collectAllClients(node: UnidadOrganizativa): Client[] {
let clients = node.clients || []; let clients = node.clients || [];
if (node.children && node.children.length > 0) { if (node.children) {
node.children.forEach((child: any) => { node.children.forEach((child) => {
clients = clients.concat(this.collectAllClients(child)); clients = clients.concat(this.collectAllClients(child));
}); });
} }
return clients; return clients;
} }
async loadChildrenAndClients(id: string): Promise<any> { private async loadChildrenAndClients(id: string): Promise<UnidadOrganizativa> {
try { try {
const childrenData = await this.dataService.getChildren(id).toPromise(); const childrenData = await this.dataService.getChildren(id).toPromise();
const processHierarchy = (nodes: UnidadOrganizativa[]): TreeNode[] => {
return nodes.map(node => ({ const processHierarchy = (nodes: UnidadOrganizativa[]): UnidadOrganizativa[] => {
name: node.name, return nodes.map((node) => ({
type: node.type, ...node,
'@id': node['@id'],
children: node.children ? processHierarchy(node.children) : [], children: node.children ? processHierarchy(node.children) : [],
clients: node.clients || []
})); }));
}; };
return { return {
...this.selectedUnidad, ...this.selectedUnidad!,
children: childrenData ? processHierarchy(childrenData) : [] children: childrenData ? processHierarchy(childrenData) : [],
}; };
} catch (error) { } catch (error) {
console.error('Error loading children:', error); console.error('Error loading children:', error);
return this.selectedUnidad; return this.selectedUnidad!;
} }
} }
convertToTreeData(data: any): TreeNode[] {
private convertToTreeData(data: UnidadOrganizativa): TreeNode[] {
const processNode = (node: UnidadOrganizativa): TreeNode => ({ const processNode = (node: UnidadOrganizativa): TreeNode => ({
name: node.name, name: node.name,
type: node.type, type: node.type,
'@id': node['@id'], '@id': node['@id'],
children: node.children?.map(processNode) || [], children: node.children?.map(processNode) || [],
hasClients: node.clients && node.clients.length > 0, hasClients: (node.clients?.length ?? 0) > 0,
}); });
return [processNode(data)]; return [processNode(data)];
} }
onNodeClick(node: TreeNode): void { onNodeClick(node: TreeNode): void {
this.selectedNode = node; this.selectedNode = node;
this.selectedClients = node.clients || []; this.fetchClientsForNode(node);
this.selectedClientsOriginal = [...this.selectedClients]; }
if (node.hasClients) {
const url = `${this.baseUrl}${node['@id']}`; private fetchClientsForNode(node: TreeNode): void {
this.http.get(url).subscribe( if (node.hasClients && node['@id']) {
(data: any) => { this.subscriptions.add(
this.http.get<{ clients: Client[] }>(`${this.baseUrl}${node['@id']}`).subscribe(
(data) => {
this.selectedClientsOriginal = [...data.clients]; this.selectedClientsOriginal = [...data.clients];
this.selectedClients = data.clients || []; this.selectedClients = data.clients || [];
}, },
(error) => { (error) => {
console.error('Error fetching clients:', error); console.error('Error fetching clients:', error);
} }
)
); );
} else { } else {
this.selectedClients = []; this.selectedClients = [];
@ -252,173 +260,154 @@ export class GroupsComponent implements OnInit {
} }
} }
getNodeIcon(node: any): string { getNodeIcon(node: TreeNode): string {
switch (node.type) { switch (node.type) {
case 'organizational-unit': return 'apartment'; case NodeType.OrganizationalUnit:
case 'classrooms-group': return 'doors'; return 'apartment';
case 'classroom': return 'school'; case NodeType.ClassroomsGroup:
case 'clients-group': return 'lan'; return 'doors';
case 'client': return 'computer'; case NodeType.Classroom:
default: return 'group'; 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(); event.stopPropagation();
const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, { data: { parent }, width: '900px' }); const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, {
dialogRef.afterClosed().subscribe(() => { data: { parent },
this.dataService.getOrganizationalUnits().subscribe( width: '900px',
data => {
this.organizationalUnits = data;
this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => {
const treeData = this.convertToTreeData(updatedData);
this.treeDataSource.data = treeData[0]?.children || [];
}); });
}, dialogRef.afterClosed().subscribe(() => {
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(); event.stopPropagation();
const dialogRef = this.dialog.open(CreateClientComponent, { data: { organizationalUnit }, width: '900px' }); const dialogRef = this.dialog.open(CreateClientComponent, {
dialogRef.afterClosed().subscribe(() => { data: { organizationalUnit },
this.dataService.getOrganizationalUnits().subscribe( width: '900px',
data => { });
this.organizationalUnits = data; dialogRef.afterClosed().subscribe(() => {
this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => { this.refreshOrganizationalUnits();
const treeData = this.convertToTreeData(updatedData); if (organizationalUnit && organizationalUnit['@id']) {
this.treeDataSource.data = treeData[0]?.children || []; this.refreshClientsForNode(organizationalUnit);
}
});
}
private refreshOrganizationalUnits(): void {
this.subscriptions.add(
this.dataService.getOrganizationalUnits().subscribe(
(data) => {
this.organizationalUnits = data;
if (this.selectedUnidad) {
this.loadChildrenAndClients(this.selectedUnidad?.id || '').then((updatedData) => {
this.selectedUnidad = updatedData;
const treeData = this.convertToTreeData(updatedData);
this.originalTreeData = treeData[0]?.children || [];
this.treeDataSource.data = [...this.originalTreeData];
}); });
if (organizationalUnit && organizationalUnit.id) {
this.loadChildrenAndClients(organizationalUnit.id);
this.refreshClients(organizationalUnit);
} }
}, },
error => console.error('Error fetching unidades organizativas', error) (error) => console.error('Error fetching organizational units', error)
)
); );
});
}
setSelectedNode(node: TreeNode): void {
this.selectedNode = node;
} }
onEditNode(event: MouseEvent, node: TreeNode | null): void { onEditNode(event: MouseEvent, node: TreeNode | null): void {
if (!node) return;
const uuid = node['@id'] ? node['@id'].split('/').pop() : '';
const type = node.type;
event.stopPropagation(); event.stopPropagation();
const uuid = node ? this.extractUuid(node['@id']) : null;
if (!uuid) return;
if (type !== 'client') { if (node && node.type !== NodeType.Client) {
this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' }); this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' });
} else { } else {
this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' }); 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{ 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(); event.stopPropagation();
const uuid = node ? this.extractUuid(node['@id']) : null;
if (!uuid) return;
if (!node) return;
const dialogRef = this.dialog.open(DeleteModalComponent, { const dialogRef = this.dialog.open(DeleteModalComponent, {
width: '400px', width: '400px',
data: { name } data: { name: node.name },
}); });
dialogRef.afterClosed().subscribe(result => { dialogRef.afterClosed().subscribe((result) => {
if (result === true) { if (result === true) {
this.dataService.deleteElement(uuid, type).subscribe( this.deleteEntity(uuid, node.type, node);
() => { }
this.loadChildrenAndClients(this.selectedUnidad?.id || '').then(updatedData => {
const treeData = this.convertToTreeData(updatedData);
this.treeDataSource.data = treeData[0]?.children || [];
}); });
if (type === 'client' && clientNode) {
this.refreshClients(clientNode);
} }
this.dataService.getOrganizationalUnits().subscribe( private deleteEntity(uuid: string, type: string, node: TreeNode): void {
data => { this.subscriptions.add(
this.organizationalUnits = data; this.dataService.deleteElement(uuid, type).subscribe(
}, () => {
error => console.error('Error fetching unidades organizativas', error) this.refreshOrganizationalUnits();
); if (type === NodeType.Client) {
this.refreshClientsForNode(node);
}
this.toastr.success('Entidad eliminada exitosamente'); this.toastr.success('Entidad eliminada exitosamente');
}, },
error => { (error) => {
console.error('Error deleting entity:', error); console.error('Error deleting entity:', error);
this.toastr.error('Error al eliminar la entidad', error.message); this.toastr.error('Error al eliminar la entidad', error.message);
} }
)
); );
} }
});
}
private refreshClients(node: TreeNode): void { private refreshClientsForNode(node: TreeNode): void {
if (!node || !node['@id']) { if (!node['@id']) {
this.selectedClients = []; this.selectedClients = [];
return; return;
} }
this.fetchClientsForNode(node);
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);
}
);
} }
onEditClick(event: MouseEvent, type: any, uuid: string): void { onEditClick(event: MouseEvent, type: string, uuid: string): void {
event.stopPropagation(); event.stopPropagation();
if (type != 'client') { if (type !== NodeType.Client) {
this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' }); this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '900px' });
} else { } else {
this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' }); this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' });
} }
} }
onRoomMap(room: any): void { onRoomMap(room: TreeNode | null): void {
this.http.get(`${this.baseUrl}`+ room['@id']).subscribe( if (!room || !room['@id']) return;
(response: any) => { this.subscriptions.add(
this.http.get<{ clients: Client[] }>(`${this.baseUrl}${room['@id']}`).subscribe(
(response) => {
this.dialog.open(ClassroomViewDialogComponent, { this.dialog.open(ClassroomViewDialogComponent, {
width: '90vw', width: '90vw',
data: { clients: response.clients } data: { clients: response.clients },
}); });
}, },
(error: any) => { (error) => {
console.error('Error en la solicitud HTTP:', error); console.error('Error fetching room data:', error);
} }
)
); );
} }
fetchCommands(): void { fetchCommands(): void {
this.commandsLoading = true; this.commandsLoading = true;
this.http.get(`${this.baseUrl}`+'/commands?page=1&itemsPerPage=30').subscribe( this.subscriptions.add(
(response: any) => { this.http.get<{ 'hydra:member': Command[] }>(`${this.baseUrl}/commands?page=1&itemsPerPage=30`).subscribe(
(response) => {
this.commands = response['hydra:member']; this.commands = response['hydra:member'];
this.commandsLoading = false; this.commandsLoading = false;
}, },
@ -426,48 +415,52 @@ export class GroupsComponent implements OnInit {
console.error('Error fetching commands:', error); console.error('Error fetching commands:', error);
this.commandsLoading = false; this.commandsLoading = false;
} }
)
); );
} }
executeCommand(command: any, selectedNode: any): void { executeCommand(command: Command, selectedNode: TreeNode | null): void {
this.toastr.success('Ejecutando comando: ' + command.name + " en " + selectedNode.name);
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 { onShowClientDetail(event: MouseEvent, client: Client): void {
console.log('Client actions:', client);
}
onShowClientDetail(event: MouseEvent, client: any): void {
event.stopPropagation(); event.stopPropagation();
this.router.navigate(['clients', client.uuid], { state: { clientData: client } }); this.router.navigate(['clients', client.uuid], { state: { clientData: client } });
} }
onShowDetailsClick(event: MouseEvent, data: any): void { onShowDetailsClick(event: MouseEvent, data: TreeNode | null): void {
event.stopPropagation(); event.stopPropagation();
if (data.type != 'client') { if (data && data.type !== NodeType.Client) {
this.dialog.open(ShowOrganizationalUnitComponent, { data: { data }, width: '700px' }); this.dialog.open(ShowOrganizationalUnitComponent, { data: { data }, width: '700px' });
} else {
if (data) {
this.router.navigate(['clients', this.extractUuid(data['@id'])], { state: { clientData: data } });
} }
if (data.type == 'client') {
this.router.navigate(['clients', data['@id'].split('/').pop()], { state: { clientData: data } });
} }
} }
onTreeClick(event: MouseEvent, data: any): void { onTreeClick(event: MouseEvent, data: TreeNode): void {
event.stopPropagation(); event.stopPropagation();
if (data.type != 'client') { if (data.type !== NodeType.Client) {
this.dialog.open(TreeViewComponent, { data: { data }, width: '800px' }); this.dialog.open(TreeViewComponent, { data: { data }, width: '800px' });
} }
} }
openBottomSheet(): void { openBottomSheet(): void {
this._bottomSheet.open(LegendComponent); this.bottomSheet.open(LegendComponent);
} }
iniciarTour(): void { iniciarTour(): void {
this.joyrideService.startTour({ this.joyrideService.startTour({
steps: ['groupsTitleStepText', 'addStep', 'keyStep', 'unitStep', 'elementsStep', 'tabsStep'], steps: ['groupsTitleStepText', 'addStep', 'keyStep', 'unitStep', 'elementsStep', 'tabsStep'],
showPrevButton: true, showPrevButton: true,
themeColor: '#3f51b5' themeColor: '#3f51b5',
}); });
} }
@ -475,24 +468,26 @@ export class GroupsComponent implements OnInit {
isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable; isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable;
filterTree(searchTerm: string, filterType: string): void { filterTree(searchTerm: string, filterType: string): void {
const filterNodes = (nodes: any[]): any[] => { const filterNodes = (nodes: TreeNode[]): TreeNode[] => {
return nodes const filteredNodes: TreeNode[] = [];
.map(node => { for (const node of nodes) {
const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase());
const matchesType = filterType ? node.type.toLowerCase() === filterType.toLowerCase() : true; const matchesType = filterType ? node.type.toLowerCase() === filterType.toLowerCase() : true;
const filteredChildren = node.children ? filterNodes(node.children) : []; const filteredChildren = node.children ? filterNodes(node.children) : [];
if (matchesName && matchesType || filteredChildren.length > 0) {
return { ...node, children: filteredChildren }; if ((matchesName && matchesType) || filteredChildren.length > 0) {
filteredNodes.push({ ...node, children: filteredChildren });
} }
return null; }
}) return filteredNodes;
.filter(node => node !== null);
}; };
const filteredData = filterNodes(this.treeDataSource.data); const filteredData = filterNodes(this.originalTreeData);
this.treeDataSource.data = filteredData; this.treeDataSource.data = filteredData;
} }
onTreeFilterInput(event: Event): void { onTreeFilterInput(event: Event): void {
const input = event.target as HTMLInputElement; const input = event.target as HTMLInputElement;
const searchTerm = input?.value || ''; const searchTerm = input?.value || '';
@ -513,32 +508,44 @@ export class GroupsComponent implements OnInit {
const lowerTerm = searchTerm.toLowerCase(); const lowerTerm = searchTerm.toLowerCase();
this.selectedClients = this.selectedClientsOriginal.filter(client => { this.selectedClients = this.selectedClientsOriginal.filter((client) => {
const matchesName = client.name.toLowerCase().includes(lowerTerm); return (
const matchesIP = client.ip?.toLowerCase().includes(lowerTerm) || false; client.name.toLowerCase().includes(lowerTerm) ||
const matchesStatus = client.status?.toLowerCase().includes(lowerTerm) || false; client.ip?.toLowerCase().includes(lowerTerm) ||
const matchesMac = client.mac?.toLowerCase().includes(lowerTerm) || false; client.status?.toLowerCase().includes(lowerTerm) ||
client.mac?.toLowerCase().includes(lowerTerm)
return matchesName || matchesIP || matchesStatus || matchesMac; );
}); });
} }
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.syncingClientId = client.uuid;
this.syncStatus = true; this.syncStatus = true;
this.subscriptions.add(
this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe( this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe(
response => { () => {
this.toastr.success('Cliente actualizado correctamente'); this.toastr.success('Cliente actualizado correctamente');
this.search(); this.search();
this.syncStatus = false; this.syncStatus = false;
this.syncingClientId = null; this.syncingClientId = null;
}, },
error => { () => {
this.toastr.error('Error de conexión con el cliente'); this.toastr.error('Error de conexión con el cliente');
this.syncStatus = false; this.syncStatus = false;
this.syncingClientId = null; this.syncingClientId = null;
} }
)
); );
} }
private extractUuid(idPath: string | undefined): string | null {
return idPath ? idPath.split('/').pop() || null : null;
}
} }

View File

@ -9,14 +9,14 @@ export interface Aula {
} }
export interface UnidadOrganizativa { export interface UnidadOrganizativa {
clients: any[];
children: UnidadOrganizativa[];
'@id'?: string;
id: string; id: string;
name: string;
uuid: string; uuid: string;
name: string;
type: string; type: string;
parent: UnidadOrganizativa[]; '@id': string;
clients?: Client[];
children?: UnidadOrganizativa[];
parent?: UnidadOrganizativa;
} }
export interface OrganizationalUnit { export interface OrganizationalUnit {
@ -29,6 +29,9 @@ export interface OrganizationalUnit {
} }
export interface Client { export interface Client {
mac: any;
status: any;
ip: any;
"@id": string; "@id": string;
"@type": string; "@type": string;
id: number; id: number;
@ -54,3 +57,35 @@ export interface ClientCollection {
"@type": string; "@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;
}