refs #1373 Add search functionality for organizational units and update translations and tests
testing/ogGui-multibranch/pipeline/head This commit looks good
Details
testing/ogGui-multibranch/pipeline/head This commit looks good
Details
parent
37460cb01d
commit
3a7bff9e74
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue