Refactor groups component
							parent
							
								
									a6797f8f77
								
							
						
					
					
						commit
						21078a8ab0
					
				| 
						 | 
				
			
			@ -593,3 +593,16 @@ button[mat-raised-button] {
 | 
			
		|||
  gap: 1rem;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filters-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap; /* Permite que los elementos pasen a la siguiente línea si no caben */
 | 
			
		||||
  gap: 16px; /* Espaciado entre los elementos */
 | 
			
		||||
  margin: 16px 0; /* Separación con otros elementos */
 | 
			
		||||
  padding: 0 16px; /* Opcional, para añadir un poco de espacio interno */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filters-container mat-form-field {
 | 
			
		||||
  flex: 1 1 300px; /* Toma todo el ancho disponible hasta 300px por elemento */
 | 
			
		||||
  max-width: 300px; /* Limita el ancho máximo */
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<mat-tab-group (selectedTabChange)="onTabChange($event)">
 | 
			
		||||
  <mat-tab label="{{ 'generalTabLabel' | translate }}">
 | 
			
		||||
<!-- <mat-tab-group (selectedTabChange)="onTabChange($event)">
 | 
			
		||||
  <mat-tab label="{{ 'generalTabLabel' | translate }}"> -->
 | 
			
		||||
 | 
			
		||||
    <!-- HEADER  -->
 | 
			
		||||
    <div class="header-container" joyrideStep="tabsStep" text="{{ 'tabsStepText' | translate }}">
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +22,52 @@
 | 
			
		|||
          {{ 'legendButton' | translate }}
 | 
			
		||||
        </button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
    </div>
 | 
			
		||||
       
 | 
			
		||||
      <!--  -->
 | 
			
		||||
    </div>    
 | 
			
		||||
    <mat-expansion-panel *ngIf="isTreeViewActive" class="filters-panel">
 | 
			
		||||
      <mat-expansion-panel-header>
 | 
			
		||||
        <mat-panel-title>
 | 
			
		||||
          Filtros
 | 
			
		||||
        </mat-panel-title>
 | 
			
		||||
      </mat-expansion-panel-header>
 | 
			
		||||
    
 | 
			
		||||
      <!-- Contenedor de filtros -->
 | 
			
		||||
      <div class="filters-container">
 | 
			
		||||
        <mat-form-field appearance="outline">
 | 
			
		||||
        <mat-select (selectionChange)="loadSelectedFilter($event.value)" placeholder="Cargar filtro">
 | 
			
		||||
          <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>Buscar en el árbol</mat-label>
 | 
			
		||||
          <input matInput (input)="onTreeFilterInput($event)" placeholder="Buscar nombre o tipo">
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
    
 | 
			
		||||
        <mat-form-field appearance="outline">
 | 
			
		||||
          <mat-label>Filtrar por tipo</mat-label>
 | 
			
		||||
          <mat-select [(value)]="selectedTreeFilter" (selectionChange)="filterTree(searchTerm, $event.value)">
 | 
			
		||||
            <mat-option [value]="">Todos</mat-option>
 | 
			
		||||
            <mat-option value="organizational-unit">Unidades Organizativas</mat-option>
 | 
			
		||||
            <mat-option value="classroom">Aulas</mat-option>
 | 
			
		||||
            <mat-option value="client">Clientes</mat-option>
 | 
			
		||||
            <mat-option value="group">Grupos</mat-option>
 | 
			
		||||
          </mat-select>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
    
 | 
			
		||||
        <mat-form-field appearance="outline">
 | 
			
		||||
          <mat-label>Buscar cliente</mat-label>
 | 
			
		||||
          <input matInput (input)="onClientFilterInput($event)" placeholder="Buscar nombre, IP, estado o MAC">
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
      </div>
 | 
			
		||||
    </mat-expansion-panel>
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <!-- LAS CARDS DE LAS FACULTADES -->
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,10 +88,7 @@
 | 
			
		|||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
          <mat-menu #menu="matMenu">
 | 
			
		||||
            <button mat-menu-item (click)="onTreeClick($event, unidad)">
 | 
			
		||||
              <mat-icon matTooltip="{{ 'viewTreeTooltip' | translate }}" matTooltipHideDelay="0">account_tree</mat-icon>
 | 
			
		||||
              <span>{{ 'viewTreeMenu' | translate }}</span>
 | 
			
		||||
            </button>
 | 
			
		||||
            
 | 
			
		||||
            <button mat-menu-item (click)="onEditClick($event, unidad.type, unidad.uuid)">
 | 
			
		||||
              <mat-icon matTooltip="{{ 'editUnitTooltip' | translate }}" matTooltipHideDelay="0">edit</mat-icon>
 | 
			
		||||
              <span>{{ 'editUnitMenu' | translate }}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +106,10 @@
 | 
			
		|||
              <mat-icon matTooltip="{{ 'addClientTooltip' | translate }}" matTooltipHideDelay="0">devices</mat-icon>
 | 
			
		||||
              <span>{{ 'addClientMenu' | translate }}</span>
 | 
			
		||||
            </button>
 | 
			
		||||
 | 
			
		||||
            <button mat-menu-item (click)="onTreeClick($event, unidad)">
 | 
			
		||||
              <mat-icon matTooltip="{{ 'viewTreeTooltip' | translate }}" matTooltipHideDelay="0">account_tree</mat-icon>
 | 
			
		||||
              <span>{{ 'viewTreeMenu' | translate }}</span>
 | 
			
		||||
            </button>
 | 
			
		||||
            <button mat-menu-item (click)="onDeleteClick($event, unidad)">
 | 
			
		||||
              <mat-icon>delete</mat-icon>
 | 
			
		||||
              <span>Delete</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +130,7 @@
 | 
			
		|||
        </button>
 | 
			
		||||
 | 
			
		||||
        <!-- Botones de alternancia -->
 | 
			
		||||
        <div class="view-toggle-container">
 | 
			
		||||
        <div class="view-toggle-container" *ngIf="selectedDetail">
 | 
			
		||||
          <button mat-button color="primary" (click)="toggleView('card')" [disabled]="currentView === 'card'">
 | 
			
		||||
            <mat-icon>grid_view</mat-icon> {{ 'Vista Tarjeta' | translate }}
 | 
			
		||||
          </button>
 | 
			
		||||
| 
						 | 
				
			
			@ -112,12 +156,11 @@
 | 
			
		|||
              </button>
 | 
			
		||||
              <mat-icon class="node-icon {{ node.type }}">
 | 
			
		||||
                {{
 | 
			
		||||
                node.type === 'organizational-unit'
 | 
			
		||||
                ? 'apartment'
 | 
			
		||||
                : node.type === 'classroom'
 | 
			
		||||
                ? 'school'
 | 
			
		||||
                : node.type === 'client'
 | 
			
		||||
                ? 'computer'
 | 
			
		||||
                node.type === 'organizational-unit' ? 'apartment'
 | 
			
		||||
                : node.type === 'classrooms-group' ? 'meeting_room'
 | 
			
		||||
                : node.type === 'classroom' ? 'school'
 | 
			
		||||
                : node.type === 'clients-group' ? 'lan'
 | 
			
		||||
                : node.type === 'client' ? 'computer'
 | 
			
		||||
                : 'group'
 | 
			
		||||
                }}
 | 
			
		||||
              </mat-icon>
 | 
			
		||||
| 
						 | 
				
			
			@ -139,15 +182,15 @@
 | 
			
		|||
            'pc-macos': node.type === 'client' && node.status === 'macos',
 | 
			
		||||
            'pc-off': node.type === 'client' && node.status === 'off'
 | 
			
		||||
          }">
 | 
			
		||||
                {{
 | 
			
		||||
                node.type === 'organizational-unit'
 | 
			
		||||
                ? 'apartment'
 | 
			
		||||
                : node.type === 'classroom'
 | 
			
		||||
                ? 'school'
 | 
			
		||||
                : node.type === 'client'
 | 
			
		||||
                ? 'computer'
 | 
			
		||||
                : 'group'
 | 
			
		||||
                }}
 | 
			
		||||
          {{
 | 
			
		||||
            node.type === 'organizational-unit' ? 'apartment'
 | 
			
		||||
            : node.type === 'classrooms-group' ? 'meeting_room'
 | 
			
		||||
            : node.type === 'classroom' ? 'school'
 | 
			
		||||
            : node.type === 'clients-group' ? 'lan'
 | 
			
		||||
            : node.type === 'client' ? 'computer'
 | 
			
		||||
            : 'group'
 | 
			
		||||
            }}
 | 
			
		||||
 | 
			
		||||
              </mat-icon>
 | 
			
		||||
              <span>{{ node.name }}</span>
 | 
			
		||||
              <ng-container *ngIf="node.type === 'client'">
 | 
			
		||||
| 
						 | 
				
			
			@ -331,7 +374,7 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
    <!-- FIN DEL TAB -->
 | 
			
		||||
  </mat-tab>
 | 
			
		||||
<!--   </mat-tab>
 | 
			
		||||
 | 
			
		||||
  <mat-tab label="{{ 'advancedSearchTabLabel' | translate }}">
 | 
			
		||||
    <app-advanced-search></app-advanced-search>
 | 
			
		||||
| 
						 | 
				
			
			@ -344,4 +387,4 @@
 | 
			
		|||
  <mat-tab label="{{ 'organizationalUnitsTabLabel' | translate }}">
 | 
			
		||||
    <app-organizational-unit-tab-view #organizationalUnitTab></app-organizational-unit-tab-view>
 | 
			
		||||
  </mat-tab>
 | 
			
		||||
</mat-tab-group>
 | 
			
		||||
</mat-tab-group> -->
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +64,10 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
  commandsLoading: boolean = false;
 | 
			
		||||
  selectedClients: any[] = [];
 | 
			
		||||
  cols: number = 4;
 | 
			
		||||
  selectedClientsOriginal: any[] = [];
 | 
			
		||||
  currentView: 'card' | 'list' = 'list';
 | 
			
		||||
  
 | 
			
		||||
  isTreeViewActive: boolean = false;
 | 
			
		||||
  savedFilterNames: any[] = [];
 | 
			
		||||
 | 
			
		||||
  @ViewChild('clientTab') clientTabComponent!: ClientTabViewComponent;
 | 
			
		||||
  @ViewChild('organizationalUnitTab') organizationalUnitTabComponent!: OrganizationalUnitTabViewComponent;
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +123,7 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
    this.selectedUnidad = null;
 | 
			
		||||
    this.selectedDetail = null;
 | 
			
		||||
    this.selectedClients = [];
 | 
			
		||||
    this.isTreeViewActive = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onTabChange(event: MatTabChangeEvent): void {
 | 
			
		||||
| 
						 | 
				
			
			@ -133,11 +136,30 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
 | 
			
		||||
  getFilters(): void {
 | 
			
		||||
    this.dataService.getFilters().subscribe(
 | 
			
		||||
      data => {},
 | 
			
		||||
      error => console.error('Error fetching filters:', error)
 | 
			
		||||
      data => {
 | 
			
		||||
        this.savedFilterNames = data.map((filter: any) => [filter.name, filter.uuid]);
 | 
			
		||||
      },
 | 
			
		||||
      error => {
 | 
			
		||||
        console.error('Error fetching filters:', error);
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadSelectedFilter(savedFilter: any) {
 | 
			
		||||
    const url = `${this.baseUrl}/views/` + savedFilter[1];
 | 
			
		||||
    console.log('llamando a:', url);
 | 
			
		||||
 | 
			
		||||
    this.dataService.getFilter(savedFilter[1]).subscribe(response => {
 | 
			
		||||
      console.log('Response from server:', response.filters);
 | 
			
		||||
      if (response) {
 | 
			
		||||
        console.log('Filter1:', response.filters);
 | 
			
		||||
      }
 | 
			
		||||
    }, error => {
 | 
			
		||||
      console.error('Error:', error);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  search(): void {
 | 
			
		||||
    this.loading = true;
 | 
			
		||||
    this.dataService.getOrganizationalUnits(this.searchTerm).subscribe(
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +182,7 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
      const treeData = this.convertToTreeData(fullData);
 | 
			
		||||
      this.treeDataSource.data = treeData[0]?.children || [];
 | 
			
		||||
    });
 | 
			
		||||
    this.isTreeViewActive = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async loadChildrenAndClients(id: string): Promise<any> {
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +227,7 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
      const url = `${this.baseUrl}${node['@id']}`;
 | 
			
		||||
      this.http.get(url).subscribe(
 | 
			
		||||
        (data: any) => {
 | 
			
		||||
          this.selectedClientsOriginal = [...data.clients];
 | 
			
		||||
          this.selectedClients = data.clients || [];
 | 
			
		||||
        },
 | 
			
		||||
        (error) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -212,13 +236,17 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      this.selectedClients = [];
 | 
			
		||||
      this.selectedClientsOriginal = [];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getNodeIcon(node: any): string {
 | 
			
		||||
    console.log('Node:', node);
 | 
			
		||||
    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';
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -436,5 +464,67 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
 | 
			
		||||
  hasChild = (_: number, node: FlatNode): boolean => node.expandable;
 | 
			
		||||
  isLeafNode = (_: number, node: FlatNode): boolean => !node.expandable;
 | 
			
		||||
 | 
			
		||||
  /* filtros */
 | 
			
		||||
  selectedTreeFilter: string = '';
 | 
			
		||||
  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;
 | 
			
		||||
  
 | 
			
		||||
          // Filtrar hijos recursivamente
 | 
			
		||||
          const filteredChildren = node.children ? filterNodes(node.children) : [];
 | 
			
		||||
  
 | 
			
		||||
          // Si el nodo o algún hijo coincide, incluirlo
 | 
			
		||||
          if (matchesName && matchesType || filteredChildren.length > 0) {
 | 
			
		||||
            return { ...node, children: filteredChildren };
 | 
			
		||||
          }
 | 
			
		||||
          return null; // Excluir nodos que no coinciden
 | 
			
		||||
        })
 | 
			
		||||
        .filter(node => node !== null); // Eliminar nodos excluidos
 | 
			
		||||
    };
 | 
			
		||||
  
 | 
			
		||||
    // Aplicar filtro sobre el árbol completo
 | 
			
		||||
    const filteredData = filterNodes(this.treeDataSource.data);
 | 
			
		||||
  
 | 
			
		||||
    // Actualizar el origen de datos del árbol
 | 
			
		||||
    this.treeDataSource.data = filteredData;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onTreeFilterInput(event: Event): void {
 | 
			
		||||
    const input = event.target as HTMLInputElement;
 | 
			
		||||
    const searchTerm = input?.value || ''; // Maneja el caso de nulo o indefinido
 | 
			
		||||
    this.filterTree(searchTerm, this.selectedTreeFilter);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClientFilterInput(event: Event): void {
 | 
			
		||||
    const input = event.target as HTMLInputElement;
 | 
			
		||||
    const searchTerm = input?.value || ''; // Maneja valores nulos o indefinidos
 | 
			
		||||
    this.filterClients(searchTerm);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  filterClients(searchTerm: string): void {
 | 
			
		||||
    if (!searchTerm) {
 | 
			
		||||
      // Restaurar los datos originales si no hay filtro
 | 
			
		||||
      this.selectedClients = [...this.selectedClientsOriginal];
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
    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;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -421,5 +421,8 @@
 | 
			
		|||
  "TOOLTIP_MENUS": "Gestión de menús (opción deshabilitada)",
 | 
			
		||||
  "search": "Buscar",
 | 
			
		||||
  "TOOLTIP_SEARCH": "Función de búsqueda (opción deshabilitada)",
 | 
			
		||||
  "detailsOf": "Detalles de"
 | 
			
		||||
  "detailsOf": "Detalles de",
 | 
			
		||||
  "editUnitMenu": "Editar",
 | 
			
		||||
  "addInternalUnitMenu": "Añadir",
 | 
			
		||||
  "addClientMenu": "Añadir cliente"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue