refs #1373 Add search functionality for organizational units and update translations and tests
testing/ogGui-multibranch/pipeline/head This commit looks good Details

pull/12/head
Lucas Lara García 2025-01-27 14:23:28 +01:00
parent 37460cb01d
commit 3a7bff9e74
5 changed files with 119 additions and 67 deletions

View File

@ -29,21 +29,23 @@
<!-- Filters Panel -->
<div class="filters-panel" joyrideStep="filtersPanelStep" text="{{ 'filtersPanelStepText' | translate }}">
<div class="filters-container">
<mat-form-field appearance="outline">
<mat-label>{{ 'searchTree' | translate }}</mat-label>
<input matInput (input)="onTreeFilterInput($event)" placeholder="Centro, aula, grupos ..." />
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{ 'searchClient' | translate }}</mat-label>
<input matInput (input)="onClientFilterInput($event)" placeholder="Nombre, IP, estado o MAC">
</mat-form-field>
<mat-form-field appearance="outline">
</mat-form-field>
<!-- Funcionalidad actualmente deshabilitada-->
<!-- <mat-form-field appearance="outline">
<mat-select (selectionChange)="loadSelectedFilter($event.value)" placeholder="Cargar filtro" disabled>
<mat-option *ngFor="let savedFilter of savedFilterNames" [value]="savedFilter">
{{ savedFilter[0] }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{ 'searchTree' | translate }}</mat-label>
<input matInput (input)="onTreeFilterInput($event)" placeholder="Buscar nombre o tipo" disabled>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{ 'filterByType' | translate }}</mat-label>
<mat-select [(value)]="selectedTreeFilter" (selectionChange)="filterTree(searchTerm, $event.value)" disabled>
@ -52,7 +54,7 @@
<mat-option value="classroom">{{ 'classrooms' | translate }}</mat-option>
<mat-option value="group">{{ 'computerGroups' | translate }}</mat-option>
</mat-select>
</mat-form-field>
</mat-form-field> -->
</div>
</div>

View File

@ -24,6 +24,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { JoyrideModule } from 'ngx-joyride';
import { MatMenuModule } from '@angular/material/menu';
import { MatTreeModule } from '@angular/material/tree';
import { TreeNode } from './model/model';
describe('GroupsComponent', () => {
let component: GroupsComponent;
@ -80,21 +81,43 @@ describe('GroupsComponent', () => {
expect(component.search).toHaveBeenCalled();
});
it('should call getFilters on ngOnInit', () => {
spyOn(component, 'getFilters');
component.ngOnInit();
expect(component.getFilters).toHaveBeenCalled();
});
it('should call search method', () => {
spyOn(component, 'search');
component.search();
expect(component.search).toHaveBeenCalled();
});
it('should call getFilters method', () => {
spyOn(component, 'getFilters');
component.getFilters();
expect(component.getFilters).toHaveBeenCalled();
it('should clear selection', () => {
spyOn(component, 'clearSelection');
component.clearSelection();
expect(component.clearSelection).toHaveBeenCalled();
});
});
it('should toggle view', () => {
component.toggleView('card');
expect(component.currentView).toBe('card');
component.toggleView('list');
expect(component.currentView).toBe('list');
});
it('should filter tree', () => {
const searchTerm = 'test';
spyOn(component, 'filterTree');
component.filterTree(searchTerm);
expect(component.filterTree).toHaveBeenCalledWith(searchTerm);
});
it('should add multiple clients', () => {
spyOn(component, 'addMultipleClients');
const event = new MouseEvent('click');
component.addMultipleClients(event);
expect(component.addMultipleClients).toHaveBeenCalledWith(event);
});
it('should expand path to node', () => {
const node: TreeNode = { id: '1', name: 'Node 1', type: 'type', children: [] };
spyOn(component, 'expandPathToNode');
component.expandPathToNode(node);
expect(component.expandPathToNode).toHaveBeenCalledWith(node);
});
});

View File

@ -109,7 +109,6 @@ export class GroupsComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.search();
this.getFilters();
this.updateGridCols();
this.refreshData();
window.addEventListener('resize', this.updateGridCols);
@ -157,33 +156,35 @@ export class GroupsComponent implements OnInit, OnDestroy {
this.selectedNode = null;
}
getFilters(): void {
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);
}
)
);
}
// Función para obtener los filtros guardados actualmente deshabilitada
// getFilters(): void {
// 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: [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);
}
)
);
}
// Función para cargar un filtro seleccionado actu
// 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;
@ -220,7 +221,8 @@ export class GroupsComponent implements OnInit, OnDestroy {
this.dataService.getOrganizationalUnits().subscribe({
next: (data) => {
this.treeDataSource.data = data.map((unidad) => this.convertToTreeData(unidad));
this.originalTreeData = data.map((unidad) => this.convertToTreeData(unidad));
this.treeDataSource.data = [...this.originalTreeData];
if (selectedNodeIdOrUuid) {
this.selectedNode = this.findNodeByIdOrUuid(this.treeDataSource.data, selectedNodeIdOrUuid);
@ -253,7 +255,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
});
}
private expandPathToNode(node: TreeNode): void {
public expandPathToNode(node: TreeNode): void {
const path: TreeNode[] = [];
let currentNode: TreeNode | null = node;
@ -543,29 +545,54 @@ export class GroupsComponent implements OnInit, OnDestroy {
hasChild = (_: number, node: FlatNode): boolean => node.expandable;
isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable;
filterTree(searchTerm: string, filterType: string): void {
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;
filterTree(searchTerm: string): void {
const expandPaths: TreeNode[][] = [];
const filterNodes = (nodes: TreeNode[], parentPath: TreeNode[] = []): TreeNode[] => {
return nodes
.map((node) => {
const matchesName = node.name.toLowerCase().includes(searchTerm.toLowerCase());
const filteredChildren = node.children ? filterNodes(node.children, [...parentPath, node]) : [];
if (matchesName) {
expandPaths.push([...parentPath, node]);
return {
...node,
children: node.children,
} as TreeNode;
} else if (filteredChildren.length > 0) {
return {
...node,
children: filteredChildren,
} as TreeNode;
}
return null;
})
.filter((node): node is TreeNode => node !== null);
};
const filteredData = filterNodes(this.originalTreeData);
this.treeDataSource.data = filteredData;
if (!searchTerm) {
this.treeDataSource.data = [...this.originalTreeData];
this.treeControl.collapseAll();
} else {
this.treeDataSource.data = filterNodes(this.originalTreeData);
expandPaths.forEach((path) => this.expandPath(path));
}
}
private expandPath(path: TreeNode[]): void {
path.forEach((pathNode) => {
const flatNode = this.treeControl.dataNodes?.find((n) => n.id === pathNode.id);
if (flatNode) {
this.treeControl.expand(flatNode);
}
});
}
onTreeFilterInput(event: Event): void {
const input = event.target as HTMLInputElement;
const searchTerm = input?.value || '';
this.filterTree(searchTerm, this.selectedTreeFilter);
const searchTerm = input?.value.trim() || '';
this.filterTree(searchTerm);
}
onClientFilterInput(event: Event): void {

View File

@ -430,7 +430,7 @@
"addClientMenu": "Add client",
"filters": "Filters",
"searchClient": "Search client",
"searchTree": "Search in tree",
"searchTree": "Search organizational unit",
"filterByType": "Filter by type",
"all": "All",
"classroomsGroup": "Classroom groups",

View File

@ -432,7 +432,7 @@
"addClientMenu": "Añadir cliente",
"filters": "Filtros",
"searchClient": "Buscar cliente",
"searchTree": "Buscar en árbol",
"searchTree": "Buscar unidad organizativa",
"filterByType": "Filtrar por tipo",
"all": "Todos",
"classroomsGroup": "Grupos de aulas",