refs #918 update client-main-view component

develop-jenkins
Alvaro Puente Mella 2024-10-14 21:13:21 +02:00
parent 0276c92680
commit 33525f1163
10 changed files with 279 additions and 240 deletions

View File

@ -89,7 +89,6 @@ import { CalendarComponent } from './components/calendar/calendar.component';
import { CreateCalendarComponent } from './components/calendar/create-calendar/create-calendar.component';
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
import { CreateCalendarRuleComponent } from './components/calendar/create-calendar-rule/create-calendar-rule.component';
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
import { CreateCommandGroupComponent } from './components/commands/commands-groups/create-command-group/create-command-group.component';
@ -106,6 +105,7 @@ import {MatSliderModule} from '@angular/material/slider';
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
import { ImagesComponent } from './components/images/images.component';
import { CreateImageComponent } from './components/images/create-image/create-image.component';
import { PartitionAssistantComponent } from './components/groups/components/client-main-view/partition-assistant/partition-assistant.component';
@NgModule({
declarations: [
AppComponent,
@ -167,7 +167,8 @@ import { CreateImageComponent } from './components/images/create-image/create-im
StatusComponent,
ClientMainViewComponent,
ImagesComponent,
CreateImageComponent
CreateImageComponent,
PartitionAssistantComponent,
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -1,50 +0,0 @@
<div class="header-container">
<h2 class="title">Asistente de Particionado de Disco</h2>
</div>
<mat-divider class="divider"></mat-divider>
<!-- Lista de particiones añadidas -->
<h3>Particiones actuales</h3>
<mat-list>
<mat-list-item *ngFor="let partition of partitions">
{{ partition.name }} - {{ partition.size }} GB - {{ partition.type }}
<button mat-icon-button color="warn" (click)="removePartition(partition)">
<mat-icon>delete</mat-icon>
</button>
</mat-list-item>
</mat-list>
<!-- Añadir nueva partición -->
<h3>Añadir Partición</h3>
<div class="partition-form">
<mat-form-field appearance="fill">
<mat-label>Nombre</mat-label>
<input matInput placeholder="Nombre de la partición" [(ngModel)]="newPartition.name">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Tamaño (GB)</mat-label>
<input matInput type="number" placeholder="Tamaño" [(ngModel)]="newPartition.size">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Tipo</mat-label>
<mat-select [(ngModel)]="newPartition.type">
<mat-option *ngFor="let type of partitionTypes" [value]="type">{{ type }}</mat-option>
</mat-select>
</mat-form-field>
<!-- Nuevo campo para seleccionar imagen -->
<mat-form-field appearance="fill">
<mat-label>Imagen</mat-label>
<mat-select [(ngModel)]="selectedImageUuid">
<mat-option *ngFor="let image of images" [value]="image.uuid">
{{ image.name }} - {{ image.description }}
</mat-option>
</mat-select>
</mat-form-field>
<!-- Botón para añadir partición -->
<button mat-raised-button color="primary" (click)="addPartition()">Añadir Partición</button>
</div>

View File

@ -1,108 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
interface ClientInfo {
name: string;
type: string;
ip: string;
mac: string;
serialNumber: string;
netiface: string;
netDriver: string;
}
interface Partition {
name: string;
size: number;
type: string;
}
interface Disk {
name: string;
size: number;
}
interface ImageData {
name: string;
uuid: string; // Usaremos el UUID como valor
description: string;
}
@Component({
selector: 'app-partition-assistant',
templateUrl: './partition-assistant.component.html',
styleUrls: ['./partition-assistant.component.css']
})
export class PartitionAssistantComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
@Input() clientUuid!: string;
clientInfo: ClientInfo | undefined;
availableDisks: Disk[] = [];
selectedDisk: Disk | undefined;
partitions: Partition[] = [];
images: ImageData[] = []; // Lista para almacenar las imágenes cargadas
selectedImageUuid: string = ''; // Variable para almacenar la imagen seleccionada
newPartition: Partition = { name: '', size: 0, type: '' };
partitionTypes: string[] = ['NTFS', 'FAT32', 'EXT4'];
constructor(
private http: HttpClient,
private toastService: ToastrService
) {}
ngOnInit(): void {
if (this.clientUuid) {
this.getClientInfo(this.clientUuid);
} else {
console.error('No client UUID provided!');
}
// Llamada a la API para obtener las imágenes
this.getImages();
}
getClientInfo(uuid: string): void {
this.http.get<ClientInfo>(`${this.baseUrl}/clients/${uuid}`)
.subscribe(
(response: ClientInfo) => {
this.clientInfo = response;
console.log('Client info:', this.clientInfo);
},
error => {
console.error('Error fetching client info:', error);
}
);
}
// Método para obtener las imágenes desde la API
getImages(): void {
this.http.get<any>('http://127.0.0.1:8001/images?page=1&itemsPerPage=30')
.subscribe(
(response) => {
this.images = response['hydra:member'];
console.log('Images loaded:', this.images);
},
error => {
console.error('Error fetching images:', error);
this.toastService.error('Error al cargar las imágenes.');
}
);
}
addPartition(): void {
if (this.newPartition.name && this.newPartition.size > 0 && this.newPartition.type) {
// Añadir la partición solo si todos los campos son válidos
this.partitions.push({ ...this.newPartition });
this.newPartition = { name: '', size: 0, type: '' };
} else {
this.toastService.error('Por favor, complete todos los campos correctamente.');
}
}
removePartition(partition: Partition): void {
this.partitions = this.partitions.filter(p => p !== partition);
}
}

View File

@ -4,7 +4,7 @@
.client-header {
display: flex;
align-items: center;
margin-bottom: 40px;
margin-bottom: 10px;
background-color: #fff;
padding: 20px;
border-radius: 12px;
@ -41,7 +41,7 @@
.client-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
gap: 10px;
}
.info-section {
@ -103,12 +103,18 @@
text-anchor: middle;
}
/* Footer */
.client-footer {
margin-top: 40px;
text-align: center;
font-size: 1rem;
color: #555;
.assistants-container{
background-color: #fff;
margin-top: 10px;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.buttons-row{
display: flex;
justify-content: space-between;
padding: 20px;
}
@keyframes progress {
@ -116,4 +122,3 @@
stroke-dasharray: 0, 100;
}
}

View File

@ -5,26 +5,30 @@
<div class="client-title">
<h1>{{ clientData?.name }}</h1>
<p><strong>UUID:</strong> {{ clientData?.uuid }}</p>
<p><strong>IP Address:</strong> {{ clientData?.ip }}</p>
<p><strong>IP:</strong> {{ clientData?.ip }}</p>
</div>
</div>
<div class="client-info">
<!-- Información General -->
<div class="info-section">
<button mat-icon-button (click)="toggleGeneralInfo()" aria-label="Minimizar/Expandir información general">
<mat-icon>{{ isGeneralInfoVisible ? 'expand_less' : 'expand_more' }}</mat-icon>
</button>
<h2 *ngIf="isGeneralInfoVisible">Información General</h2>
<p *ngIf="isGeneralInfoVisible"><strong>MAC:</strong> {{ clientData?.mac }}</p>
<p *ngIf="isGeneralInfoVisible"><strong>Número de serie:</strong> {{ clientData?.serialNumber }}</p>
<h2 *ngIf="isGeneralInfoVisible">Unidad Organizativa</h2>
<p *ngIf="isGeneralInfoVisible">{{ clientData?.organizationalUnit?.name }}</p>
</div>
<div class="client-info">
<!-- General Information -->
<!-- Uso del Espacio en Disco -->
<div class="info-section">
<h2>General Information</h2>
<p><strong>Type:</strong> {{ clientData?.type }}</p>
<p><strong>MAC Address:</strong> {{ clientData?.mac }}</p>
<p><strong>Serial Number:</strong> {{ clientData?.serialNumber }}</p>
<h2>Organizational Unit</h2>
<p><strong>Name:</strong> {{ clientData?.organizationalUnit?.name }}</p>
<p><strong>Type:</strong> {{ clientData?.organizationalUnit?.type }}</p>
</div>
<!-- Disk Space Usage -->
<div class="info-section">
<h2>Disk Space</h2>
<div class="disk-usage">
<button mat-icon-button (click)="toggleDiskUsage()" aria-label="Minimizar/Expandir uso de disco">
<mat-icon>{{ isDiskUsageVisible ? 'expand_less' : 'expand_more' }}</mat-icon>
</button>
<h2 *ngIf="isDiskUsageVisible">Disco</h2>
<div class="disk-usage" *ngIf="isDiskUsageVisible">
<div class="chart-container">
<svg viewBox="0 0 36 36" class="circular-chart green">
<path class="circle-bg"
@ -39,9 +43,17 @@
<text x="18" y="20.35" class="percentage">75%</text>
</svg>
</div>
<p>Used: 75% (375GB)</p>
<p>Usado: 75% (375GB)</p>
<p>Total: 500GB</p>
</div>
</div>
</div>
</div>
<div class="buttons-row">
<button mat-flat-button color="primary" (click)="togglePartitionAssistant()">Asistente a particionado</button>
<button mat-flat-button color="primary" (click)="showBootImage()">Asignar imagen de arranque</button>
</div>
<div class="assistants-container" *ngIf="isPartitionAssistantVisible">
<app-partition-assistant></app-partition-assistant>
</div>

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientMainViewComponent } from './client-main-view.component';
describe('ClientMainViewComponent', () => {
let component: ClientMainViewComponent;
let fixture: ComponentFixture<ClientMainViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ClientMainViewComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ClientMainViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -10,6 +10,10 @@ export class ClientMainViewComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
clientUuid: string;
clientData: any;
isPartitionAssistantVisible: boolean = false;
isBootImageVisible: boolean = false;
isGeneralInfoVisible: boolean = true;
isDiskUsageVisible: boolean = true;
constructor(private http: HttpClient) {
const url = window.location.href;
@ -32,5 +36,21 @@ export class ClientMainViewComponent implements OnInit {
);
}
togglePartitionAssistant() {
this.isPartitionAssistantVisible = !this.isPartitionAssistantVisible;
}
toggleGeneralInfo() {
this.isGeneralInfoVisible = !this.isGeneralInfoVisible;
}
toggleDiskUsage() {
this.isDiskUsageVisible = !this.isDiskUsageVisible;
}
showBootImage() {
this.isPartitionAssistantVisible = false;
}
}

View File

@ -14,6 +14,7 @@
margin: 10px 0;
height: 30px;
border: 1px solid #ccc;
background-color: #c5c5c5;
}
.partition-segment {
@ -36,7 +37,7 @@
.actions {
display: flex;
justify-content: space-between;
justify-content: flex-end;
}
.remove-btn {
@ -46,3 +47,8 @@
padding: 5px;
cursor: pointer;
}
.error-message {
color: red;
margin-top: 10px;
}

View File

@ -0,0 +1,69 @@
<div class="partition-assistant">
<div class="header">
<label for="disk-number">Num. disco:</label>
<input id="disk-number" type="number" [(ngModel)]="diskNumber" />
<label for="partition-table">Tabla particiones:</label>
<select id="partition-table" [(ngModel)]="partitionTable">
<option value="MSDOS">MSDOS</option>
<option value="GPT">GPT</option>
</select>
<span class="disk-size">Tamaño: {{ totalDiskSize }} GB</span>
</div>
<!-- Barra visual de particiones -->
<div class="partition-bar">
<div
*ngFor="let partition of partitions"
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
class="partition-segment"
>
{{ partition.type }} ({{ partition.size }} GB)
</div>
</div>
<button mat-button (click)="addPartition()">Añadir</button>
<!-- Tabla de particiones (simplificada) -->
<table class="partition-table">
<thead>
<tr>
<th>Partición</th>
<th>Tipo partición</th>
<th>Tamaño (GB)</th>
<th>Formatear</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let partition of partitions; let i = index">
<td>{{ i + 1 }}</td>
<td>
<select [(ngModel)]="partition.type">
<option value="NTFS">NTFS</option>
<option value="LINUX">LINUX</option>
<option value="CACHE">CACHE</option>
</select>
</td>
<td>
<input
type="number"
[(ngModel)]="partition.size"
(input)="updatePartitionSize(i, 'gb')"
/>
</td>
<td>
<input type="checkbox" [(ngModel)]="partition.format" />
</td>
<td>
<button (click)="removePartition(i)" class="remove-btn">X</button>
</td>
</tr>
</tbody>
</table>
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
<!-- Solo el botón de Guardar -->
<div class="actions">
<button mat-button (click)="save()">Guardar</button>
</div>
</div>

View File

@ -0,0 +1,107 @@
import { Component } from '@angular/core';
interface Partition {
size: number;
type: string;
sizeBytes: number;
format: boolean;
color: string;
percentage: number;
}
@Component({
selector: 'app-partition-assistant',
templateUrl: './partition-assistant.component.html',
styleUrls: ['./partition-assistant.component.css']
})
export class PartitionAssistantComponent {
diskNumber = 1;
partitionTable = 'MSDOS';
totalDiskSize = 100;
errorMessage = '';
partitions: Partition[] = [
{ size: 17.96, type: 'NTFS', sizeBytes: 17.96 * 1024 * 1024 * 1024, format: false, color: '#00a2e8', percentage: 17.96 },
{ size: 17.29, type: 'LINUX', sizeBytes: 17.29 * 1024 * 1024 * 1024, format: false, color: '#6a5acd', percentage: 17.29 },
{ size: 12.64, type: 'CACHE', sizeBytes: 12.64 * 1024 * 1024 * 1024, format: false, color: '#ff0000', percentage: 12.64 }
];
addPartition() {
const remainingGB = this.getRemainingGB();
if (remainingGB > 0) {
this.partitions.push({
size: 0,
type: 'NTFS',
sizeBytes: 0,
format: false,
color: '#cccccc',
percentage: 0
});
} else {
this.errorMessage = 'No hay suficiente espacio libre en el disco para crear una nueva partición.';
}
}
removePartition(index: number) {
this.partitions.splice(index, 1);
this.updatePartitionPercentages();
}
updatePartitionSize(index: number, type: 'gb') {
const partition = this.partitions[index];
const remainingGB = this.getRemainingGB() + partition.size;
if (partition.size > remainingGB) {
this.errorMessage = `El tamaño de la partición no puede superar el espacio libre (${remainingGB.toFixed(2)} GB).`;
partition.size = 0;
} else {
this.errorMessage = '';
partition.sizeBytes = partition.size * 1024 * 1024 * 1024;
this.updatePartitionPercentages();
}
}
updatePartitionPercentages() {
const totalUsedGB = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
const remainingGB = this.totalDiskSize - totalUsedGB;
this.partitions.forEach(partition => {
partition.percentage = (partition.size / this.totalDiskSize) * 100;
});
if (remainingGB > 0) {
const freeSpaceIndex = this.partitions.findIndex(partition => partition.type === 'LIBRE');
if (freeSpaceIndex > -1) {
this.partitions[freeSpaceIndex].size = remainingGB;
this.partitions[freeSpaceIndex].percentage = (remainingGB / this.totalDiskSize) * 100;
}
}
}
getRemainingGB(): number {
const totalUsedGB = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
return Math.max(0, this.totalDiskSize - totalUsedGB);
}
save() {
const totalSize = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
if (totalSize > this.totalDiskSize) {
this.errorMessage = `El tamaño total de las particiones (${totalSize} GB) supera el tamaño total del disco (${this.totalDiskSize} GB).`;
return;
} else {
this.errorMessage = '';
}
const payload = this.partitions.map((partition, index) => ({
diskNumber: this.diskNumber,
partitionNumber: index + 1,
size: partition.size,
filesystem: partition.type,
format: partition.format
}));
console.log('Payload:', JSON.stringify(payload, null, 2));
}
}