diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.css
index 87ee533..23a405a 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.css
+++ b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.css
@@ -175,6 +175,22 @@
margin-bottom: 20px;
}
+.selected-disk-hint {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: #2e7d32;
+ font-weight: 500;
+ font-size: 14px;
+}
+
+.hint-icon {
+ font-size: 16px;
+ width: 16px;
+ height: 16px;
+ color: #4caf50;
+}
+
/* Opciones del select */
::ng-deep .disk-option {
display: flex;
@@ -232,7 +248,9 @@
background: #e8f5e8;
border: 1px solid #c8e6c9;
border-radius: 12px;
- margin-top: 16px;
+ margin-top: 0;
+ flex-shrink: 0;
+ min-width: 250px;
}
.info-icon {
@@ -1126,7 +1144,6 @@
}
}
-/* ===== ANIMACIONES ===== */
.progress-segment {
animation: slideIn 0.3s ease-out;
}
@@ -1157,8 +1174,7 @@
}
}
-/* ===== ESTADOS DE ADVERTENCIA ===== */
-/* Advertencia (90% a 99% usado) */
+
.warning {
color: #ff9800 !important;
}
@@ -1171,7 +1187,6 @@
color: #ff9800 !important;
}
-/* Peligro (100% o más usado) */
.danger {
color: #f44336 !important;
font-weight: bold !important;
@@ -1199,7 +1214,6 @@
}
}
-/* ===== INSTRUCCIONES ===== */
.instructions-box {
margin-top: 15px;
background-color: #f5f5f5;
@@ -1228,7 +1242,6 @@
line-height: 1.5;
}
-/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.header-container {
flex-direction: column;
@@ -1292,6 +1305,72 @@
}
}
+.partition-validation-indicator {
+ margin: 16px 0;
+ padding: 16px 20px;
+ border-radius: 12px;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.validation-status {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 12px 16px;
+ border-radius: 8px;
+ border: 2px solid;
+}
+
+.validation-status.loading {
+ color: #1976d2;
+ background: #e3f2fd;
+ border-color: #bbdefb;
+}
+
+.validation-status.success {
+ color: #2e7d32;
+ background: #e8f5e8;
+ border-color: #4caf50;
+}
+
+.validation-status.error {
+ color: #d32f2f;
+ background: #ffebee;
+ border-color: #f44336;
+}
+
+.validation-icon {
+ font-size: 20px;
+ width: 20px;
+ height: 20px;
+}
+
+.validation-icon.loading {
+ color: #1976d2;
+ animation: spin 1s linear infinite;
+}
+
+.validation-icon.success {
+ color: #2e7d32;
+}
+
+.validation-icon.error {
+ color: #d32f2f;
+}
+
+.validation-message {
+ flex: 1;
+ margin-left: 8px;
+}
+
+@keyframes spin {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+}
+
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 eadd905..74e023b 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
@@ -16,7 +16,7 @@
- Ejecutar
+ Ejecutar
@@ -26,7 +26,7 @@
-
+
Opciones de programación
@@ -51,7 +51,7 @@
-
Selecciona el disco que deseas particionar
+
Selecciona el disco que deseas particionar
+
+ check_circle
+ Disco {{ selectedDisk.diskNumber }} seleccionado - {{ (selectedDisk.totalDiskSize / 1024).toFixed(2) }} GB
+
-
-
info
-
- Disco seleccionado: {{ selectedDisk.diskNumber }}
- Tamaño total: {{ (selectedDisk.totalDiskSize / 1024).toFixed(2) }} GB
-
-
-
warning
@@ -207,6 +203,16 @@
+
+
0">
+
+ hourglass_empty
+ check_circle
+ error
+ {{ partitionValidationMessage }}
+
+
+
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 eed1294..795b3d1 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
@@ -63,6 +63,12 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
partitionCode: string = '';
generatedInstructions: string = '';
+ // Propiedades para validación de particiones
+ partitionValidationStatus: 'idle' | 'loading' | 'success' | 'error' = 'idle';
+ partitionValidationMessage: string = '';
+ private validationDebounceTime = 500; // ms
+ private validationSubject = new Subject();
+
// Columnas para mat-table
displayedColumns: string[] = ['partitionNumber', 'partitionCode', 'filesystem', 'size', 'percentage', 'format', 'actions'];
@@ -95,10 +101,22 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
this.clientId = this.clientData?.length ? this.clientData[0]['@id'] : null;
this.clientData.forEach((client: { selected: boolean; status: string}) => { client.selected = true; });
- this.selectedClients = this.clientData.filter((client: { selected: boolean; status: string}) => client.selected);
+ this.selectedClients = this.clientData.filter(
+ (client: { selected: boolean; status: string }) => client.selected
+ );
+
+ if (this.selectedClients.length === 0 && this.clientData.length > 0) {
+ this.selectedClients = [this.clientData[0]];
+ this.clientData[0].selected = true;
+ }
this.selectedModelClient = this.clientData.find((client: { selected: boolean; status: string}) => client.selected) || null;
+ if (!this.selectedModelClient && this.clientData.length > 0) {
+ this.selectedModelClient = this.clientData[0];
+ this.clientData[0].selected = true;
+ }
+
if (this.selectedModelClient) {
this.loadPartitions(this.selectedModelClient);
}
@@ -115,6 +133,13 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
).subscribe(() => {
this.resizeChart();
});
+
+ this.validationSubject.pipe(
+ takeUntil(this.destroy$),
+ debounceTime(this.validationDebounceTime)
+ ).subscribe(() => {
+ this.validatePartitionSizes();
+ });
}
ngAfterViewInit(): void {
@@ -293,7 +318,14 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
}
updateSelectedClients() {
- this.selectedClients = this.clientData.filter((client: { selected: any; }) => client.selected);
+ this.selectedClients = this.clientData.filter(
+ (client: { selected: boolean; status: string }) => client.selected
+ );
+
+ if (this.selectedClients.length === 0 && this.clientData.length > 0) {
+ this.selectedClients = [this.clientData[0]];
+ this.clientData[0].selected = true;
+ }
}
getPartitionsTooltip(client: any): string {
@@ -333,7 +365,8 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
this.updateDiskChart(disk);
- // Redimensionar el gráfico después de añadir partición
+ this.validationSubject.next();
+
setTimeout(() => {
this.resizeChart();
}, 100);
@@ -361,6 +394,8 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
partition.percentage = (size / disk.totalDiskSize) * 100;
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
this.updateDiskChart(disk);
+
+ this.validationSubject.next();
}
}
}
@@ -375,6 +410,10 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
save() {
+ if (this.selectedClients.length === 0 && this.clientData.length > 0) {
+ this.updateSelectedClients();
+ }
+
if (!this.selectedDisk) {
this.toastService.error('No se ha seleccionado un disco.');
return;
@@ -454,7 +493,8 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
this.updateDiskChart(disk);
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
- // Redimensionar el gráfico después de eliminar partición
+ this.validationSubject.next();
+
setTimeout(() => {
this.resizeChart();
}, 100);
@@ -470,9 +510,55 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
}
this.updateDiskChart(disk);
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
+
+ this.validationSubject.next();
}
}
+ validatePartitionSizes(): void {
+ if (!this.selectedModelClient || !this.selectedDisk) {
+ return;
+ }
+
+ this.partitionValidationStatus = 'loading';
+ this.partitionValidationMessage = '';
+
+ const partitions = this.selectedDisk.partitions
+ .filter((partition: Partition) => !partition.removed)
+ .map((partition: Partition) => ({
+ diskNumber: this.selectedDisk.diskNumber,
+ partitionNumber: partition.partitionNumber,
+ size: partition.size,
+ partitionCode: partition.partitionCode,
+ filesystem: partition.filesystem
+ }));
+
+ const payload = {
+ partitions: partitions
+ };
+
+ const url = `${this.baseUrl}${this.selectedModelClient.uuid}/check-partition-sizes`;
+
+ this.http.post(url, payload).subscribe(
+ (response: any) => {
+ if (response.res === 1) {
+ this.partitionValidationStatus = 'success';
+ this.partitionValidationMessage = 'Las particiones cumplen con los requisitos del disco.';
+ } else if (response.res === 2) {
+ this.partitionValidationStatus = 'error';
+ this.partitionValidationMessage = response.der || 'Las particiones no cumplen con los requisitos del disco.';
+ } else {
+ this.partitionValidationStatus = 'error';
+ this.partitionValidationMessage = 'Respuesta inesperada del servidor.';
+ }
+ },
+ (error) => {
+ this.partitionValidationStatus = 'error';
+ this.partitionValidationMessage = error.error?.message || 'Error al validar las particiones.';
+ }
+ );
+ }
+
calculateUsedSpace(partitions: Partition[]): number {
return partitions
.filter(partition => !partition.removed)
@@ -498,24 +584,36 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
}
updateDiskChart(disk: any) {
- console.log('disk', disk);
disk.chartData = this.generateChartData(disk.partitions);
disk.used = this.calculateUsedSpace(disk.partitions);
disk.percentage = (disk.used / disk.totalDiskSize) * 100;
- // Redimensionar el gráfico después de actualizar los datos
setTimeout(() => {
this.resizeChart();
}, 50);
}
openScheduleModal(): void {
+ let scope = this.runScriptContext?.type || 'clients';
+ let selectedClients = null;
+
+ if (this.selectedClients.length === 0 && this.clientData.length > 0) {
+ this.updateSelectedClients();
+ }
+
+ if (this.selectedClients && this.selectedClients.length > 0) {
+ scope = 'clients';
+ selectedClients = this.selectedClients;
+ }
+
const dialogRef = this.dialog.open(CreateTaskComponent, {
width: '800px',
data: {
- scope: this.runScriptContext.type,
- organizationalUnit: this.runScriptContext['@id'],
- source: 'assistant'
+ scope: scope,
+ selectedClients: selectedClients,
+ organizationalUnit: this.runScriptContext?.['@id'],
+ source: 'assistant',
+ runScriptContext: this.runScriptContext
}
});
@@ -577,7 +675,8 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
if (this.selectedDiskNumber) {
this.scrollToPartitionTable();
- // Redimensionar el gráfico después de cambiar de disco
+ this.validationSubject.next();
+
setTimeout(() => {
this.resizeChart();
}, 150);
@@ -585,7 +684,6 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
}
scrollToPartitionTable() {
- // Pequeño delay para asegurar que el contenido se haya renderizado
setTimeout(() => {
const diskInfo = document.getElementById('disk-info');
@@ -630,9 +728,7 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
((this.selectedDisk.totalDiskSize - this.selectedDisk.used) / this.selectedDisk.totalDiskSize) * 100 : 0;
}
- /**
- * Asigna un color único a una nueva partición
- */
+
private getNextPartitionColor(): string {
if (!this.selectedDisk) return this.partitionColors[0];
@@ -640,20 +736,16 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
.filter((p: Partition) => !p.removed)
.map((p: Partition) => p.color);
- // Buscar el primer color no usado
for (const color of this.partitionColors) {
if (!usedColors.includes(color)) {
return color;
}
}
- // Si todos están usados, generar uno aleatorio
return this.partitionColors[Math.floor(Math.random() * this.partitionColors.length)];
}
- /**
- * Asigna un color basado en el número de partición
- */
+
private getColorForPartition(partitionNumber: number): string {
return this.partitionColors[(partitionNumber - 1) % this.partitionColors.length];
}