Updated partition assistant
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details
testing/ogGui-multibranch/pipeline/pr-main Build queued... Details

develop
Manuel Aranda Rosales 2025-10-09 10:57:31 +02:00
parent f3ccdd9498
commit 4c9757a7c2
7 changed files with 117 additions and 79 deletions

View File

@ -1,4 +1,18 @@
# Changelog
## [0.26.0] - 2025-10-09
### Improved
- Se han eliminado campos redudantes en la creacion/edicion de cliente
- En el particionador, ahora el despleagble del disco se autocompleta en caso de ser 1.
---
## [0.25.0] - 2025-10-08
### Added
- Se ha añadido una logica en el particionador, el cual filtra las opciones del sistema de ficheros, segun el tipo de particion seleccionado.
### Fixed
- Se ha corregido el componente "estado global" ya que no funcionaba correctamente para el repositorio-
---
## [0.24.1] - 2025-10-06
### Fixed
- Se ha corregido un error en los casos de 2 discos o mas en el asistente de particionado.

View File

@ -210,21 +210,32 @@ export class GlobalStatusComponent implements OnInit {
this.loading = true;
}
this.errorRepositories = {};
const timeoutId = setTimeout(() => {
if (showLoading) {
this.loading = false;
}
this.repositories.forEach(repository => {
if (!(repository.uuid in this.errorRepositories)) {
this.errorRepositories[repository.uuid] = true;
}
});
}, 5000);
this.http.get<any>(`${this.repositoriesUrl}?page=1&itemsPerPage=10`).subscribe(
data => {
this.repositories = data['hydra:member'];
console.log('Respuesta de repositorios:', data); // Debug log
this.repositories = data['hydra:member'] || [];
console.log('Repositorios cargados:', this.repositories); // Debug log
if (this.repositories.length === 0) {
if (showLoading) {
this.loading = false;
}
return;
}
let remainingRepositories = this.repositories.length;
const timeoutId = setTimeout(() => {
if (showLoading) {
this.loading = false;
}
// Marcar como error los repositorios que no han respondido
this.repositories.forEach(repository => {
if (!(repository.uuid in this.errorRepositories)) {
this.errorRepositories[repository.uuid] = true;
}
});
}, 5000);
this.repositories.forEach(repository => {
this.loadRepositoryStatus(repository.uuid, (errorOccurred: boolean) => {
@ -242,39 +253,61 @@ export class GlobalStatusComponent implements OnInit {
});
},
error => {
console.error('Error al cargar repositorios:', error); // Debug log
this.loading = false;
this.repositories.forEach(repository => {
this.errorRepositories[repository.uuid] = true;
});
clearTimeout(timeoutId);
this.repositories = [];
this.toastService.error('Error al cargar la lista de repositorios');
}
);
}
loadRepositoryStatus(repositoryUuid: string, callback: (errorOccurred: boolean) => void): void {
console.log(`Cargando estado del repositorio: ${repositoryUuid}`); // Debug log
const timeoutId = setTimeout(() => {
console.log(`Timeout al cargar estado del repositorio: ${repositoryUuid}`); // Debug log
callback(true);
}, 5000);
this.http.get<any>(`${this.baseUrl}/image-repositories/server/${repositoryUuid}/status`).subscribe(
data => {
const output = data.output;
console.log(`Estado del repositorio ${repositoryUuid}:`, data); // Debug log
if (!data.success) {
console.error(`Error en la respuesta del repositorio ${repositoryUuid}:`, data);
clearTimeout(timeoutId);
callback(true);
return;
}
const details = data.details;
const { disk, services, ram, cpu, processes } = details;
this.repositoryStatuses[repositoryUuid] = {
...output,
disk: {
...output.disk,
used: parseFloat(output.disk.used),
available: parseFloat(output.disk.available)
...disk,
used: parseFloat(disk.used.replace('GB', '')),
available: parseFloat(disk.available.replace('GB', '')),
total: parseFloat(disk.total.replace('GB', '')),
percentage: disk.used_percentage
},
ram: {
...output.ram,
used: parseFloat(output.ram.used),
available: parseFloat(output.ram.available)
}
...ram,
used: parseFloat(ram.used.replace('GB', '')),
available: parseFloat(ram.available.replace('GB', '')),
total: parseFloat(ram.total.replace('GB', '')),
percentage: ram.used_percentage
},
cpu: {
used_percentage: cpu.used_percentage
},
services: services,
processes: processes
};
clearTimeout(timeoutId);
callback(false);
},
error => {
console.error(`Error al cargar estado del repositorio ${repositoryUuid}:`, error); // Debug log
this.toastService.error(error.error['hydra:description'] || 'Error al cargar el estado del repositorio');
clearTimeout(timeoutId);
callback(true);

View File

@ -63,16 +63,13 @@ 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 validationDebounceTime = 500;
private validationSubject = new Subject<void>();
// Columnas para mat-table
displayedColumns: string[] = ['partitionNumber', 'partitionCode', 'filesystem', 'size', 'percentage', 'format', 'actions'];
// Paleta de colores para las particiones
private partitionColors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9',
@ -304,6 +301,11 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
this.disks.forEach((disk) => {
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
});
if (this.disks.length === 1) {
this.selectedDiskNumber = this.disks[0].diskNumber;
this.onDiskSelectionChange();
}
}
convertBytesToGB(bytes: number): number {
@ -855,7 +857,6 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes
return this.partitionColors[(partitionNumber - 1) % this.partitionColors.length];
}
private validatePartitionCode(partitionCode: string | undefined | null): string {
if (!partitionCode || partitionCode.trim() === '') {
return 'EMPTY';

View File

@ -44,15 +44,6 @@
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'netDriverLabel' | translate }}</mat-label>
<mat-select formControlName="netDriver">
<mat-option *ngFor="let type of netDriverTypes" [value]="type.value">
{{ type.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'macLabel' | translate }}</mat-label>
<input matInput formControlName="mac" required>
@ -74,15 +65,6 @@
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'hardwareProfileLabel' | translate }}</mat-label>
<mat-select formControlName="hardwareProfile">
<mat-option *ngFor="let profile of hardwareProfiles" [value]="profile['@id']">
{{ profile.description }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'repositoryLabel' | translate }}</mat-label>
<mat-select formControlName="repository">

View File

@ -16,7 +16,6 @@ export class ManageClientComponent implements OnInit {
clientForm!: FormGroup;
parentUnits: any[] = [];
parentUnitsWithPaths: { id: string, name: string, path: string }[] = [];
hardwareProfiles: any[] = [];
ogLives: any[] = [];
menus: any[] = [];
templates: any[] = [];
@ -29,9 +28,6 @@ export class ManageClientComponent implements OnInit {
{ name: 'Eth1', value: 'eth1' },
{ name: 'Eth2', value: 'eth2' }
];
protected netDriverTypes = [
{ name: 'Generic', value: 'generic' }
];
isEditMode: boolean;
constructor(
@ -53,7 +49,6 @@ export class ManageClientComponent implements OnInit {
this.initForm();
const observables = [
this.loadParentUnits(),
this.loadHardwareProfiles(),
this.loadOgLives(),
this.loadPxeTemplates(),
this.loadRepositories(),
@ -83,11 +78,10 @@ export class ManageClientComponent implements OnInit {
name: ['', Validators.required],
serialNumber: [''],
netiface: null,
netDriver: null,
netDriver: 'generic',
mac: ['', Validators.pattern(/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/)],
ip: ['', Validators.required],
pxeTemplate: [null],
hardwareProfile: [null],
ogLive: [null],
repository: [null],
menu: [null],
@ -108,7 +102,6 @@ export class ManageClientComponent implements OnInit {
netiface: unit.networkSettings?.netiface,
path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits),
repository: unit.networkSettings?.repository?.['@id'],
hardwareProfile: unit.networkSettings?.hardwareProfile?.['@id'],
ogLive: unit.networkSettings?.ogLive?.['@id'],
menu: unit.networkSettings?.menu?.['@id'],
pxeTemplate: unit.networkSettings?.pxeTemplate?.['@id'],
@ -134,21 +127,6 @@ export class ManageClientComponent implements OnInit {
return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;
}
loadHardwareProfiles(): Promise<void> {
return new Promise((resolve, reject) => {
this.dataService.getHardwareProfiles().subscribe(
(data: any[]) => {
this.hardwareProfiles = data;
resolve();
},
error => {
console.error('Error fetching hardware profiles:', error);
reject(error);
}
);
});
}
loadOgLives(): Promise<void> {
return new Promise((resolve, reject) => {
const url = `${this.baseUrl}/og-lives?page=1&itemsPerPage=30`;
@ -210,7 +188,7 @@ export class ManageClientComponent implements OnInit {
resolve();
},
error => {
console.error('Error fetching ogLives:', error);
console.error('Error fetching repositories:', error);
reject(error);
}
);
@ -226,7 +204,6 @@ export class ManageClientComponent implements OnInit {
if (selectedUnit) {
this.clientForm.patchValue({
repository: selectedUnit.repository || null,
hardwareProfile: selectedUnit.hardwareProfile || null,
ogLive: selectedUnit.ogLive || null,
menu: selectedUnit.menu || null,
netiface: selectedUnit.netiface || null,
@ -246,9 +223,8 @@ export class ManageClientComponent implements OnInit {
ip: data.ip,
mac: data.mac,
netiface: data.netiface,
netDriver: data.netDriver,
netDriver: 'generic',
serialNumber: data.serialNumber,
hardwareProfile: data.hardwareProfile ? data.hardwareProfile['@id'] : null,
organizationalUnit: data.organizationalUnit ? data.organizationalUnit['@id'] : null,
repository: data.repository ? data.repository['@id'] : null,
ogLive: data.ogLive ? data.ogLive['@id'] : null,

View File

@ -11,6 +11,7 @@
<div class="images-button-row">
<button class="action-button" (click)="openImageInfoDialog()">Ver Información</button>
<button class="action-button" (click)="openBackupDialog()">Crear Backup</button>
<button class="action-button" style="background-color: #f44336; color: white;" (click)="deleteRepository()" [disabled]="!selectedRepository">Eliminar Repositorio</button>
</div>
</div>
@ -100,10 +101,6 @@
<button mat-icon-button color="accent" (click)="toggleAction(commit, 'create-tag')" matTooltip="Crear tag">
<mat-icon>local_offer</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="toggleAction(commit, 'create-branch')" matTooltip="Crear rama">
<mat-icon>account_tree</mat-icon>
</button>
</div>
</td>
</ng-container>

View File

@ -11,6 +11,7 @@ import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-
import {CreateTagModalComponent} from "./create-tag-modal/create-tag-modal.component";
import {CreateBranchModalComponent} from "./create-branch-modal/create-branch-modal.component";
import { BackupRepositoryModalComponent } from './backup-repository-modal/backup-repository-modal.component';
import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component";
@Component({
selector: 'app-show-git-commits',
@ -317,6 +318,40 @@ export class ShowGitCommitsComponent implements OnInit{
window.open(`http://localhost:3100/oggit/${this.selectedRepository}/commit/${commit.hexsha}`, '_blank');
}
deleteRepository(): void {
if (!this.selectedRepository) {
this.toastService.warning('Por favor, selecciona un repositorio primero');
return;
}
const dialogRef = this.dialog.open(DeleteModalComponent, {
width: '400px',
data: {
name: this.selectedRepository,
title: 'Eliminar Repositorio',
message: '¿Estás seguro que deseas eliminar este repositorio? Esta acción no se puede deshacer.',
confirmText: 'Eliminar',
cancelText: 'Cancelar'
}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
const payload = { repositoryName: this.selectedRepository };
this.http.post(`${this.baseUrl}/image-repositories/server/git/${this.data.repositoryUuid}/delete`, payload).subscribe({
next: () => {
this.toastService.success('Repositorio eliminado con éxito');
this.dialogRef.close(true);
},
error: (error) => {
console.error('Error al eliminar el repositorio:', error);
this.toastService.error('Error al eliminar el repositorio');
}
});
}
});
}
onNoClick(): void {
this.dialogRef.close();
}