refs #798 Added task logs view

oggui/calendar
Alvaro Puente Mella 2024-10-01 19:53:07 +02:00
parent f2cbb90de5
commit 50fe2d830d
20 changed files with 236 additions and 43 deletions

View File

@ -19,6 +19,7 @@ import { CalendarComponent } from "./components/calendar/calendar.component";
import { CommandsComponent } from './components/commands/main-commands/commands.component';
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
const routes: Routes = [
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
{
@ -39,6 +40,7 @@ const routes: Routes = [
{ path: 'commands', component: CommandsComponent },
{ path: 'commands-groups', component: CommandsGroupsComponent },
{ path: 'commands-task', component: CommandsTaskComponent },
{ path: 'commands-logs', component: TaskLogsComponent },
{ path: 'calendars', component: CalendarComponent },
],
},

View File

@ -99,6 +99,7 @@ import { CreateTaskComponent } from './components/commands/commands-task/create-
import { DetailTaskComponent } from './components/commands/commands-task/detail-task/detail-task.component';
import { ClientTabViewComponent } from './components/groups/components/client-tab-view/client-tab-view.component';
import { AdvancedSearchComponent } from './components/groups/components/advanced-search/advanced-search.component';
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
@NgModule({
declarations: [
AppComponent,
@ -154,7 +155,8 @@ import { AdvancedSearchComponent } from './components/groups/components/advanced
CreateTaskComponent,
DetailTaskComponent,
ClientTabViewComponent,
AdvancedSearchComponent
AdvancedSearchComponent,
TaskLogsComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -1,23 +1,58 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateCalendarRuleComponent } from './create-calendar-rule.component';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { HttpClientModule } from '@angular/common/http'; // Importar HttpClientModule
import { provideHttpClientTesting } from '@angular/common/http/testing'; // Importar el nuevo método
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
describe('CreateCalendarRuleComponent', () => {
let component: CreateCalendarRuleComponent;
let fixture: ComponentFixture<CreateCalendarRuleComponent>;
let dialogRefMock: any;
let toastrServiceMock: any;
beforeEach(async () => {
dialogRefMock = {
close: jasmine.createSpy('close')
};
toastrServiceMock = {
success: jasmine.createSpy('success'),
error: jasmine.createSpy('error')
};
await TestBed.configureTestingModule({
declarations: [CreateCalendarRuleComponent]
})
.compileComponents();
imports: [
HttpClientModule, // Importar el módulo HttpClient
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatCheckboxModule,
MatSlideToggleModule,
MatDatepickerModule,
MatNativeDateModule
],
declarations: [CreateCalendarRuleComponent],
providers: [
provideHttpClientTesting(), // Usar el nuevo método para pruebas de HttpClient
{ provide: MatDialogRef, useValue: dialogRefMock },
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: ToastrService, useValue: toastrServiceMock },
]
}).compileComponents();
fixture = TestBed.createComponent(CreateCalendarRuleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,23 +1,63 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatDividerModule } from '@angular/material/divider';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatTableModule } from '@angular/material/table';
import { MatMenuModule } from '@angular/material/menu';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrService } from 'ngx-toastr';
import { CommandsGroupsComponent } from './commands-groups.component';
class MockToastrService {
success() {}
}
class MockMatDialogRef {
close() {}
}
describe('CommandsGroupsComponent', () => {
let component: CommandsGroupsComponent;
let fixture: ComponentFixture<CommandsGroupsComponent>;
let toastService: ToastrService;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CommandsGroupsComponent]
})
.compileComponents();
declarations: [CommandsGroupsComponent],
imports: [
HttpClientModule,
MatDialogModule,
MatPaginatorModule,
MatDividerModule,
MatButtonModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatTableModule,
MatMenuModule,
FormsModule,
BrowserAnimationsModule,
],
providers: [
{ provide: ToastrService, useClass: MockToastrService },
{ provide: MatDialogRef, useClass: MockMatDialogRef }
]
}).compileComponents();
fixture = TestBed.createComponent(CommandsGroupsComponent);
component = fixture.componentInstance;
toastService = TestBed.inject(ToastrService);
fixture.detectChanges();
});
it('should create', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});
});

View File

@ -9,7 +9,6 @@
<mat-card-content>
<p><strong>ID del Grupo:</strong> {{ data.uuid }}</p>
<p><strong>Posición:</strong> {{ data.position }}</p>
<p><strong>Fecha de Creación:</strong> {{ data.createdAt | date:'short' }}</p>
<h3>Comandos Incluidos:</h3>

View File

@ -57,4 +57,4 @@
[pageSize]="itemsPerPage"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>
</mat-paginator>

View File

@ -11,6 +11,7 @@
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Selecciona Comandos</mat-label>
<mat-select formControlName="commandGroup" (selectionChange)="onCommandGroupChange()">
<mat-option *ngFor="let group of availableCommandGroups" [value]="group.uuid">

View File

@ -137,16 +137,21 @@ export class CreateTaskComponent implements OnInit {
const formData = this.taskForm.value;
const dateTime = this.combineDateAndTime(formData.date, formData.time);
const selectedCommands = formData.extraCommands.length > 0
? formData.extraCommands.map((id: any) => `/commands/${id}`)
: [""];
const selectedCommands = formData.extraCommands && formData.extraCommands.length > 0
? formData.extraCommands.map((id: any) => `/commands/${id}`)
: null; // No asignamos nada si no hay comandos seleccionados
const payload = {
commandGroups: [formData.commandGroup ? `/command-groups/${formData.commandGroup}` : [""]],
commands: selectedCommands,
dateTime: dateTime,
notes: formData.notes || ''
};
// Creamos el objeto payload
const payload: any = {
commandGroups: formData.commandGroup ? [`/command-groups/${formData.commandGroup}`] : null,
dateTime: dateTime,
notes: formData.notes || ''
};
// Solo agregamos commands al payload si hay comandos seleccionados
if (selectedCommands) {
payload.commands = selectedCommands;
}
if (this.editing) {
const taskId = this.data.task.uuid;

View File

@ -0,0 +1,40 @@
<div class="header-container">
<h2 class="title">Trazas de ejecuciones</h2>
</div>
<table mat-table [dataSource]="traces" class="mat-elevation-z8">
<ng-container matColumnDef="commandName">
<th mat-header-cell *matHeaderCellDef> Comando </th>
<td mat-cell *matCellDef="let trace"> {{ trace.command.name }} </td>
</ng-container>
<ng-container matColumnDef="clientName">
<th mat-header-cell *matHeaderCellDef> Cliente </th>
<td mat-cell *matCellDef="let trace"> {{ trace.client.name }} </td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef> Estado </th>
<td mat-cell *matCellDef="let trace"> {{ trace.status }} </td>
</ng-container>
<ng-container matColumnDef="executedAt">
<th mat-header-cell *matHeaderCellDef> Ejecutado En </th>
<td mat-cell *matCellDef="let trace"> {{ trace.executedAt | date:'short' }} </td>
</ng-container>
<ng-container matColumnDef="createdBy">
<th mat-header-cell *matHeaderCellDef> Creado Por </th>
<td mat-cell *matCellDef="let trace"> {{ trace.createdBy }} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>

View File

@ -0,0 +1,47 @@
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-task-logs',
templateUrl: './task-logs.component.html',
styleUrls: ['./task-logs.component.css']
})
export class TaskLogsComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; // Utilizando baseUrl desde el env
traces: any[] = [];
length: number = 0;
itemsPerPage: number = 20;
page: number = 1;
pageSizeOptions: number[] = [10, 20, 30, 50];
displayedColumns: string[] = ['commandName', 'clientName', 'status', 'executedAt', 'createdBy'];
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.loadTraces();
}
// Método para cargar las trazas con paginación
loadTraces(): void {
const url = `${this.baseUrl}/traces?page=${this.page}&itemsPerPage=${this.itemsPerPage}`;
this.http.get<any>(url).subscribe(
(data) => {
this.traces = data['hydra:member'];
this.length = data['hydra:totalItems'];
console.log('Traces:', this.traces);
},
(error) => {
console.error('Error fetching traces', error);
}
);
}
// Método que se llama cuando cambia la paginación
onPageChange(event: any): void {
this.page = event.pageIndex + 1; // Actualiza el número de página
this.itemsPerPage = event.pageSize; // Actualiza los ítems por página
this.loadTraces(); // Recarga las trazas con los nuevos parámetros de paginación
}
}

View File

@ -16,9 +16,9 @@ export class CommandsComponent implements OnInit {
commands: any[] = [];
filters: { [key: string]: string | boolean } = {};
length: number = 0;
itemsPerPage: number = 10;
itemsPerPage: number = 20;
page: number = 1;
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
pageSizeOptions: number[] = [10, 20, 40, 100];
displayedColumns: string[] = ['name', 'createdBy', 'createdAt', 'actions'];
private apiUrl = `${this.baseUrl}/commands`;
@ -80,7 +80,7 @@ export class CommandsComponent implements OnInit {
}
onPageChange(event: any): void {
this.page = event.pageIndex;
this.page = event.pageIndex === 0 ? 1 : event.pageIndex;
this.itemsPerPage = event.pageSize;
this.search();
}

View File

@ -22,7 +22,7 @@
</mat-form-field>
<div mat-dialog-actions class="action-buttons">
<button mat-button (click)="onCancel()" class="cancel-button">Cancelar</button>
<button mat-button (click)="onCancel($event)" class="cancel-button">Cancelar</button>
<button mat-flat-button color="primary" type="submit" class="submit-button">{{ isEditMode ? 'Guardar' : 'Añadir' }}</button>
</div>
</form>

View File

@ -33,7 +33,8 @@ export class CreateCommandComponent {
});
}
onCancel(): void {
onCancel(event: Event): void {
event.preventDefault();
this.dialogRef.close();
}

View File

@ -18,7 +18,7 @@
<mat-form-field appearance="fill">
<mat-label>Clientes</mat-label>
<mat-select formControlName="selectedClients" multiple (selectionChange)="onClientSelectionChange($event)">
<mat-option *ngFor="let client of clients" [value]="client.id">
<mat-option *ngFor="let client of clients" [value]="client.uuid">
{{ client.name }}
</mat-option>
</mat-select>
@ -26,7 +26,7 @@
Debes seleccionar al menos un cliente.
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="scheduleExecution" (change)="onScheduleChange($event)">
<!-- <mat-checkbox formControlName="scheduleExecution" (change)="onScheduleChange($event)">
Programar ejecución
</mat-checkbox>
<div *ngIf="showDatePicker" class="schedule-container">
@ -40,7 +40,7 @@
<mat-label>Hora</mat-label>
<input matInput type="time" formControlName="scheduleTime">
</mat-form-field>
</div>
</div> -->
</form>
</div>

View File

@ -38,6 +38,7 @@ export class CommandDetailComponent implements OnInit {
this.http.get<any>('http://127.0.0.1:8001/clients?page=1&itemsPerPage=30').subscribe(response => {
this.clients = response['hydra:member'];
});
}
execute(): void {
@ -50,11 +51,9 @@ export class CommandDetailComponent implements OnInit {
clients: this.form.value.selectedClients.map((uuid: any) => `/clients/${uuid}`)
};
const apiUrl = `${this.baseUrl}/commands/${this.data.command.uuid}/execute`;
const apiUrl = `${this.baseUrl}/commands/${this.data.uuid}/execute`;
this.http.post(apiUrl, payload).subscribe({
next: () => {
console.log('Command executed successfully');
this.dialogRef.close();
this.toastService.success('Command executed successfully');
},
@ -95,14 +94,13 @@ export class CommandDetailComponent implements OnInit {
dialogRef.afterClosed().subscribe(result => {
if (result) {
console.log('Comando editado:', result);
this.toastService.success('Comando editado' );
this.data.command = result;
}
});
}
cancel(): void {
console.log('Comando cancelado');
this.dialogRef.close();
}
}

View File

@ -49,8 +49,6 @@
<mat-label i18n="@@capacityLabel">Aforo</mat-label>
<input matInput formControlName="capacity" type="number">
</mat-form-field>
<!-- Campo nuevo para seleccionar el calendario asociado -->
<mat-form-field appearance="fill">
<mat-label>Calendario Asociado</mat-label>
<mat-select (selectionChange)="onCalendarChange($event)">

View File

@ -45,12 +45,24 @@
<mat-label i18n="@@capacityLabel">Aforo</mat-label>
<input matInput formControlName="capacity" type="number">
</mat-form-field>
<!-- Campo nuevo para seleccionar el calendario asociado -->
<!-- <mat-form-field appearance="fill">
<mat-label>Calendario Asociado</mat-label>
<mat-select (selectionChange)="onCalendarChange($event)">
<mat-option *ngFor="let calendar of calendars" [value]="calendar.uuid">
{{ calendar.name }}
</mat-option>
</mat-select>
</mat-form-field> -->
<div>
<button mat-button matStepperPrevious i18n="@@backButton">Atrás</button>
<button mat-button matStepperNext i18n="@@nextButton">Siguiente</button>
</div>
</form>
</mat-step>
<!-- Step 3: Información Adicional -->
<mat-step [stepControl]="additionalInfoFormGroup">

View File

@ -41,6 +41,12 @@
<span i18n="@@gallery">Tareas</span>
</span>
</mat-list-item>
<mat-list-item routerLink="/commands-logs">
<span class="entry">
<mat-icon class="icon">notifications</mat-icon>
<span i18n="@@gallery">Trazas</span>
</span>
</mat-list-item>
</mat-nav-list>
<!-- End commands sub -->

View File

@ -1,5 +1,5 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DeleteModalComponent } from './delete-modal.component';
describe('DeleteModalComponent', () => {
@ -8,16 +8,23 @@ describe('DeleteModalComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DeleteModalComponent]
})
.compileComponents();
declarations: [DeleteModalComponent],
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: { name: 'Test Name' } }
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DeleteModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});
});