From 011094b232cfdae2dac3aba5295b8ef8b470ff1a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 3 Oct 2024 14:47:18 +0200 Subject: [PATCH] Added interceptor 401 and refactor commands --- .../calendar/calendar.component.css | 22 +---- .../calendar/calendar.component.html | 3 +- .../main-commands/commands.component.css | 82 ++++++++++--------- .../main-commands/commands.component.html | 54 ++++-------- .../main-commands/commands.component.ts | 49 ++++++++--- .../create-command.component.css | 77 ++++++++++------- .../create-command.component.html | 48 +++++------ .../create-command.component.ts | 78 +++++++++++++----- .../commands/main-commands/data.service.ts | 54 ++++++++++++ .../command-detail.component.css | 23 ++---- .../command-detail.component.html | 18 +--- .../app/core/services/custom.interceptor.ts | 19 ++++- 12 files changed, 310 insertions(+), 217 deletions(-) create mode 100644 ogWebconsole/src/app/components/commands/main-commands/data.service.ts diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.css b/ogWebconsole/src/app/components/calendar/calendar.component.css index cbdd6d8..2fd1786 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.css +++ b/ogWebconsole/src/app/components/calendar/calendar.component.css @@ -2,7 +2,7 @@ font-size: 24px; } -.images-button-row { +.calendar-button-row { display: flex; justify-content: flex-start; margin-top: 16px; @@ -16,31 +16,11 @@ padding: 16px; } -.imagesLists-container { - flex: 1; -} - .card.unidad-card { height: 100%; box-sizing: border-box; } -.image-container { - display: flex; - align-items: center; - margin-bottom: 16px; - border-bottom: 1px solid rgba(122, 122, 122, 0.555); -} - -.image-container h4 { - margin: 0; - flex: 1; -} - -.image-name{ - cursor: pointer; -} - table { width: 100%; margin-top: 50px; diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.html b/ogWebconsole/src/app/components/calendar/calendar.component.html index 9e6a6b6..ebac9b9 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.html +++ b/ogWebconsole/src/app/components/calendar/calendar.component.html @@ -1,6 +1,6 @@

Administrar calendarios

-
+
@@ -26,7 +26,6 @@ {{ column.cell(image) }} - diff --git a/ogWebconsole/src/app/components/commands/main-commands/commands.component.css b/ogWebconsole/src/app/components/commands/main-commands/commands.component.css index 489cdf7..2ffa5b8 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/commands.component.css +++ b/ogWebconsole/src/app/components/commands/main-commands/commands.component.css @@ -1,49 +1,33 @@ -.commands-list { - margin-bottom: 20px; +.title { + font-size: 24px; } -.command-item { - cursor: pointer; - margin-bottom: 10px; - padding: 10px; - border: 1px solid #ccc; - border-radius: 4px; - background-color: #f9f9f9; +.calendar-button-row { + display: flex; + justify-content: flex-start; + margin-top: 16px; } -.command-item:hover { - background-color: #e9e9e9; +.divider { + margin: 20px 0; } -.command-details { - padding: 20px; - border: 1px solid #ddd; - background-color: #f4f4f4; - border-radius: 4px; +.lists-container { + padding: 16px; } -.script-display { - margin-top: 20px; - background-color: #000; - color: #fff; - padding: 10px; - border-radius: 4px; +.imagesLists-container { + flex: 1; } -pre { - margin: 0; - font-family: 'Courier New', Courier, monospace; +.card.unidad-card { + height: 100%; + box-sizing: border-box; } -.mat-elevation-z8{ - margin-top: 20px; -} -tr:hover { - background-color: rgba(219, 219, 219, 0.219); -} - -.detailBtn{ - cursor: pointer; +table { + width: 100%; + margin-top: 50px; } .search-container { @@ -65,6 +49,30 @@ tr:hover { padding: 5px; } -.command-button-row{ - margin-bottom: 10px; -} \ No newline at end of file +.header-container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; +} + +.mat-elevation-z8 { + box-shadow: 0px 0px 0px rgba(0,0,0,0.2); +} + +.paginator-container { + display: flex; + justify-content: end; + margin-bottom: 30px; +} + +.mat-chip-readonly-true { + background-color: #4CAF50 !important; + color: white !important; +} + +.mat-chip-readonly-false { + background-color: #F44336 !important; + color: white !important; +} + diff --git a/ogWebconsole/src/app/components/commands/main-commands/commands.component.html b/ogWebconsole/src/app/components/commands/main-commands/commands.component.html index 9ac9e19..a5df741 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/commands.component.html +++ b/ogWebconsole/src/app/components/commands/main-commands/commands.component.html @@ -14,46 +14,28 @@
- - - - - +
Nombre {{ command.name }}
+ + + - - - - - - - - - - - - - - - + diff --git a/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts b/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts index 04d7925..a146b8d 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts @@ -5,6 +5,8 @@ import { ToastrService } from 'ngx-toastr'; import { CommandDetailComponent } from './detail-command/command-detail.component'; import { CreateCommandComponent } from './create-command/create-command.component'; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; +import {MatTableDataSource} from "@angular/material/table"; +import {DatePipe} from "@angular/common"; @Component({ selector: 'app-commands', @@ -13,13 +15,36 @@ import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/ }) export class CommandsComponent implements OnInit { baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - commands: any[] = []; + dataSource = new MatTableDataSource(); filters: { [key: string]: string | boolean } = {}; length: number = 0; itemsPerPage: number = 20; - page: number = 1; + page: number = 0; pageSizeOptions: number[] = [10, 20, 40, 100]; - displayedColumns: string[] = ['name', 'createdBy', 'createdAt', 'actions']; + datePipe: DatePipe = new DatePipe('es-ES'); + columns = [ + { + columnDef: 'id', + header: 'ID', + cell: (command: any) => `${command.id}`, + }, + { + columnDef: 'name', + header: 'Nombre', + cell: (command: any) => `${command.name}` + }, + { + columnDef: 'readOnly', + header: 'Solo Lectura', + cell: (command: any) => `${command.readOnly}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (command: any) => `${this.datePipe.transform(command.createdAt, 'dd/MM/yyyy hh:mm:ss')}`, + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; private apiUrl = `${this.baseUrl}/commands`; constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {} @@ -29,9 +54,9 @@ export class CommandsComponent implements OnInit { } search(): void { - this.http.get(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + this.http.get(`${this.apiUrl}?page=${this.page +1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( (data) => { - this.commands = data['hydra:member']; + this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; }, (error) => { @@ -40,7 +65,8 @@ export class CommandsComponent implements OnInit { ); } - viewDetails(command: any): void { + viewDetails(event: MouseEvent, command: any): void { + event.stopPropagation(); this.dialog.open(CommandDetailComponent, { width: '800px', data: command, @@ -53,14 +79,16 @@ export class CommandsComponent implements OnInit { }).afterClosed().subscribe(() => this.search()); } - editCommand(command: any): void { + editCommand(event: MouseEvent, command: any): void { + event.stopPropagation(); this.dialog.open(CreateCommandComponent, { width: '600px', - data: { command }, + data: command['@id'] }).afterClosed().subscribe(() => this.search()); } - deleteCommand(command: any): void { + deleteCommand(event: MouseEvent,command: any): void { + event.stopPropagation(); this.dialog.open(DeleteModalComponent, { width: '300px', data: { name: command.name }, @@ -80,8 +108,9 @@ export class CommandsComponent implements OnInit { } onPageChange(event: any): void { - this.page = event.pageIndex === 0 ? 1 : event.pageIndex; + this.page = event.pageIndex; this.itemsPerPage = event.pageSize; + this.length = event.length; this.search(); } } diff --git a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.css b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.css index 96352a5..aefc820 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.css +++ b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.css @@ -1,43 +1,60 @@ -.dialog-title { - font-size: 1.5em; - color: #343a40; - margin: 0; - border-bottom: 2px solid #007bff; - padding-bottom: 10px; -} - -.command-form { - padding: 20px; - background-color: #fff; -} - -.form-field { +.full-width { width: 100%; - margin-bottom: 20px; +} +.form-container { + padding: 40px; } -.checkbox-group { - display: flex; - flex-direction: column; - margin-bottom: 20px; +.form-group { + margin-top: 20px; + margin-bottom: 26px; } -.action-buttons { - display: flex; - justify-content: flex-end; +.full-width { + width: 100%; + margin-bottom: 16px; +} + +.additional-form { margin-top: 20px; } -.cancel-button { - margin-right: 10px; - color: #dc3545; +.checkbox-group { + display: flex; + flex-direction: column; + margin: 15px 0; + align-items: flex-start; } -.submit-button { - background-color: #007bff; - color: #fff; +.time-fields { + display: flex; + gap: 15px; } -.submit-button:hover { - background-color: #0056b3; +.time-field { + flex: 1; } + +.list-item-content { + display: flex; + align-items: flex-start; + justify-content: space-between; + width: 100%; +} + +.text-content { + flex-grow: 1; + margin-right: 16px; + margin-left: 10px; +} + +.icon-container { + display: flex; + align-items: center; +} + +.right-icon { + margin-left: 8px; + cursor: pointer; +} + diff --git a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.html b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.html index 3f31bc8..38dd632 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.html +++ b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.html @@ -1,28 +1,30 @@ -

{{ isEditMode ? 'Editar Comando' : 'Añadir Comando' }}

+

{{ commandId ? 'Editar' : 'Crear' }} Comando

+ +
+ + Nombre + + - - - Nombre - - + + Script + + - - Script - - +
+ Solo lectura + Habilitado +
-
- Solo lectura - Habilitado -
+ + Comentarios + + + +
- - Comentarios - - + + + + -
- - -
- diff --git a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts index 2f63786..27257e8 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts @@ -3,6 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; +import {DataService} from "../data.service"; @Component({ selector: 'app-create-command', @@ -11,27 +12,49 @@ import { ToastrService } from 'ngx-toastr'; }) export class CreateCommandComponent { baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - createCommandForm!: FormGroup; - isEditMode: boolean; + createCommandForm: FormGroup; private apiUrl = `${this.baseUrl}/commands`; + commandId: string | null = null; constructor( private fb: FormBuilder, private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, + private dataService: DataService, @Inject(MAT_DIALOG_DATA) public data: any ) { - this.isEditMode = data && data.command; + this.createCommandForm = this.fb.group({ + name: ['', Validators.required], + script: [''], + readOnly: [false], + enabled: [true], + comments: [''], + }); } ngOnInit(): void { - this.createCommandForm = this.fb.group({ - name: [this.isEditMode ? this.data.command.name : '', Validators.required], - script: [this.isEditMode ? this.data.command.script : '', Validators.required], - readOnly: [this.isEditMode ? this.data.command.readOnly : false], - enabled: [this.isEditMode ? this.data.command.enabled : true], - comments: [this.isEditMode ? this.data.command.comments : ''] + if (this.data) { + this.load() + } + } + + load(): void { + this.dataService.getCommand(this.data).subscribe({ + next: (response) => { + console.log(response); + this.createCommandForm = this.fb.group({ + name: [response.name, Validators.required], + notes: [response.notes], + script: [response.script], + readOnly: [response.readOnly], + enabled: [response.enabled], + }); + this.commandId = response['@id']; + }, + error: (err) => { + console.error('Error fetching remote calendar:', err); + } }); } @@ -42,24 +65,35 @@ export class CreateCommandComponent { onSubmit(): void { if (this.createCommandForm.valid) { - const commandData = this.createCommandForm.value; - if (this.isEditMode) { - this.http.patch(`${this.apiUrl}/${this.data.command.uuid}`, commandData).subscribe( - response => { - this.toastService.success('Comando creado con éxito'); - this.dialogRef.close(commandData); + + const payload = { + name: this.createCommandForm.value.name, + script: this.createCommandForm.value.script, + readOnly: this.createCommandForm.value.readOnly, + enabled: this.createCommandForm.value.enabled, + comments: this.createCommandForm.value.comments, + }; + + if (this.commandId) { + this.http.put(`${this.baseUrl}${this.commandId}`, payload).subscribe( + (response) => { + this.toastService.success('Comando editado correctamente'); + this.dialogRef.close(); }, - error => { - console.error('Error editing command', error); + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al editar el comando', error); } ); } else { - this.http.post(this.apiUrl, commandData).subscribe( - response => { - this.dialogRef.close(true); + this.http.post(`${this.baseUrl}/commands`, payload).subscribe( + (response) => { + this.toastService.success('Comando añadido correctamente'); + this.dialogRef.close(); }, - error => { - console.error('Error adding command', error); + (error) => { + this.toastService.error(error['error']['hydra:description']); + console.error('Error al añadir comando', error); } ); } diff --git a/ogWebconsole/src/app/components/commands/main-commands/data.service.ts b/ogWebconsole/src/app/components/commands/main-commands/data.service.ts new file mode 100644 index 0000000..d8bd7e1 --- /dev/null +++ b/ogWebconsole/src/app/components/commands/main-commands/data.service.ts @@ -0,0 +1,54 @@ + +import { Injectable } from '@angular/core'; +import {HttpClient, HttpParams} from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class DataService { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + private apiUrl = `${this.baseUrl}/commands?page=1&itemsPerPage=1000`; + + constructor(private http: HttpClient) {} + + getCommands(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { + const params = new HttpParams({ fromObject: filters }); + + return this.http.get(this.apiUrl, { params }).pipe( + map(response => { + if (response['hydra:member'] && Array.isArray(response['hydra:member'])) { + return { + data: response['hydra:member'], + totalItems: response['hydra:totalItems'] + } + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching commands', error); + return throwError(error); + }) + ); + } + + getCommand(id: string): Observable { + return this.http.get(`${this.baseUrl}${id}`).pipe( + map(response => { + if (response.name) { + return response; + } else { + throw new Error('Unexpected response format'); + } + }), + catchError(error => { + console.error('Error fetching user group', error); + return throwError(error); + }) + ); + } + + +} diff --git a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.css b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.css index 08d7eb3..e01e8aa 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.css +++ b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.css @@ -1,19 +1,18 @@ .modal-container { background-color: #fff; padding: 20px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); border-radius: 10px; width: calc(100% - 40px); - max-width: 800px; + max-width: 800px; height: calc(100% - 40px); - max-height: 90vh; - margin: 20px auto; + max-height: 90vh; + margin: 20px auto; display: flex; flex-direction: column; justify-content: space-between; font-family: 'Roboto', sans-serif; box-sizing: border-box; - overflow: hidden; + overflow: hidden; } .modal-title { @@ -28,7 +27,7 @@ flex-grow: 1; overflow-y: auto; padding: 10px; - max-height: calc(100vh - 200px); + max-height: calc(100vh - 200px); } .modal-content p { @@ -45,8 +44,8 @@ border-radius: 8px; font-size: 1.2em; overflow: auto; - max-height: 300px; - white-space: pre-wrap; + max-height: 300px; + white-space: pre-wrap; word-wrap: break-word; } @@ -73,12 +72,6 @@ border-radius: 5px; } -.cancel-button { - background-color: #dc3545; - color: white; - border-radius: 5px; -} - .primary-button:hover, .accent-button:hover, .cancel-button:hover { @@ -100,4 +93,4 @@ mat-checkbox { margin-left: 20px; -} \ No newline at end of file +} diff --git a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.html b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.html index 2b3bbd3..e57b564 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.html +++ b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.html @@ -26,21 +26,6 @@ Debes seleccionar al menos un cliente. - @@ -48,7 +33,6 @@ - - + diff --git a/ogWebconsole/src/app/core/services/custom.interceptor.ts b/ogWebconsole/src/app/core/services/custom.interceptor.ts index 87f2f35..319a1e4 100644 --- a/ogWebconsole/src/app/core/services/custom.interceptor.ts +++ b/ogWebconsole/src/app/core/services/custom.interceptor.ts @@ -1,10 +1,12 @@ import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { Router } from '@angular/router'; @Injectable() export class CustomInterceptor implements HttpInterceptor { - constructor() { } + constructor(private router: Router) { } intercept(req: HttpRequest, next: HttpHandler): Observable> { const token = localStorage.getItem('loginToken'); @@ -24,6 +26,15 @@ export class CustomInterceptor implements HttpInterceptor { }); } - return next.handle(newCloneRequest); + return next.handle(newCloneRequest).pipe( + catchError((error: HttpErrorResponse) => { + if (error.status === 401) { + this.router.navigate(['/auth/login']).then(r => { + console.error('Error 401, redirect to login page'); + }); + } + return throwError(() => error); + }) + ); } }
{{ column.header }} + + {{ column.cell(command) }} + + + check + close + + Creado por {{ command.createdBy }} Fecha de creación {{ command.createdAt | date:'short' }} Acciones - Acciones + + + - - - - -