refs #1136 Add joyride in all components
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details

oggui/translations
Alvaro Puente Mella 2024-11-13 12:39:49 +01:00
parent 4109188913
commit c168c87fc9
34 changed files with 746 additions and 367 deletions

View File

@ -1,11 +1,16 @@
<div class="header-container">
<h2 class="title" i18n="@@adminCommandGroupsTitle">Administrar Grupos de Comandos</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminCommandGroupsTitle" joyrideStep="titleStep" text="Desde aquí puedes gestionar los grupos de comandos.">Administrar Grupos de Comandos</h2>
<div class="command-groups-button-row">
<button mat-flat-button color="primary" (click)="openCreateCommandGroupModal()">Añadir Grupo de Comandos</button>
<button mat-flat-button color="primary" (click)="openCreateCommandGroupModal()" joyrideStep="addCommandGroupStep" text="Haz clic para añadir un nuevo grupo de comandos.">Añadir Grupo de Comandos</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="searchStep" text="Busca un grupo de comandos específico ingresando su nombre y pulsando 'Enter'.">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de grupo</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
@ -14,12 +19,12 @@
</mat-form-field>
</div>
<div *ngIf="loading" class="loading-container">
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="Espera mientras se cargan los grupos de comandos.">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de grupos de comandos disponibles.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let commandGroup">
@ -27,7 +32,7 @@
{{ column.cell(commandGroup) }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'commands'">
<ng-container *ngIf="column.columnDef === 'commands'" joyrideStep="viewCommandsStep" text="Haz clic para ver los comandos en este grupo.">
<button mat-button [matMenuTriggerFor]="menu">Ver comandos</button>
<mat-menu #menu="matMenu">
<button mat-menu-item *ngFor="let command of commandGroup.commands">
@ -37,11 +42,12 @@
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let client" style="text-align: center;">
<td mat-cell *matCellDef="let client" style="text-align: center;" joyrideStep="actionsStep" text="Usa estas opciones para ver, editar o eliminar un grupo de comandos.">
<button mat-icon-button color="info" (click)="viewGroupDetails(client)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="editCommandGroup(client)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" (click)="editCommandGroup(client)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteCommandGroup(client)">
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
@ -53,7 +59,7 @@
</table>
</div>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de grupos de comandos usando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -7,6 +7,7 @@ import { DetailCommandGroupComponent } from './detail-command-group/detail-comma
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import { MatTableDataSource } from "@angular/material/table";
import { DatePipe } from "@angular/common";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-commands-groups',
@ -48,7 +49,8 @@ export class CommandsGroupsComponent implements OnInit {
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
private apiUrl = `${this.baseUrl}/command-groups`;
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {}
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.search();
@ -114,4 +116,21 @@ export class CommandsGroupsComponent implements OnInit {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'addCommandGroupStep',
'searchStep',
'tableStep',
'viewCommandsStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,13 +1,16 @@
<div class="header-container">
<h2 class="title">Administrar Tareas</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" joyrideStep="titleStep" text="Desde aquí puedes gestionar las tareas de manera centralizada.">Administrar Tareas</h2>
<div class="task-button-row">
<button mat-flat-button color="primary" (click)="openCreateTaskModal()">Añadir Tarea</button>
<button mat-flat-button color="primary" (click)="openCreateTaskModal()" joyrideStep="addTaskStep" text="Haz clic para añadir una nueva tarea.">Añadir Tarea</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="searchStep" text="Busca una tarea específica ingresando su nombre y pulsando 'Enter'.">
<mat-form-field appearance="fill" class="search-string">
<mat-label>Buscar tarea</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" />
@ -16,12 +19,8 @@
</mat-form-field>
</div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="tasks" class="mat-elevation-z8">
<table mat-table [dataSource]="tasks" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de tareas disponibles.">
<ng-container matColumnDef="taskid">
<th mat-header-cell *matHeaderCellDef> Id</th>
<td mat-cell *matCellDef="let task"> {{ task.id }} </td>
@ -49,9 +48,9 @@
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let task" style="text-align: center;">
<td mat-cell *matCellDef="let task" style="text-align: center;" joyrideStep="actionsStep" text="Usa estas opciones para ver, editar o eliminar una tarea.">
<button mat-icon-button color="info" (click)="viewTaskDetails(task)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="editTask(task)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" (click)="editTask(task)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteTask(task)">
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
@ -63,7 +62,8 @@
</table>
</div>
<mat-paginator [length]="length"
<mat-paginator joyrideStep="paginationStep" text="Navega entre las páginas de tareas usando el paginador."
[length]="length"
[pageSize]="itemsPerPage"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">

View File

@ -5,6 +5,7 @@ import { ToastrService } from 'ngx-toastr';
import { CreateTaskComponent } from './create-task/create-task.component';
import { DetailTaskComponent } from './detail-task/detail-task.component';
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-commands-task',
@ -23,7 +24,8 @@ export class CommandsTaskComponent implements OnInit {
loading: boolean = false;
private apiUrl = `${this.baseUrl}/command-tasks`;
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {}
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.loadTasks();
@ -93,4 +95,20 @@ export class CommandsTaskComponent implements OnInit {
this.itemsPerPage = event.pageSize;
this.loadTasks();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'addTaskStep',
'searchStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,13 +1,17 @@
<div class="header-container">
<h2 class="title" i18n="@@adminCommandsTitle">Trazas de comandos y procedimientos</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminCommandsTitle" joyrideStep="titleStep" text="Desde aquí puedes ver las trazas y logs de las ejecuciones de comandos y procedimientos.">Trazas de comandos y procedimientos</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
<button mat-flat-button color="primary" (click)="resetFilters()" joyrideStep="resetFiltersStep" text="Haz clic para reiniciar los filtros aplicados y ver todas las trazas.">Reiniciar filtros</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-select">
<mat-form-field appearance="fill" class="search-select" joyrideStep="clientSelectStep" text="Selecciona un cliente para ver las trazas asociadas.">
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente">
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" (optionSelected)="onOptionClientSelected($event.option.value)">
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
@ -16,7 +20,7 @@
</mat-autocomplete>
</mat-form-field>
<mat-form-field appearance="fill" class="search-select">
<mat-form-field appearance="fill" class="search-select" joyrideStep="commandSelectStep" text="Selecciona un comando para ver las trazas específicas de ese comando.">
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="Seleccione un comando">
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)">
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
@ -26,14 +30,12 @@
</mat-form-field>
</div>
<!-- Indicador de carga -->
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<!-- Tabla de trazas -->
<div *ngIf="!loading">
<table mat-table [dataSource]="traces" class="mat-elevation-z8">
<table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de trazas de comandos y procedimientos, según los filtros aplicados.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let trace">
@ -46,7 +48,7 @@
</table>
</div>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de trazas usando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
import { FormControl } from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { DatePipe } from '@angular/common';
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-task-logs',
@ -63,7 +64,8 @@ export class TaskLogsComponent implements OnInit {
filteredCommands!: Observable<any[]>;
commandControl = new FormControl();
constructor(private http: HttpClient) {}
constructor(private http: HttpClient,
private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.loadTraces();
@ -183,4 +185,20 @@ export class TaskLogsComponent implements OnInit {
this.length = event.length;
this.loadTraces();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'resetFiltersStep',
'clientSelectStep',
'commandSelectStep',
'tableStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,12 +1,16 @@
<div class="header-container">
<h2 class="title" i18n="@@adminCommandsTitle">Administrar Comandos</h2>
<div class="command-button-row">
<button mat-flat-button color="primary" (click)="openCreateCommandModal()">Añadir Comando</button>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminCommandsTitle" joyrideStep="titleStep" text="Desde aquí puedes gestionar los comandos disponibles.">Administrar Comandos</h2>
<div class="command-button-row" joyrideStep="addCommandStep" text="Haz clic para añadir un nuevo comando.">
<button mat-flat-button color="primary" (click)="openCreateCommandModal()" >Añadir Comando</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="searchStep" text="Busca un comando específico ingresando su nombre y pulsando 'Enter'.">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de comando</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
@ -15,12 +19,12 @@
</mat-form-field>
</div>
<div *ngIf="loading" class="loading-container">
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="Espera mientras se cargan los comandos.">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de comandos disponibles.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let command">
@ -36,10 +40,10 @@
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let command" style="text-align: center;">
<td mat-cell *matCellDef="let command" style="text-align: center;" joyrideStep="actionsStep" text="Usa estos botones para ejecutar, ver, editar o eliminar un comando.">
<button mat-icon-button color="info" (click)="executeCommand($event, command)"><mat-icon>play_arrow</mat-icon></button>
<button mat-icon-button color="info" (click)="viewDetails($event, command)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" [disabled]="command.readOnly" (click)="editCommand($event, command)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" [disabled]="command.readOnly" (click)="editCommand($event, command)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" [disabled]="command.readOnly" (click)="deleteCommand($event, command)"><mat-icon i18n="@@deleteElementTooltip">delete</mat-icon></button>
</td>
</ng-container>
@ -49,7 +53,7 @@
</table>
</div>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de comandos usando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -8,6 +8,7 @@ import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/
import { MatTableDataSource } from '@angular/material/table';
import { DatePipe } from '@angular/common';
import { ExecuteCommandComponent } from './execute-command/execute-command.component';
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-commands',
@ -49,7 +50,8 @@ export class CommandsComponent implements OnInit {
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
private apiUrl = `${this.baseUrl}/commands`;
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {}
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.search();
@ -131,4 +133,19 @@ export class CommandsComponent implements OnInit {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'addCommandStep',
'searchStep',
'tableStep',
'actionsStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,89 +1,96 @@
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar clientes</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
<button mat-flat-button color="primary" (click)="addClient($event)">Añadir cliente</button>
</div>
<div class="header-container">
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="title3Step" text="En esta pantalla se podran administrar todos los clientes del sistema sin jerarquias.">Administrar clientes</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="resetFilters()" joyrideStep="resetFiltersStep" text="Reinicia los filtros aplicados para mostrar todos los clientes.">Reiniciar filtros</button>
<button mat-flat-button color="primary" (click)="addClient($event)" joyrideStep="addClientStep" text="Añade un nuevo cliente a la lista.">Añadir cliente</button>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de cliente</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar IP</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['ip']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar MAC</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['mac']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-select">
<mat-label i18n="@@organizational-unit-label">U. Organizativa</mat-label>
<mat-select [(ngModel)]="filters['organizationalUnit.id']" (selectionChange)="search()">
<mat-option *ngFor="let unit of organizationalUnits" [value]="unit.id" >
{{ unit.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="!loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let client" >
<ng-container *ngIf="column.columnDef === 'name'">
<div class="client-info">
<div class="client-name">{{ client.name }}</div>
<div class="client-ip">{{ client.ip }}</div>
<div class="client-mac">{{ client.mac }}</div>
</div>
</ng-container>
<ng-container *ngIf="column.columnDef === 'status'">
<mat-chip>
{{ client.status }}
</mat-chip>
</ng-container>
<ng-container *ngIf="column.columnDef !== 'status' && column.columnDef !== 'name'" >
{{ column.cell(client) }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let client" style="text-align: center;">
<button *ngIf="!syncStatus" mat-icon-button color="primary" (click)="getStatus(client)"><mat-icon>sync</mat-icon></button>
<button *ngIf="syncStatus" mat-icon-button color="primary"><mat-spinner diameter="24"></mat-spinner></button>
<button mat-icon-button color="info" (click)="handleClientClick($event, client)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="onEditClick($event, client.uuid)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="onDeleteClick($event, client)">
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container" joyrideStep="searchContainerStep" text="Filtra los clientes por nombre, IP, MAC o unidad organizativa.">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de cliente</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar IP</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['ip']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar MAC</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['mac']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-select" >
<mat-label i18n="@@organizational-unit-label">U. Organizativa</mat-label>
<mat-select [(ngModel)]="filters['organizationalUnit.id']" (selectionChange)="search()">
<mat-option *ngFor="let unit of organizationalUnits" [value]="unit.id" >
{{ unit.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="!loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Lista de clientes filtrados por tus criterios de búsqueda.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let client" >
<ng-container *ngIf="column.columnDef === 'name'">
<div class="client-info">
<div class="client-name">{{ client.name }}</div>
<div class="client-ip">{{ client.ip }}</div>
<div class="client-mac">{{ client.mac }}</div>
</div>
</ng-container>
<ng-container *ngIf="column.columnDef === 'status'">
<mat-chip>{{ client.status }}</mat-chip>
</ng-container>
<ng-container *ngIf="column.columnDef !== 'status' && column.columnDef !== 'name'">
{{ column.cell(client) }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let client" style="text-align: center;" joyrideStep="actionsStep" text="Acciones disponibles para cada cliente, como ver, editar o eliminar.">
<button *ngIf="!syncStatus" mat-icon-button color="primary" (click)="getStatus(client)"><mat-icon>sync</mat-icon></button>
<button *ngIf="syncStatus" mat-icon-button color="primary"><mat-spinner diameter="24"></mat-spinner></button>
<button mat-icon-button color="info" (click)="handleClientClick($event, client)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="onEditClick($event, client.uuid)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="onDeleteClick($event, client)">
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de resultados utilizando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>
</div>
</div>

View File

@ -13,6 +13,7 @@ import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import {ClientViewComponent} from "../../shared/client-view/client-view.component";
import { Router } from '@angular/router';
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-client-tab-view',
@ -78,7 +79,8 @@ export class ClientTabViewComponent {
public dialog: MatDialog,
private toastService: ToastrService,
private http: HttpClient,
private router: Router
private router: Router,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -190,4 +192,20 @@ export class ClientTabViewComponent {
this.length = event.length;
this.getClients();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'title3Step',
'resetFiltersStep',
'addClientStep',
'searchContainerStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,21 +1,27 @@
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar unidades organizativas</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="titleS4tep" text="Gestiona las unidades organizativas desde esta pantalla.">Administrar unidades organizativas</h2>
<div class="button-row">
<button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
<button mat-flat-button color="primary" (click)="addOrganizationalUnit($event)">Añadir OU</button>
<button mat-flat-button color="primary" (click)="resetFilters()" joyrideStep="resetFiltersStep" text="Reinicia los filtros aplicados para mostrar todas las unidades organizativas.">Reiniciar filtros</button>
<button mat-flat-button color="primary" (click)="addOrganizationalUnit($event)" joyrideStep="addOUStep" text="Añade una nueva unidad organizativa.">Añadir OU</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="searchContainerStep" text="Filtra las unidades organizativas por nombre o tipo.">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de OU</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-label i18n="@@searchLabel">Tipo</mat-label>
<mat-select [(ngModel)]="filters['type']" (selectionChange)="search()" placeholder="Seleccionar opción" >
<mat-select [(ngModel)]="filters['type']" (selectionChange)="search()" placeholder="Seleccionar opción">
<mat-option [value]="''">Todos</mat-option>
<mat-option [value]="'organizational-unit'">Centro</mat-option>
<mat-option [value]="'classrooms-group'">Grupos de aulas</mat-option>
@ -24,28 +30,29 @@
</mat-select>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestran las unidades organizativas según los filtros aplicados.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let ou" >
<td mat-cell *matCellDef="let ou">
<ng-container *ngIf="column.columnDef !== 'available' && column.columnDef !== 'type'">
{{ column.cell(ou) }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'available'" >
<ng-container *ngIf="column.columnDef === 'available'">
<mat-chip *ngIf="ou.available" class="mat-chip-success"><mat-icon class="calendar-ico">event_available</mat-icon></mat-chip>
<mat-chip *ngIf="!ou.available" class="mat-chip-error"><mat-icon class="calendar-ico">event_busy</mat-icon></mat-chip>
<mat-chip *ngIf="!ou.available" class="mat-chip-error"><mat-icon class="calendar-ico">event_busy</mat-icon></mat-chip>
</ng-container>
<ng-container *ngIf="column.columnDef === 'type'" >
<mat-chip> {{ ou.type }} </mat-chip>
<ng-container *ngIf="column.columnDef === 'type'">
<mat-chip>{{ ou.type }}</mat-chip>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let ou" style="text-align: center;">
<td mat-cell *matCellDef="let ou" style="text-align: center;" joyrideStep="actionsStep" text="Usa estas opciones para ver, editar o eliminar una unidad organizativa.">
<button mat-icon-button color="info" (click)="onShowClick($event, ou)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="onEditClick($event, ou.uuid)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" (click)="onEditClick($event, ou.uuid)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="onDeleteClick($event, ou)"><mat-icon>delete</mat-icon></button>
<button mat-icon-button color="info" [matMenuTriggerFor]="menu">
<mat-icon>menu</mat-icon>
@ -64,7 +71,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de unidades organizativas con el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -21,6 +21,7 @@ import {
} from "../../shared/organizational-units/edit-organizational-unit/edit-organizational-unit.component";
import {ClassroomViewDialogComponent} from "../../shared/classroom-view/classroom-view-modal";
import {TreeViewComponent} from "../../shared/tree-view/tree-view.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-organizational-unit-tab-view',
@ -79,7 +80,8 @@ export class OrganizationalUnitTabViewComponent {
private dataService: DataService,
public dialog: MatDialog,
private toastService: ToastrService,
private http: HttpClient
private http: HttpClient,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -169,4 +171,21 @@ export class OrganizationalUnitTabViewComponent {
this.length = event.length;
this.getOrganizationalUnits();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleS4tep',
'resetFiltersStep',
'addOUStep',
'searchContainerStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,53 +1,63 @@
<div class="header-container">
<h2 class="title">Administrar imágenes</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="addImage()">Añadir imagen</button>
</div>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" joyrideStep="imagesTitleStep" text="En esta pantalla, puedes gestionar las imágenes disponibles.">
Administrar imágenes
</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="addImage()" joyrideStep="addImageButton" text="Añade una nueva imagen a la lista.">
Añadir imagen
</button>
</div>
<mat-divider class="divider"></mat-divider>
</div>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-label>Buscar nombre de imagen</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint>Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
</div>
<mat-divider class="divider"></mat-divider>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >
<ng-container *ngIf="column.columnDef === 'remotePc'">
<mat-icon [color]="image[column.columnDef] ? 'primary' : 'warn'">
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
</mat-icon>
</ng-container>
<ng-container *ngIf="column.columnDef !== 'remotePc'">
{{ column.cell(image) }}
</ng-container>
</td>
</ng-container>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchImageField" text="Busca una imagen por nombre. Pulsa 'enter' para iniciar la búsqueda.">
<mat-label>Buscar nombre de imagen</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint>Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
</div>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let client" style="text-align: center;">
<button mat-icon-button color="primary" (click)="editImage($event, client)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteImage($event, client)">
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="imagesTable" text="Esta tabla muestra las imágenes disponibles.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image">
<ng-container *ngIf="column.columnDef === 'remotePc'">
<mat-icon [color]="image[column.columnDef] ? 'primary' : 'warn'">
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
</mat-icon>
</ng-container>
<ng-container *ngIf="column.columnDef !== 'remotePc'">
{{ column.cell(image) }}
</ng-container>
</td>
</ng-container>
<div class="paginator-container">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"
[pageSizeOptions]="[5, 10, 20, 40, 100]"
(page)="onPageChange($event)">
</mat-paginator>
</div>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;" joyrideStep="actionsHeader" text="Acciones disponibles para cada imagen.">Acciones</th>
<td mat-cell *matCellDef="let client" style="text-align: center;">
<button mat-icon-button color="primary" (click)="editImage($event, client)" joyrideStep="editImageButton" text="Editar la imagen seleccionada.">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteImage($event, client)" joyrideStep="deleteImageButton" text="Eliminar la imagen seleccionada.">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container" joyrideStep="imagesPagination" text="Navega entre las páginas de imágenes.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"
[pageSizeOptions]="[5, 10, 20, 40, 100]"
(page)="onPageChange($event)">
</mat-paginator>
</div>

View File

@ -7,6 +7,7 @@ import { DatePipe } from '@angular/common';
import { CreateImageComponent } from './create-image/create-image.component';
import {CreateCommandComponent} from "../commands/main-commands/create-command/create-command.component";
import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-images',
@ -61,7 +62,8 @@ export class ImagesComponent implements OnInit {
constructor(
public dialog: MatDialog,
private http: HttpClient,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -127,4 +129,22 @@ export class ImagesComponent implements OnInit {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'imagesTitleStep',
'addImageButton',
'searchImageField',
'imagesTable',
'actionsHeader',
'editImageButton',
'deleteImageButton',
'imagesPagination'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,4 +1,11 @@
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 100px;
padding: 10px;
}
.disk-usage-info{
display: flex;
justify-content: start;

View File

@ -1,59 +1,64 @@
<div class="dashboard">
<h2>OgBoot server Status</h2>
<div class="disk-usage-container">
<div class="disk-usage">
<h3>Uso de disco</h3>
<ngx-charts-pie-chart
[view]="view"
[scheme]="colorScheme"
[results]="diskUsageChartData"
[gradient]="gradient"
[doughnut]="isDoughnut"
[labels]="showLabels"
[legend]="showLegend">
</ngx-charts-pie-chart>
<div class="disk-usage-info">
<p>Total: {{ formatBytes(diskUsage.total) }}</p>
<p>Ocupado: {{ formatBytes(diskUsage.used) }}</p>
<p>Disponible: {{ formatBytes(diskUsage.available) }}</p>
<p>Libre: {{ diskUsage.percentage }}%</p>
</div>
</div>
<div class="services-status">
<h3>Servicios</h3>
<ul>
<li *ngFor="let service of getServices()">
<span
class="status-led"
[ngClass]="{ 'active': service.status === 'active', 'inactive': service.status !== 'active' }"
></span>
{{ service.name }}: {{ service.status }}
</li>
</ul>
<div class="header-container" >
<h2 joyrideStep="titleStep" text="Esta sección muestra el estado general del servidor OgBoot.">OgBoot server Status</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
</div>
<div class="disk-usage-container">
<div class="disk-usage" joyrideStep="diskUsageStep" text="Visualiza el uso del disco del servidor.">
<h3>Uso de disco</h3>
<ngx-charts-pie-chart
[view]="view"
[scheme]="colorScheme"
[results]="diskUsageChartData"
[gradient]="gradient"
[doughnut]="isDoughnut"
[labels]="showLabels"
[legend]="showLegend">
</ngx-charts-pie-chart>
<div class="disk-usage-info">
<p>Total: {{ formatBytes(diskUsage.total) }}</p>
<p>Ocupado: {{ formatBytes(diskUsage.used) }}</p>
<p>Disponible: {{ formatBytes(diskUsage.available) }}</p>
<p>Libre: {{ diskUsage.percentage }}%</p>
</div>
</div>
<div class="installed-oglives">
<h3>OGLives instalados</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Kernel</th>
<th>Architecture</th>
<th>Revision</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let oglive of installedOglives">
<td>{{ oglive.id }}</td>
<td>{{ oglive.kernel }}</td>
<td>{{ oglive.architecture }}</td>
<td>{{ oglive.revision }}</td>
</tr>
</tbody>
</table>
<div class="services-status" joyrideStep="servicesStatusStep" text="Aquí puedes ver el estado de los servicios importantes del servidor.">
<h3>Servicios</h3>
<ul>
<li *ngFor="let service of getServices()">
<span
class="status-led"
[ngClass]="{ 'active': service.status === 'active', 'inactive': service.status !== 'active' }"
></span>
{{ service.name }}: {{ service.status }}
</li>
</ul>
</div>
</div>
<div class="installed-oglives" joyrideStep="oglivesStep" text="Consulta la información de los OGLives instalados en el servidor.">
<h3>OGLives instalados</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Kernel</th>
<th>Architecture</th>
<th>Revision</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let oglive of installedOglives">
<td>{{ oglive.id }}</td>
<td>{{ oglive.kernel }}</td>
<td>{{ oglive.architecture }}</td>
<td>{{ oglive.revision }}</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-ogboot-status',
@ -23,7 +24,7 @@ export class OgbootStatusComponent implements OnInit {
domain: ['#FF6384', '#3f51b5']
};
constructor(private http: HttpClient) {}
constructor(private http: HttpClient, private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.loadStatus();
@ -68,4 +69,18 @@ export class OgbootStatusComponent implements OnInit {
return bytes + ' B';
}
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'diskUsageStep',
'servicesStatusStep',
'oglivesStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,10 +1,12 @@
<div class="header-container">
<h2 class="title">Netboot avanzado</h2>
<h2 class="title" joyrideStep="titleStep" text="Esta sección permite la asignación masiva de plantillas de arranque a clientes.">Netboot avanzado</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
</div>
<div [formGroup]="taskForm" class="search-container">
<mat-form-field appearance="fill" class="search-boolean">
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="selectUnitStep" text="Selecciona la Unidad Organizacional para listar las aulas disponibles.">
<mat-label>Selecciona Unidad Organizacional</mat-label>
<mat-select formControlName="organizationalUnit" (selectionChange)="onOrganizationalUnitChange()">
<mat-option *ngIf="loadingUnits" disabled>Cargando unidades...</mat-option>
@ -15,7 +17,7 @@
<mat-error *ngIf="taskForm.get('organizationalUnit')?.invalid">Este campo es obligatorio</mat-error>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="selectClassStep" text="Selecciona el aula para configurar las plantillas en sus dispositivos.">
<mat-label>Selecciona aula</mat-label>
<mat-select formControlName="selectedChild" (selectionChange)="onChildChange()">
<mat-option *ngIf="selectedUnitChildren.length === 0" disabled>No hay aulas disponibles</mat-option>
@ -29,17 +31,17 @@
<mat-divider class="divider"></mat-divider>
<div class="global-selectors">
<mat-form-field appearance="fill" class="selected-global">
<mat-form-field appearance="fill" class="selected-global" joyrideStep="applyToAllStep" text="Selecciona una plantilla para aplicarla a todos los clientes de esta aula.">
<mat-label>Seleccione plantilla para aplicar a todos los clientes</mat-label>
<mat-select [(value)]="globalOgLive" (selectionChange)="applyToAll()" >
<mat-select [(value)]="globalOgLive" (selectionChange)="applyToAll()">
<mat-option *ngFor="let option of ogLiveOptions" [value]="option['@id']">{{ option.name }}</mat-option>
</mat-select>
</mat-form-field>
<button mat-flat-button color="primary" [disabled]="selectedUnitChildren.length === 0" (click)="saveOgLiveTemplates()">Guardar</button>
<button mat-flat-button color="primary" [disabled]="selectedUnitChildren.length === 0" (click)="saveOgLiveTemplates()" joyrideStep="saveButtonStep" text="Haz clic para guardar la configuración actual de plantillas.">Guardar</button>
</div>
<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Administra las plantillas de Netboot para cada cliente en esta tabla.">
<ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef> Id </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.id}} </mat-cell>
@ -53,7 +55,7 @@
<ng-container matColumnDef="ogLive">
<mat-header-cell *matHeaderCellDef> Plantilla </mat-header-cell>
<mat-cell *matCellDef="let client">
<mat-form-field appearance="fill">
<mat-form-field appearance="fill" joyrideStep="selectTemplateStep" text="Selecciona una plantilla específica para cada cliente.">
<mat-label>Seleccione una plantilla</mat-label>
<mat-select [(ngModel)]="client.ogLive" [name]="'ogLive' + client.id">
<mat-option [value]="null">Ninguna</mat-option>
@ -65,7 +67,6 @@
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>

View File

@ -5,6 +5,7 @@ import { ToastrService } from 'ngx-toastr';
import { PageEvent } from '@angular/material/paginator';
import {Observable} from "rxjs";
import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-pxe-boot-files',
@ -32,7 +33,8 @@ export class PxeBootFilesComponent implements OnInit {
constructor(
private fb: FormBuilder,
private http: HttpClient,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {
this.taskForm = this.fb.group({
organizationalUnit: ['', Validators.required],
@ -155,4 +157,21 @@ export class PxeBootFilesComponent implements OnInit {
this.itemsPerPage = event.pageSize;
this.fetchPxeTemplates();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'selectUnitStep',
'selectClassStep',
'applyToAllStep',
'saveButtonStep',
'tableStep',
'selectTemplateStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,41 +1,47 @@
<mat-accordion class="example-headers-align">
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-expansion-panel-header joyrideStep="serverInfoStep" text="Accede a información y opciones de sincronización en el servidor OgBoot.">
<mat-panel-title> Información en servidor ogBoot </mat-panel-title>
</mat-expansion-panel-header>
<div class="button-row">
<button mat-flat-button color="primary" (click)="syncOgBoot()"> Sincronizar base de datos</button>
<button mat-flat-button color="primary" (click)="syncOgBoot()" > Sincronizar base de datos</button>
</div>
<div class="button-row">
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">Ver Información</button>
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()" >Ver Información</button>
</div>
</mat-expansion-panel>
</mat-accordion>
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar imágenes</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="titleStep" text="Desde aquí puedes gestionar las imágenes configuradas en el servidor OgBoot.">Administrar imágenes</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="addImage()">Añadir imagen</button>
<button mat-flat-button color="primary" (click)="addImage()" joyrideStep="addImageStep" text="Haz clic para añadir una nueva imagen.">Añadir imagen</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchNameStep" text="Busca imágenes por nombre para encontrar rápidamente una imagen específica.">
<mat-label i18n="@@searchLabel">Buscar nombre de imagen</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="searchDefaultImageStep" text="Filtra las imágenes para mostrar solo las imágenes por defecto o no por defecto.">
<mat-label i18n="@@searchLabel">Imagen por defecto</mat-label>
<mat-select [(ngModel)]="filters['isDefault']" (selectionChange)="search()" placeholder="Seleccionar opción" >
<mat-select [(ngModel)]="filters['isDefault']" (selectionChange)="search()" placeholder="Seleccionar opción">
<mat-option [value]="''">Todos</mat-option>
<mat-option [value]="true"></mat-option>
<mat-option [value]="false">No</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="searchInstalledStep" text="Filtra las imágenes para mostrar solo las instaladas en el servidor OgBoot.">
<mat-label i18n="@@searchLabel">Instalado servidor ogBoot</mat-label>
<mat-select [(ngModel)]="filters['installed']" (selectionChange)="search()" placeholder="Seleccionar opción">
<mat-option [value]="''">Todos</mat-option>
@ -44,11 +50,11 @@
</mat-select>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de imágenes disponibles para administrar.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >
<td mat-cell *matCellDef="let image">
<ng-container *ngIf="column.columnDef === 'isDefault' || column.columnDef === 'installed'">
<mat-icon [color]="image[column.columnDef] ? 'primary' : 'warn'">
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
@ -76,15 +82,14 @@
<ng-container *ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl' && column.columnDef !== 'status' && column.columnDef !== 'name' ">
{{ column.cell(image) }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
<td mat-cell *matCellDef="let image">
<td mat-cell *matCellDef="let image" joyrideStep="actionsStep" text="Administra cada imagen con opciones para ver, editar, eliminar y más.">
<button mat-icon-button color="info" (click)="showOgLive($event, image)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="editImage(image)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" (click)="editImage(image)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteImage(image)" i18n="@@buttonDelete"><mat-icon>delete</mat-icon></button>
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>menu</mat-icon>
@ -100,7 +105,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de imágenes usando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -12,6 +12,7 @@ import {DataService} from "./data.service";
import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
import {ShowTemplateContentComponent} from "../pxe/show-template-content/show-template-content.component";
import {Observable} from "rxjs";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-pxe-images',
@ -77,7 +78,8 @@ export class PXEimagesComponent implements OnInit {
public dialog: MatDialog,
private http: HttpClient,
private dataService: DataService,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -254,4 +256,23 @@ export class PXEimagesComponent implements OnInit {
this.toastService.error('Error al sincronizar');
});
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'serverInfoStep',
'titleStep',
'addImageStep',
'searchNameStep',
'searchDefaultImageStep',
'searchInstalledStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,10 +1,10 @@
<mat-accordion class="example-headers-align">
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-expansion-panel-header joyrideStep="serverInfoStep" text="Accede a información y opciones de sincronización en el servidor OgBoot.">
<mat-panel-title> Información en servidor ogBoot </mat-panel-title>
</mat-expansion-panel-header>
<div class="example-button-row">
<button mat-flat-button color="primary" (click)="syncTemplates()"> Sincronizar base de datos</button>
<button mat-flat-button color="primary" (click)="syncTemplates()" > Sincronizar base de datos</button>
</div>
<div class="example-button-row">
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">Ver Información</button>
@ -12,34 +12,39 @@
</mat-expansion-panel>
</mat-accordion>
<div class="header-container">
<h2 class="title" i18n="@@adminPXETitle">Administrar plantillas PXE</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminPXETitle" joyrideStep="titleStep" text="Desde aquí puedes gestionar las plantillas PXE configuradas en el servidor OgBoot.">Administrar plantillas PXE</h2>
<div class="pxe-button-row">
<button mat-flat-button color="primary" (click)="addPxeTemplate()">Añadir plantilla PXE</button>
<button mat-flat-button color="primary" (click)="addPxeTemplate()" joyrideStep="addTemplateStep" text="Haz clic para añadir una nueva plantilla PXE.">Añadir plantilla PXE</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchNameStep" text="Busca plantillas PXE por nombre para localizar rápidamente una plantilla específica.">
<mat-label i18n="@@searchLabel">Buscar nombre de plantilla</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="searchSyncStep" text="Filtra para ver solo las plantillas creadas en el servidor OgBoot.">
<mat-label i18n="@@searchLabel">Creada en ogBoot</mat-label>
<mat-select [(ngModel)]="filters['synchronized']" (selectionChange)="search()" placeholder="Seleccionar opción" >
<mat-select [(ngModel)]="filters['synchronized']" (selectionChange)="search()" placeholder="Seleccionar opción">
<mat-option [value]="''">Todos</mat-option>
<mat-option [value]="true"></mat-option>
<mat-option [value]="false">No</mat-option>
</mat-select>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se muestra la lista de plantillas PXE disponibles para administrar.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >
<td mat-cell *matCellDef="let image">
<ng-container *ngIf="column.columnDef === 'synchronized'">
<mat-icon [color]="image[column.columnDef] ? 'primary' : 'warn'">
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
@ -52,19 +57,19 @@
</td>
</ng-container>
<ng-container matColumnDef="actions" >
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let template" style="text-align: center;">
<td mat-cell *matCellDef="let template" style="text-align: center;" joyrideStep="actionsStep" text="Gestiona cada plantilla PXE con opciones para ver, editar, eliminar y más.">
<button mat-icon-button color="info" (click)="showTemplate($event, template)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="info" [disabled]="template.clientsLength === 0" (click)="editClients($event, template)"><mat-icon i18n="@@deleteElementTooltip">computer</mat-icon></button>
<button mat-icon-button color="primary" (click)="editPxeTemplate(template)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="info" [disabled]="template.clientsLength === 0" (click)="editClients($event, template)"><mat-icon i18n="@@deleteElementTooltip">computer</mat-icon></button>
<button mat-icon-button color="primary" (click)="editPxeTemplate(template)" i18n="@@editImage"><mat-icon>edit</mat-icon></button>
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>menu</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="toggleAction(template, 'create')">Crear en servidor ogBoot</button>
<button mat-menu-item (click)="addClientsToPxe(template)">Añadir cliente</button>
<button mat-menu-item (click)="addClientsToPxe(template)">Añadir cliente</button>
<button mat-menu-item (click)="toggleAction(template, 'sync')">Sincronizar base de datos</button>
<button mat-menu-item (click)="toggleAction(template, 'delete')">Eliminar</button>
</mat-menu>
@ -74,7 +79,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de plantillas PXE usando el paginador.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -20,6 +20,7 @@ import {Subnet} from "../../ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component";
import {AddClientsToPxeComponent} from "./add-clients-to-pxe/add-clients-to-pxe.component";
import {Observable} from "rxjs";
import {ClientsComponent} from "./clients/clients.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-pxe',
@ -72,7 +73,8 @@ export class PxeComponent {
public dialog: MatDialog,
private http: HttpClient,
private toastService: ToastrService,
private dataService: DataService
private dataService: DataService,
private joyrideService: JoyrideService
) { }
ngOnInit(): void {
@ -228,4 +230,22 @@ export class PxeComponent {
this.itemsPerPage = event.pageSize;
this.applyFilter();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'serverInfoStep',
'titleStep',
'addTemplateStep',
'searchNameStep',
'searchSyncStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,57 +1,62 @@
<mat-accordion class="example-headers-align">
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-expansion-panel-header joyrideStep="serverInfoStep" text="Despliega este contenedor para acceder a la información y opciones de sincronización en el servidor OgDHCP.">
<mat-panel-title> Información en servidor ogDHCP </mat-panel-title>
</mat-expansion-panel-header>
<div class="example-button-row">
<button mat-flat-button color="primary" (click)="syncSubnets()"> Sincronizar base de datos</button>
<button mat-flat-button color="primary" (click)="syncSubnets()" joyrideStep="syncDbStep" text="Sincroniza la base de datos del servidor OgDHCP para actualizar la información de las subredes."> Sincronizar base de datos</button>
</div>
<div class="example-button-row">
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">Ver Información</button>
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()" joyrideStep="viewInfoStep" text="Haz clic para ver información detallada de las subredes en el servidor.">Ver Información</button>
</div>
</mat-expansion-panel>
</mat-accordion>
<div class="header-container">
<h2 class="title" i18n="@@subnetsTitle">Administrar Subredes</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@subnetsTitle" joyrideStep="titleStep" text="Desde aquí puedes gestionar las subredes configuradas en el servidor OgDHCP.">Administrar Subredes</h2>
<div class="subnets-button-row">
<button mat-flat-button color="primary" (click)="addSubnet()">Añadir Subred</button>
<button mat-flat-button color="primary" (click)="addSubnet()" joyrideStep="addSubnetStep" text="Haz clic para añadir una nueva subred.">Añadir Subred</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchNameStep" text="Busca subredes por nombre para localizar una subred específica rápidamente.">
<mat-label i18n="@@searchLabel">Buscar nombre de la subred</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" i18n-placeholder="@@searchPlaceholder">
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchNetmaskStep" text="Busca subredes usando una netmask específica.">
<mat-label i18n="@@searchLabel">Buscar netmask</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['netmask']" i18n-placeholder="@@searchPlaceholder">
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['netmask']" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchIpStep" text="Busca subredes por dirección IP.">
<mat-label i18n="@@searchLabel">Buscar IP</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['ip']" i18n-placeholder="@@searchPlaceholder">
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['ip']" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchBootFileStep" text="Busca subredes según el nombre del archivo de arranque.">
<mat-label i18n="@@searchLabel">Buscar Boot file name</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['bootFileName']" i18n-placeholder="@@searchPlaceholder">
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['bootFileName']" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
</div>
<mat-divider class="divider"></mat-divider>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Visualiza y administra las subredes listadas según los filtros aplicados.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let subnet">
<ng-container *ngIf="column.columnDef === 'synchronized'">
<mat-icon [color]="subnet[column.columnDef] ? 'primary' : 'warn'">
{{ subnet[column.columnDef] ? 'check_circle' : 'cancel' }}
@ -75,9 +80,10 @@
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let subnet" style="text-align: center;">
<td mat-cell *matCellDef="let subnet" style="text-align: center;" joyrideStep="actionsStep" text="Gestiona cada subred con opciones para editar, eliminar y más.">
<button mat-icon-button color="primary" (click)="editSubnet(subnet)" i18n="@@editSubnet">
<mat-icon>edit</mat-icon></button>
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="toggleAction(subnet, 'delete')"><mat-icon>delete</mat-icon></button>
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>menu</mat-icon>
@ -93,8 +99,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
<div class="paginator-container" joyrideStep="paginationStep" text="Navega entre las páginas de subredes usando el paginador.">
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions" (page)="onPageChange($event)">
</mat-paginator>
</div>

View File

@ -7,8 +7,9 @@ import { HttpClient } from '@angular/common/http';
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import { ToastrService } from 'ngx-toastr';
import { AddClientsToSubnetComponent } from './add-clients-to-subnet/add-clients-to-subnet.component';
import {ServerInfoDialogComponent} from "./server-info-dialog/server-info-dialog.component";
import {Observable} from "rxjs";
import { ServerInfoDialogComponent } from "./server-info-dialog/server-info-dialog.component";
import { Observable } from "rxjs";
import { JoyrideService } from 'ngx-joyride';
export interface Subnet {
'@id': string;
@ -32,7 +33,7 @@ export interface Subnet {
templateUrl: './og-dhcp-subnets.component.html',
styleUrls: ['./og-dhcp-subnets.component.css']
})
export class OgDhcpSubnetsComponent {
export class OgDhcpSubnetsComponent {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
displayedColumns: string[] = ['id', 'name', 'netmask', 'ipAddress', 'nextServer', 'bootFileName', 'synchronized', 'serverId', 'clients', 'actions'];
dataSource = new MatTableDataSource<Subnet>([]);
@ -52,14 +53,15 @@ export class OgDhcpSubnetsComponent {
{ columnDef: 'ipAddress', header: 'IP Address', cell: (subnet: Subnet) => subnet.ipAddress },
{ columnDef: 'nextServer', header: 'Next Server', cell: (subnet: Subnet) => subnet.nextServer },
{ columnDef: 'bootFileName', header: 'Boot File Name', cell: (subnet: Subnet) => subnet.bootFileName },
{ columnDef: 'synchronized', header: 'Sincronizado', cell: (subnet: Subnet) => `${subnet.synchronized}`},
{ columnDef: 'synchronized', header: 'Sincronizado', cell: (subnet: Subnet) => `${subnet.synchronized}` },
{ columnDef: 'serverId', header: 'Id Servidor DHCP', cell: (subnet: Subnet) => subnet.serverId },
{ columnDef: 'clients', header: 'Lista de clientes', cell: (subnet: Subnet) => `${subnet.clients}`},
{ columnDef: 'clients', header: 'Lista de clientes', cell: (subnet: Subnet) => `${subnet.clients}` },
];
private apiUrl = `${this.baseUrl}/subnets`;
constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService) {}
constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService,
private joyrideService: JoyrideService) { }
ngOnInit() {
this.loadSubnets();
@ -93,7 +95,7 @@ export class OgDhcpSubnetsComponent {
});
}
toggleAction(subnet: any, action:string): void {
toggleAction(subnet: any, action: string): void {
switch (action) {
case 'get':
this.http.post(`${this.baseUrl}/og-dhcp/server/${subnet.uuid}/get`, {}).subscribe({
@ -149,7 +151,8 @@ export class OgDhcpSubnetsComponent {
this.toastService.error(error.error['hydra:description']);
}
});
}})
}
})
break;
default:
console.error('Acción no soportada:', action);
@ -216,4 +219,26 @@ export class OgDhcpSubnetsComponent {
this.itemsPerPage = event.pageSize;
this.loadSubnets();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'serverInfoStep',
'syncDbStep',
'viewInfoStep',
'titleStep',
'addSubnetStep',
'searchNameStep',
'searchNetmaskStep',
'searchIpStep',
'searchBootFileStep',
'tableStep',
'actionsStep',
'paginationStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -78,11 +78,17 @@ th {
.button-container {
display: flex;
flex-direction: column;
gap: 10px; /* Espacio entre botones */
gap: 10px;
margin-top: 50px;
}
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 100px;
padding: 10px;
}
.btn:first-child {
margin-left: 0;

View File

@ -1,8 +1,13 @@
<div class="dashboard">
<h2>OgDhcp server Status</h2>
<div class="header-container" >
<h2 joyrideStep="titleStep" text="Esta sección muestra el estado general del servidor OgDhcp.">OgDhcp server Status</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
</div>
<div class="disk-usage-container">
<div class="disk-usage">
<div class="disk-usage" joyrideStep="diskUsageStep" text="Visualiza el uso del disco del servidor.">
<h3>Uso de disco</h3>
<ngx-charts-pie-chart
[view]="view"
@ -21,40 +26,40 @@
</div>
</div>
<div class="services-status">
<div class="services-status" joyrideStep="servicesStatusStep" text="Aquí puedes ver el estado de los servicios importantes del servidor.">
<h3>Servicios</h3>
<ul>
<li *ngFor="let service of getServices()">
<span
class="status-led"
[ngClass]="{ 'active': service.status === 'active', 'inactive': service.status !== 'active' }"
></span>
<span
class="status-led"
[ngClass]="{ 'active': service.status === 'active', 'inactive': service.status !== 'active' }"
></span>
{{ service.name }}: {{ service.status }}
</li>
</ul>
</div>
</div>
<div class="installed-oglives">
<div class="installed-oglives" joyrideStep="subnetsStep" text="Consulta la información de las subredes configuradas en el servidor.">
<h3>Subredes</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Boot file name</th>
<th>Next server</th>
<th>Ip</th>
<th>Ordenadores</th>
</tr>
<tr>
<th>ID</th>
<th>Boot file name</th>
<th>Next server</th>
<th>Ip</th>
<th>Ordenadores</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let subnet of subnets">
<td>{{ subnet.id }}</td>
<td>{{ subnet['boot-file-name'] }}</td>
<td>{{ subnet['next-server'] }}</td>
<td>{{ subnet.subnet }}</td>
<td>{{ subnet.reservations.length }}</td>
</tr>
<tr *ngFor="let subnet of subnets">
<td>{{ subnet.id }}</td>
<td>{{ subnet['boot-file-name'] }}</td>
<td>{{ subnet['next-server'] }}</td>
<td>{{ subnet.subnet }}</td>
<td>{{ subnet.reservations.length }}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-status',
@ -23,7 +24,8 @@ export class StatusComponent {
domain: ['#FF6384', '#3f51b5']
};
constructor(private http: HttpClient) {}
constructor(private http: HttpClient,
private joyrideService: JoyrideService) {}
ngOnInit(): void {
this.loadStatus();
@ -55,4 +57,18 @@ export class StatusComponent {
status: this.servicesStatus[key]
}));
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'diskUsageStep',
'servicesStatusStep',
'subnetsStep'
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,22 +1,30 @@
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar sistemas operativos</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="osTitleStep" text="En esta pantalla, puedes gestionar los sistemas operativos disponibles.">
Administrar sistemas operativos
</h2>
<div class="calendar-button-row">
<button mat-flat-button color="primary" (click)="addSoftware()">Añadir sistema operativo</button>
<button mat-flat-button color="primary" (click)="addSoftware()" joyrideStep="addOsButton" text="Añade un nuevo sistema operativo a la lista.">Añadir sistema operativo</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-string">
<mat-form-field appearance="fill" class="search-string" joyrideStep="searchField" text="Busca un sistema operativo por nombre. Pulsa 'enter' para iniciar la búsqueda.">
<mat-label i18n="@@searchLabel">Buscar nombre de sistema operativo</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="table" text="Esta tabla muestra los sistemas operativos existentes.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >
<td mat-cell *matCellDef="let image">
<ng-container>
{{ column.cell(image) }}
</ng-container>
@ -24,17 +32,22 @@
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;" joyrideStep="actionsHeader" text="Acciones disponibles para cada sistema operativo.">Acciones</th>
<td mat-cell *matCellDef="let calendar" style="text-align: center;">
<button mat-icon-button color="primary" (click)="editSoftware(calendar)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteSoftware(calendar)" i18n="@@buttonDelete"><mat-icon>delete</mat-icon></button>
<button mat-icon-button color="primary" (click)="editSoftware(calendar)" i18n="@@editImage" joyrideStep="editButton" text="Editar el sistema operativo seleccionado.">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteSoftware(calendar)" i18n="@@buttonDelete" joyrideStep="deleteButton" text="Eliminar el sistema operativo seleccionado.">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div class="paginator-container">
<div class="paginator-container" joyrideStep="pagination" text="Navega entre las páginas de sistemas operativos.">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"

View File

@ -9,6 +9,7 @@ import {CreateSoftwareComponent} from "../software/create-software/create-softwa
import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component";
import {PageEvent} from "@angular/material/paginator";
import {CreateOperativeSystemComponent} from "./create-operative-system/create-operative-system.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-operative-system',
@ -53,7 +54,8 @@ export class OperativeSystemComponent {
public dialog: MatDialog,
private http: HttpClient,
private dataService: DataService,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -124,4 +126,13 @@ export class OperativeSystemComponent {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: ['osTitleStep', 'addOsButton', 'searchField', 'table', 'actionsHeader', 'editButton', 'deleteButton', 'pagination'],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,11 +1,14 @@
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar perfiles software</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="titleStep" text="En esta pantalla podrás administrar los diferentes perfiles de software">Administrar perfiles software</h2>
<div class="calendar-button-row">
<button mat-flat-button color="primary" (click)="addSoftware()">Añadir perfil software</button>
<button mat-flat-button color="primary" (click)="addSoftware()" joyrideStep="addStep" text="Crea nuevos perfiles de software.">Añadir perfil software</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="filterStep" text="Utiliza los filtros para buscar entre los perfiles de software existentes." >
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de perfil</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['description']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
@ -13,7 +16,7 @@
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="Aquí se listarán los perfiles existentes y sus detalles.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >
@ -29,7 +32,7 @@
</td>
</ng-container>
<ng-container matColumnDef="actions">
<ng-container matColumnDef="actions" joyrideStep="actionsStep" text="Utiliza los botones dedicados para realizar diferentes acciones sobre los perfiles.">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let calendar" style="text-align: center;">
<button mat-icon-button color="primary" (click)="editSoftware(calendar)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>

View File

@ -8,6 +8,7 @@ import {ToastrService} from "ngx-toastr";
import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component";
import {PageEvent} from "@angular/material/paginator";
import {CreateSoftwareProfileComponent} from "./create-software-profile/create-software-profile.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-software-profile',
@ -57,7 +58,8 @@ export class SoftwareProfileComponent {
public dialog: MatDialog,
private http: HttpClient,
private dataService: DataService,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -128,4 +130,12 @@ export class SoftwareProfileComponent {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: ['titleStep','addStep', 'filterStep', 'tableStep', 'actionsStep'],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}

View File

@ -1,11 +1,14 @@
<div class="header-container">
<h2 class="title" i18n="@@adminImagesTitle">Administrar Software</h2>
<button mat-icon-button color="primary" (click)="iniciarTour()">
<mat-icon>help</mat-icon>
</button>
<h2 class="title" i18n="@@adminImagesTitle" joyrideStep="titleStep" text="Administra el software deisponible desde este componente.">Administrar Software</h2>
<div class="calendar-button-row">
<button mat-flat-button color="primary" (click)="addSoftware()">Añadir software</button>
<button mat-flat-button color="primary" (click)="addSoftware()" joyrideStep="addSoftwareStep" text="Utiliza este botón par añadir software nuevo.">Añadir software</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<div class="search-container" joyrideStep="searchStep" text="Utiliza los filtros para buscar entre el software listado.">
<mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de software</mat-label>
<input matInput name="searchBar" placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
@ -21,7 +24,7 @@
</mat-select>
</mat-form-field>
</div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyride="tableStep" text="Aquí se mostrará todo el software disponible y sus características.">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let image" >

View File

@ -5,10 +5,10 @@ import {MatDialog} from "@angular/material/dialog";
import {HttpClient} from "@angular/common/http";
import {DataService} from "./data.service";
import {ToastrService} from "ngx-toastr";
import {CreateCalendarComponent} from "../calendar/create-calendar/create-calendar.component";
import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component";
import {PageEvent} from "@angular/material/paginator";
import {CreateSoftwareComponent} from "./create-software/create-software.component";
import { JoyrideService } from 'ngx-joyride';
@Component({
selector: 'app-software',
@ -63,7 +63,8 @@ export class SoftwareComponent {
public dialog: MatDialog,
private http: HttpClient,
private dataService: DataService,
private toastService: ToastrService
private toastService: ToastrService,
private joyrideService: JoyrideService
) {}
ngOnInit(): void {
@ -134,4 +135,18 @@ export class SoftwareComponent {
this.length = event.length;
this.search();
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [
'titleStep',
'addSoftwareStep',
'searchStep',
'tableStep',
],
showPrevButton: true,
themeColor: '#3f51b5'
});
}
}