refs #917 Update sourceMap to false and add responsive styles for small screens in create-image component CSS. Refactor partition-assistant component HTML to use nested ngFor loops.

develop-jenkins
Alvaro Puente Mella 2024-10-17 00:07:48 +02:00
parent f85e6e0d65
commit a4e1f44218
6 changed files with 323 additions and 120 deletions

View File

@ -69,7 +69,7 @@
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
"sourceMap": false
},
"es": {
"localize": [

View File

@ -1,53 +1,134 @@
.partition-assistant {
font-family: Arial, sans-serif;
padding: 10px;
font-family: 'Roboto', sans-serif;
background-color: #f9f9f9;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin: 20px auto;
}
.header {
display: flex;
align-items: center;
gap: 10px;
justify-content: space-between;
margin-bottom: 15px;
padding: 10px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header label {
font-weight: 500;
margin-right: 10px;
}
.disk-size {
font-size: 1rem;
font-weight: 600;
color: #555;
}
.partition-bar {
display: flex;
margin: 10px 0;
height: 30px;
border: 1px solid #000000;
margin: 20px 0;
height: 40px;
border-radius: 8px;
overflow: hidden;
background-color: #e0e0e0;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}
.partition-segment {
text-align: center;
color: white;
line-height: 30px;
line-height: 40px;
font-weight: 500;
font-size: 0.9rem;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
}
.partition-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 10px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
margin-bottom: 20px;
}
.partition-table th, .partition-table td {
border: 1px solid #ccc;
padding: 5px;
.partition-table th {
background-color: #f5f5f5;
color: #333;
padding: 12px;
font-weight: 600;
}
.partition-table td {
padding: 10px;
text-align: center;
border-bottom: 1px solid #eee;
}
.partition-table select,
.partition-table input[type="number"],
.partition-table input[type="checkbox"] {
padding: 5px;
border-radius: 4px;
border: 1px solid #ccc;
width: 100%;
}
.actions {
display: flex;
justify-content: flex-end;
padding-top: 10px;
}
.remove-btn {
background-color: red;
color: white;
button {
border: none;
padding: 5px;
padding: 10px 20px;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
button.mat-button {
background-color: #007bff;
color: white;
}
button.mat-button:hover {
background-color: #0056b3;
}
button.mat-flat-button {
background-color: #28a745;
color: white;
}
button.mat-flat-button:hover {
background-color: #218838;
}
button.remove-btn {
background-color: #dc3545;
color: white;
border-radius: 4px;
}
button.remove-btn:hover {
background-color: #c82333;
}
.error-message {
color: red;
color: #dc3545;
font-size: 0.9rem;
padding: 10px;
background-color: #f8d7da;
border-radius: 4px;
margin-top: 10px;
}

View File

@ -1,18 +1,12 @@
<div class="partition-assistant">
<div class="partition-assistant" *ngFor="let disk of disks; let i = index">
<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>
<label for="disk-number-{{i}}">Disco {{ disk.diskNumber }}:</label>
<span class="disk-size">Tamaño: {{ disk.totalDiskSize }} GB</span>
</div>
<div class="partition-bar">
<div
*ngFor="let partition of partitions"
*ngFor="let partition of disk.partitions"
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
class="partition-segment"
>
@ -20,7 +14,7 @@
</div>
</div>
<button mat-button (click)="addPartition()">Añadir +</button>
<button mat-button (click)="addPartition(disk.diskNumber)">Añadir +</button>
<table class="partition-table">
<thead>
@ -33,8 +27,8 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let partition of partitions; let i = index">
<td>{{ i + 1 }}</td>
<tr *ngFor="let partition of disk.partitions; let j = index">
<td>{{ j + 1 }}</td>
<td>
<select [(ngModel)]="partition.type">
<option value="NTFS">NTFS</option>
@ -46,21 +40,22 @@
<input
type="number"
[(ngModel)]="partition.size"
(input)="updatePartitionSize(i, 'gb')"
(input)="updatePartitionSize(disk.diskNumber, j, partition.size)"
/>
</td>
<td>
<input type="checkbox" [(ngModel)]="partition.format" />
</td>
<td>
<button (click)="removePartition(i)" class="remove-btn">X</button>
<button (click)="removePartition(disk.diskNumber, j)" class="remove-btn">X</button>
</td>
</tr>
</tbody>
</table>
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
<div class="actions">
<button mat-flat-button color="primary" (click)="save()">Guardar</button>
</div>
</div>
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
<div class="actions">
<button mat-flat-button color="primary" (click)="save()">Guardar</button>
</div>

View File

@ -1,4 +1,5 @@
import { Component, Input } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface Partition {
size: number;
@ -14,98 +15,176 @@ interface Partition {
templateUrl: './partition-assistant.component.html',
styleUrls: ['./partition-assistant.component.css']
})
export class PartitionAssistantComponent {
export class PartitionAssistantComponent implements OnInit {
@Input() data: any;
diskNumber = 1;
partitionTable = 'MSDOS';
totalDiskSize = 100;
@Input() clientUuid: string | undefined; // El clientUuid que necesitas
errorMessage = '';
originalPartitions: any[] = [];
disks: { diskNumber: number, totalDiskSize: number, partitions: Partition[] }[] = [];
private apiUrl = 'http://127.0.0.1:8001/partitions';
ngOnInit() { console.log('Data:', this.data); }
constructor(private http: HttpClient) {}
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 }
];
ngOnInit() {
this.initializeDisks();
}
addPartition() {
const remainingGB = this.getRemainingGB();
if (remainingGB > 0) {
this.partitions.push({
size: 0,
type: 'NTFS',
sizeBytes: 0,
initializeDisks() {
const partitionsFromData = this.data.partitions;
this.originalPartitions = JSON.parse(JSON.stringify(partitionsFromData)); // Guardar una copia de las particiones originales
const disksMap = new Map<number, { totalDiskSize: number, partitions: Partition[] }>();
partitionsFromData.forEach((partition: any) => {
if (!disksMap.has(partition.diskNumber)) {
disksMap.set(partition.diskNumber, { totalDiskSize: 0, partitions: [] });
}
const disk = disksMap.get(partition.diskNumber);
if (partition.partitionNumber === 0) {
disk!.totalDiskSize = this.convertBytesToGB(partition.size);
} else {
disk!.partitions.push({
size: this.convertBytesToGB(partition.size),
type: 'NTFS', // Puedes cambiar el tipo según sea necesario
sizeBytes: partition.size,
format: false,
color: '#' + Math.floor(Math.random() * 16777215).toString(16),
percentage: 0
});
}
});
disksMap.forEach((disk, diskNumber) => {
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
this.disks.push({
diskNumber: diskNumber,
totalDiskSize: disk.totalDiskSize,
partitions: disk.partitions
});
});
}
convertBytesToGB(bytes: number): number {
return bytes / (1024 * 1024 * 1024);
}
updatePartitionPercentages(partitions: Partition[], totalDiskSize: number) {
partitions.forEach(partition => {
partition.percentage = (partition.size / totalDiskSize) * 100;
});
}
// Añadir una nueva partición
addPartition(diskNumber: number) {
const disk = this.disks.find(d => d.diskNumber === diskNumber);
if (disk) {
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize);
if (remainingGB > 0) {
disk.partitions.push({
size: 0,
type: 'NTFS', // Tipo por defecto, puede ser cambiado por el usuario
sizeBytes: 0,
format: false,
color: '#' + Math.floor(Math.random() * 16777215).toString(16), // Color aleatorio
percentage: 0
});
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
} 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;
// Actualizar el tamaño de una partición
updatePartitionSize(diskNumber: number, index: number, size: number) {
const disk = this.disks.find(d => d.diskNumber === diskNumber);
if (disk) {
const partition = disk.partitions[index];
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize) + partition.size;
if (partition.size > remainingGB) {
if (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();
partition.size = size;
partition.sizeBytes = size * 1024 * 1024 * 1024;
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
}
}
}
updatePartitionPercentages() {
const totalUsedGB = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
const remainingGB = this.totalDiskSize - totalUsedGB;
getRemainingGB(partitions: Partition[], totalDiskSize: number): number {
const totalUsedGB = partitions.reduce((acc, partition) => acc + partition.size, 0);
return Math.max(0, totalDiskSize - totalUsedGB);
}
this.partitions.forEach(partition => {
partition.percentage = (partition.size / this.totalDiskSize) * 100;
isPartitionModified(original: any, current: Partition): boolean {
return (
this.convertBytesToGB(original.size) !== current.size ||
original.type !== current.type
);
}
getModifiedOrNewPartitions() {
const modifiedPartitions: any[] = [];
this.disks.forEach(disk => {
disk.partitions.forEach((partition, index) => {
const originalPartition = this.originalPartitions.find(
p => p.diskNumber === disk.diskNumber && p.partitionNumber === index + 1
);
// Si no existe en las originales, es nueva
if (!originalPartition) {
modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 });
} else if (this.isPartitionModified(originalPartition, partition)) {
modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 });
}
});
});
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);
return modifiedPartitions;
}
save() {
const totalSize = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
const modifiedPartitions = this.getModifiedOrNewPartitions();
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).`;
if (modifiedPartitions.length === 0) {
this.errorMessage = 'No hay cambios para guardar.';
return;
} else {
this.errorMessage = '';
}
const payload = this.partitions.map((partition, index) => ({
diskNumber: this.diskNumber,
partitionNumber: index + 1,
modifiedPartitions.forEach(({ partition, diskNumber, partitionNumber }) => {
const payload = {
diskNumber: diskNumber,
partitionNumber: partitionNumber,
size: partition.size,
filesystem: partition.type,
format: partition.format
}));
client: `https://example.com/${this.clientUuid}`
};
console.log('Payload:', JSON.stringify(payload, null, 2));
this.http.post(this.apiUrl, payload).subscribe(
response => {
console.log('Partición guardada exitosamente:', response);
},
error => {
console.error('Error al guardar la partición:', error);
}
);
});
}
// Eliminar partición de un disco
removePartition(diskNumber: number, index: number) {
const disk = this.disks.find(d => d.diskNumber === diskNumber);
if (disk) {
disk.partitions.splice(index, 1);
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
}
}
}

View File

@ -0,0 +1,47 @@
.dialog-content {
display: flex;
flex-direction: column;
gap: 16px; /* Espacio entre los elementos del formulario */
}
.image-form {
width: 100%;
display: flex;
flex-direction: column;
}
.form-field {
width: 100%;
margin-bottom: 16px;
}
/* Botones alineados al final, con margen superior */
.dialog-actions {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
button {
margin-left: 8px; /* Espacio entre los botones */
}
/* Responsividad para pantallas pequeñas */
@media (max-width: 600px) {
.form-field {
width: 100%;
}
/* Alineación vertical para botones en pantallas pequeñas */
.dialog-actions {
flex-direction: column;
align-items: stretch;
}
button {
width: 100%;
margin-left: 0;
margin-bottom: 8px;
}
}

View File

@ -1,29 +1,30 @@
<h2 mat-dialog-title>Añadir nueva imagen</h2>
<mat-dialog-content>
<form>
<mat-form-field appearance="fill">
<mat-dialog-content class="dialog-content">
<form class="image-form">
<mat-form-field appearance="fill" class="form-field">
<mat-label>Nombre de la imagen</mat-label>
<input matInput [(ngModel)]="imagePayload.name" name="name" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-form-field appearance="fill" class="form-field">
<mat-label>Descripción</mat-label>
<input matInput [(ngModel)]="imagePayload.description" name="description">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-form-field appearance="fill" class="form-field">
<mat-label>Comentarios</mat-label>
<input matInput [(ngModel)]="imagePayload.comments" name="comments">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-form-field appearance="fill" class="form-field">
<mat-label>Perfil de software</mat-label>
<input matInput [(ngModel)]="imagePayload.softwareProfile" name="softwareProfile">
</mat-form-field>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<mat-dialog-actions align="end" class="dialog-actions">
<button mat-button (click)="close()">Cancelar</button>
<button mat-button color="primary" (click)="saveImage()">Guardar</button>
</mat-dialog-actions>