solve conflicts
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
commit
cd09659e6c
|
@ -20,6 +20,7 @@
|
|||
"@angular/router": "^18.0.0",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"ngx-joyride": "^2.5.0",
|
||||
"ngx-toastr": "^19.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
|
@ -11228,6 +11229,18 @@
|
|||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ngx-joyride": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-joyride/-/ngx-joyride-2.5.0.tgz",
|
||||
"integrity": "sha512-C/J8C4uWZjKl9aMmRBt9egVjuIpwWFplJgBZDl1EfqNVTJkdEC51nt9DpAOuDwOgkbArhJ9sZIk3bZT4vkud/w==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=8.2.14",
|
||||
"@angular/core": ">=8.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-toastr": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.0.0.tgz",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@angular/router": "^18.0.0",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"ngx-joyride": "^2.5.0",
|
||||
"ngx-toastr": "^19.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
|
|
|
@ -120,6 +120,8 @@ import { CreateRepositoryComponent } from './components/repositories/create-repo
|
|||
import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component';
|
||||
import { DeployImageComponent } from './components/groups/components/client-main-view/deploy-image/deploy-image.component';
|
||||
import { MainRepositoryViewComponent } from './components/repositories/main-repository-view/main-repository-view.component';
|
||||
import { ExecuteCommandOuComponent } from './components/groups/shared/execute-command-ou/execute-command-ou.component';
|
||||
import { JoyrideModule } from 'ngx-joyride';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -197,6 +199,7 @@ import { MainRepositoryViewComponent } from './components/repositories/main-repo
|
|||
ExecuteCommandComponent,
|
||||
DeployImageComponent,
|
||||
MainRepositoryViewComponent,
|
||||
ExecuteCommandOuComponent,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [BrowserModule,
|
||||
|
@ -225,6 +228,7 @@ import { MainRepositoryViewComponent } from './components/repositories/main-repo
|
|||
MatDatepickerModule,
|
||||
MatNativeDateModule,
|
||||
MatSliderModule,
|
||||
JoyrideModule.forRoot(),
|
||||
ToastrModule.forRoot(
|
||||
{
|
||||
timeOut: 5000,
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Administrar calendarios</h2>
|
||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||
<mat-icon>help</mat-icon>
|
||||
</button>
|
||||
<h2 joyrideStep="titleStep" text="En esta pantalla, puedes gestionar los calendarios de los equipos remotos conectados con el servicio UDS" class="title" i18n="@@adminImagesTitle">Administrar calendarios</h2>
|
||||
<div class="calendar-button-row">
|
||||
<button mat-flat-button color="primary" (click)="addImage()">Añadir calendario</button>
|
||||
|
||||
<button joyrideStep="addButtonStep" text="Haz clic aquí para añadir un nuevo calendario." mat-flat-button color="primary" (click)="addImage()">Añadir calendario</button>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div class="search-container">
|
||||
<mat-form-field appearance="fill" class="search-string">
|
||||
<mat-form-field joyrideStep="searchStep" text="Utiliza esta barra de búsqueda para filtrar los calendarios existentes." appearance="fill" class="search-string">
|
||||
<mat-label i18n="@@searchLabel">Buscar nombre de calendario</mat-label>
|
||||
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
|
||||
<mat-icon matSuffix>search</mat-icon>
|
||||
|
@ -19,7 +24,7 @@
|
|||
</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 muestran los calendarios existentes con sus características y configuraciones.">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" >
|
||||
|
@ -35,7 +40,7 @@
|
|||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<ng-container matColumnDef="actions" joyrideStep="actionsStep" text="Accede a las acciones disponibles para cada calendario aquí.">
|
||||
<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)="editCalendar(calendar)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
|
||||
|
|
|
@ -8,6 +8,7 @@ import { ToastrService } from "ngx-toastr";
|
|||
import { PageEvent } from "@angular/material/paginator";
|
||||
import { CreateCalendarComponent } from "./create-calendar/create-calendar.component";
|
||||
import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
import { JoyrideService } from 'ngx-joyride';
|
||||
|
||||
@Component({
|
||||
selector: 'app-calendar',
|
||||
|
@ -51,14 +52,14 @@ export class CalendarComponent implements OnInit {
|
|||
}
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||
|
||||
private apiUrl = `${this.baseUrl}/remote-calendars`;
|
||||
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private dataService: DataService,
|
||||
private toastService: ToastrService
|
||||
private toastService: ToastrService,
|
||||
private joyrideService: JoyrideService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -71,7 +72,6 @@ export class CalendarComponent implements OnInit {
|
|||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
console.log('The dialog was closed');
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
|
@ -84,24 +84,21 @@ export class CalendarComponent implements OnInit {
|
|||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching og lives', error);
|
||||
console.error('Error fetching calendars', error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sync(calendar: any): void {
|
||||
console.log('Syncing calendars');
|
||||
this.syncUds = true;
|
||||
this.http.post(`${this.apiUrl}/${calendar.uuid}/sync-uds`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Calendars synced successfully');
|
||||
this.toastService.success('Calendarios sincronizados correctamente');
|
||||
this.search();
|
||||
this.syncUds = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al sincronizar los calendarios:', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
this.syncUds = false;
|
||||
}
|
||||
|
@ -133,16 +130,13 @@ export class CalendarComponent implements OnInit {
|
|||
|
||||
this.http.delete(apiUrl).subscribe({
|
||||
next: () => {
|
||||
console.log('Calendar deleted successfully');
|
||||
this.search();
|
||||
this.toastService.success('Calendar deleted successfully');
|
||||
},
|
||||
error: (error) => {
|
||||
error: () => {
|
||||
this.toastService.error('Error deleting calendar');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('calendar deletion cancelled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -155,8 +149,7 @@ export class CalendarComponent implements OnInit {
|
|||
this.length = response['hydra:totalItems'];
|
||||
this.loading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
error: () => {
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
|
@ -167,4 +160,12 @@ export class CalendarComponent implements OnInit {
|
|||
this.itemsPerPage = event.pageSize;
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: ['titleStep', 'addButtonStep', 'searchStep', 'tableStep', 'actionsStep'],
|
||||
showPrevButton: true,
|
||||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)">
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminCommandsTitle">Trazas</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 +21,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">
|
||||
|
@ -41,7 +46,7 @@
|
|||
</div>
|
||||
|
||||
<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">
|
||||
|
@ -75,8 +80,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"
|
||||
|
|
|
@ -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',
|
||||
|
@ -68,7 +69,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();
|
||||
|
@ -188,4 +190,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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<mat-dialog-content class="form-container">
|
||||
<form [formGroup]="form" class="command-form">
|
||||
|
||||
<!-- Selector de Unidad Organizativa -->
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Unidad Organizativa</mat-label>
|
||||
<mat-select formControlName="unit">
|
||||
|
@ -11,7 +10,6 @@
|
|||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Selector de Subunidad Organizativa -->
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Subunidad Organizativa</mat-label>
|
||||
<mat-select formControlName="childUnit">
|
||||
|
@ -19,7 +17,6 @@
|
|||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Selector de Cliente (PC) con checkboxes -->
|
||||
<div class="checkbox-group">
|
||||
<label>Clientes (PC)</label>
|
||||
<div *ngIf="clients.length > 0">
|
||||
|
@ -40,4 +37,4 @@
|
|||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="closeModal()">Cancelar</button>
|
||||
<button mat-button (click)="executeCommand()" [disabled]="!form.get('clientSelection')?.value.length">Ejecutar</button>
|
||||
</mat-dialog-actions>
|
||||
</mat-dialog-actions>
|
||||
|
|
|
@ -21,7 +21,11 @@
|
|||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.unidad-card, .elements-card {
|
||||
|
@ -183,44 +187,49 @@ mat-spinner {
|
|||
margin: 10px 10px;
|
||||
}
|
||||
|
||||
.result-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.result-checkbox {
|
||||
float: right;
|
||||
margin: 0;
|
||||
.result-card.small-card {
|
||||
width: 100%;
|
||||
max-width: 180px;
|
||||
height: auto;
|
||||
min-height: 130px;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
padding-top: 10px;
|
||||
padding-top: 5px;
|
||||
color: #555;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.result-type {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
.result-type, .result-ip, .result-mac, .result-status, .result-internal-units, .result-clients {
|
||||
font-size: 0.8rem;
|
||||
margin: 2px 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.result-ip, .result-mac, .result-status {
|
||||
font-size: 0.9rem;
|
||||
margin: 5px 0;
|
||||
.result-checkbox {
|
||||
align-self: flex-start;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.result-internal-units,
|
||||
.result-clients {
|
||||
font-size: 0.9rem;
|
||||
color: #007bff;
|
||||
color: #007bff;
|
||||
margin: 5px 0;
|
||||
.result-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.paginator-container {
|
||||
|
@ -242,7 +251,6 @@ mat-card {
|
|||
}
|
||||
|
||||
.red-card {
|
||||
background-color: #f35f53;
|
||||
background-color: #f35f53;
|
||||
color: white;
|
||||
}
|
||||
|
@ -250,8 +258,6 @@ mat-card {
|
|||
.green-card {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.view-mode-buttons button.active {
|
||||
|
@ -273,7 +279,6 @@ mat-card {
|
|||
}
|
||||
|
||||
.result-card-list .result-title {
|
||||
font-size: 14px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
|
@ -284,12 +289,10 @@ mat-card {
|
|||
flex-direction: row;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-card-list p {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.result-list {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
<h2 class="title" i18n="@@searchTitle">Búsqueda avanzada</h2>
|
||||
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@searchTitle" joyrideStep="title2Step" text="Aquí puedes realizar una búsqueda avanzada.">Búsqueda avanzada</h2>
|
||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||
<mat-icon>help</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header" joyrideStep="filterSelectionStep" text="Selecciona entre los filtros guardados para ajustar los resultados de la búsqueda.">
|
||||
<mat-form-field>
|
||||
<mat-label i18n="@@selectFilterLabel">Seleccione filtro</mat-label>
|
||||
<mat-select (selectionChange)="loadSelectedFilter($event.value)">
|
||||
|
@ -14,7 +19,7 @@
|
|||
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div class="view-mode-buttons">
|
||||
<div class="view-mode-buttons" joyrideStep="viewModeStep" text="Elige cómo quieres ver los resultados: en cuadrícula o en lista.">
|
||||
<button mat-button (click)="changeViewMode('grid')" [class.active]="viewMode === 'grid'">
|
||||
<mat-icon>grid_view</mat-icon> Cuadrícula
|
||||
</button>
|
||||
|
@ -26,7 +31,7 @@
|
|||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="filters">
|
||||
<div class="filters" joyrideStep="filtersStep" text="Aplica filtros específicos para encontrar los resultados exactos que necesitas.">
|
||||
<mat-form-field>
|
||||
<mat-label i18n="@@selectOptionLabel">Selecciona una opción</mat-label>
|
||||
<mat-select [(value)]="selectedFilter1" (selectionChange)="applyFilter()">
|
||||
|
@ -41,9 +46,9 @@
|
|||
</mat-form-field>
|
||||
|
||||
<div class="button-group">
|
||||
<button mat-raised-button color="primary" (click)="saveFilters()" i18n="@@saveFiltersButton">Guardar Filtros</button>
|
||||
<button mat-raised-button color="accent" [disabled]="selectedElements.length === 0 || selectedFilter1 === 'ou'" (click)="sendActions()" i18n="@@sendFiltersButton" >Enviar Acción</button>
|
||||
<button mat-flat-button color="primary" [disabled]="selectedElements.length === 0 || selectedFilter1 === 'ou'" (click)="onPxeBootFile()">Añadir fichero PXE</button>
|
||||
<button mat-raised-button color="primary" (click)="saveFilters()" i18n="@@saveFiltersButton" joyrideStep="saveFiltersStep" text="Guarda tus filtros seleccionados para usarlos en el futuro.">Guardar Filtros</button>
|
||||
<button mat-raised-button color="accent" (click)="sendActions()" i18n="@@sendFiltersButton" [disabled]="selectedElements.length === 0" joyrideStep="sendActionStep" text="Envía una acción a los elementos seleccionados.">Enviar Acción</button>
|
||||
<button mat-flat-button color="primary" [disabled]="selectedElements.length === 0" (click)="onPxeBootFile()" joyrideStep="addPxeStep" text="Añade un archivo PXE a los elementos seleccionados.">Añadir fichero PXE</button>
|
||||
<button mat-raised-button color="primary" [matMenuTriggerFor]="menu" [disabled]="selectedFilter1 === 'ou'">
|
||||
Asistentes
|
||||
</button>
|
||||
|
@ -55,12 +60,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="results">
|
||||
<div class="results" joyrideStep="resultsStep" text="Aquí verás los resultados de tu búsqueda filtrada.">
|
||||
<ng-container *ngIf="filteredResults && filteredResults.length > 0; else noResults">
|
||||
<ng-container *ngIf="viewMode === 'grid'">
|
||||
<mat-grid-list cols="5" rowHeight="1:1">
|
||||
<mat-grid-list cols="8" rowHeight="1:1">
|
||||
<mat-grid-tile *ngFor="let result of filteredResults">
|
||||
<mat-card class="result-card">
|
||||
<mat-card class="result-card small-card">
|
||||
<mat-checkbox [checked]="isSelected(result.name)" (change)="onCheckboxChange($event, result.name, result['@id'])" class="result-checkbox"></mat-checkbox>
|
||||
<mat-card-title class="result-title">{{ result.name }}</mat-card-title>
|
||||
<mat-card-content class="result-content">
|
||||
|
@ -102,7 +107,7 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="paginator-container">
|
||||
<div class="paginator-container" joyrideStep="paginationStep" text="Usa el paginador para navegar entre los resultados.">
|
||||
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions" (page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
|
|
@ -23,6 +23,7 @@ import { Router } from '@angular/router';
|
|||
import {
|
||||
CreatePxeBootFileComponent
|
||||
} from "../../../ogboot/pxe-boot-files/create-pxeBootFile/create-pxe-boot-file/create-pxe-boot-file.component";
|
||||
import { JoyrideService } from 'ngx-joyride';
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -69,7 +70,8 @@ export class AdvancedSearchComponent {
|
|||
private toastService: ToastrService,
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
private http: HttpClient,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
private joyrideService: JoyrideService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -472,4 +474,23 @@ export class AdvancedSearchComponent {
|
|||
}
|
||||
}
|
||||
|
||||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: [
|
||||
'title2Step',
|
||||
'filterSelectionStep',
|
||||
'viewModeStep',
|
||||
'filtersStep',
|
||||
'selectAllStep',
|
||||
'saveFiltersStep',
|
||||
'sendActionStep',
|
||||
'addPxeStep',
|
||||
'resultsStep',
|
||||
'paginationStep'
|
||||
],
|
||||
showPrevButton: true,
|
||||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,49 +1,57 @@
|
|||
<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>
|
||||
<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 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>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!loading" class="loading-container">
|
||||
<mat-spinner></mat-spinner>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div *ngIf="loading">
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<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">
|
||||
<td mat-cell *matCellDef="let client" >
|
||||
<ng-container *ngIf="column.columnDef === 'name'">
|
||||
<div class="client-info">
|
||||
<div class="client-name">{{ client.name }}</div>
|
||||
|
@ -51,7 +59,6 @@
|
|||
<div class="client-mac">{{ client.mac }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'status'">
|
||||
<mat-chip [ngClass]="{
|
||||
'chip-og-live': client.status === 'og-live',
|
||||
|
@ -64,21 +71,19 @@
|
|||
{{ 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;">
|
||||
<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="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>
|
||||
|
@ -88,7 +93,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 resultados utilizando el paginador.">
|
||||
<mat-paginator [length]="length"
|
||||
[pageSize]="itemsPerPage"
|
||||
[pageIndex]="page"
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -194,8 +194,7 @@ mat-spinner {
|
|||
.result-card {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
height: 250px; /* Fijo para mantener la forma cuadrada */
|
||||
}
|
||||
height: 250px;
|
||||
|
||||
.paginator-container {
|
||||
display: flex;
|
||||
|
@ -210,3 +209,7 @@ mat-spinner {
|
|||
mat-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mat-tooltip {
|
||||
white-space: pre-line;
|
||||
}
|
|
@ -1,21 +1,39 @@
|
|||
<mat-tab-group (selectedTabChange)="onTabChange($event)">
|
||||
<mat-tab label="General">
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminGroupsTitle">Administrar grupos</h2>
|
||||
<div class="groups-button-row">
|
||||
<button mat-flat-button color="primary" (click)="addOU($event)" i18n="@@newOrganizationalUnitButton">Nueva Unidad Organizativa</button>
|
||||
<button mat-flat-button color="primary" (click)="addClient($event)" i18n="@@newClientButton">Nuevo Cliente</button>
|
||||
<button mat-raised-button (click)="openBottomSheet()" i18n="@@legendButton">Leyenda</button>
|
||||
<div class="header-container" joyrideStep="tabsStep" text="Utiliza las pestañás para acceder a las diferentes opciones de visualización y busqueda de unidades organizativas y clientes.">
|
||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||
<mat-icon>help</mat-icon>
|
||||
</button>
|
||||
<h2 class="title" i18n="@@adminGroupsTitle" joyrideStep="titleStep" text="En esta pantalla, puedes gestionar las unidades organizativas y sus clientes.">Administrar grupos</h2>
|
||||
<div class="groups-button-row" joyrideStep="addStep" text="Utiliza estos botones para crear nuevas unidades organizativas o clientes.">
|
||||
<button mat-flat-button color="primary" (click)="addOU($event)"
|
||||
i18n="@@newOrganizationalUnitButton"
|
||||
matTooltip="Abrir modal para crear unidades organizativas de cualquier tipo (Centro, Aula, Grupo de aulas o Grupo de clientes)"
|
||||
matTooltipShowDelay="1000">Nueva Unidad Organizativa</button>
|
||||
|
||||
<button mat-flat-button color="primary" (click)="addClient($event)"
|
||||
i18n="@@newClientButton"
|
||||
matTooltipShowDelay="1000">Nuevo Cliente</button>
|
||||
|
||||
<button mat-raised-button (click)="openBottomSheet()"
|
||||
i18n="@@legendButton"
|
||||
joyrideStep="keyStep" text="La leyenda te mostrará los tipos de unidades organizativas y sus iconos correspondientes"
|
||||
matTooltipShowDelay="1000">Leyenda</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="groupLists-container">
|
||||
<mat-card class="card unidad-card">
|
||||
<mat-card class="card unidad-card"
|
||||
joyrideStep="unitStep" text="Esta es la sección donde se mostrarán las unidades organizativas de tipo 'Centro'"
|
||||
matTooltipShowDelay="1000"
|
||||
matTooltipPosition="above">
|
||||
<mat-card-title i18n="@@organizationalUnitTitle">Centros</mat-card-title>
|
||||
<mat-card-content>
|
||||
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||
<mat-list *ngIf="!loading">
|
||||
<mat-list-item *ngFor="let unidad of organizationalUnits"
|
||||
[ngClass]="{'selected-item': unidad === selectedUnidad, 'clickable-item': true}" (click)="onSelectUnidad(unidad)">
|
||||
[ngClass]="{'selected-item': unidad === selectedUnidad, 'clickable-item': true}"
|
||||
(click)="onSelectUnidad(unidad)">
|
||||
<div class="item-content">
|
||||
<mat-icon>apartment</mat-icon>
|
||||
{{ unidad.name }}
|
||||
|
@ -23,48 +41,37 @@
|
|||
<mat-icon mat-button [matMenuTriggerFor]="menu" (click)="$event.stopPropagation()">more_vert</mat-icon>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="onTreeClick($event, unidad)">
|
||||
<mat-icon
|
||||
#tooltip="matTooltip"
|
||||
matTooltip="Visualizar en forma de arbol"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@viewTreeTooltip">account_tree
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="Visualizar unidad en forma de árbol"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@viewTreeTooltip">account_tree</mat-icon>
|
||||
<span i18n="@@viewTreeMenu">Ver organigrama</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="onEditClick($event, unidad.type, unidad.uuid)">
|
||||
<mat-icon
|
||||
#tooltip="matTooltip"
|
||||
matTooltip="Editar unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@editUnitTooltip">edit
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="Editar esta unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@editUnitTooltip">edit</mat-icon>
|
||||
<span i18n="@@editUnitMenu">Editar</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="onShowClick($event, unidad)">
|
||||
<mat-icon
|
||||
#tooltip="matTooltip"
|
||||
matTooltip="Visualizar unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@viewUnitTool">visibility
|
||||
</mat-icon>
|
||||
<span i18n="@@viewUnitMenu">Visualizar datos</span>
|
||||
<mat-icon matTooltip="Ver detalles de la unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@viewUnitTooltip">visibility</mat-icon>
|
||||
<span i18n="@@viewUnitMenu">Ver datos</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="addOU($event, unidad)">
|
||||
<mat-icon
|
||||
#tooltip="matTooltip"
|
||||
matTooltip="Crear unidad organizativa interna"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addInternalUnitTool">add_home_work
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="Crear una nueva unidad organizativa interna"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addInternalUnitTooltip">add_home_work</mat-icon>
|
||||
<span i18n="@@addInternalUnitMenu">Añadir unidad organizativa</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="addClient($event, unidad)">
|
||||
<mat-icon
|
||||
#tooltip="matTooltip"
|
||||
matTooltip="Crear cliente en esta unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addClientDevice">devices
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="Registrar un cliente en esta unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addClientTooltip">devices</mat-icon>
|
||||
<span i18n="@@addClientMenu">Crear cliente</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
@ -74,7 +81,11 @@
|
|||
</mat-list>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="card elements-card">
|
||||
|
||||
<mat-card class="card elements-card"
|
||||
joyrideStep="elementsStep" text="Esta es la sección para visualizar unidades internas del centro seleccionado y navegar por ellas."
|
||||
matTooltipShowDelay="1000"
|
||||
matTooltipPosition="above">
|
||||
<mat-card-title>
|
||||
<div class="title-with-breadcrumb">
|
||||
<span i18n="@@internalElementsTitle"></span>
|
||||
|
@ -93,7 +104,9 @@
|
|||
<mat-icon>info</mat-icon>
|
||||
<span i18n="@@noInternalElementsMessage">No hay elementos internos</span>
|
||||
</div>
|
||||
<mat-list-item *ngFor="let child of children" [ngClass]="{'selected-item': child === selectedUnidad, 'clickable-item': true}" (click)="onSelectChild(child)">
|
||||
<mat-list-item *ngFor="let child of children"
|
||||
[ngClass]="{'selected-item': child === selectedUnidad, 'clickable-item': true}"
|
||||
(click)="onSelectChild(child)">
|
||||
<div class="item-content">
|
||||
<mat-icon [ngSwitch]="child.type">
|
||||
<ng-container *ngSwitchCase="'organizational-unit'">apartment</ng-container>
|
||||
|
@ -108,24 +121,44 @@
|
|||
<mat-icon mat-button [matMenuTriggerFor]="menu" (click)="$event.stopPropagation()">more_vert</mat-icon>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="onEditClick($event, child.type, child.uuid)">
|
||||
<mat-icon class="edit-icon" #tooltip="matTooltip" matTooltip="Editar elemento" matTooltipHideDelay="0" i18n="@@editElementTooltip">edit</mat-icon>
|
||||
<mat-icon matTooltip="Editar elemento"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@editElementTooltip">edit</mat-icon>
|
||||
<span i18n="@@editElementMenu">Editar</span>
|
||||
</button>
|
||||
|
||||
<button *ngIf="child.type !== 'client'" mat-menu-item (click)="onShowClick($event, child)">
|
||||
<mat-icon class="edit-icon" #tooltip="matTooltip" matTooltip="Visualizar unidad organizativa" matTooltipHideDelay="0" i18n="@@viewUnitTooltip">visibility</mat-icon>
|
||||
<span i18n="@@viewUnitMenu">Visualizar datos</span>
|
||||
<mat-icon matTooltip="Ver detalles de la unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@viewUnitTooltip">visibility</mat-icon>
|
||||
<span i18n="@@viewUnitMenu">Ver datos</span>
|
||||
</button>
|
||||
|
||||
<button *ngIf="child.type !== 'client'" mat-menu-item (click)="addOU($event, child)">
|
||||
<mat-icon class="edit-icon" #tooltip="matTooltip" matTooltip="Crear unidad organizativa interna" matTooltipHideDelay="0" i18n="@@addInternalUnitTooltip">add_home_work</mat-icon>
|
||||
<mat-icon matTooltip="Crear una nueva unidad organizativa interna"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addInternalUnitTooltip">add_home_work</mat-icon>
|
||||
<span i18n="@@addInternalUnitMenu">Añadir unidad organizativa</span>
|
||||
</button>
|
||||
|
||||
<button *ngIf="child.type !== 'client'" mat-menu-item (click)="addClient($event, child)">
|
||||
<mat-icon class="edit-icon" #tooltip="matTooltip" matTooltip="Crear cliente en esta unidad organizativa" matTooltipHideDelay="0" i18n="@@addClientTooltip">devices</mat-icon>
|
||||
<mat-icon matTooltip="Registrar un cliente en esta unidad organizativa"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@addClientTooltip">devices</mat-icon>
|
||||
<span i18n="@@addClientMenu">Crear cliente</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="onDeleteClick($event, child.uuid, child.name, child.type)">
|
||||
<mat-icon class="delete-icon" #tooltip="matTooltip" matTooltip="Borrar elemento" matTooltipHideDelay="0" i18n="@@deleteElementTooltip">delete</mat-icon>
|
||||
<span i18n="@@deleteElementMenu">Borrar elemento</span>
|
||||
<mat-icon matTooltip="Eliminar este elemento"
|
||||
matTooltipHideDelay="0"
|
||||
i18n="@@deleteElementTooltip">delete</mat-icon>
|
||||
<span i18n="@@deleteElementMenu">Eliminar elemento</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="onExecuteCommand($event, child.uuid, child.name, child.type)">
|
||||
<mat-icon matTooltip="Ejecutar comando en este elemento"
|
||||
matTooltipHideDelay="0">play_arrow</mat-icon>
|
||||
<span>Ejecutar comando</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
@ -136,12 +169,15 @@
|
|||
</mat-card>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab i18n-label label="Búsqueda avanzada">
|
||||
<app-advanced-search></app-advanced-search>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab i18n-label label="Clientes">
|
||||
<app-client-tab-view #clientTab></app-client-tab-view>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab i18n-label label="Unidades organizativas">
|
||||
<app-organizational-unit-tab-view #organizationalUnitTab></app-organizational-unit-tab-view>
|
||||
</mat-tab>
|
||||
|
|
|
@ -25,6 +25,9 @@ import {ClientTabViewComponent} from "./components/client-tab-view/client-tab-vi
|
|||
import {
|
||||
OrganizationalUnitTabViewComponent
|
||||
} from "./components/organizational-unit-tab-view/organizational-unit-tab-view.component";
|
||||
import { ExecuteCommandComponent } from '../commands/main-commands/execute-command/execute-command.component';
|
||||
import { ExecuteCommandOuComponent } from './shared/execute-command-ou/execute-command-ou.component';
|
||||
import { JoyrideService } from 'ngx-joyride';
|
||||
|
||||
@Component({
|
||||
selector: 'app-groups',
|
||||
|
@ -67,7 +70,8 @@ export class GroupsComponent implements OnInit {
|
|||
public dialog: MatDialog,
|
||||
private toastService: ToastrService,
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
private http: HttpClient
|
||||
private http: HttpClient,
|
||||
private joyrideService: JoyrideService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -298,6 +302,21 @@ export class GroupsComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
onExecuteCommand(event: MouseEvent, child: any, name: string, type:string): void {
|
||||
console.log('Executing command on:', child);
|
||||
|
||||
this.dialog.open(ExecuteCommandOuComponent, {
|
||||
width: '50%',
|
||||
data: { childUnitUuid: child }
|
||||
}).afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
console.log('Comando ejecutado con éxito');
|
||||
} else {
|
||||
console.log('Ejecución de comando cancelada');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openSnackBar(isError: boolean, message: string) {
|
||||
if (isError) {
|
||||
this.toastService.error(' Error al eliminar la entidad: ' + message, 'Error');
|
||||
|
@ -432,4 +451,11 @@ export class GroupsComponent implements OnInit {
|
|||
const dialogRef = this.dialog.open(AcctionsModalComponent, { data: { selectedElements: this.selectedElements }, width: '700px'});
|
||||
}
|
||||
|
||||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: ['titleStep', 'addStep', 'keyStep', 'unitStep', 'elementsStep', 'tabsStep'],
|
||||
showPrevButton: true,
|
||||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
.form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.command-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mat-checkbox {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mat-dialog-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mat-dialog-content {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mat-dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
button[mat-button] {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button[mat-button]:disabled {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<h2 mat-dialog-title>Ejecutar Comando o Grupo de Comandos</h2>
|
||||
|
||||
<mat-dialog-content class="form-container">
|
||||
<form [formGroup]="form" class="command-form">
|
||||
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Seleccione Comando</mat-label>
|
||||
<mat-select formControlName="selectedCommand" (selectionChange)="form.get('selectedCommandGroup')?.reset()">
|
||||
<mat-option *ngFor="let command of commands" [value]="command.uuid">{{ command.name }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Seleccione Grupo de Comandos</mat-label>
|
||||
<mat-select formControlName="selectedCommandGroup" (selectionChange)="form.get('selectedCommand')?.reset()">
|
||||
<mat-option *ngFor="let group of commandGroups" [value]="group.uuid">{{ group.name }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<label>Clientes</label>
|
||||
<div *ngIf="clients.length > 0">
|
||||
<mat-checkbox *ngFor="let client of clients"
|
||||
(change)="toggleClientSelection(client.uuid)"
|
||||
[checked]="form.get('clientSelection')?.value.includes(client.uuid)">
|
||||
{{ client.name }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div *ngIf="clients.length === 0">
|
||||
<p>No hay clientes disponibles</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="closeModal()">Cancelar</button>
|
||||
<button mat-button
|
||||
(click)="executeCommand()"
|
||||
[disabled]="!form.get('clientSelection')?.value.length ||
|
||||
(!form.get('selectedCommand')?.value && !form.get('selectedCommandGroup')?.value)">
|
||||
Ejecutar
|
||||
</button>
|
||||
</mat-dialog-actions>
|
|
@ -0,0 +1,120 @@
|
|||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
@Component({
|
||||
selector: 'app-execute-command-ou',
|
||||
templateUrl: './execute-command-ou.component.html',
|
||||
styleUrls: ['./execute-command-ou.component.css']
|
||||
})
|
||||
export class ExecuteCommandOuComponent implements OnInit {
|
||||
form: FormGroup;
|
||||
clients: any[] = [];
|
||||
commands: any[] = [];
|
||||
commandGroups: any[] = [];
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<ExecuteCommandOuComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private http: HttpClient,
|
||||
private fb: FormBuilder,
|
||||
private toastService: ToastrService,
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
selectedCommand: [null],
|
||||
selectedCommandGroup: [null],
|
||||
clientSelection: [[]]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadClients();
|
||||
this.loadCommands();
|
||||
this.loadCommandGroups();
|
||||
}
|
||||
|
||||
loadClients(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/organizational-units/${this.data.childUnitUuid}`).subscribe(
|
||||
response => {
|
||||
this.clients = this.getAllClients(response);
|
||||
const clientIds = this.clients.map(client => client.uuid);
|
||||
this.form.get('clientSelection')?.setValue(clientIds);
|
||||
},
|
||||
error => console.error('Error al cargar los clientes:', error)
|
||||
);
|
||||
}
|
||||
|
||||
getAllClients(unit: any): any[] {
|
||||
let allClients = unit.clients || [];
|
||||
if (unit.children && unit.children.length > 0) {
|
||||
unit.children.forEach((child: any) => {
|
||||
allClients = allClients.concat(this.getAllClients(child));
|
||||
});
|
||||
}
|
||||
return allClients;
|
||||
}
|
||||
|
||||
loadCommands(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/commands?page=1&itemsPerPage=30`).subscribe(
|
||||
response => {
|
||||
this.commands = response['hydra:member'] || [];
|
||||
},
|
||||
error => this.toastService.error('Error al cargar comandos:', error)
|
||||
);
|
||||
}
|
||||
|
||||
loadCommandGroups(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/command-groups?page=1&itemsPerPage=30`).subscribe(
|
||||
response => {
|
||||
this.commandGroups = response['hydra:member'] || [];
|
||||
},
|
||||
error => this.toastService.error('Error al cargar grupos de comandos:', error)
|
||||
);
|
||||
}
|
||||
|
||||
toggleClientSelection(clientId: string): void {
|
||||
const selectedClients = this.form.get('clientSelection')?.value || [];
|
||||
if (selectedClients.includes(clientId)) {
|
||||
this.form.get('clientSelection')?.setValue(selectedClients.filter((id: string) => id !== clientId));
|
||||
} else {
|
||||
this.form.get('clientSelection')?.setValue([...selectedClients, clientId]);
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(): void {
|
||||
const selectedCommandUuid = this.form.get('selectedCommand')?.value || this.form.get('selectedCommandGroup')?.value;
|
||||
const isCommandGroup = !!this.form.get('selectedCommandGroup')?.value;
|
||||
|
||||
if (!selectedCommandUuid) {
|
||||
console.warn('No se ha seleccionado ningún comando o grupo de comandos');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
clients: (this.form.get('clientSelection')?.value || []).map((clientId: string) => `/clients/${clientId}`)
|
||||
};
|
||||
|
||||
const url = isCommandGroup
|
||||
? `${this.baseUrl}/command-groups/${selectedCommandUuid}/execute`
|
||||
: `${this.baseUrl}/commands/${selectedCommandUuid}/execute`;
|
||||
|
||||
this.http.post(url, payload)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.toastService.success('Comando ejecutado con éxito');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error('Error al ejecutar el comando:', error);
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeModal(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
|
@ -1,78 +1,64 @@
|
|||
|
||||
<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>
|
||||
<div class="header-container">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<!-- Chip for 'status' column -->
|
||||
<ng-container *ngIf="column.columnDef === 'status'">
|
||||
<mat-chip [ngClass]="{
|
||||
'status-success': image[column.columnDef] === 'success',
|
||||
'status-failed': image[column.columnDef] === 'failed',
|
||||
'status-pending': image[column.columnDef] === 'pending'
|
||||
}"
|
||||
>
|
||||
{{ image[column.columnDef] }}
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
<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>
|
||||
>>>>>>> c168c87fc9ebcc4b5cf9a68effa16bdb2a848e00
|
||||
|
||||
<!-- Icon for 'remotePc' column -->
|
||||
<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 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>
|
||||
|
||||
<!-- Default cell content for other columns -->
|
||||
<ng-container *ngIf="column.columnDef !== 'status' && column.columnDef !== 'remotePc'">
|
||||
{{ column.cell(image) }}
|
||||
</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 image" style="text-align: center;">
|
||||
<button mat-icon-button color="info" (click)="showImageInfo($event, image)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
|
||||
<button mat-icon-button color="primary" (click)="editImage($event, image)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
|
||||
<button mat-icon-button color="warn" (click)="deleteImage($event, image)">
|
||||
<mat-icon i18n="@@deleteElementTooltip">delete</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(image, 'get-aux')">Obtener ficheros auxiliares</button>
|
||||
<button mat-menu-item [disabled]="!image.imageFullsum" (click)="toggleAction(image, 'get-aux')">Eliminar imagen temporalmente</button>
|
||||
</mat-menu>
|
||||
</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]="[5, 10, 20, 40, 100]"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
<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>
|
||||
|
|
|
@ -10,6 +10,7 @@ import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delet
|
|||
import {ServerInfoDialogComponent} from "../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
|
||||
import {Observable} from "rxjs";
|
||||
import {InfoImageComponent} from "../ogboot/pxe-images/info-image/info-image/info-image.component";
|
||||
import { JoyrideService } from 'ngx-joyride';
|
||||
|
||||
@Component({
|
||||
selector: 'app-images',
|
||||
|
@ -70,7 +71,8 @@ export class ImagesComponent implements OnInit {
|
|||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private toastService: ToastrService
|
||||
private toastService: ToastrService,
|
||||
private joyrideService: JoyrideService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -178,4 +180,20 @@ export class ImagesComponent implements OnInit {
|
|||
break;
|
||||
}
|
||||
}
|
||||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: [
|
||||
'imagesTitleStep',
|
||||
'addImageButton',
|
||||
'searchImageField',
|
||||
'imagesTable',
|
||||
'actionsHeader',
|
||||
'editImageButton',
|
||||
'deleteImageButton',
|
||||
'imagesPagination'
|
||||
],
|
||||
showPrevButton: true,
|
||||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {{ diskUsage.total }}</p>
|
||||
<p>Ocupado: {{ diskUsage.used }}</p>
|
||||
<p>Disponible: {{ 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>
|
||||
|
|
|
@ -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();
|
||||
|
@ -56,4 +57,30 @@ export class OgbootStatusComponent implements OnInit {
|
|||
status: this.servicesStatus[key]
|
||||
}));
|
||||
}
|
||||
|
||||
formatBytes(bytes: number): string {
|
||||
if (bytes >= 1e9) {
|
||||
return (bytes / 1e9).toFixed(2) + ' GB';
|
||||
} else if (bytes >= 1e6) {
|
||||
return (bytes / 1e6).toFixed(2) + ' MB';
|
||||
} else if (bytes >= 1e3) {
|
||||
return (bytes / 1e3).toFixed(2) + ' KB';
|
||||
} else {
|
||||
return bytes + ' B';
|
||||
}
|
||||
}
|
||||
|
||||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: [
|
||||
'titleStep',
|
||||
'diskUsageStep',
|
||||
'servicesStatusStep',
|
||||
'oglivesStep'
|
||||
],
|
||||
showPrevButton: true,
|
||||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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">Sí</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"
|
||||
|
|
|
@ -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 {
|
||||
|
@ -255,4 +257,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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
|
@ -53,9 +52,9 @@ h3 {
|
|||
|
||||
.list-item-content {
|
||||
display: flex;
|
||||
align-items: flex-start; /* Alinea el contenido al inicio */
|
||||
justify-content: space-between; /* Espacio entre los textos y los íconos */
|
||||
width: 100%; /* Asegúrate de que el contenido ocupe todo el ancho */
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
|
@ -73,3 +72,15 @@ h3 {
|
|||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} plantilla </h2>
|
||||
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} plantilla</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="spacing-container">
|
||||
<form [formGroup]="templateForm" (ngSubmit)="onSave()">
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Nombre de la Plantilla</mat-label>
|
||||
<input matInput formControlName="name" placeholder="Introduce el nombre de la plantilla">
|
||||
|
@ -23,9 +22,19 @@
|
|||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button type="button" (click)="onCancel()">Cancelar</button>
|
||||
<button mat-raised-button color="primary" type="submit" (click)="onSave()" [disabled]="!templateForm.valid">
|
||||
{{ isEditMode ? 'Actualizar' : 'Crear' }}
|
||||
</button>
|
||||
<mat-dialog-actions>
|
||||
<div class="actions-container">
|
||||
<button mat-flat-button color="accent" [matMenuTriggerFor]="templateMenu">Cargar plantilla modelo</button>
|
||||
<mat-menu #templateMenu="matMenu">
|
||||
<button mat-menu-item (click)="loadTemplateModel('ogLive')">ogLive</button>
|
||||
<button mat-menu-item (click)="loadTemplateModel('disco')">Arranque por disco</button>
|
||||
</mat-menu>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button mat-button type="button" (click)="onCancel()">Cancelar</button>
|
||||
<button mat-raised-button color="primary" type="submit" (click)="onSave()" [disabled]="!templateForm.valid">
|
||||
{{ isEditMode ? 'Actualizar' : 'Crear' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import {MatDialogRef, MAT_DIALOG_DATA, MatDialog} from '@angular/material/dialog';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
import { DeleteModalComponent } from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-pxe-template',
|
||||
|
@ -17,6 +17,39 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
isEditMode: boolean = false;
|
||||
clients: any[] = [];
|
||||
|
||||
templateModels = {
|
||||
ogLive: `#!ipxe
|
||||
set timeout 0
|
||||
set timeout-style hidden
|
||||
set ISODIR __OGLIVE__
|
||||
set default 0
|
||||
set kernelargs __INFOHOST__
|
||||
:try_iso
|
||||
kernel http://__SERVERIP__/tftpboot/\${ISODIR}/ogvmlinuz \${kernelargs} || goto fallback
|
||||
initrd http://__SERVERIP__/tftpboot/\${ISODIR}/oginitrd.img
|
||||
boot
|
||||
|
||||
:fallback
|
||||
set ISODIR ogLive
|
||||
kernel http://__SERVERIP__/tftpboot/\${ISODIR}/ogvmlinuz \${kernelargs}
|
||||
initrd http://__SERVERIP__/tftpboot/\${ISODIR}/oginitrd.img
|
||||
boot`,
|
||||
|
||||
disco: `#!ipxe
|
||||
|
||||
iseq \${platform} efi && goto uefi_boot || goto bios_boot
|
||||
|
||||
:bios_boot
|
||||
echo "Running in BIOS mode - Booting first disk"
|
||||
chain http://__SERVERIP__/tftpboot/grub.exe --config-file="title FirstHardDisk;chainloader (hd0)+1;rootnoverify (hd0);boot" || echo "Failed to boot in BIOS mode"
|
||||
exit
|
||||
|
||||
:uefi_boot
|
||||
echo "Running in UEFI mode - Booting first disk"
|
||||
sanboot --no-describe --drive 0 --filename \\EFI\\grub\\Boot\\grubx64.efi || echo "Failed to boot in UEFI mode"
|
||||
exit`
|
||||
};
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<CreatePxeTemplateComponent>,
|
||||
public dialog: MatDialog,
|
||||
|
@ -29,8 +62,8 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
ngOnInit() {
|
||||
this.isEditMode = !!this.data;
|
||||
|
||||
if (this.isEditMode){
|
||||
this.getPxeClients()
|
||||
if (this.isEditMode) {
|
||||
this.getPxeClients();
|
||||
}
|
||||
|
||||
this.templateForm = this.fb.group({
|
||||
|
@ -50,7 +83,7 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
getPxeClients(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/clients?template.id=${this.data.id}`).subscribe({
|
||||
next: data => {
|
||||
this.clients = data['hydra:member']
|
||||
this.clients = data['hydra:member'];
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al obtener los clientes PXE:', error);
|
||||
|
@ -67,12 +100,10 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
|
||||
this.http.post<any>(`${this.baseUrl}/pxe-templates`, payload).subscribe({
|
||||
next: data => {
|
||||
console.log('Plantilla PXE creada:', data);
|
||||
this.toastService.success('Plantilla PXE creada exitosamente');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al crear la plantilla PXE:', error);
|
||||
this.toastService.error('Error al crear la plantilla PXE');
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
@ -98,13 +129,19 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
loadTemplateModel(type: 'ogLive' | 'disco'): void {
|
||||
const selectedContent = this.templateModels[type];
|
||||
this.templateForm.get('templateContent')?.setValue(selectedContent);
|
||||
this.toastService.info(`Plantilla ${type} cargada.`);
|
||||
}
|
||||
|
||||
addClientToTemplate(client: any): void {
|
||||
const postData = {
|
||||
client: client['@id']
|
||||
};
|
||||
|
||||
this.http.post(`${this.baseUrl}/pxe-templates/${this.data.uuid}/sync-client`, postData).subscribe(
|
||||
response => {
|
||||
() => {
|
||||
this.toastService.success('Clientes asignados correctamente');
|
||||
},
|
||||
error => {
|
||||
|
@ -126,11 +163,12 @@ export class CreatePxeTemplateComponent implements OnInit {
|
|||
this.toastService.success('Cliente eliminado exitosamente');
|
||||
this.dialogRef.close();
|
||||
},
|
||||
error: (error) => {
|
||||
error: error => {
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
|
|
|
@ -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">Sí</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"
|
||||
|
|
|
@ -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 {
|
||||
|
@ -229,4 +231,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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +1,32 @@
|
|||
<h2 mat-dialog-title>Añade clientes a {{data.subnetName}}</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field appearance="fill" class="search-select">
|
||||
<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">
|
||||
{{ client.name }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Unidad Organizativa</mat-label>
|
||||
<mat-select [formControl]="unitControl" (selectionChange)="onUnitChange($event.value)">
|
||||
<mat-option *ngFor="let unit of units" [value]="unit.uuid">{{ unit.name }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="selectedClients.length > 0">
|
||||
<h3>Clientes seleccionados:</h3>
|
||||
<ul>
|
||||
<li *ngFor="let client of selectedClients">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Subunidad Organizativa</mat-label>
|
||||
<mat-select [formControl]="childUnitControl" (selectionChange)="onChildUnitChange($event.value)">
|
||||
<mat-option *ngFor="let child of childUnits" [value]="child.uuid">{{ child.name }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<label>Clientes</label>
|
||||
<div *ngIf="clients.length > 0">
|
||||
<mat-checkbox *ngFor="let client of clients"
|
||||
(change)="toggleClientSelection(client.uuid)"
|
||||
[checked]="selectedClients.includes(client.uuid)">
|
||||
{{ client.name }}
|
||||
<button mat-icon-button color="warn" (click)="removeClient(client)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div *ngIf="clients.length === 0">
|
||||
<p>No hay clientes disponibles</p>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import {Observable, startWith} from "rxjs";
|
||||
import {map} from "rxjs/operators";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-clients-to-subnet',
|
||||
|
@ -13,59 +11,93 @@ import {ToastrService} from "ngx-toastr";
|
|||
})
|
||||
export class AddClientsToSubnetComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
units: any[] = [];
|
||||
childUnits: any[] = [];
|
||||
clients: any[] = [];
|
||||
selectedClients: any[] = [];
|
||||
selectedClients: string[] = [];
|
||||
loading: boolean = true;
|
||||
filters: { [key: string]: string } = {};
|
||||
filteredClients!: Observable<any[]>;
|
||||
clientControl = new FormControl();
|
||||
unitControl = new FormControl();
|
||||
childUnitControl = new FormControl();
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
public dialogRef: MatDialogRef<AddClientsToSubnetComponent>,
|
||||
private toastService: ToastrService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { subnetUuid: string, subnetName: string }
|
||||
) {}
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log('Selected subnet UUID:', this.data);
|
||||
this.loading = true;
|
||||
this.loadUnits();
|
||||
}
|
||||
|
||||
this.loadClients();
|
||||
|
||||
this.filteredClients = this.clientControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => (typeof value === 'string' ? value : value?.name)),
|
||||
map(name => (name ? this._filterClients(name) : this.clients.slice()))
|
||||
loadUnits() {
|
||||
this.http.get<any>(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=50`).subscribe(
|
||||
response => {
|
||||
this.units = response['hydra:member'].filter((unit: { type: string; }) => unit.type === 'organizational-unit');
|
||||
this.loading = false;
|
||||
},
|
||||
error => console.error('Error fetching organizational units:', error)
|
||||
);
|
||||
}
|
||||
|
||||
loadClients() {
|
||||
this.http.get<any>( `${this.baseUrl}/clients?&page=1&itemsPerPage=10000&exists[subnet]=false`).subscribe(
|
||||
response => {
|
||||
this.clients = response['hydra:member'];
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching parent units:', error);
|
||||
this.loading = false;
|
||||
onUnitChange(unitId: string): void {
|
||||
const unit = this.units.find(unit => unit.uuid === unitId);
|
||||
this.childUnits = unit ? this.getAllChildren(unit) : [];
|
||||
this.clients = [];
|
||||
this.childUnitControl.setValue(null);
|
||||
this.selectedClients = [];
|
||||
}
|
||||
|
||||
getAllChildren(unit: any): any[] {
|
||||
let allChildren = [];
|
||||
if (unit.children && unit.children.length > 0) {
|
||||
for (const child of unit.children) {
|
||||
allChildren.push(child);
|
||||
allChildren = allChildren.concat(this.getAllChildren(child));
|
||||
}
|
||||
);
|
||||
}
|
||||
return allChildren;
|
||||
}
|
||||
|
||||
onChildUnitChange(childUnitId: string): void {
|
||||
const childUnit = this.childUnits.find(unit => unit.uuid === childUnitId);
|
||||
this.clients = childUnit && childUnit.clients ? childUnit.clients : [];
|
||||
this.selectedClients = [];
|
||||
}
|
||||
|
||||
toggleClientSelection(clientId: string): void {
|
||||
const index = this.selectedClients.indexOf(clientId);
|
||||
if (index >= 0) {
|
||||
this.selectedClients.splice(index, 1);
|
||||
} else {
|
||||
this.selectedClients.push(clientId);
|
||||
}
|
||||
}
|
||||
|
||||
toggleSelectAll(): void {
|
||||
if (this.areAllClientsSelected()) {
|
||||
this.selectedClients = [];
|
||||
} else {
|
||||
this.selectedClients = this.clients.map(client => client.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
areAllClientsSelected(): boolean {
|
||||
return this.selectedClients.length === this.clients.length;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.selectedClients.forEach(client => {
|
||||
const postData = {
|
||||
client: client['@id']
|
||||
};
|
||||
this.selectedClients.forEach(clientId => {
|
||||
const postData = { client: `/clients/${clientId}` };
|
||||
|
||||
this.http.post(`${this.baseUrl}/og-dhcp/server/${this.data.subnetUuid}/post-host`, postData).subscribe(
|
||||
response => {
|
||||
this.toastService.success(`Cliente ${client.name} asignado correctamente`);
|
||||
this.toastService.success(`Cliente asignado correctamente`);
|
||||
},
|
||||
error => {
|
||||
console.error(`Error al asignar el cliente ${client.name}:`, error);
|
||||
this.toastService.error(`Error al asignar el cliente ${client.name}: ${error.error['hydra:description']}`);
|
||||
console.error(`Error al asignar el cliente:`, error);
|
||||
this.toastService.error(`Error al asignar el cliente: ${error.error['hydra:description']}`);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -76,27 +108,4 @@ export class AddClientsToSubnetComponent implements OnInit {
|
|||
close() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
removeClient(client: any) {
|
||||
const index = this.selectedClients.indexOf(client);
|
||||
if (index >= 0) {
|
||||
this.selectedClients.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private _filterClients(name: string): any[] {
|
||||
const filterValue = name.toLowerCase();
|
||||
return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
displayFnClient(client: any): string {
|
||||
return client && client.name ? client.name : '';
|
||||
}
|
||||
|
||||
onOptionClientSelected(client: any) {
|
||||
if (!this.selectedClients.includes(client)) {
|
||||
this.selectedClients.push(client);
|
||||
}
|
||||
this.clientControl.setValue('');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ form{
|
|||
|
||||
.list-item-content {
|
||||
display: flex;
|
||||
align-items: flex-start; /* Alinea el contenido al inicio */
|
||||
justify-content: space-between; /* Espacio entre los textos y los íconos */
|
||||
width: 100%; /* Asegúrate de que el contenido ocupe todo el ancho */
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
|
@ -33,4 +33,3 @@ form{
|
|||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,21 +16,29 @@
|
|||
<mat-label>Dirección IP</mat-label>
|
||||
<input matInput [(ngModel)]="ipAddress" placeholder="Dirección IP" required>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Next Server</mat-label>
|
||||
<input matInput [(ngModel)]="nextServer" placeholder="Next Server" required>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Boot File Name</mat-label>
|
||||
<input matInput [(ngModel)]="bootFileName" placeholder="Boot File Name" required>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Parámetros Avanzados -->
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>Parámetros avanzados</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Next Server</mat-label>
|
||||
<input matInput [(ngModel)]="nextServer" placeholder="Next Server">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Boot File Name</mat-label>
|
||||
<input matInput [(ngModel)]="bootFileName" placeholder="Boot File Name">
|
||||
</mat-form-field>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab *ngIf="isEditMode" label="Clientes">
|
||||
<mat-list>
|
||||
<ng-container *ngFor="let client of clients">
|
||||
<mat-list-item >
|
||||
<mat-list-item>
|
||||
<div class="list-item-content">
|
||||
<mat-icon matListItemIcon>computer</mat-icon>
|
||||
<div class="text-content">
|
||||
|
|
|
@ -58,8 +58,8 @@ export class CreateSubnetComponent implements OnInit {
|
|||
name: this.name,
|
||||
netmask: this.netmask,
|
||||
ipAddress: this.ipAddress,
|
||||
nextServer: this.nextServer,
|
||||
bootFileName: this.bootFileName
|
||||
nextServer: this.nextServer || null,
|
||||
bootFileName: this.bootFileName || null
|
||||
};
|
||||
|
||||
if (!this.data){
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
@ -217,4 +220,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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<mat-dialog-content class="form-container">
|
||||
<mat-tab-group>
|
||||
<!-- Primer tab: formulario de comandos -->
|
||||
<mat-tab label="Formulario">
|
||||
<form [formGroup]="formGroup" (ngSubmit)="onSubmit()" class="form-group">
|
||||
<mat-form-field appearance="fill" class="full-width" >
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {DataService as SoftwareService} from "../../software/data.service";
|
||||
import {DataService} from "../data.service";
|
||||
import {Observable, startWith} from "rxjs";
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { DataService as SoftwareService } from '../../software/data.service';
|
||||
import { DataService } from '../data.service';
|
||||
import { Observable, startWith } from 'rxjs';
|
||||
import { debounceTime, switchMap, map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-software-profile',
|
||||
templateUrl: './create-software-profile.component.html',
|
||||
styleUrl: './create-software-profile.component.css'
|
||||
styleUrls: ['./create-software-profile.component.css']
|
||||
})
|
||||
export class CreateSoftwareProfileComponent {
|
||||
export class CreateSoftwareProfileComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
formGroup: FormGroup<any>;
|
||||
formGroup: FormGroup;
|
||||
private apiUrl = `${this.baseUrl}/software-profiles`;
|
||||
softwareCollection: any[] = [];
|
||||
organizationalUnits: any[] = [];
|
||||
|
@ -45,11 +45,13 @@ export class CreateSoftwareProfileComponent {
|
|||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.load()
|
||||
this.softwareProfileId = this.data['@id'];
|
||||
this.patchFormData();
|
||||
}
|
||||
|
||||
this.loadSoftware();
|
||||
this.loadOrganizationalUnits()
|
||||
this.loadOperativeSystems()
|
||||
this.loadOrganizationalUnits();
|
||||
this.loadOperativeSystems();
|
||||
|
||||
this.filteredSoftware = this.softwareControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
|
@ -58,42 +60,50 @@ export class CreateSoftwareProfileComponent {
|
|||
);
|
||||
}
|
||||
|
||||
patchFormData(): void {
|
||||
this.formGroup.patchValue({
|
||||
description: this.data.description || '',
|
||||
comments: this.data.comments || '',
|
||||
organizationalUnit: this.data.organizationalUnit ? this.data.organizationalUnit['@id'] : null,
|
||||
operativeSystem: this.data.operativeSystem ? this.data.operativeSystem['@id'] : null,
|
||||
});
|
||||
}
|
||||
|
||||
loadSoftware() {
|
||||
this.http.get<any>( `${this.baseUrl}/software?&page=1&itemsPerPage=10`).subscribe(
|
||||
this.http.get<any>(`${this.baseUrl}/software?page=1&itemsPerPage=10`).subscribe(
|
||||
response => {
|
||||
this.softwareCollection = response['hydra:member'];
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching parent units:', error);
|
||||
console.error('Error fetching software:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
loadOrganizationalUnits() {
|
||||
this.http.get<any>( `${this.baseUrl}/organizational-units?&page=1&itemsPerPage=10000`).subscribe(
|
||||
this.http.get<any>(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=10000`).subscribe(
|
||||
response => {
|
||||
this.organizationalUnits = response['hydra:member'];
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching parent units:', error);
|
||||
console.error('Error fetching organizational units:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
loadOperativeSystems() {
|
||||
this.http.get<any>( `${this.baseUrl}/operative-systems?&page=1&itemsPerPage=10000`).subscribe(
|
||||
this.http.get<any>(`${this.baseUrl}/operative-systems?page=1&itemsPerPage=10000`).subscribe(
|
||||
response => {
|
||||
this.operativeSystems = response['hydra:member'];
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching parent units:', error);
|
||||
console.error('Error fetching operative systems:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _filterSoftware(value: string): Observable<any[]> {
|
||||
|
||||
return this.softwareDataService.getSoftwareCollection({ 'name': value}).pipe(
|
||||
return this.softwareDataService.getSoftwareCollection({ name: value }).pipe(
|
||||
map(response => response || [])
|
||||
);
|
||||
}
|
||||
|
@ -109,23 +119,6 @@ export class CreateSoftwareProfileComponent {
|
|||
this.softwareControl.setValue('');
|
||||
}
|
||||
|
||||
load(): void {
|
||||
this.dataService.getSoftwareProfile(this.data).subscribe({
|
||||
next: (response) => {
|
||||
this.formGroup = this.fb.group({
|
||||
description: [response.description],
|
||||
comments: [response.comments],
|
||||
organizationalUnit: [response.organizationalUnit ? response.organizationalUnit['@id'] : null],
|
||||
operativeSystem: [response.operativeSystem ? response.operativeSystem['@id'] : null],
|
||||
});
|
||||
this.softwareProfileId = response['@id'];
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error fetching software:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addSoftware() {
|
||||
const software = this.softwareCollection.find(s => s.id === this.selectedSoftware);
|
||||
if (software && !this.selectedSoftwares.includes(software)) {
|
||||
|
@ -144,7 +137,6 @@ export class CreateSoftwareProfileComponent {
|
|||
|
||||
onSubmit(): void {
|
||||
if (this.formGroup.valid) {
|
||||
|
||||
const payload = {
|
||||
description: this.formGroup.value.description,
|
||||
comments: this.formGroup.value.comments,
|
||||
|
@ -155,24 +147,24 @@ export class CreateSoftwareProfileComponent {
|
|||
|
||||
if (this.softwareProfileId) {
|
||||
this.http.put(`${this.baseUrl}${this.softwareProfileId}`, payload).subscribe(
|
||||
(response) => {
|
||||
() => {
|
||||
this.toastService.success('Software editado correctamente');
|
||||
this.dialogRef.close();
|
||||
},
|
||||
(error) => {
|
||||
this.toastService.error(error['error']['hydra:description']);
|
||||
console.error('Error al editar el comando', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
console.error('Error al editar el software', error);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.http.post(`${this.baseUrl}/software-profiles`, payload).subscribe(
|
||||
(response) => {
|
||||
this.http.post(`${this.apiUrl}`, payload).subscribe(
|
||||
() => {
|
||||
this.toastService.success('Software añadido correctamente');
|
||||
this.dialogRef.close();
|
||||
},
|
||||
(error) => {
|
||||
this.toastService.error(error['error']['hydra:description']);
|
||||
console.error('Error al añadir comando', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
console.error('Error al añadir software', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -5,10 +5,10 @@ import {MatDialog} from "@angular/material/dialog";
|
|||
import {HttpClient} from "@angular/common/http";
|
||||
import {DataService} from "../software/data.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {CreateSoftwareComponent} from "../software/create-software/create-software.component";
|
||||
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',
|
||||
|
@ -58,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 {
|
||||
|
@ -90,7 +91,7 @@ export class SoftwareProfileComponent {
|
|||
editSoftware(softwareProfile: any): void {
|
||||
const dialogRef = this.dialog.open(CreateSoftwareProfileComponent, {
|
||||
width: '600px',
|
||||
data: softwareProfile['@id']
|
||||
data: softwareProfile
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
|
@ -129,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'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" >
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,35 @@
|
|||
<mat-toolbar>
|
||||
<span class="navbar-title" i18n="@@webConsoleTitle">Opengnsys webconsole</span>
|
||||
<button mat-icon-button (click)="onToggleSidebar()">
|
||||
<mat-icon class="navbar-icon" >menu</mat-icon>
|
||||
<span class="navbar-title" i18n="@@webConsoleTitle" matTooltip="Consola web de administración de Opengnsys" matTooltipShowDelay="1000">
|
||||
Opengnsys webconsole
|
||||
</span>
|
||||
|
||||
<button mat-icon-button (click)="onToggleSidebar()" matTooltip="Abrir o cerrar la barra lateral" matTooltipShowDelay="1000">
|
||||
<mat-icon class="navbar-icon">menu</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="navbar-button-row">
|
||||
<button class="trace-button" routerLink="/commands-logs" mat-button i18n="@@admin"><mat-icon>notifications</mat-icon></button>
|
||||
<button class="admin-button" *ngIf="isSuperAdmin" mat-button [matMenuTriggerFor]="menu" i18n="@@admin">Administración</button>
|
||||
<button class="user-button" mat-button *ngIf="!isSuperAdmin" (click)="editUser()" i18n="@@editUser">Editar usuario</button>
|
||||
<button class="admin-button" *ngIf="isSuperAdmin" mat-button [matMenuTriggerFor]="menu" i18n="@@admin"
|
||||
matTooltip="Gestión de usuarios y roles de la aplicación" matTooltipShowDelay="1000">
|
||||
Administración
|
||||
</button>
|
||||
|
||||
<button class="user-button" mat-button *ngIf="!isSuperAdmin" (click)="editUser()" i18n="@@editUser"
|
||||
matTooltip="Editar tu información de usuario" matTooltipShowDelay="1000">
|
||||
Editar usuario
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item routerLink="/users" i18n="@@usersMenuItem">Usuarios</button>
|
||||
<button mat-menu-item routerLink="/user-groups" i18n="@@rolesMenuItem">Roles</button>
|
||||
<button mat-menu-item routerLink="/var-envs" i18n="@@rolesMenuItem">Variables de entorno</button>
|
||||
<button mat-menu-item routerLink="/users" i18n="@@usersMenuItem" matTooltip="Ver y gestionar todos los usuarios" matTooltipShowDelay="1000">
|
||||
Usuarios
|
||||
</button>
|
||||
<button mat-menu-item routerLink="/user-groups" i18n="@@rolesMenuItem" matTooltip="Gestionar roles de usuario" matTooltipShowDelay="1000">
|
||||
Roles
|
||||
</button>
|
||||
</mat-menu>
|
||||
<button mat-flat-button color="warn" routerLink="/auth/login" i18n="@@logout">Salir</button>
|
||||
|
||||
<button mat-flat-button color="warn" routerLink="/auth/login" i18n="@@logout"
|
||||
matTooltip="Cerrar sesión y salir de la aplicación" matTooltipShowDelay="1000">
|
||||
Salir
|
||||
</button>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
|
|
@ -1,51 +1,49 @@
|
|||
<mat-nav-list>
|
||||
<mat-list-item disabled>
|
||||
<span class="user-logged">
|
||||
<span class="user-logged" matTooltip="Bienvenido, {{username}}" matTooltipShowDelay="1000">
|
||||
<span i18n="@@welcomeUser">Bienvenido {{username}}</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<mat-list-item routerLink="/groups">
|
||||
<mat-list-item routerLink="/groups" matTooltip="Gestionar grupos de usuarios" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">apartment</mat-icon>
|
||||
<span i18n="@@groups">Grupos</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
<span class="entry" (click)="toggleCommandSub()">
|
||||
<mat-icon class="icon">playlist_play </mat-icon>
|
||||
<mat-list-item (click)="toggleCommandSub()" matTooltip="Ver y ejecutar acciones predefinidas" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">playlist_play</mat-icon>
|
||||
<span i18n="@@actions">Acciones</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<!-- Submenu items for commands -->
|
||||
<mat-nav-list *ngIf="showCommandSub" style="padding-left: 20px;">
|
||||
<mat-list-item routerLink="/commands">
|
||||
<mat-list-item routerLink="/commands" matTooltip="Lista de comandos disponibles" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">chevron_right</mat-icon>
|
||||
<span i18n="@@gallery">Comandos</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/commands-groups">
|
||||
<mat-list-item routerLink="/commands-groups" matTooltip="Gestionar grupos de comandos" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">chevron_right</mat-icon>
|
||||
<span i18n="@@gallery">Grupos</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/commands-task">
|
||||
<mat-list-item routerLink="/commands-task" matTooltip="Ver y gestionar tareas programadas" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">chevron_right</mat-icon>
|
||||
<span i18n="@@gallery">Tareas</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
<!-- End commands sub -->
|
||||
|
||||
<!-- OGDHCP -->
|
||||
<mat-list-item (click)="toggleOgDhcpSub()">
|
||||
<mat-list-item (click)="toggleOgDhcpSub()" matTooltip="Configurar y administrar DHCP" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">settings_ethernet</mat-icon>
|
||||
<span i18n="@@images">DHCP</span>
|
||||
|
@ -54,23 +52,21 @@
|
|||
|
||||
<!-- Submenu items ogdhcp -->
|
||||
<mat-nav-list *ngIf="showOgDhcpSub" style="padding-left: 20px;">
|
||||
<mat-list-item routerLink="/ogdhcp-status">
|
||||
<mat-list-item routerLink="/ogdhcp-status" matTooltip="Estado actual del servicio DHCP" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">analytics</mat-icon>
|
||||
<span i18n="@@gallery">Estado</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/subnets">
|
||||
<mat-list-item routerLink="/subnets" matTooltip="Gestionar y crea subredes" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">lan</mat-icon>
|
||||
<span i18n="@@gallery">Subredes</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
<!-- Submenu items ogdhcp -->
|
||||
|
||||
|
||||
<mat-list-item (click)="toggleOgBootSub()">
|
||||
<mat-list-item (click)="toggleOgBootSub()" matTooltip="Configurar y administrar opciones de arranque" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">desktop_windows</mat-icon>
|
||||
<span i18n="@@images">Boot</span>
|
||||
|
@ -79,41 +75,40 @@
|
|||
|
||||
<!-- Submenu items for ogBoot -->
|
||||
<mat-nav-list *ngIf="showOgBootSub" style="padding-left: 20px;">
|
||||
<mat-list-item routerLink="/ogboot-status">
|
||||
<mat-list-item routerLink="/ogboot-status" matTooltip="Estado del servicio de arranque" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">analytics</mat-icon>
|
||||
<span i18n="@@gallery">Estado</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/pxe-images">
|
||||
<mat-list-item routerLink="/pxe-images" matTooltip="Ver imágenes disponibles para arranque PXE" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">album</mat-icon>
|
||||
<span i18n="@@gallery">ogLive</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/pxe">
|
||||
<mat-list-item routerLink="/pxe" matTooltip="Gestionar plantillas de arranque PXE" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">assignment</mat-icon>
|
||||
<span i18n="@@upload">Plantillas PXE</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/pxe-boot-file">
|
||||
<mat-list-item routerLink="/pxe-boot-file" matTooltip="Configurar archivos de arranque PXE" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">save</mat-icon>
|
||||
<span i18n="@@upload">Arranque PXE</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
<!-- End ogBoot sub -->
|
||||
|
||||
<mat-list-item routerLink="/calendars">
|
||||
<mat-list-item routerLink="/calendars" matTooltip="Gestionar calendarios de remotePC" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">calendar_month</mat-icon>
|
||||
<span i18n="@@calendars">Calendarios</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item (click)="toggleSoftwareSub()">
|
||||
<mat-list-item (click)="toggleSoftwareSub()" matTooltip="Administrar configuraciones de software" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">terminal</mat-icon>
|
||||
<span i18n="@@images">Software</span>
|
||||
|
@ -122,19 +117,19 @@
|
|||
|
||||
<!-- Submenu items ogdhcp -->
|
||||
<mat-nav-list *ngIf="showSoftwareSub" style="padding-left: 20px;">
|
||||
<mat-list-item routerLink="/software">
|
||||
<mat-list-item routerLink="/software" matTooltip="Ver lista de software disponible" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">list</mat-icon>
|
||||
<span i18n="@@gallery">Listado </span>
|
||||
<span i18n="@@gallery">Listado</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/software-profiles">
|
||||
<mat-list-item routerLink="/software-profiles" matTooltip="Gestionar perfiles de software" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">folder_shared</mat-icon>
|
||||
<span i18n="@@gallery">Perfiles</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/operative-systems">
|
||||
<mat-list-item routerLink="/operative-systems" matTooltip="Configurar sistemas operativos" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">terminal</mat-icon>
|
||||
<span i18n="@@gallery">S. Operativos</span>
|
||||
|
@ -142,33 +137,31 @@
|
|||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
|
||||
<mat-list-item routerLink="/images">
|
||||
<mat-list-item routerLink="/images" matTooltip="Gestionar imágenes del sistema" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">photo</mat-icon>
|
||||
<span i18n="@@images">imágenes</span>
|
||||
<span i18n="@@images">Imágenes</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
<span class="entry" routerLink="/repositories">
|
||||
<mat-icon class="icon">warehouse </mat-icon>
|
||||
<span i18n="@@actions">Repositorios</span>
|
||||
<mat-list-item routerLink="/repositories" matTooltip="Ver y gestionar repositorios de software" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">warehouse</mat-icon>
|
||||
<span i18n="@@repositories">Repositorios</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item class="disabled">
|
||||
<mat-list-item class="disabled" matTooltip="Gestión de menús (opción deshabilitada)" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">list</mat-icon>
|
||||
<span i18n="@@menus">Menús</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item class="disabled">
|
||||
<mat-list-item class="disabled" matTooltip="Función de búsqueda (opción deshabilitada)" matTooltipShowDelay="1000">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">search</mat-icon>
|
||||
<span i18n="@@search">Buscar</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
|
||||
</mat-nav-list>
|
||||
|
|
Loading…
Reference in New Issue