avance interfaces e interceptor

-se simula login en el servicio
This commit is contained in:
luis cespedes 2025-05-05 11:24:49 -04:00
parent 86b3417fc5
commit 8fdba09449
27 changed files with 609 additions and 81 deletions

View File

@ -1,6 +1,39 @@
# CronogramasPrimeng
# Cronogramas PrimeNG Application
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.9.
Este proyecto es una aplicación Angular 19 utilizando PrimeNG para la gestión de cronogramas.
## Estructura de Interfaces
Se han creado las siguientes interfaces para los modelos de datos:
- **Cronograma**: Modelo base para todos los cronogramas
- **Empresa**: Modelo para empresas
- **TipoCarga**: Modelo para tipos de carga
- **EstadoAprobacion**: Modelo para estados de aprobación
- **ActualizacionPd**: Modelo para actualizaciones de PD
- **AjustePd**: Modelo para ajustes de PD
- **UnidadInformacion**: Modelo para unidades de información
## Servicios
Los servicios implementados permiten conectarse a un backend mediante HTTP:
- **CronogramaService**: CRUD para cronogramas
- **EmpresaService**: CRUD para empresas
- **ActualizacionPdService**: CRUD para actualizaciones de PD
- **AjustePdService**: CRUD para ajustes de PD
- **UnidadInformacionService**: CRUD para unidades de información
- **TipoCargaService**: Consulta de tipos de carga
- **EstadoAprobacionService**: Consulta de estados de aprobación
- **AuthService**: Autenticación y gestión de tokens
## Seguridad
La aplicación incluye:
- Interceptor HTTP para añadir tokens de autenticación
- Guard para proteger rutas
- Login y sistema de autenticación
## Development server
@ -12,20 +45,6 @@ ng serve
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
@ -36,24 +55,6 @@ ng build
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

View File

@ -1,16 +1,19 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { provideAnimations } from '@angular/platform-browser/animations';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideAnimations(),
provideHttpClient(withFetch(), withInterceptors([authInterceptor])),
providePrimeNG({
theme: {
preset: Aura,
@ -18,7 +21,6 @@ export const appConfig: ApplicationConfig = {
darkModeSelector: false || 'none'
}
}
})
]
};

View File

@ -8,6 +8,7 @@ import { ActualizacionPdComponent } from './pages/actualizacion-pd/actualizacion
import { AjustePdComponent } from './pages/ajuste-pd/ajuste-pd.component';
import { ResumenComponent } from './pages/resumen/resumen.component';
import { UnidadInformacionComponent } from './pages/unidad-informacion/unidad-informacion.component';
import { authGuard } from './guards/auth.guard';
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
@ -15,6 +16,7 @@ export const routes: Routes = [
{
path: '',
component: LayoutComponent,
canActivate: [authGuard],
children: [
{ path: '', redirectTo: 'inicio', pathMatch: 'full' },
{ path: 'inicio', component: HomeComponent, data: { title: 'Inicio' } },

View File

@ -12,7 +12,7 @@
</ul>
<div class="navbar-right">
<span class="user-name">Luis Muñoz</span>
<span class="user-name">{{ userName }}</span>
<button pButton icon="pi pi-sign-out" class="p-button-text p-button-rounded logout-button"
(click)="confirmarAccion()"></button>
</div>
@ -22,8 +22,12 @@
<div class="page-header">
<div class="page-title tituloNavbar">{{ pageTitle }}</div>
<div class="breadcrumb-container">
<a routerLink="/dashboard" class="breadcrumb-link">Inicio</a>
<a routerLink="/inicio" class="breadcrumb-link">Inicio</a>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">{{ pageTitle }}</span>
</div>
</div>
<!-- Componentes para mensajes y diálogos -->
<p-toast></p-toast>
<p-confirmDialog [style]="{width: '450px'}"></p-confirmDialog>

View File

@ -1,21 +1,40 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { Component, EventEmitter, Output, OnInit } from '@angular/core';
import { RouterLink } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { ConfirmationService } from 'primeng/api';
import { ConfirmationService, MessageService } from 'primeng/api';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { filter, map, mergeMap } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { ToastModule } from 'primeng/toast';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-navbar',
imports: [RouterLink, ButtonModule],
imports: [
CommonModule,
RouterLink,
ButtonModule,
ConfirmDialogModule,
ToastModule
],
providers: [ConfirmationService, MessageService],
templateUrl: './navbar.component.html',
styleUrl: './navbar.component.scss',
standalone: true
})
export class NavbarComponent {
export class NavbarComponent implements OnInit {
@Output() sidebarToggle = new EventEmitter<void>();
pageTitle: string = 'Starter Pages';
constructor(private confirmationService: ConfirmationService,private router: Router, private activatedRoute: ActivatedRoute) {
userName: string = '';
constructor(
private confirmationService: ConfirmationService,
private messageService: MessageService,
private router: Router,
private activatedRoute: ActivatedRoute,
private authService: AuthService
) {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd),
map(() => {
@ -30,7 +49,17 @@ export class NavbarComponent {
this.pageTitle = data['title'] || 'Sin título';
});
}
ngOnInit() {
// Obtener nombre de usuario del usuario logueado
this.authService.user$.subscribe(user => {
if (user) {
this.userName = user.name || user.username || 'Usuario';
} else {
this.userName = 'Usuario';
}
});
}
toggleSidebar() {
this.sidebarToggle.emit();
@ -46,13 +75,21 @@ export class NavbarComponent {
acceptButtonStyleClass: 'p-button-danger',
rejectButtonStyleClass: 'p-button-secondary',
accept: () => {
console.log('Sesión cerrada');
this.logout();
},
reject: () => {
console.log('Canceló cierre de sesión');
}
});
}
logout() {
this.authService.logout();
this.messageService.add({
severity: 'success',
summary: 'Sesión cerrada',
detail: 'Has cerrado sesión exitosamente'
});
this.router.navigate(['/login']);
}
}

View File

@ -0,0 +1,16 @@
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
export const authGuard: CanActivateFn = () => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isLoggedIn()) {
return true;
}
// Redirect to login if not authenticated
router.navigate(['/login']);
return false;
};

View File

@ -0,0 +1,46 @@
import { inject } from '@angular/core';
import {
HttpRequest,
HttpHandlerFn,
HttpInterceptorFn,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';
export const authInterceptor: HttpInterceptorFn = (
request: HttpRequest<unknown>,
next: HttpHandlerFn
): Observable<any> => {
const authService = inject(AuthService);
const router = inject(Router);
// Get the auth token
const token = authService.getToken();
// Clone the request and add the token if it exists
if (token) {
const authRequest = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
// Handle the authenticated request
return next(authRequest).pipe(
catchError((error: HttpErrorResponse) => {
// Handle 401 Unauthorized errors by logging out and redirecting to login
if (error.status === 401) {
authService.logout();
router.navigate(['/login']);
}
return throwError(() => error);
})
);
}
// If no token, just pass the request through
return next(request);
};

View File

@ -0,0 +1,10 @@
import { Cronograma } from './cronograma.model';
export interface ActualizacionPd extends Cronograma {
dato9?: string;
dato10?: string;
dato11?: string;
dato12?: string;
dato13?: string;
dato14?: string;
}

View File

@ -0,0 +1,12 @@
import { Cronograma } from './cronograma.model';
export interface AjustePd extends Cronograma {
dato9?: string;
dato10?: string;
dato13?: string;
dato14?: string;
dato15?: string;
dato16?: string;
dato17?: string;
dato18?: string;
}

View File

@ -0,0 +1,11 @@
export interface Cronograma {
id?: number;
empresa: string;
codigoCronograma: string;
codigoCronogramaAjuste: string;
tipoCarga: string;
estadoRevision?: string;
analista?: string;
fechaIngreso?: string;
semaforo?: 'green' | 'yellow' | 'red';
}

View File

@ -0,0 +1,4 @@
export interface Empresa {
id?: number;
name: string;
}

View File

@ -0,0 +1,4 @@
export interface EstadoAprobacion {
id?: number;
name: string;
}

7
src/app/models/index.ts Normal file
View File

@ -0,0 +1,7 @@
export * from './cronograma.model';
export * from './empresa.model';
export * from './tipo-carga.model';
export * from './estado-aprobacion.model';
export * from './actualizacion-pd.model';
export * from './ajuste-pd.model';
export * from './unidad-informacion.model';

View File

@ -0,0 +1,4 @@
export interface TipoCarga {
id?: number;
name: string;
}

View File

@ -0,0 +1,6 @@
import { Cronograma } from './cronograma.model';
export interface UnidadInformacion extends Cronograma {
dato5?: string;
dato6?: string;
}

View File

@ -1,5 +1,3 @@
<!-- src/app/pages/home/home.component.html -->
<div class="home-page">
<!-- Simple Card -->

View File

@ -12,10 +12,11 @@
</div>
</div>
<!-- Toast para mensajes -->
<p-toast></p-toast>
<!-- MAIN CONTENT -->
<main class="main-content">
<!-- MAIN CONTENT -->
<main class="main-content">
<div class="login-container">
<div class="login-box">
<div class="login-card">
@ -34,6 +35,7 @@
placeholder="Email"
class="input-with-icon w-full mb-3"
style="background-color: white; color: black;"
required
/>
<!-- Password -->
@ -45,51 +47,45 @@
placeholder="Password"
class="input-with-lock w-full"
style="background-color: white; color: black;"
required
/>
<!-- Mensaje de error -->
<div *ngIf="errorMessage" class="error-message mt-2">
<p-message severity="error" [text]="errorMessage"></p-message>
</div>
<!-- Botón -->
<div class="login-actions">
<button
pButton
type="submit"
label="Autenticar"
[label]="loading ? 'Autenticando...' : 'Autenticar'"
class="p-button-primary w-full"
style="color: white">
style="color: white"
[disabled]="loading || !email || !password">
<i *ngIf="loading" class="pi pi-spin pi-spinner mr-2"></i>
</button>
</div>
</form>
<!-- <form (ngSubmit)="onLogin()">
<div class="p-inputgroup mb-3">
<input type="email" pInputText [(ngModel)]="email" name="email" placeholder="Email" class="w-full">
<span class="p-inputgroup-addon bg-red-600 text-white">&#64;</span>
<span class="p-inputgroup-addon">
<i class="pi pi-envelope"></i>
</span>
</div>
<div class="p-inputgroup mb-4">
<input type="password" pInputText [(ngModel)]="password" name="password" placeholder="Password" class="w-full">
<span class="p-inputgroup-addon bg-red-600 text-white">***</span>
<span class="p-inputgroup-addon">
<i class="pi pi-lock"></i>
</span>
</div>
<div class="login-actions">
<button pButton type="submit" label="Autenticar" class="p-button-primary"></button>
</div>
</form> -->
<!-- Recuperar contraseña -->
<div class="password-recovery">
<a href="#">Recuperar Contraseña</a>
</div>
<!-- Credenciales de prueba -->
<div class="test-credentials mt-3 p-2" style="background-color: #f8f9fa; border-radius: 4px;">
<p class="mb-1"><strong>Credenciales de prueba:</strong></p>
<p class="mb-1">Email: admin&#64;example.com</p>
<p>Password: admin123</p>
</div>
</div>
</div>
</div>
</main>
<!-- FOOTER -->
<app-footer></app-footer>
<!-- FOOTER -->
<app-footer></app-footer>

View File

@ -8,7 +8,12 @@ import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
import { PasswordModule } from 'primeng/password';
import { DividerModule } from 'primeng/divider';
import { MessagesModule } from 'primeng/messages';
import { MessageModule } from 'primeng/message';
import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api';
import { FooterComponent } from "../../components/footer/footer.component";
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-login',
@ -20,20 +25,50 @@ import { FooterComponent } from "../../components/footer/footer.component";
ButtonModule,
PasswordModule,
DividerModule,
MessagesModule,
MessageModule,
ToastModule,
FooterComponent
],
],
providers: [MessageService],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent {
email: string = '';
password: string = '';
loading: boolean = false;
errorMessage: string = '';
constructor(
private router: Router,
private authService: AuthService,
private messageService: MessageService
) { }
constructor(private router: Router) { }
onLogin() {
// Aquí iría la lógica de autenticación
// Por ahora, solo navegamos a la página de inicio
this.router.navigate(['/inicio']);
this.loading = true;
this.errorMessage = '';
// Llamar al servicio auth
this.authService.login({ email: this.email, password: this.password })
.subscribe({
next: () => {
console.log('Login exitoso');
this.loading = false;
this.router.navigate(['/inicio']);
},
error: (error) => {
console.error('Error en login:', error);
this.loading = false;
this.errorMessage = 'Credenciales incorrectas';
this.messageService.add({
severity: 'error',
summary: 'Error',
detail: 'Credenciales incorrectas'
});
}
});
}
}

View File

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ActualizacionPd } from '../models/actualizacion-pd.model';
@Injectable({
providedIn: 'root'
})
export class ActualizacionPdService {
private apiUrl = 'api/actualizaciones';
constructor(private http: HttpClient) {}
getActualizaciones(): Observable<ActualizacionPd[]> {
return this.http.get<ActualizacionPd[]>(this.apiUrl);
}
getActualizacionById(id: number): Observable<ActualizacionPd> {
return this.http.get<ActualizacionPd>(`${this.apiUrl}/${id}`);
}
createActualizacion(actualizacion: ActualizacionPd): Observable<ActualizacionPd> {
return this.http.post<ActualizacionPd>(this.apiUrl, actualizacion);
}
updateActualizacion(actualizacion: ActualizacionPd): Observable<ActualizacionPd> {
return this.http.put<ActualizacionPd>(`${this.apiUrl}/${actualizacion.id}`, actualizacion);
}
deleteActualizacion(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
aprobarActualizacion(id: number): Observable<ActualizacionPd> {
return this.http.patch<ActualizacionPd>(`${this.apiUrl}/${id}/aprobar`, {});
}
rechazarActualizacion(id: number, motivo: string): Observable<ActualizacionPd> {
return this.http.patch<ActualizacionPd>(`${this.apiUrl}/${id}/rechazar`, { motivo });
}
}

View File

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AjustePd } from '../models/ajuste-pd.model';
@Injectable({
providedIn: 'root'
})
export class AjustePdService {
private apiUrl = 'api/ajustes';
constructor(private http: HttpClient) {}
getAjustes(): Observable<AjustePd[]> {
return this.http.get<AjustePd[]>(this.apiUrl);
}
getAjusteById(id: number): Observable<AjustePd> {
return this.http.get<AjustePd>(`${this.apiUrl}/${id}`);
}
createAjuste(ajuste: AjustePd): Observable<AjustePd> {
return this.http.post<AjustePd>(this.apiUrl, ajuste);
}
updateAjuste(ajuste: AjustePd): Observable<AjustePd> {
return this.http.put<AjustePd>(`${this.apiUrl}/${ajuste.id}`, ajuste);
}
deleteAjuste(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
aprobarAjuste(id: number): Observable<AjustePd> {
return this.http.patch<AjustePd>(`${this.apiUrl}/${id}/aprobar`, {});
}
rechazarAjuste(id: number, motivo: string): Observable<AjustePd> {
return this.http.patch<AjustePd>(`${this.apiUrl}/${id}/rechazar`, { motivo });
}
}

View File

@ -0,0 +1,111 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject, of, throwError } from 'rxjs';
import { tap, delay } from 'rxjs/operators';
interface LoginCredentials {
username?: string;
password?: string;
email?: string;
}
interface AuthResponse {
token: string;
user: {
id: number;
username: string;
name: string;
role: string;
email?: string;
};
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'api/auth';
private userSubject = new BehaviorSubject<any>(null);
public user$ = this.userSubject.asObservable();
// Usuarios de prueba para simular login
private mockUsers = [
{
email: 'admin@example.com',
password: 'admin123',
user: {
id: 1,
username: 'admin',
name: 'Administrador',
role: 'admin',
email: 'admin@example.com'
}
},
{
email: 'user@example.com',
password: 'user123',
user: {
id: 2,
username: 'user',
name: 'Usuario',
role: 'user',
email: 'user@example.com'
}
}
];
constructor(private http: HttpClient) {
// Check if user is already logged in on service initialization
const user = localStorage.getItem('user');
if (user) {
this.userSubject.next(JSON.parse(user));
}
}
login(credentials: LoginCredentials): Observable<AuthResponse> {
// Simular login con usuarios de prueba
const user = this.mockUsers.find(u =>
u.email === credentials.email && u.password === credentials.password);
if (user) {
// Generar token falso
const mockResponse: AuthResponse = {
token: 'mock-jwt-token-' + Math.random().toString(36).substring(2, 15),
user: user.user
};
return of(mockResponse).pipe(
// Simular retraso de red
delay(800),
tap(response => {
// Store token and user info
localStorage.setItem('token', response.token);
localStorage.setItem('user', JSON.stringify(response.user));
this.userSubject.next(response.user);
})
);
} else {
// Simular error de credenciales inválidas
return throwError(() => new Error('Credenciales incorrectas'));
}
}
logout(): void {
// Clear storage and update subject
localStorage.removeItem('token');
localStorage.removeItem('user');
this.userSubject.next(null);
}
isLoggedIn(): boolean {
return !!localStorage.getItem('token');
}
getToken(): string | null {
return localStorage.getItem('token');
}
getCurrentUser(): any {
return this.userSubject.value;
}
}

View File

@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Cronograma } from '../models/cronograma.model';
@Injectable({
providedIn: 'root'
})
export class CronogramaService {
private apiUrl = 'api/cronogramas';
constructor(private http: HttpClient) {}
getCronogramas(): Observable<Cronograma[]> {
return this.http.get<Cronograma[]>(this.apiUrl);
}
getCronogramaById(id: number): Observable<Cronograma> {
return this.http.get<Cronograma>(`${this.apiUrl}/${id}`);
}
createCronograma(cronograma: Cronograma): Observable<Cronograma> {
return this.http.post<Cronograma>(this.apiUrl, cronograma);
}
updateCronograma(cronograma: Cronograma): Observable<Cronograma> {
return this.http.put<Cronograma>(`${this.apiUrl}/${cronograma.id}`, cronograma);
}
deleteCronograma(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}

View File

@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Empresa } from '../models/empresa.model';
@Injectable({
providedIn: 'root'
})
export class EmpresaService {
private apiUrl = 'api/empresas';
constructor(private http: HttpClient) {}
getEmpresas(): Observable<Empresa[]> {
return this.http.get<Empresa[]>(this.apiUrl);
}
getEmpresaById(id: number): Observable<Empresa> {
return this.http.get<Empresa>(`${this.apiUrl}/${id}`);
}
createEmpresa(empresa: Empresa): Observable<Empresa> {
return this.http.post<Empresa>(this.apiUrl, empresa);
}
updateEmpresa(empresa: Empresa): Observable<Empresa> {
return this.http.put<Empresa>(`${this.apiUrl}/${empresa.id}`, empresa);
}
deleteEmpresa(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}

View File

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { EstadoAprobacion } from '../models/estado-aprobacion.model';
@Injectable({
providedIn: 'root'
})
export class EstadoAprobacionService {
private apiUrl = 'api/estadosAprobacion';
constructor(private http: HttpClient) {}
getEstadosAprobacion(): Observable<EstadoAprobacion[]> {
return this.http.get<EstadoAprobacion[]>(this.apiUrl);
}
}

View File

@ -0,0 +1,7 @@
export * from './cronograma.service';
export * from './empresa.service';
export * from './actualizacion-pd.service';
export * from './ajuste-pd.service';
export * from './unidad-informacion.service';
export * from './tipo-carga.service';
export * from './estado-aprobacion.service';

View File

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { TipoCarga } from '../models/tipo-carga.model';
@Injectable({
providedIn: 'root'
})
export class TipoCargaService {
private apiUrl = 'api/tiposCarga';
constructor(private http: HttpClient) {}
getTiposCarga(): Observable<TipoCarga[]> {
return this.http.get<TipoCarga[]>(this.apiUrl);
}
}

View File

@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { UnidadInformacion } from '../models/unidad-informacion.model';
@Injectable({
providedIn: 'root'
})
export class UnidadInformacionService {
private apiUrl = 'api/unidades';
constructor(private http: HttpClient) {}
getUnidades(): Observable<UnidadInformacion[]> {
return this.http.get<UnidadInformacion[]>(this.apiUrl);
}
getUnidadById(id: number): Observable<UnidadInformacion> {
return this.http.get<UnidadInformacion>(`${this.apiUrl}/${id}`);
}
createUnidad(unidad: UnidadInformacion): Observable<UnidadInformacion> {
return this.http.post<UnidadInformacion>(this.apiUrl, unidad);
}
updateUnidad(unidad: UnidadInformacion): Observable<UnidadInformacion> {
return this.http.put<UnidadInformacion>(`${this.apiUrl}/${unidad.id}`, unidad);
}
deleteUnidad(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}