refs #2597. Git repository show

pull/33/head
Manuel Aranda Rosales 2025-08-05 10:26:36 +02:00
parent fbdcfdb3ea
commit 229ff86c5b
7 changed files with 206 additions and 5 deletions

View File

@ -0,0 +1,67 @@
.dialog-content {
min-width: 400px;
max-width: 600px;
}
.commit-info {
background-color: #f5f5f5;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.commit-info p {
margin: 5px 0;
font-size: 14px;
}
.branch-form {
display: flex;
flex-direction: column;
gap: 15px;
}
.form-field {
width: 100%;
}
.action-container {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding: 0 24px 24px 24px;
}
.ordinary-button {
background-color: #f5f5f5;
color: #333;
border: 1px solid #ddd;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.ordinary-button:hover {
background-color: #e0e0e0;
}
.submit-button {
background-color: #3f51b5;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.submit-button:hover:not(:disabled) {
background-color: #303f9f;
}
.submit-button:disabled {
background-color: #ccc;
cursor: not-allowed;
}

View File

@ -0,0 +1,31 @@
<app-loading [isLoading]="loading"></app-loading>
<h2 mat-dialog-title>Crear Rama desde Commit</h2>
<mat-dialog-content class="dialog-content">
<div class="commit-info">
<p><strong>Commit ID:</strong> {{ data.commit?.hexsha }}</p>
<p><strong>Mensaje:</strong> {{ data.commit?.message }}</p>
</div>
<form [formGroup]="branchForm" (ngSubmit)="createBranch()" class="branch-form">
<mat-form-field appearance="fill" class="form-field">
<mat-label>Nombre de la rama</mat-label>
<input matInput formControlName="name" placeholder="ej: feature-nueva-funcionalidad" required>
<mat-error *ngIf="branchForm.get('name')?.hasError('required')">
El nombre de la rama es obligatorio
</mat-error>
<mat-error *ngIf="branchForm.get('name')?.hasError('pattern')">
El nombre de la rama solo puede contener letras, números, puntos, guiones y guiones bajos
</mat-error>
<mat-hint>Ejemplo: feature-nueva-funcionalidad, hotfix-bug-123, release-v2.0</mat-hint>
</mat-form-field>
</form>
</mat-dialog-content>
<div mat-dialog-actions class="action-container">
<button class="ordinary-button" (click)="close()">Cancelar</button>
<button class="submit-button" (click)="createBranch()" [disabled]="branchForm.invalid || loading">
Crear Rama
</button>
</div>

View File

@ -0,0 +1,61 @@
import { Component, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { ConfigService } from '@services/config.service';
@Component({
selector: 'app-create-branch-modal',
templateUrl: './create-branch-modal.component.html',
styleUrl: './create-branch-modal.component.css'
})
export class CreateBranchModalComponent {
branchForm: FormGroup;
loading: boolean = false;
baseUrl: string;
constructor(
private fb: FormBuilder,
private http: HttpClient,
public dialogRef: MatDialogRef<CreateBranchModalComponent>,
private toastService: ToastrService,
private configService: ConfigService,
@Inject(MAT_DIALOG_DATA) public data: { commit: any, repositoryName: string, repositoryUuid: string }
) {
this.baseUrl = this.configService.apiUrl;
this.branchForm = this.fb.group({
name: ['', [Validators.required, Validators.pattern(/^[a-zA-Z0-9._-]+$/)]],
});
}
createBranch(): void {
if (this.branchForm.valid) {
this.loading = true;
const payload = {
commit: this.data.commit?.hexsha || 'master',
name: this.branchForm.value.name,
repository: this.data.repositoryName
};
const url = `${this.baseUrl}/image-repositories/server/git/${this.data.repositoryUuid}/create-branch`;
this.http.post(url, payload).subscribe({
next: (response) => {
this.toastService.success('Rama creada correctamente');
this.dialogRef.close(response);
},
error: (error) => {
this.toastService.error(error.error?.message || 'Error al crear la rama');
this.loading = false;
}
});
} else {
this.toastService.error('Por favor, complete todos los campos requeridos');
}
}
close(): void {
this.dialogRef.close();
}
}

View File

@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/materia
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
import { ToastrModule } from 'ngx-toastr';
import { ConfigService } from '@services/config.service';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
@ -11,29 +12,44 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { LoadingComponent } from '../../../../shared/loading/loading.component';
describe('CreateTagModalComponent', () => {
let component: CreateTagModalComponent;
let fixture: ComponentFixture<CreateTagModalComponent>;
beforeEach(async () => {
const mockToastrService = {
success: jasmine.createSpy('success'),
error: jasmine.createSpy('error'),
warning: jasmine.createSpy('warning'),
info: jasmine.createSpy('info')
};
const mockConfigService = {
apiUrl: 'http://mock-api-url'
};
await TestBed.configureTestingModule({
declarations: [CreateTagModalComponent],
declarations: [CreateTagModalComponent, LoadingComponent],
imports: [
MatDialogModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
NoopAnimationsModule
NoopAnimationsModule,
TranslateModule.forRoot(),
ToastrModule.forRoot()
],
providers: [
FormBuilder,
HttpClient,
ToastrService,
ConfigService,
provideHttpClient(),
provideHttpClientTesting(),
{ provide: ToastrService, useValue: mockToastrService },
{ provide: ConfigService, useValue: mockConfigService },
{
provide: MatDialogRef,
useValue: {}

View File

@ -48,7 +48,6 @@ export class CreateTagModalComponent {
this.dialogRef.close(response);
},
error: (error) => {
console.error('Error creating tag:', error);
this.toastService.error(error.error?.message || 'Error al crear el tag');
this.loading = false;
}

View File

@ -99,6 +99,10 @@
<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

@ -9,6 +9,7 @@ import {ConfigService} from "@services/config.service";
import {Router} from "@angular/router";
import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component";
import {CreateTagModalComponent} from "./create-tag-modal/create-tag-modal.component";
import {CreateBranchModalComponent} from "./create-branch-modal/create-branch-modal.component";
@Component({
selector: 'app-show-git-commits',
@ -210,6 +211,9 @@ export class ShowGitCommitsComponent implements OnInit{
case 'create-tag':
this.openCreateTagDialog(commit);
break;
case 'create-branch':
this.openCreateBranchDialog(commit);
break;
default:
console.error('Acción no soportada:', action);
break;
@ -269,6 +273,25 @@ export class ShowGitCommitsComponent implements OnInit{
});
}
openCreateBranchDialog(commit: any) {
const dialogRef = this.dialog.open(CreateBranchModalComponent, {
width: '500px',
data: {
commit: commit,
repositoryName: this.selectedRepository,
repositoryUuid: this.data.repositoryUuid
}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
// Recargar los datos para mostrar la nueva rama
this.loadBranches();
this.loadData();
}
});
}
goToPage(commit: any) {
window.open(`http://localhost:3100/oggit/${this.selectedRepository}/commit/${commit.hexsha}`, '_blank');
}