Merge pull request 'develop' (#7) from develop into main
Reviewed-on: #7pull/10/head opengnsys_devel-0.0.15
commit
ad0d4c1a40
|
@ -113,8 +113,6 @@ import { CreateSoftwareProfileComponent } from './components/software-profile/cr
|
|||
import { OperativeSystemComponent } from './components/operative-system/operative-system.component';
|
||||
import { CreateOperativeSystemComponent } from './components/operative-system/create-operative-system/create-operative-system.component';
|
||||
import { ShowTemplateContentComponent } from './components/ogboot/pxe/show-template-content/show-template-content.component';
|
||||
import { AddClientsToPxeComponent } from './components/ogboot/pxe/add-clients-to-pxe/add-clients-to-pxe.component';
|
||||
import { ClientsComponent } from './components/ogboot/pxe/clients/clients.component';
|
||||
import { RepositoriesComponent } from './components/repositories/repositories.component';
|
||||
import { CreateRepositoryComponent } from './components/repositories/create-repository/create-repository.component';
|
||||
import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component';
|
||||
|
@ -200,8 +198,6 @@ export function HttpLoaderFactory(http: HttpClient) {
|
|||
OperativeSystemComponent,
|
||||
CreateOperativeSystemComponent,
|
||||
ShowTemplateContentComponent,
|
||||
AddClientsToPxeComponent,
|
||||
ClientsComponent,
|
||||
RepositoriesComponent,
|
||||
CreateRepositoryComponent,
|
||||
ExecuteCommandComponent,
|
||||
|
|
|
@ -42,7 +42,6 @@ table {
|
|||
align-items: center;
|
||||
height: 100px;
|
||||
padding: 10px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
|
@ -55,21 +54,15 @@ table {
|
|||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.images-button-row {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 16px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: table-cell;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.button-row .mat-mdc-button-base {
|
||||
margin: 8px 8px 8px 0;
|
||||
mat-spinner {
|
||||
margin: 0 auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
<mat-accordion class="example-headers-align">
|
||||
<mat-expansion-panel hideToggle>
|
||||
<mat-expansion-panel-header joyrideStep="serverInfoStep" [text]="'serverInfoDescription' | translate">
|
||||
<mat-panel-title>{{ 'serverInfoTitle' | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="button-row">
|
||||
<button mat-flat-button color="primary" (click)="syncOgBoot()">{{ 'syncDatabaseButton' | translate }}</button>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">{{ 'viewInfoButton' | translate }}</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="header-container">
|
||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||
<mat-icon>help</mat-icon>
|
||||
|
@ -20,6 +6,7 @@
|
|||
{{ 'adminImagesTitle' | translate }}
|
||||
</h2>
|
||||
<div class="images-button-row">
|
||||
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">{{ 'viewInfoButton' | translate }}</button>
|
||||
<button mat-flat-button color="primary" (click)="addImage()" joyrideStep="addImageStep" [text]="'addImageButtonDescription' | translate">
|
||||
{{ 'addImageButton' | translate }}
|
||||
</button>
|
||||
|
@ -35,7 +22,7 @@
|
|||
<mat-icon matSuffix>search</mat-icon>
|
||||
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<mat-form-field appearance="fill" class="search-boolean" joyrideStep="searchDefaultImageStep" [text]="'searchDefaultDescription' | translate">
|
||||
<mat-label>{{ 'searchDefaultLabel' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="filters['isDefault']" (selectionChange)="search()" [placeholder]="'selectOptionPlaceholder' | translate">
|
||||
|
@ -55,7 +42,8 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" [text]="'tableDescription' | translate">
|
||||
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||
<table *ngIf="!loading" mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" [text]="'tableDescription' | translate">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ column.header }}</th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
|
@ -78,7 +66,7 @@
|
|||
|
||||
<ng-container *ngIf="column.columnDef === 'name'">
|
||||
<span matTooltip="{{ image.name }}">
|
||||
{{ image.name ? image.name.substring(0, 20) + '...' : '' }}
|
||||
{{ image.name }}
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
|
@ -95,8 +83,8 @@
|
|||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'actionsColumnHeader' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let image" joyrideStep="actionsStep" [text]="'actionsDescription' | translate">
|
||||
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'actionsColumnHeader' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let image" style="text-align: center;" joyrideStep="actionsStep" [text]="'actionsDescription' | translate">
|
||||
<button mat-icon-button color="info" (click)="showOgLive($event, image)">
|
||||
<mat-icon>{{ 'viewIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
|
@ -110,7 +98,7 @@
|
|||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="toggleAction(image, 'install')">{{ 'installOption' | translate }}</button>
|
||||
<button mat-menu-item [disabled]="image.installed" (click)="toggleAction(image, 'install')">{{ 'installOption' | translate }}</button>
|
||||
<button mat-menu-item [disabled]="!image.installed" (click)="toggleAction(image, 'uninstall')">
|
||||
{{ 'uninstallOption' | translate }}
|
||||
</button>
|
||||
|
|
|
@ -44,11 +44,6 @@ export class PXEimagesComponent implements OnInit {
|
|||
header: 'Nombre de imagen',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'downloadUrl',
|
||||
header: 'Url descarga',
|
||||
cell: (user: any) => `${user.downloadUrl}`
|
||||
},
|
||||
{
|
||||
columnDef: 'isDefault',
|
||||
header: 'Imagen por defecto',
|
||||
|
@ -56,7 +51,7 @@ export class PXEimagesComponent implements OnInit {
|
|||
},
|
||||
{
|
||||
columnDef: 'installed',
|
||||
header: 'Imagen instalada en ogBoot',
|
||||
header: 'Imagen instalada',
|
||||
cell: (user: any) => `${user.installed}`
|
||||
},
|
||||
{
|
||||
|
@ -83,8 +78,11 @@ export class PXEimagesComponent implements OnInit {
|
|||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loading = true;
|
||||
this.search();
|
||||
this.loadAlert();
|
||||
this.syncOgBoot()
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
addImage(): void {
|
||||
|
@ -99,15 +97,12 @@ export class PXEimagesComponent implements OnInit {
|
|||
}
|
||||
|
||||
search(): void {
|
||||
this.loading = true;
|
||||
this.dataService.getImages(this.filters).subscribe(
|
||||
data => {
|
||||
this.dataSource.data = data;
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching og lives', error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -250,7 +245,7 @@ export class PXEimagesComponent implements OnInit {
|
|||
syncOgBoot(): void {
|
||||
this.http.post(`${this.apiUrl}/sync`, {})
|
||||
.subscribe(response => {
|
||||
this.toastService.success('Sincronización completada');
|
||||
this.toastService.success('Sincronización con oGBoot exitosa');
|
||||
this.search()
|
||||
}, error => {
|
||||
console.error('Error al sincronizar', error);
|
||||
|
@ -261,7 +256,6 @@ export class PXEimagesComponent implements OnInit {
|
|||
iniciarTour(): void {
|
||||
this.joyrideService.startTour({
|
||||
steps: [
|
||||
'serverInfoStep',
|
||||
'titleStep',
|
||||
'addImageStep',
|
||||
'searchNameStep',
|
||||
|
@ -275,5 +269,5 @@ export class PXEimagesComponent implements OnInit {
|
|||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
mat-dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<h2 mat-dialog-title>{{ 'addClientsTitle' | translate: { subnetName: data.subnetName } }}</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field appearance="fill" class="search-select">
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
[formControl]="clientControl"
|
||||
[matAutocomplete]="clientAuto"
|
||||
[placeholder]="'selectClientPlaceholder' | translate">
|
||||
<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>
|
||||
|
||||
<div *ngIf="selectedClients.length > 0">
|
||||
<h3>{{ 'selectedClientsTitle' | translate }}</h3>
|
||||
<ul>
|
||||
<li *ngFor="let client of selectedClients">
|
||||
{{ client.name }}
|
||||
<button mat-icon-button color="warn" (click)="removeClient(client)">
|
||||
<mat-icon>{{ 'deleteIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="close()">{{ 'cancelButton' | translate }}</button>
|
||||
<button mat-button (click)="save()">{{ 'addButton' | translate }}</button>
|
||||
</mat-dialog-actions>
|
|
@ -1,69 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AddClientsToPxeComponent } from './add-clients-to-pxe.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('AddClientsToPxeComponent', () => {
|
||||
let component: AddClientsToPxeComponent;
|
||||
let fixture: ComponentFixture<AddClientsToPxeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [AddClientsToPxeComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
ToastrModule.forRoot(),
|
||||
BrowserAnimationsModule,
|
||||
MatDividerModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatTableModule,
|
||||
MatPaginatorModule,
|
||||
MatTooltipModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatDialogModule,
|
||||
MatSelectModule,
|
||||
MatTabsModule,
|
||||
MatAutocompleteModule,
|
||||
MatListModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: {} },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: {} }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AddClientsToPxeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,98 +0,0 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {Observable, startWith} from "rxjs";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-clients-to-pxe',
|
||||
templateUrl: './add-clients-to-pxe.component.html',
|
||||
styleUrl: './add-clients-to-pxe.component.css'
|
||||
})
|
||||
export class AddClientsToPxeComponent {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
clients: any[] = [];
|
||||
selectedClients: any[] = [];
|
||||
loading: boolean = true;
|
||||
filters: { [key: string]: string } = {};
|
||||
filteredClients!: Observable<any[]>;
|
||||
clientControl = new FormControl();
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
public dialogRef: MatDialogRef<AddClientsToPxeComponent>,
|
||||
private toastService: ToastrService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { subnetUuid: string, subnetName: string }
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loading = true;
|
||||
|
||||
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()))
|
||||
);
|
||||
}
|
||||
|
||||
loadClients() {
|
||||
this.http.get<any>( `${this.baseUrl}/clients?&page=1&itemsPerPage=10000&exists[template]=false`).subscribe(
|
||||
response => {
|
||||
this.clients = response['hydra:member'];
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching parent units:', error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
save() {
|
||||
const postData = {
|
||||
clients: this.selectedClients.map(client => client['@id'])
|
||||
};
|
||||
|
||||
this.http.post(`${this.baseUrl}/pxe-templates/${this.data.subnetUuid}/add-clients`, postData).subscribe(
|
||||
response => {
|
||||
this.toastService.success('Clientes asignados correctamente');
|
||||
},
|
||||
error => {
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
);
|
||||
|
||||
this.dialogRef.close(this.selectedClients);
|
||||
}
|
||||
|
||||
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('');
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
mat-dialog-actions {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.green-icon {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.red-icon {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
.spacing-container {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.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 */
|
||||
}
|
||||
|
||||
.text-content {
|
||||
flex-grow: 1;
|
||||
margin-right: 16px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<h2 mat-dialog-title>{{ 'manageClientsTitle' | translate }}</h2>
|
||||
<mat-dialog-content>
|
||||
<mat-list>
|
||||
<ng-container *ngFor="let client of clients">
|
||||
<mat-list-item>
|
||||
<div class="list-item-content">
|
||||
<mat-icon matListItemIcon [ngClass]="{'red-icon': client.pxeSync === false || !client.pxeSync, 'green-icon': client.pxeSync === true}">
|
||||
computer
|
||||
</mat-icon>
|
||||
<div class="text-content">
|
||||
<div matListItemTitle>{{ client.name }}</div>
|
||||
<div matListItemLine>{{ client.mac }}</div>
|
||||
</div>
|
||||
<div class="icon-container">
|
||||
<button mat-icon-button color="info" (click)="showInfo(client)">
|
||||
<mat-icon>{{ 'viewIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="primary" (click)="addClientToTemplate(client)">
|
||||
<mat-icon>{{ 'syncIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" class="right-icon" (click)="deleteClient(client)">
|
||||
<mat-icon>{{ 'deleteIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</ng-container>
|
||||
</mat-list>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button type="button" (click)="onCancel()">{{ 'cancelButton' | translate }}</button>
|
||||
</mat-dialog-actions>
|
|
@ -1,70 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ClientsComponent } from './clients.component';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('ClientsComponent', () => {
|
||||
let component: ClientsComponent;
|
||||
let fixture: ComponentFixture<ClientsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ClientsComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
ToastrModule.forRoot(),
|
||||
BrowserAnimationsModule,
|
||||
MatDividerModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatTableModule,
|
||||
MatPaginatorModule,
|
||||
MatTooltipModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatDialogModule,
|
||||
MatSelectModule,
|
||||
MatTabsModule,
|
||||
MatAutocompleteModule,
|
||||
MatListModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: {} },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: {data: {id: 123}} }
|
||||
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ClientsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,107 +0,0 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
import {Observable} from "rxjs";
|
||||
import {
|
||||
ServerInfoDialogComponent
|
||||
} from "../../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-clients',
|
||||
templateUrl: './clients.component.html',
|
||||
styleUrl: './clients.component.css'
|
||||
})
|
||||
export class ClientsComponent {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
templateForm!: FormGroup;
|
||||
clients: any[] = [];
|
||||
alertMessage: string | null = null;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ClientsComponent>,
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private toastService: ToastrService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.getPxeClients()
|
||||
}
|
||||
|
||||
getPxeClients(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/clients?template.id=${this.data.data.id}`).subscribe({
|
||||
next: data => {
|
||||
this.clients = data['hydra:member']
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al obtener los clientes PXE:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addClientToTemplate(client: any): void {
|
||||
const postData = {
|
||||
client: client['@id']
|
||||
};
|
||||
|
||||
this.http.post(`${this.baseUrl}/pxe-templates/${this.data.data.uuid}/sync-client`, postData).subscribe(
|
||||
response => {
|
||||
this.toastService.success('Clientes asignados correctamente');
|
||||
this.getPxeClients()
|
||||
},
|
||||
error => {
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
loadAlert(client: any): Observable<any> {
|
||||
return this.http.post<any>(`${this.baseUrl}/clients/server/${client.uuid}/get-pxe`, {});
|
||||
}
|
||||
|
||||
showInfo(client:any) {
|
||||
this.loadAlert(client).subscribe(
|
||||
response => {
|
||||
this.alertMessage = response.message;
|
||||
|
||||
this.dialog.open(ServerInfoDialogComponent, {
|
||||
width: '600px',
|
||||
data: {
|
||||
message: this.alertMessage
|
||||
}
|
||||
});
|
||||
},
|
||||
error => {
|
||||
console.error('Error al cargar la información del alert', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
deleteClient(client: any): void {
|
||||
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
||||
width: '300px',
|
||||
data: { name: client.name }
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.http.post(`${this.baseUrl}/pxe-templates/${this.data.data.uuid}/delete-client`, { client: client['@id'] }).subscribe({
|
||||
next: () => {
|
||||
this.toastService.success('Cliente eliminado exitosamente');
|
||||
this.dialogRef.close();
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}})
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ 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
|
||||
|
@ -104,8 +104,7 @@ exit`
|
|||
this.dialogRef.close(true);
|
||||
},
|
||||
error: error => {
|
||||
this.toastService.error('Error al crear la plantilla PXE');
|
||||
this.dialogRef.close(false);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ table {
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
@ -41,27 +42,21 @@ table {
|
|||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.template-button-row {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 16px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
mat-spinner {
|
||||
margin: 0 auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.paginator-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.example-button-row {
|
||||
display: table-cell;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.example-button-row .mat-mdc-button-base {
|
||||
margin: 8px 8px 8px 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,10 @@
|
|||
<mat-accordion class="example-headers-align">
|
||||
<mat-expansion-panel hideToggle>
|
||||
<mat-expansion-panel-header joyrideStep="serverInfoStep" text="{{ 'serverInfoDescription' | translate }}">
|
||||
<mat-panel-title>{{ 'serverInfoTitle' | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="example-button-row">
|
||||
<button mat-flat-button color="primary" (click)="syncTemplates()">{{ 'syncDatabaseButton' | translate }}</button>
|
||||
</div>
|
||||
<div class="example-button-row">
|
||||
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">{{ 'viewInfoButton' | translate }}</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="header-container">
|
||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||
<mat-icon>help</mat-icon>
|
||||
</button>
|
||||
<h2 class="title" joyrideStep="titleStep" text="{{ 'adminPxeDescription' | translate }}">{{ 'adminPxeTitle' | translate }}</h2>
|
||||
<div class="pxe-button-row">
|
||||
<div class="template-button-row">
|
||||
<button mat-flat-button color="accent" (click)="openInfoDialog()">{{ 'viewInfoButton' | translate }}</button>
|
||||
<button mat-flat-button color="primary" (click)="addPxeTemplate()" joyrideStep="addTemplateStep" text="{{ 'addTemplateButtonDescription' | translate }}">{{ 'addTemplateButton' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,7 +28,8 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="{{ 'tableDescription' | translate }}">
|
||||
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||
<table *ngIf="!loading" mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="{{ 'tableDescription' | translate }}">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ column.header }}</th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
|
@ -57,26 +45,17 @@
|
|||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'actionsColumn' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let template">
|
||||
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'actionsColumn' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let template" style="text-align: center;">
|
||||
<button mat-icon-button color="info" (click)="showTemplate($event, template)">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="info" [disabled]="template.clientsLength === 0" (click)="editClients($event, template)">
|
||||
<mat-icon>computer</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="primary" (click)="editPxeTemplate(template)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||
<mat-icon>menu</mat-icon>
|
||||
<button mat-icon-button color="warn" (click)="toggleAction(template, 'delete')">
|
||||
<mat-icon>{{ 'deleteIcon' | translate }}</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="toggleAction(template, 'create')">{{ 'createServerButton' | translate }}</button>
|
||||
<button mat-menu-item (click)="addClientsToPxe(template)">{{ 'addClientButton' | translate }}</button>
|
||||
<button mat-menu-item (click)="toggleAction(template, 'sync')">{{ 'syncDatabaseButton' | translate }}</button>
|
||||
<button mat-menu-item (click)="toggleAction(template, 'delete')">{{ 'deleteButton' | translate }}</button>
|
||||
</mat-menu>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
|
|
@ -6,21 +6,12 @@ import { MatTableDataSource } from '@angular/material/table';
|
|||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||
import { DataService } from './data.service';
|
||||
import {
|
||||
ShowOrganizationalUnitComponent
|
||||
} from "../../groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component";
|
||||
import {ShowTemplateContentComponent} from "./show-template-content/show-template-content.component";
|
||||
import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component";
|
||||
import {
|
||||
AddClientsToSubnetComponent
|
||||
} from "../../ogdhcp/og-dhcp-subnets/add-clients-to-subnet/add-clients-to-subnet.component";
|
||||
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';
|
||||
import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pxe',
|
||||
|
@ -78,19 +69,20 @@ export class PxeComponent {
|
|||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loading = true;
|
||||
this.search();
|
||||
this.loadAlert()
|
||||
this.syncTemplates()
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
search(): void {
|
||||
this.loading = true;
|
||||
this.dataService.getPxeTemplates(this.filters).subscribe(
|
||||
data => {
|
||||
this.dataSource.data = data;
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching pxe templates', error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -105,6 +97,7 @@ export class PxeComponent {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
editPxeTemplate(template: any) {
|
||||
const dialogRef = this.dialog.open(CreatePxeTemplateComponent, {
|
||||
data: template, // Pasa los datos del template para edición
|
||||
|
@ -118,40 +111,26 @@ export class PxeComponent {
|
|||
|
||||
toggleAction(image: any, action: string): void {
|
||||
switch (action) {
|
||||
case 'create':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/post`, {}).subscribe({
|
||||
next: (response) => {
|
||||
this.search();
|
||||
// @ts-ignore
|
||||
this.toastService.success(response.success);
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(error.error.error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'sync':
|
||||
this.http.get(`${this.apiUrl}/server/${image.uuid}/get`, {}).subscribe({
|
||||
next: (response) => {
|
||||
this.search();
|
||||
// @ts-ignore
|
||||
this.toastService.success(response.success);
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(error.error.error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/delete`, {}).subscribe({
|
||||
next: () => {
|
||||
this.toastService.success('Plantilla eliminada correctamente');
|
||||
this.search();
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(error.error.error);
|
||||
}
|
||||
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
||||
width: '300px',
|
||||
data: { name: image.name }
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/delete`, {}).subscribe({
|
||||
next: () => {
|
||||
this.toastService.success('Plantilla eliminada exitosamente');
|
||||
this.search();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al eliminar la subred', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
break;
|
||||
default:
|
||||
console.error('Acción no soportada:', action);
|
||||
|
@ -164,11 +143,6 @@ export class PxeComponent {
|
|||
const dialogRef = this.dialog.open(ShowTemplateContentComponent, { data: { data }, width: '700px'});
|
||||
}
|
||||
|
||||
editClients(event: MouseEvent, data: any): void {
|
||||
event.stopPropagation();
|
||||
const dialogRef = this.dialog.open(ClientsComponent, { data: { data }, width: '700px'});
|
||||
}
|
||||
|
||||
syncTemplates() {
|
||||
this.http.post(`${this.apiUrl}/sync`, {})
|
||||
.subscribe(response => {
|
||||
|
@ -180,17 +154,6 @@ export class PxeComponent {
|
|||
});
|
||||
}
|
||||
|
||||
addClientsToPxe(template: Subnet) {
|
||||
const dialogRef = this.dialog.open(AddClientsToPxeComponent, {
|
||||
width: '600px',
|
||||
data: { subnetUuid: template.uuid, subnetName: template.name }
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
|
@ -207,7 +170,7 @@ export class PxeComponent {
|
|||
return this.http.get<any>(`${this.apiUrl}/server/get-collection`);
|
||||
}
|
||||
|
||||
openSubnetInfoDialog() {
|
||||
openInfoDialog() {
|
||||
this.loadAlert().subscribe(
|
||||
response => {
|
||||
this.alertMessage = response.message;
|
||||
|
@ -248,5 +211,5 @@ export class PxeComponent {
|
|||
themeColor: '#3f51b5'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
<mat-spinner *ngIf="loading"></mat-spinner>
|
||||
<table *ngIf="!loading" 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">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"loginError": "Login error: {{error}}",
|
||||
"labelUsers": "Users",
|
||||
"labelRoles": "Roles",
|
||||
"adminImagesTitle": "Manage clients",
|
||||
"adminImagesTitle": "Manage images",
|
||||
"addUser": "Add users",
|
||||
"searchLabel": "Search image name",
|
||||
"searchPlaceholder": "Search",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"loginError": "Login error: {{error}}",
|
||||
"labelUsers": "Usuarios",
|
||||
"labelRoles": "Roles",
|
||||
"adminImagesTitle": "Administrar clientes",
|
||||
"adminImagesTitle": "Administrar OgLives",
|
||||
"addUser": "Añadir usuarios",
|
||||
"searchLabel": "Buscar nombre de imagen",
|
||||
"searchPlaceholder": "Búsqueda",
|
||||
|
|
Loading…
Reference in New Issue