From 9969ab303aff97e3d76247a24631ee3bd11443b7 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 5 Aug 2025 10:28:06 +0200 Subject: [PATCH] refs #2505. Update GIT image --- .../create-image/create-image.component.css | 337 ++++++++++++++++++ .../create-image/create-image.component.html | 233 +++++++----- .../create-image/create-image.component.ts | 294 ++++++++++++--- 3 files changed, 722 insertions(+), 142 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css index d7c8b69..a1ed15d 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css @@ -675,3 +675,340 @@ mat-form-field { } } +@media (max-width: 768px) { + .scroll-to-top-button { + right: 16px; + bottom: 16px; + } +} + +/* Estilos para el selector de ramas */ +.branch-selector-header { + display: flex; + gap: 16px; + align-items: flex-end; + width: 100%; +} + +.create-branch-button { + background: #4caf50; + color: white; + border: none; + padding: 12px 20px; + border-radius: 8px; + font-weight: 500; + transition: all 0.3s ease; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + white-space: nowrap; + height: 56px; +} + +.create-branch-button:hover:not(:disabled) { + background: #45a049; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3); +} + +.create-branch-button:disabled { + background: #ccc; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.create-branch-button mat-icon { + font-size: 18px; + width: 18px; + height: 18px; +} + +/* Estilos para la sección de commits */ +.commits-section { + margin-top: 24px; + padding: 24px; + background: white; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; +} + +.search-container { + margin-bottom: 20px; +} + +.search-string { + width: 100%; +} + +.commits-table-container { + margin-top: 20px; + overflow-x: auto; +} + +.commit-id { + font-family: 'Courier New', monospace; + background: #f5f5f5; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + color: #333; +} + +.commit-message { + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.commit-stats { + font-size: 12px; + color: #666; +} + +.commit-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.commit-tags mat-chip { + font-size: 10px; + height: 20px; +} + +.no-tags { + color: #999; + font-size: 12px; + font-style: italic; +} + +.action-buttons { + display: flex; + gap: 8px; + justify-content: center; +} + +.action-buttons button { + transition: all 0.2s ease; +} + +.action-buttons button:hover { + transform: scale(1.1); +} + +.paginator-container { + margin-top: 20px; +} + +/* Responsive para ramas y commits */ +@media (max-width: 768px) { + .branch-selector-header { + flex-direction: column; + align-items: stretch; + } + + .create-branch-button { + height: auto; + padding: 12px 16px; + } + + .commits-section { + padding: 16px; + } + + .commit-message { + max-width: 200px; + } + + .action-buttons { + flex-direction: column; + gap: 4px; + } +} + +.git-info-container { + margin-bottom: 20px; +} + +.git-info-card { + background: #f8f9fa; + border-radius: 8px; + padding: 20px; + color: #495057; + border: 1px solid #e9ecef; + animation: fadeInUp 0.5s ease-out; +} + +.git-info-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; +} + +.git-info-icon { + font-size: 20px; + width: 20px; + height: 20px; + color: #6c757d; +} + +.git-info-title { + font-size: 16px; + font-weight: 600; + flex-grow: 1; + color: #495057; +} + +.git-loading-spinner { + margin-left: auto; +} + +.git-info-content { + background: #ffffff; + border-radius: 6px; + padding: 16px; + margin-top: 12px; + border: 1px solid #dee2e6; +} + +.git-info-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 16px; +} + +.git-info-item { + display: flex; + flex-direction: column; +} + +.git-form-field { + width: 100%; +} + +.git-item-icon { + font-size: 16px; + width: 16px; + height: 16px; + color: #495057; + margin-right: 4px; +} + + + + + +.git-data-display { + background: #f8f9fa; + border-radius: 4px; + padding: 12px; + margin: 0; + font-family: 'Courier New', monospace; + font-size: 12px; + color: #495057; + overflow-x: auto; + white-space: pre-wrap; + word-break: break-word; + max-height: 300px; + overflow-y: auto; + border: 1px solid #e9ecef; +} + +.git-info-loading { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + font-size: 14px; + color: #6c757d; +} + +.git-info-empty { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + font-size: 14px; + color: #6c757d; + font-style: italic; +} + +/* Estilos para la advertencia de repositorio no encontrado */ +.repository-warning { + display: flex; + align-items: flex-start; + gap: 12px; + margin-top: 16px; + padding: 16px; + background: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 8px; + color: #856404; +} + +.warning-icon { + font-size: 20px; + width: 20px; + height: 20px; + color: #f39c12; + flex-shrink: 0; + margin-top: 2px; +} + +.warning-content { + flex-grow: 1; +} + +.warning-title { + font-size: 14px; + font-weight: 600; + margin-bottom: 4px; + color: #e67e22; +} + +.warning-message { + font-size: 13px; + line-height: 1.4; + color: #856404; +} + +/* Responsive para el cuadro informativo de Git */ +@media (max-width: 768px) { + .git-info-card { + padding: 16px; + } + + .git-info-header { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .git-loading-spinner { + margin-left: 0; + align-self: flex-end; + } + + .git-data-display { + font-size: 11px; + max-height: 200px; + } + + .git-info-grid { + grid-template-columns: 1fr; + gap: 12px; + } + + .git-info-item { + padding: 10px; + } + + .git-info-value { + font-size: 13px; + } +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html index 5092b02..70eedfe 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html @@ -1,4 +1,5 @@ - + + @@ -20,12 +21,11 @@
- +
-
settings @@ -43,15 +43,81 @@
-
code Configuración Git
-
-
+
+
+
+ info + La imagen en caché a actualizar es: + +
+
+
+
+ + + folder + Repositorio + + + +
+
+ + + account_tree + Rama origen + + + +
+
+ + + call_split + Rama destino + + + + +
+
+ +
+ warning +
+
Repositorio no encontrado
+
+ El repositorio "{{ gitData.repo }}" no existe en el listado de repositorios disponibles. + Es posible que necesites crearlo primero. +
+
+
+
+
+ Cargando información de Git... +
+
+ No se encontró información de Git para esta partición +
+
+
+ +
-
- - Seleccionar repositorio Git - - Seleccionar repositorio git / SO - {{ repo.name }} - - - - info - Selecciona el repositorio git para obtener las imágenes disponibles. - - No hay repositorios disponibles. Crea uno nuevo para continuar. - - - -
-
+
+ + Seleccionar repositorio Git + + Seleccionar repositorio git / SO + {{ repo.name }} + + + + info + Selecciona el repositorio git para obtener las imágenes disponibles. + + No hay repositorios disponibles. Crea uno nuevo para continuar. + + + - -
-
- - - Crear imagen - - - Actualizar imagen - - -
-
- info - Crea una nueva imagen con el nombre especificado - Actualiza una imagen existente seleccionada -
-
-
- - -
-
- image - Configuración de imagen +
- -
- - - Crear imagen - - - Actualizar imagen - - -
-
- info - Crea una nueva imagen con el nombre especificado - Actualiza una imagen existente seleccionada -
- -
- - Nombre canónico - - Introduce el nombre para la nueva imagen que se creará. - -
- -
- - Seleccione imagen - - Seleccionar imagen para actualizar - {{ image?.name }} - - - Selecciona la imagen existente que quieres actualizar. - -
-
- +
+
+ image + Configuración de imagen monolítica +
+ +
+ + + Crear imagen + + + Actualizar imagen + + +
+
+ info + Crea una nueva imagen con el nombre especificado + Actualiza una imagen existente seleccionada +
+ +
+ + Nombre canónico + + Introduce el nombre para la nueva imagen que se creará. + +
+ +
+ + Seleccione imagen + + Seleccionar imagen para actualizar + {{ image?.name }} + + + Selecciona la imagen existente que quieres actualizar. + +
+
+
storage diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts index 2f4e8d9..88a8f57 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts @@ -8,6 +8,7 @@ import { ConfigService } from '@services/config.service'; import {MatDialog} from "@angular/material/dialog"; import {QueueConfirmationModalComponent} from "../../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component"; import {CreateRepositoryModalComponent} from "./create-repository-modal/create-repository-modal.component"; +import {CreateBranchModalComponent} from "../../../../repositories/show-git-images/create-branch-modal/create-branch-modal.component"; @Component({ selector: 'app-create-image', @@ -38,12 +39,27 @@ export class CreateClientImageComponent implements OnInit{ loadingGitRepositories: boolean = false; loadingGitImageRepositories: boolean = false; creatingRepository: boolean = false; - gitAction: string = 'create'; monolithicAction: string = 'create'; existingImages: any[] = []; selectedExistingImage: any = null; loadingExistingImages: boolean = false; dataSource = new MatTableDataSource(); + + branches: string[] = []; + selectedBranch: string = ''; + loadingBranches: boolean = false; + + gitData: any = null; + loadingGitData: boolean = false; + destinationBranch: string = ''; + isDestinationBranchEditable: boolean = false; + repositoryNotFound: boolean = false; + newlyCreatedRepository: any = null; + + get hasValidGitData(): boolean { + return this.gitData && this.gitData.repo && this.gitData.branch; + } + columns = [ { columnDef: 'diskNumber', @@ -107,9 +123,19 @@ export class CreateClientImageComponent implements OnInit{ this.clientName = response.name; this.selectedRepository = response.repository; - this.dataSource.data = response.partitions.filter((partition: any) => { + const validPartitions = response.partitions.filter((partition: any) => { return partition.partitionNumber !== 0; }); + + this.dataSource.data = validPartitions; + + const firstValidPartition = validPartitions.find((partition: any) => { + return partition.operativeSystem; + }); + + if (firstValidPartition) { + this.selectedPartition = firstValidPartition; + } } }, (error) => { @@ -141,6 +167,23 @@ export class CreateClientImageComponent implements OnInit{ (response: any) => { this.gitRepositories = response['hydra:member']; this.loadingGitRepositories = false; + + if (this.newlyCreatedRepository) { + + const newRepo = this.gitRepositories.find(repo => + repo.name === this.newlyCreatedRepository.name + ); + + if (newRepo) { + this.selectedGitRepository = newRepo; + this.onGitRepositorySelected(newRepo); + this.toastService.success(`Repositorio "${newRepo.name}" preseleccionado`); + } + + this.newlyCreatedRepository = null; + } + + this.checkRepositoryExists(); }, (error) => { console.error('Error al cargar los repositorios git:', error); @@ -151,7 +194,7 @@ export class CreateClientImageComponent implements OnInit{ loadGitImageRepositories(gitRepository: any) { this.loadingGitImageRepositories = true; - const url = `${this.baseUrl}/git-image-repositories?gitRepository.id=${gitRepository.id}&page=1&itemsPerPage=100`; + const url = `${this.baseUrl}/git-image-repositories?gitRepository.uuid=${gitRepository.uuid}&page=1&itemsPerPage=100`; this.http.get(url).subscribe( (response: any) => { this.gitImageRepositories = response['hydra:member']; @@ -168,47 +211,97 @@ export class CreateClientImageComponent implements OnInit{ this.selectedGitRepository = gitRepository; this.selectedExistingImage = null; this.existingImages = []; + this.selectedBranch = ''; + this.branches = []; + if (gitRepository) { this.loadGitImageRepositories(gitRepository); + this.loadBranches(); } else { this.gitImageRepositories = []; } } - onGitActionSelected(event: any) { - console.log('onGitActionSelected llamado con:', event); - this.gitAction = event.value; - this.selectedExistingImage = null; - this.gitImageName = ''; - - // Si se selecciona 'update' y ya hay un repositorio Git seleccionado, cargar los repositorios de imágenes - if (event.value === 'update' && this.selectedGitRepository) { - this.loadGitImageRepositories(this.selectedGitRepository); + loadBranches(): void { + if (!this.selectedGitRepository) { + return; } - - console.log('Antes del setTimeout'); - // Hacer scroll hacia la sección de partición después de un delay más largo - setTimeout(() => { - console.log('Dentro del setTimeout, llamando a scrollToPartitionSection'); - this.scrollToPartitionSection(); - }, 300); + this.loadingBranches = true; + const url = `${this.baseUrl}/image-repositories/server/git/${this.selectedRepository.uuid}/branches`; + this.http.post(url, { repositoryName: this.selectedGitRepository.name }).subscribe( + data => { + this.branches = data.branches || []; + this.loadingBranches = false; + if (this.branches.length > 0) { + this.selectedBranch = this.branches[0]; + } + }, + error => { + console.error('Error fetching branches', error); + this.toastService.error('Error al cargar las ramas del repositorio'); + this.loadingBranches = false; + } + ); + } + + onBranchChange(): void { + // La rama ha sido seleccionada, no necesitamos hacer nada más + } + + openCreateBranchModal(): void { + if (this.hasValidGitData) { + const dialogRef = this.dialog.open(CreateBranchModalComponent, { + width: '500px', + data: { + commit: this.gitData.branch, + repositoryName: this.gitData.repo, + repositoryUuid: this.selectedRepository.uuid + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.toastService.success('Rama creada correctamente'); + this.destinationBranch = result.branchName || result; + this.loadBranches(); + } + }); + return; + } + + if (!this.selectedGitRepository) { + this.toastService.error('Debe seleccionar un repositorio primero'); + return; + } + + const dialogRef = this.dialog.open(CreateBranchModalComponent, { + width: '500px', + data: { + commit: this.selectedBranch || 'master', + repositoryName: this.selectedGitRepository.name, + repositoryUuid: this.selectedRepository.uuid + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.toastService.success('Rama creada correctamente'); + this.destinationBranch = result.branchName || result; + this.loadBranches(); + } + }); } onMonolithicActionSelected(event: any) { - console.log('onMonolithicActionSelected llamado con:', event); this.monolithicAction = event.value; this.selectedImage = null; this.name = ''; - // Si se selecciona 'update', cargar las imágenes existentes if (event.value === 'update') { this.loadImages(); } - console.log('Antes del setTimeout (monolithic)'); - // Hacer scroll hacia la sección de partición después de un delay más largo setTimeout(() => { - console.log('Dentro del setTimeout (monolithic), llamando a scrollToPartitionSection'); this.scrollToPartitionSection(); }, 300); } @@ -217,8 +310,7 @@ export class CreateClientImageComponent implements OnInit{ if (!this.selectedExistingImage) return; this.loadingExistingImages = true; - // Aquí deberías hacer el GET al endpoint externo - // Por ahora uso un endpoint de ejemplo, ajusta según tu API + const url = `${this.baseUrl}/images?gitImageRepository.id=${this.selectedExistingImage.id}&page=1&itemsPerPage=100`; this.http.get(url).subscribe( @@ -238,14 +330,15 @@ export class CreateClientImageComponent implements OnInit{ this.selectedGitRepository = null; this.selectedExistingImage = null; this.gitImageName = ''; - this.gitAction = 'create'; + this.monolithicAction = 'create'; this.existingImages = []; this.gitRepositories = []; this.gitImageRepositories = []; + this.selectedBranch = ''; + this.branches = []; this.selectedImage = null; this.name = ''; - this.monolithicAction = 'create'; } resetCanonicalName() { @@ -258,12 +351,6 @@ export class CreateClientImageComponent implements OnInit{ return; } - if (this.imageType === 'git') { - if (!this.selectedGitRepository) { - this.toastService.error('Debes seleccionar un repositorio Git'); - return; - } - } if (this.imageType === 'monolithic') { if (this.monolithicAction === 'create' && !this.name) { @@ -295,25 +382,32 @@ export class CreateClientImageComponent implements OnInit{ let payload: any; if (this.imageType === 'git') { - if (this.gitAction === 'update') { - endpoint = `${this.baseUrl}/git-repositories/update-image`; - payload = { - client: `/clients/${this.clientId}`, - partition: this.selectedPartition['@id'], - gitRepository: this.selectedGitRepository.name, - queue: result - }; - } else { + const gitRepoName = this.hasValidGitData ? this.gitData.repo : this.selectedGitRepository?.name; + const originBranch = this.hasValidGitData ? this.gitData.branch : this.selectedBranch; + + if (this.branches.length === 0) { endpoint = `${this.baseUrl}/images`; payload = { client: `/clients/${this.clientId}`, partition: this.selectedPartition['@id'], type: this.imageType, - gitRepository: this.selectedGitRepository.name, - name: this.selectedGitRepository.name, + gitRepository: gitRepoName, + name: gitRepoName, + originalBranch: originBranch, + destinationBranch: this.destinationBranch, action: 'create', queue: result }; + } else { + endpoint = `${this.baseUrl}/git-repositories/update-image`; + payload = { + client: `/clients/${this.clientId}`, + partition: this.selectedPartition['@id'], + gitRepository: gitRepoName, + originalBranch: originBranch, + destinationBranch: this.destinationBranch, + queue: result + }; } } else { endpoint = `${this.baseUrl}/images`; @@ -337,9 +431,9 @@ export class CreateClientImageComponent implements OnInit{ .subscribe({ next: (response) => { let actionText = 'creación'; - if (this.imageType === 'git' && this.gitAction === 'update') { + if (this.imageType === 'monolithic' && this.monolithicAction === 'update') { actionText = 'actualización'; - } else if (this.imageType === 'monolithic' && this.monolithicAction === 'update') { + } else if (this.imageType === 'git' && this.branches.length > 0) { actionText = 'actualización'; } this.toastService.success(`Petición de ${actionText} de imagen enviada`); @@ -370,14 +464,28 @@ export class CreateClientImageComponent implements OnInit{ dialogRef.afterClosed().subscribe(result => { this.creatingRepository = false; if (result) { + + + this.newlyCreatedRepository = result; + this.loadGitRepositories(); + setTimeout(() => { - const newRepository = this.gitRepositories.find(repo => repo['@id'] === result['@id']); - if (newRepository) { - this.selectedGitRepository = newRepository; - this.onGitRepositorySelected(newRepository); + if (this.newlyCreatedRepository && !this.selectedGitRepository) { + const newRepo = this.gitRepositories.find(repo => + repo.name === this.newlyCreatedRepository.name + ); + + if (newRepo) { + this.selectedGitRepository = newRepo; + this.onGitRepositorySelected(newRepo); + this.toastService.success(`Repositorio "${newRepo.name}" preseleccionado`); + } + + this.newlyCreatedRepository = null; } - }, 200); + }, 1000); + } else { } }); } @@ -394,8 +502,13 @@ export class CreateClientImageComponent implements OnInit{ this.selectedImage = null; this.name = ''; this.monolithicAction = 'create'; + + if (this.selectedPartition) { + this.loadGitData(this.selectedPartition); + } } else { this.resetGitSelections(); + this.gitData = null; } } @@ -425,5 +538,82 @@ export class CreateClientImageComponent implements OnInit{ set selectedPartition(value: any) { this._selectedPartition = value; + + if (value && this.imageType === 'git') { + this.loadGitData(value); + } else { + this.gitData = null; + } + } + + loadGitData(partition: any): void { + this.loadingGitData = true; + this.gitData = null; + this.repositoryNotFound = false; + + const payload = { + partition: partition['@id'], + client: `/clients/${this.clientId}` + }; + + this.http.post(`${this.baseUrl}/git-repositories/get-git-data`, payload).subscribe({ + next: (response) => { + this.gitData = response; + if (this.gitData && this.gitData.branch) { + this.destinationBranch = this.gitData.branch; + } + this.loadingGitData = false; + + this.checkRepositoryExists(); + + if (this.hasValidGitData) { + this.loadBranchesForGitData(); + } + }, + error: (error) => { + console.error('Error al cargar datos de Git:', error); + this.toastService.error('Error al cargar información de Git'); + this.loadingGitData = false; + } + }); + } + + checkRepositoryExists(): void { + if (this.gitData && this.gitData.repo && this.gitRepositories.length > 0) { + const repoExists = this.gitRepositories.some((repo: any) => + repo.name === this.gitData.repo + ); + this.repositoryNotFound = !repoExists; + } else { + this.repositoryNotFound = false; + } + } + + loadBranchesForGitData(): void { + if (!this.gitData || !this.gitData.repo) { + return; + } + + this.loadingBranches = true; + const url = `${this.baseUrl}/image-repositories/server/git/${this.selectedRepository.uuid}/branches`; + this.http.post(url, { repositoryName: this.gitData.repo }).subscribe( + data => { + this.branches = data.branches || []; + this.loadingBranches = false; + }, + error => { + console.error('Error fetching branches for Git data', error); + this.loadingBranches = false; + } + ); + } + + toggleDestinationBranchEdit(): void { + this.isDestinationBranchEditable = !this.isDestinationBranchEditable; + + if (!this.isDestinationBranchEditable) { + // Opcional: Aquí se pueden agregar validaciones adicionales + console.log('Rama destino guardada:', this.destinationBranch); + } } }