refs #2496. Check partition sizes. Integration UX
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
parent
847504f286
commit
48a2a7f061
|
@ -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); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<button class="action-button" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0" (click)="save()">Ejecutar</button>
|
||||
<button class="action-button" [disabled]="!selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0 || partitionValidationStatus === 'error'" (click)="save()">Ejecutar</button>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button class="action-button" color="accent" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0" (click)="openScheduleModal()">
|
||||
<button class="action-button" color="accent" [disabled]="!selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0" (click)="openScheduleModal()">
|
||||
Opciones de programación
|
||||
</button>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<div class="clients-grid">
|
||||
<div *ngFor="let client of clientData" class="client-item">
|
||||
<div class="client-card"
|
||||
(click)="client.status === 'og-live' && toggleClientSelection(client)"
|
||||
(click)="toggleClientSelection(client)"
|
||||
[ngClass]="{'selected-client': client.selected}"
|
||||
[matTooltip]="getPartitionsTooltip(client)"
|
||||
matTooltipPosition="above"
|
||||
|
@ -110,17 +110,13 @@
|
|||
</div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-hint>Selecciona el disco que deseas particionar</mat-hint>
|
||||
<mat-hint *ngIf="!selectedDisk">Selecciona el disco que deseas particionar</mat-hint>
|
||||
<mat-hint *ngIf="selectedDisk" class="selected-disk-hint">
|
||||
<mat-icon class="hint-icon">check_circle</mat-icon>
|
||||
Disco {{ selectedDisk.diskNumber }} seleccionado - {{ (selectedDisk.totalDiskSize / 1024).toFixed(2) }} GB
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="selection-info" *ngIf="selectedDisk">
|
||||
<mat-icon class="info-icon">info</mat-icon>
|
||||
<div class="info-text">
|
||||
<span class="info-title">Disco seleccionado: {{ selectedDisk.diskNumber }}</span>
|
||||
<span class="info-subtitle">Tamaño total: {{ (selectedDisk.totalDiskSize / 1024).toFixed(2) }} GB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="no-disks-message" *ngIf="!disks || disks.length === 0">
|
||||
<mat-icon class="warning-icon">warning</mat-icon>
|
||||
<div class="message-text">
|
||||
|
@ -207,6 +203,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicador de validación de particiones -->
|
||||
<div class="partition-validation-indicator" *ngIf="selectedDisk && selectedDisk.partitions.length > 0">
|
||||
<div class="validation-status" [ngClass]="partitionValidationStatus">
|
||||
<mat-icon *ngIf="partitionValidationStatus === 'loading'" class="validation-icon loading">hourglass_empty</mat-icon>
|
||||
<mat-icon *ngIf="partitionValidationStatus === 'success'" class="validation-icon success">check_circle</mat-icon>
|
||||
<mat-icon *ngIf="partitionValidationStatus === 'error'" class="validation-icon error">error</mat-icon>
|
||||
<span class="validation-message" *ngIf="partitionValidationMessage">{{ partitionValidationMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="partition-table" id="partition-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -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<void>();
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue