Added new styles, and ogboot endpoints
parent
e4319608cd
commit
45e1679e83
|
@ -11,6 +11,8 @@ import { RolesComponent } from './components/pages/admin/roles/roles/roles.compo
|
|||
import { GroupsComponent } from './components/groups/groups.component';
|
||||
import { ImagesComponent } from './components/images/images/images.component';
|
||||
import { PxeComponent } from './components/pxe/pxe/pxe.component';
|
||||
import {PxeBootFilesComponent} from "./components/pxe-boot-files/pxe-boot-files.component";
|
||||
import {OgbootStatusComponent} from "./components/ogboot-status/ogboot-status.component";
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
||||
{
|
||||
|
@ -24,6 +26,8 @@ const routes: Routes = [
|
|||
{ path: 'groups', component: GroupsComponent },
|
||||
{ path: 'images', component: ImagesComponent },
|
||||
{ path: 'pxe', component: PxeComponent },
|
||||
{ path: 'pxe-boot-file', component: PxeBootFilesComponent },
|
||||
{ path: 'ogboot-status', component: OgbootStatusComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -33,7 +37,7 @@ const routes: Routes = [
|
|||
{ path: 'login', component: LoginComponent },
|
||||
],
|
||||
},
|
||||
{ path: '**', component: PageNotFoundComponent },
|
||||
{ path: '**', component: PageNotFoundComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -52,6 +52,7 @@ import { MatChip, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule } fr
|
|||
import { ClientViewComponent } from './components/groups/client-view/client-view.component';
|
||||
import { MatTab, MatTabGroup } from "@angular/material/tabs";
|
||||
import { MatTooltip } from "@angular/material/tooltip";
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { DeleteGroupsModalComponent } from './components/groups/delete-groups-modal/delete-groups-modal.component';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
|
@ -78,6 +79,9 @@ import { InfoImageComponent } from './components/images/images/info-image/info-i
|
|||
import { PxeComponent } from './components/pxe/pxe/pxe.component';
|
||||
import { CreatePxeTemplateComponent } from './components/pxe/pxe/create-pxeTemplate/create-pxe-template/create-pxe-template.component';
|
||||
import { EditPxeTemplateComponent } from './components/pxe/pxe/edit-pxe-template/edit-pxe-template.component';
|
||||
import { PxeBootFilesComponent } from './components/pxe-boot-files/pxe-boot-files.component';
|
||||
import {MatExpansionPanel, MatExpansionPanelDescription, MatExpansionPanelTitle} from "@angular/material/expansion";
|
||||
import { OgbootStatusComponent } from './components/ogboot-status/ogboot-status.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -118,7 +122,9 @@ import { EditPxeTemplateComponent } from './components/pxe/pxe/edit-pxe-template
|
|||
InfoImageComponent,
|
||||
PxeComponent,
|
||||
CreatePxeTemplateComponent,
|
||||
EditPxeTemplateComponent
|
||||
EditPxeTemplateComponent,
|
||||
PxeBootFilesComponent,
|
||||
OgbootStatusComponent
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [BrowserModule,
|
||||
|
@ -142,6 +148,7 @@ import { EditPxeTemplateComponent } from './components/pxe/pxe/edit-pxe-template
|
|||
MatStepperModule,
|
||||
DragDropModule,
|
||||
MatSlideToggleModule, MatMenu, MatMenuTrigger, MatMenuItem, MatAutocomplete, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule, MatChip, MatProgressSpinner, MatTabGroup, MatTab, MatTooltip,
|
||||
MatExpansionModule,
|
||||
ToastrModule.forRoot(
|
||||
{
|
||||
timeOut: 5000,
|
||||
|
@ -151,7 +158,7 @@ import { EditPxeTemplateComponent } from './components/pxe/pxe/edit-pxe-template
|
|||
progressAnimation: 'increasing',
|
||||
closeButton: true
|
||||
}
|
||||
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile
|
||||
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
|
|
|
@ -159,8 +159,8 @@ export class DataService {
|
|||
getFilter(id: string): Observable<any> {
|
||||
return this.http.get<any>('http://127.0.0.1:8080/views/' + id).pipe(
|
||||
map(response => {
|
||||
|
||||
|
||||
|
||||
|
||||
if (response.name && response.filters) {
|
||||
return response;
|
||||
} else {
|
||||
|
@ -173,6 +173,6 @@ export class DataService {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
.form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px; /* Espacio entre los campos */
|
||||
}
|
||||
|
||||
.form-field {
|
||||
width: 100%; /* Para que cada campo ocupe todo el ancho disponible */
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px; /* Espacio entre los botones */
|
||||
margin-top: 16px; /* Separación superior para los botones */
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
<h1 mat-dialog-title>Editar Imagen</h1>
|
||||
<div mat-dialog-content>
|
||||
<mat-form-field appearance="fill">
|
||||
<div mat-dialog-content class="form-container">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>Nombre</mat-label>
|
||||
<input matInput [(ngModel)]="name" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>URL de descarga</mat-label>
|
||||
<input matInput [(ngModel)]="downloadUrl" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<div mat-dialog-actions class="actions-container">
|
||||
<button mat-button (click)="onNoClick()">Cancelar</button>
|
||||
<button mat-button color="primary" (click)="editOgLive()">Guardar</button>
|
||||
</div>
|
||||
|
|
|
@ -59,12 +59,41 @@ button {
|
|||
min-width: 160px;
|
||||
}
|
||||
|
||||
.cd-icon {
|
||||
color: #888888;
|
||||
margin-right: 20px;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
|
||||
.image-name{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-container h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
<mat-accordion class="example-headers-align">
|
||||
<mat-expansion-panel hideToggle>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Sincronización ogBoot </mat-panel-title>
|
||||
<mat-panel-description>
|
||||
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<p *ngIf="alertMessage">Isos alojadas en servidor ogBoot: {{ alertMessage }}</p>
|
||||
<p *ngIf="alertMessage">Isos alojadas en servidor ogCore (base de datos): {{ length }}</p>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Administrar imágenes</h2>
|
||||
<div class="images-button-row">
|
||||
|
@ -5,28 +18,40 @@
|
|||
</div>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" [ngClass]="{'clickable': column.columnDef === 'name'}"
|
||||
(click)="column.columnDef === 'name' && showInfo(image)">
|
||||
{{ column.cell(image) }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let user" > {{ column.cell(user) }} </td>
|
||||
</ng-container>
|
||||
|
||||
<div class="lists-container">
|
||||
<div class="imagesLists-container">
|
||||
<mat-card class="card unidad-card">
|
||||
<mat-card-title>Imágenes disponibles</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div *ngIf="images.length === 0">No hay imágenes disponibles.</div>
|
||||
<div *ngFor="let image of images">
|
||||
<div class="image-container">
|
||||
<mat-icon class="cd-icon">album</mat-icon>
|
||||
<h4 class="image-name" (click)="showInfo(image)">{{ image.name }}</h4>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="toggleStatus(image)">Montar/Desmontar</button>
|
||||
<button mat-menu-item (click)="deleteImage(image.uuid)">Eliminar</button>
|
||||
<button mat-menu-item (click)="editImage(image)">Editar</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
<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="@@buttonDeleteUser"><mat-icon>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, 'install')">Instalar</button>
|
||||
<button mat-menu-item (click)="toggleAction(image, 'uninstall')">Desinstalar</button>
|
||||
<button mat-menu-item (click)="toggleAction(image, 'set-default')">Cambiar a imagen por defecto</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]="pageSizeOptions"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, OnInit, signal} from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CreateImageComponent } from './create-image/create-image/create-image.component';
|
||||
import { EditImageComponent } from './edit-image/edit-image/edit-image.component';
|
||||
import { InfoImageComponent } from './info-image/info-image/info-image.component';
|
||||
import { MatTableDataSource } from "@angular/material/table";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import { DatePipe } from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: 'app-images',
|
||||
|
@ -12,23 +16,62 @@ import { InfoImageComponent } from './info-image/info-image/info-image.component
|
|||
})
|
||||
export class ImagesComponent implements OnInit {
|
||||
images: { downloadUrl: string; name: string; uuid: string }[] = [];
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
length: number = 0;
|
||||
itemsPerPage: number = 10;
|
||||
page: number = 1;
|
||||
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
||||
selectedElements: string[] = [];
|
||||
alertMessage: string | null = null;
|
||||
readonly panelOpenState = signal(false);
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'id',
|
||||
header: 'ID',
|
||||
cell: (user: any) => `${user.id}`
|
||||
},
|
||||
{
|
||||
columnDef: 'name',
|
||||
header: 'Nombre de imagen',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'downloadUrl',
|
||||
header: 'Url descarga',
|
||||
cell: (user: any) => `${user.downloadUrl}`
|
||||
},
|
||||
{
|
||||
columnDef: 'default',
|
||||
header: 'Imagen por defecto',
|
||||
cell: (user: any) => `${user.default}`
|
||||
},
|
||||
{
|
||||
columnDef: 'createdAt',
|
||||
header: 'Fecha de creación',
|
||||
cell: (user: any) => `${this.datePipe.transform(user.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
|
||||
}
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||
|
||||
private apiUrl = 'http://127.0.0.1:8080/og-lives';
|
||||
|
||||
constructor(public dialog: MatDialog, private http: HttpClient) {}
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private toastService: ToastrService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadImages();
|
||||
this.loadAlert();
|
||||
}
|
||||
|
||||
loadImages(): void {
|
||||
this.http.get<any>(`${this.apiUrl}?page=1&itemsPerPage=30`).subscribe({
|
||||
this.http.get<any>(`${this.apiUrl}?page=1&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.images = response['hydra:member'].map((item: any) => ({
|
||||
downloadUrl: item.downloadUrl,
|
||||
name: item.name,
|
||||
uuid: item.uuid
|
||||
}));
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
|
@ -49,20 +92,57 @@ export class ImagesComponent implements OnInit {
|
|||
|
||||
showInfo(image: any): void {
|
||||
const dialogRef = this.dialog.open(InfoImageComponent, {
|
||||
width: '300px',
|
||||
data: image
|
||||
width: '700px',
|
||||
data: image
|
||||
});
|
||||
}
|
||||
|
||||
toggleStatus(image: any): void {
|
||||
// Implementar lógica para montar/desmontar
|
||||
console.log('Montar/Desmontar:', image);
|
||||
toggleAction(image: any, action:string): void {
|
||||
switch (action) {
|
||||
case 'set-default':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/set-default`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Imagen cambiada');
|
||||
this.loadImages();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'install':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/install`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Imagen cambiada');
|
||||
this.loadImages();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'uninstall':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/uninstall`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Imagen cambiada');
|
||||
this.loadImages();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error('Acción no soportada:', action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deleteImage(uuid: string): void {
|
||||
this.http.delete(`${this.apiUrl}/${uuid}`).subscribe({
|
||||
deleteImage(image: any): void {
|
||||
this.http.delete(`${this.apiUrl}/${image.uuid}`).subscribe({
|
||||
next: () => {
|
||||
console.log('Imagen eliminada');
|
||||
this.toastService.success('Image deleted successfully');
|
||||
this.loadImages();
|
||||
},
|
||||
error: (error) => {
|
||||
|
@ -73,15 +153,50 @@ export class ImagesComponent implements OnInit {
|
|||
|
||||
editImage(image: any): void {
|
||||
const dialogRef = this.dialog.open(EditImageComponent, {
|
||||
width: '300px',
|
||||
data: image
|
||||
width: '700px',
|
||||
data: image
|
||||
});
|
||||
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.loadImages();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
applyFilter() {
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(event: PageEvent) {
|
||||
this.page = event.pageIndex;
|
||||
this.itemsPerPage = event.pageSize;
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
loadAlert() {
|
||||
this.http.get(`${this.apiUrl}/server/get-isos`)
|
||||
.subscribe(response => {
|
||||
// @ts-ignore
|
||||
this.alertMessage = response.data.downloads.length
|
||||
}, error => {
|
||||
console.error('Error al cargar la información del alert', error);
|
||||
});
|
||||
}
|
||||
|
||||
getIcon(): { name: string, color: string } {
|
||||
if (Number(this.alertMessage) === this.length) {
|
||||
return { name: 'check_circle', color: 'green' }; // Icono de check verde
|
||||
} else {
|
||||
return { name: 'cancel', color: 'red' }; // Icono de cruz roja
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@
|
|||
|
||||
<!-- Submenu items for Images -->
|
||||
<mat-nav-list *ngIf="showSubList" style="padding-left: 20px;">
|
||||
<mat-list-item routerLink="/ogboot-status">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">analytics</mat-icon>
|
||||
<span i18n="@@gallery">Estado de servidor</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/images">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">album</mat-icon>
|
||||
|
@ -40,7 +46,13 @@
|
|||
<mat-list-item routerLink="/pxe">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">assignment</mat-icon>
|
||||
<span i18n="@@upload">PXE</span>
|
||||
<span i18n="@@upload">Plantillas PXE</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/pxe-boot-file">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">save</mat-icon>
|
||||
<span i18n="@@upload">Arranque PXE</span>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<p>ogboot-status works!</p>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { OgbootStatusComponent } from './ogboot-status.component';
|
||||
|
||||
describe('OgbootStatusComponent', () => {
|
||||
let component: OgbootStatusComponent;
|
||||
let fixture: ComponentFixture<OgbootStatusComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [OgbootStatusComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(OgbootStatusComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ogboot-status',
|
||||
templateUrl: './ogboot-status.component.html',
|
||||
styleUrl: './ogboot-status.component.css'
|
||||
})
|
||||
export class OgbootStatusComponent {
|
||||
|
||||
}
|
|
@ -46,7 +46,6 @@ export class UsersComponent implements OnInit {
|
|||
loadUsers() {
|
||||
this.userService.getUsers().subscribe(response => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
console.log(this.dataSource.data);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.templates-button-row {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.lists-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.templatesLists-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card.unidad-card {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.template-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid rgba(122, 122, 122, 0.555);
|
||||
}
|
||||
|
||||
.template-container h4 {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mat-icon-button {
|
||||
margin-left: 16px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.mat-menu {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.template-name{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-container h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.paginator-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 30px;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminPXETitle">Administrar ficheros de arranque PXE</h2>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" [ngClass]="{'clickable': column.columnDef === 'name'}"
|
||||
(click)="column.columnDef === 'name' && showPxeInfo(image)">
|
||||
{{ column.cell(image) }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let user" > {{ column.cell(user) }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
<button mat-button color="primary" (click)="editPxeTemplate(image)" i18n="@@editImage">Editar</button>
|
||||
<button mat-button color="warn" (click)="deletePxeTemplate(image)" i18n="@@buttonDeleteUser">Eliminar</button>
|
||||
<button mat-button [matMenuTriggerFor]="menu">
|
||||
Acciones (ogBoot)
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="toggleAction(image, 'create')">Crear</button>
|
||||
<button mat-menu-item (click)="toggleAction(image, 'delete')">Eliminar</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]="pageSizeOptions"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PxeBootFilesComponent } from './pxe-boot-files.component';
|
||||
|
||||
describe('PxeBootFilesComponent', () => {
|
||||
let component: PxeBootFilesComponent;
|
||||
let fixture: ComponentFixture<PxeBootFilesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PxeBootFilesComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PxeBootFilesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {
|
||||
CreatePxeTemplateComponent
|
||||
} from "../pxe/pxe/create-pxeTemplate/create-pxe-template/create-pxe-template.component";
|
||||
import {EditPxeTemplateComponent} from "../pxe/pxe/edit-pxe-template/edit-pxe-template.component";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pxe-boot-files',
|
||||
templateUrl: './pxe-boot-files.component.html',
|
||||
styleUrl: './pxe-boot-files.component.css'
|
||||
})
|
||||
export class PxeBootFilesComponent {
|
||||
pxeTemplates: any[] = []; // Inicializa el array de plantillas
|
||||
currentPage: number = 1;
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
length: number = 0;
|
||||
itemsPerPage: number = 10;
|
||||
page: number = 1;
|
||||
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
||||
selectedElements: string[] = [];
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'id',
|
||||
header: 'ID',
|
||||
cell: (user: any) => `${user.id}`
|
||||
},
|
||||
{
|
||||
columnDef: 'name',
|
||||
header: 'Nombre de la plantilla',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'createdAt',
|
||||
header: 'Fecha de creación',
|
||||
cell: (user: any) => `${user.createdAt}`
|
||||
}
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||
|
||||
private apiUrl = 'http://127.0.0.1:8080/pxe-boot-files';
|
||||
|
||||
constructor(public dialog: MatDialog, private http: HttpClient) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadPxeTemplates();
|
||||
}
|
||||
|
||||
loadPxeTemplates(): void {
|
||||
this.http.get<any>(`${this.apiUrl}?page=1&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
console.log('Plantillas PXE cargadas:', this.pxeTemplates);
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al cargar plantillas PXE:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addPxeTemplate() {
|
||||
const dialogRef = this.dialog.open(CreatePxeTemplateComponent, {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(() => {
|
||||
this.loadPxeTemplates();
|
||||
});
|
||||
}
|
||||
|
||||
showPxeInfo(template: any) {
|
||||
|
||||
}
|
||||
|
||||
toggleAction(image: any, action:string): void {
|
||||
switch (action) {
|
||||
case 'create':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/post`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Plantilla cambiada');
|
||||
this.loadPxeTemplates();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/delete`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Plantilla cambiada');
|
||||
this.loadPxeTemplates();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error('Acción no soportada:', action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deletePxeTemplate(uuid: string) {
|
||||
// Lógica para eliminar una plantilla
|
||||
}
|
||||
|
||||
editPxeTemplate(template: any) {
|
||||
const dialogRef = this.dialog.open(EditPxeTemplateComponent, {
|
||||
data: template
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(() => {
|
||||
this.loadPxeTemplates();
|
||||
});
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(event: PageEvent) {
|
||||
this.page = event.pageIndex;
|
||||
this.itemsPerPage = event.pageSize;
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
}
|
|
@ -59,12 +59,32 @@ button {
|
|||
min-width: 160px;
|
||||
}
|
||||
|
||||
.cd-icon {
|
||||
color: #888888;
|
||||
margin-right: 20px;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
.template-name{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.template-name{
|
||||
cursor: pointer;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-container h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.paginator-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
|
|
@ -1,31 +1,57 @@
|
|||
<mat-accordion>
|
||||
<mat-expansion-panel hideToggle>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>Sincronización ogBoot </mat-panel-title>
|
||||
<mat-panel-description>
|
||||
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<p *ngIf="alertMessage">Plantillas creadsa en servidor ogBoot: {{ alertMessage }}</p>
|
||||
<p *ngIf="alertMessage">Plantillas creadsa en servidor ogCore (base de datos): {{ length }}</p>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminPXETitle">Administrar plantillas PXE</h2>
|
||||
<div class="pxe-button-row">
|
||||
<button mat-flat-button color="primary" (click)="addPxeTemplate()">Añadir plantilla PXE</button>
|
||||
</div>
|
||||
<h2 class="title" i18n="@@adminPXETitle">Administrar plantillas PXE</h2>
|
||||
<div class="pxe-button-row">
|
||||
<button mat-flat-button color="primary" (click)="addPxeTemplate()">Añadir plantilla PXE</button>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div class="lists-container">
|
||||
<div class="pxeLists-container">
|
||||
<mat-card class="card pxe-card">
|
||||
<mat-card-title>Plantillas PXE disponibles</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div *ngIf="pxeTemplates.length === 0">No hay plantillas PXE disponibles.</div>
|
||||
<div *ngFor="let template of pxeTemplates">
|
||||
<div class="template-container">
|
||||
<mat-icon class="cd-icon">insert_drive_file</mat-icon>
|
||||
<h4 class="template-name" (click)="showPxeInfo(template)">{{ template.name }}</h4>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="editPxeTemplate(template)">Editar</button>
|
||||
<button mat-menu-item (click)="deletePxeTemplate(template.uuid)">Eliminar</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" [ngClass]="{'clickable': column.columnDef === 'name'}"
|
||||
(click)="column.columnDef === 'name' && showPxeInfo(image)">
|
||||
{{ column.cell(image) }}
|
||||
</td>
|
||||
<td mat-cell *matCellDef="let user" > {{ column.cell(user) }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
|
||||
<td mat-cell *matCellDef="let template">
|
||||
<button mat-icon-button color="primary" (click)="editPxeTemplate(template)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
|
||||
<button mat-icon-button color="warn" (click)="deletePxeTemplate(template)" i18n="@@buttonDeleteUser"><mat-icon>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(template, 'create')">Crear</button>
|
||||
<button mat-menu-item (click)="toggleAction(template, 'delete')">Eliminar</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]="pageSizeOptions"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,10 @@ import { Component } from '@angular/core';
|
|||
import { CreatePxeTemplateComponent } from './create-pxeTemplate/create-pxe-template/create-pxe-template.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { EditPxeTemplateComponent } from './edit-pxe-template/edit-pxe-template.component';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {DatePipe} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pxe',
|
||||
|
@ -11,20 +15,57 @@ import { EditPxeTemplateComponent } from './edit-pxe-template/edit-pxe-template.
|
|||
})
|
||||
export class PxeComponent {
|
||||
pxeTemplates: any[] = []; // Inicializa el array de plantillas
|
||||
itemsPerPage: number = 30;
|
||||
currentPage: number = 1;
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
length: number = 0;
|
||||
itemsPerPage: number = 10;
|
||||
page: number = 1;
|
||||
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
||||
selectedElements: string[] = [];
|
||||
alertMessage: string | null = null;
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'id',
|
||||
header: 'ID',
|
||||
cell: (user: any) => `${user.id}`
|
||||
},
|
||||
{
|
||||
columnDef: 'name',
|
||||
header: 'Nombre de la plantilla',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'synchronized',
|
||||
header: 'Creado en ogBoot',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'createdAt',
|
||||
header: 'Fecha de creación',
|
||||
cell: (user: any) => `${this.datePipe.transform(user.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
|
||||
}
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||
|
||||
constructor(public dialog: MatDialog, private http: HttpClient) { }
|
||||
private apiUrl = 'http://127.0.0.1:8080/pxe-templates';
|
||||
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private toastService: ToastrService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadPxeTemplates();
|
||||
this.loadAlert()
|
||||
}
|
||||
|
||||
loadPxeTemplates(): void {
|
||||
const url = `http://127.0.0.1:8080/pxe-templates?page=${this.currentPage}&itemsPerPage=${this.itemsPerPage}`;
|
||||
this.http.get<any>(url).subscribe({
|
||||
next: data => {
|
||||
this.pxeTemplates = data['hydra:member'];
|
||||
this.http.get<any>(`${this.apiUrl}?page=1&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
console.log('Plantillas PXE cargadas:', this.pxeTemplates);
|
||||
},
|
||||
error: error => {
|
||||
|
@ -43,7 +84,38 @@ export class PxeComponent {
|
|||
}
|
||||
|
||||
showPxeInfo(template: any) {
|
||||
// Lógica para mostrar información de una plantilla
|
||||
|
||||
}
|
||||
|
||||
toggleAction(image: any, action:string): void {
|
||||
switch (action) {
|
||||
case 'create':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/post`, {}).subscribe({
|
||||
next: (response) => {
|
||||
this.loadPxeTemplates();
|
||||
// @ts-ignore
|
||||
this.toastService.success(response.message);
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(error.error.error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
this.http.post(`${this.apiUrl}/server/${image.uuid}/delete`, {}).subscribe({
|
||||
next: () => {
|
||||
console.log('Plantilla cambiada');
|
||||
this.loadPxeTemplates();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cambiar la imagen:', error);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error('Acción no soportada:', action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deletePxeTemplate(uuid: string) {
|
||||
|
@ -59,4 +131,41 @@ export class PxeComponent {
|
|||
this.loadPxeTemplates();
|
||||
});
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(event: PageEvent) {
|
||||
this.page = event.pageIndex;
|
||||
this.itemsPerPage = event.pageSize;
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
loadAlert() {
|
||||
this.http.get(`${this.apiUrl}/server/get-collection`)
|
||||
.subscribe(response => {
|
||||
// @ts-ignore
|
||||
this.alertMessage = response.templates.length
|
||||
}, error => {
|
||||
console.error('Error al cargar la información del alert', error);
|
||||
});
|
||||
}
|
||||
|
||||
getIcon(): { name: string, color: string } {
|
||||
if (Number(this.alertMessage) === this.length) {
|
||||
return { name: 'check_circle', color: 'green' }; // Icono de check verde
|
||||
} else {
|
||||
return { name: 'cancel', color: 'red' }; // Icono de cruz roja
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue