diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html index 273c99e..2a01f5c 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html @@ -63,8 +63,8 @@
- +
- +
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.html index bc674a1..b38b624 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.html @@ -28,7 +28,7 @@ - {{ j + 1 }} + {{ partition.partitionNumber }} - + diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts index 2ef9ded..f020f81 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts @@ -2,6 +2,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; interface Partition { + uuid?: string; // Agregamos uuid opcional + partitionNumber: number; size: number; type: string; sizeBytes: number; @@ -17,11 +19,11 @@ interface Partition { }) export class PartitionAssistantComponent implements OnInit { @Input() data: any; - @Input() clientUuid: string | undefined; // El clientUuid que necesitas + @Input() clientUuid: string | undefined; errorMessage = ''; originalPartitions: any[] = []; - disks: { diskNumber: number, totalDiskSize: number, partitions: Partition[] }[] = []; + disks: { diskNumber: number; totalDiskSize: number; partitions: Partition[] }[] = []; private apiUrl = 'http://127.0.0.1:8001/partitions'; @@ -33,9 +35,9 @@ export class PartitionAssistantComponent implements OnInit { initializeDisks() { const partitionsFromData = this.data.partitions; - this.originalPartitions = JSON.parse(JSON.stringify(partitionsFromData)); // Guardar una copia de las particiones originales + this.originalPartitions = JSON.parse(JSON.stringify(partitionsFromData)); - const disksMap = new Map(); + const disksMap = new Map(); partitionsFromData.forEach((partition: any) => { if (!disksMap.has(partition.diskNumber)) { @@ -47,8 +49,10 @@ export class PartitionAssistantComponent implements OnInit { disk!.totalDiskSize = this.convertBytesToGB(partition.size); } else { disk!.partitions.push({ + uuid: partition.uuid, // Incluimos el uuid + partitionNumber: partition.partitionNumber, size: this.convertBytesToGB(partition.size), - type: 'NTFS', // Puedes cambiar el tipo según sea necesario + type: partition.type, sizeBytes: partition.size, format: false, color: '#' + Math.floor(Math.random() * 16777215).toString(16), @@ -72,24 +76,28 @@ export class PartitionAssistantComponent implements OnInit { } updatePartitionPercentages(partitions: Partition[], totalDiskSize: number) { - partitions.forEach(partition => { + 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); + const disk = this.disks.find((d) => d.diskNumber === diskNumber); if (disk) { const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize); if (remainingGB > 0) { + const maxPartitionNumber = + disk.partitions.length > 0 ? Math.max(...disk.partitions.map((p) => p.partitionNumber)) : 0; + const newPartitionNumber = maxPartitionNumber + 1; + disk.partitions.push({ + partitionNumber: newPartitionNumber, size: 0, - type: 'NTFS', // Tipo por defecto, puede ser cambiado por el usuario + type: 'NTFS', sizeBytes: 0, format: false, - color: '#' + Math.floor(Math.random() * 16777215).toString(16), // Color aleatorio + color: '#' + Math.floor(Math.random() * 16777215).toString(16), percentage: 0 }); this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize); @@ -99,15 +107,16 @@ export class PartitionAssistantComponent implements OnInit { } } - // Actualizar el tamaño de una partición updatePartitionSize(diskNumber: number, index: number, size: number) { - const disk = this.disks.find(d => d.diskNumber === diskNumber); + 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 (size > remainingGB) { - this.errorMessage = `El tamaño de la partición no puede superar el espacio libre (${remainingGB.toFixed(2)} GB).`; + this.errorMessage = `El tamaño de la partición no puede superar el espacio libre (${remainingGB.toFixed( + 2 + )} GB).`; } else { this.errorMessage = ''; partition.size = size; @@ -132,17 +141,28 @@ export class PartitionAssistantComponent implements OnInit { getModifiedOrNewPartitions() { const modifiedPartitions: any[] = []; - this.disks.forEach(disk => { - disk.partitions.forEach((partition, index) => { + this.disks.forEach((disk) => { + disk.partitions.forEach((partition) => { const originalPartition = this.originalPartitions.find( - p => p.diskNumber === disk.diskNumber && p.partitionNumber === index + 1 + (p) => p.diskNumber === disk.diskNumber && p.partitionNumber === partition.partitionNumber ); - // Si no existe en las originales, es nueva if (!originalPartition) { - modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 }); + // Es una nueva partición + modifiedPartitions.push({ + partition, + diskNumber: disk.diskNumber, + partitionNumber: partition.partitionNumber, + isNew: true + }); } else if (this.isPartitionModified(originalPartition, partition)) { - modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 }); + // La partición ha sido modificada + modifiedPartitions.push({ + partition, + diskNumber: disk.diskNumber, + partitionNumber: partition.partitionNumber, + isNew: false + }); } }); }); @@ -158,33 +178,62 @@ export class PartitionAssistantComponent implements OnInit { return; } - modifiedPartitions.forEach(({ partition, diskNumber, partitionNumber }) => { + modifiedPartitions.forEach(({ partition, diskNumber, partitionNumber, isNew }) => { const payload = { diskNumber: diskNumber, partitionNumber: partitionNumber, size: partition.size, filesystem: partition.type, - client: `https://example.com/${this.clientUuid}` + client: `/clients/${this.clientUuid}` }; - 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); - } - ); + if (isNew) { + // Es una nueva partición, usamos POST + this.http.post(this.apiUrl, payload).subscribe( + (response) => { + console.log('Partición creada exitosamente:', response); + }, + (error) => { + console.error('Error al crear la partición:', error); + } + ); + } else if (partition.uuid) { + // Es una partición existente modificada, usamos PATCH + const patchUrl = `${this.apiUrl}/${partition.uuid}`; + this.http.patch(patchUrl, payload).subscribe( + (response) => { + console.log('Partición actualizada exitosamente:', response); + }, + (error) => { + console.error('Error al actualizar la partición:', error); + } + ); + } }); } - // Eliminar partición de un disco - removePartition(diskNumber: number, index: number) { - const disk = this.disks.find(d => d.diskNumber === diskNumber); + removePartition(diskNumber: number, partition: Partition) { + const disk = this.disks.find((d) => d.diskNumber === diskNumber); if (disk) { - disk.partitions.splice(index, 1); - this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize); + const index = disk.partitions.indexOf(partition); + if (index !== -1) { + disk.partitions.splice(index, 1); + this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize); + + if (partition.uuid) { + // La partición existía originalmente, enviamos DELETE + const deleteUrl = `${this.apiUrl}/${partition.uuid}`; + this.http.delete(deleteUrl).subscribe( + (response) => { + console.log('Partición eliminada exitosamente:', response); + }, + (error) => { + console.error('Error al eliminar la partición:', error); + } + ); + } + } } } } diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.css index e69de29..ad6b310 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.css @@ -0,0 +1,71 @@ +.partition-assistant { + 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; + 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); + } + + .partition-table { + width: 100%; + border-collapse: collapse; + 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 { + 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 { + padding: 5px; + border-radius: 4px; + border: 1px solid #ccc; + width: 100%; + } + + .actions { + display: flex; + justify-content: flex-end; + padding-top: 10px; + } + + button.mat-flat-button { + background-color: #28a745; + color: white; + padding: 10px 20px; + border-radius: 4px; + font-size: 1rem; + font-weight: 500; + cursor: pointer; + transition: background-color 0.3s ease; + } + + button.mat-flat-button:hover { + background-color: #218838; + } + \ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.html index 8351543..f8f105a 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.html @@ -1 +1,38 @@ -

restore-image works!

+
+
+ +
+ + + + + + + + + + + + + + + + +
ParticiónImagen ISOOgLive
{{ partition.partitionNumber }} + + + +
+
+ +
+ +
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.ts index 46f1874..65d424d 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/restore-image/restore-image.component.ts @@ -1,10 +1,95 @@ -import { Component } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +interface Image { + '@id': string; + '@type': string; + name: string; + description: string; + comments: string; + uuid: string; + id: number; +} + +interface Partition { + diskNumber: number; + partitionNumber: number; + associatedImageId?: string; + associatedOgLive?: string; +} @Component({ selector: 'app-restore-image', templateUrl: './restore-image.component.html', - styleUrl: './restore-image.component.css' + styleUrls: ['./restore-image.component.css'] }) -export class RestoreImageComponent { +export class RestoreImageComponent implements OnInit { + @Input() data: any; + disks: { diskNumber: number; partitions: Partition[] }[] = []; + availableImages: Image[] = []; + availableOgLives: string[] = []; + + constructor(private http: HttpClient) {} + + ngOnInit(): void { + this.initializeDisks(); + this.fetchAvailableImages(); + this.availableOgLives = ['LiveCD1', 'LiveCD2', 'LiveCD3']; + } + + initializeDisks() { + const partitionsFromData = this.data.partitions; + const disksMap = new Map(); + + partitionsFromData.forEach((partition: any) => { + if (!disksMap.has(partition.diskNumber)) { + disksMap.set(partition.diskNumber, []); + } + + disksMap.get(partition.diskNumber)!.push({ + diskNumber: partition.diskNumber, + partitionNumber: partition.partitionNumber + }); + }); + + disksMap.forEach((partitions, diskNumber) => { + this.disks.push({ diskNumber, partitions }); + }); + } + + fetchAvailableImages() { + const url = 'http://127.0.0.1:8001/images?page=1&itemsPerPage=30'; + this.http.get(url).subscribe( + (response: any) => { + this.availableImages = response['hydra:member']; + }, + (error) => { + console.error('Error al obtener las imágenes:', error); + } + ); + } + + onImageSelected(partition: Partition, event: Event) { + const selectElement = event.target as HTMLSelectElement; + partition.associatedImageId = selectElement.value; + } + + onOgLiveSelected(partition: Partition, event: Event) { + const selectElement = event.target as HTMLSelectElement; + partition.associatedOgLive = selectElement.value; + } + + saveAssociations() { + this.disks.forEach(disk => { + disk.partitions.forEach(partition => { + if (partition.associatedImageId || partition.associatedOgLive) { + console.log( + `Guardando para disco ${partition.diskNumber}, partición ${partition.partitionNumber}, ` + + `Imagen ID: ${partition.associatedImageId}, OgLive: ${partition.associatedOgLive}` + ); + } + }); + }); + } } diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.html b/ogWebconsole/src/app/components/images/create-image/create-image.component.html index e54bd91..8c13383 100644 --- a/ogWebconsole/src/app/components/images/create-image/create-image.component.html +++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.html @@ -19,7 +19,11 @@ Perfil de software - + + + {{ profile.description }} + + diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts index f68a14a..9939531 100644 --- a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts @@ -1,16 +1,30 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; +interface ImagePayload { + [key: string]: any; + name: string | null; + description: string | null; + comments: string | null; + type: string | null; + path: string | null; + revision: string | null; + info: string | null; + size: number | null; + client: string | null; + softwareProfile: string | null; +} + @Component({ selector: 'app-create-image', templateUrl: './create-image.component.html', styleUrls: ['./create-image.component.css'] }) -export class CreateImageComponent { +export class CreateImageComponent implements OnInit { baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - imagePayload = { + imagePayload: ImagePayload = { name: null, description: null, comments: null, @@ -23,21 +37,44 @@ export class CreateImageComponent { softwareProfile: null }; + softwareProfiles: any[] = []; + constructor( public dialogRef: MatDialogRef, private http: HttpClient, private toastService: ToastrService ) {} - saveImage(): void { - // Remover propiedades que son null antes de enviar la solicitud - const payload = { ...this.imagePayload }; + ngOnInit() { + this.fetchSoftwareProfiles(); + } - // Enviar la solicitud POST al servidor + fetchSoftwareProfiles() { + const url = 'http://127.0.0.1:8001/software-profiles?page=1&itemsPerPage=30'; + this.http.get(url).subscribe({ + next: (response: any) => { + this.softwareProfiles = response['hydra:member']; + }, + error: (error) => { + console.error('Error al obtener los perfiles de software:', error); + this.toastService.error('Error al obtener los perfiles de software'); + } + }); + } + + saveImage(): void { + const payload = { ...this.imagePayload }; + Object.keys(payload).forEach(key => { + if (payload[key] == null) { + delete payload[key]; + } + }); + + console.log('Payload:', payload); this.http.post(`${this.baseUrl}/images`, payload).subscribe({ next: () => { this.toastService.success('Imagen creada con éxito'); - this.dialogRef.close(true); // Cierra el diálogo y retorna true + this.dialogRef.close(true); }, error: (error) => { console.error('Error al crear la imagen:', error); @@ -47,6 +84,6 @@ export class CreateImageComponent { } close(): void { - this.dialogRef.close(); // Cierra el diálogo sin retorno + this.dialogRef.close(); } }