diff --git a/CHANGELOG.md b/CHANGELOG.md
index e29efc6..caef9f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
# Changelog
+## [0.24.0] - 2025-09-23
+### Added
+- Se han añadido nuevos componentes de visualizacion de todos los apartados de hardware y hardware profile.
+
+---
## [0.23.4] - 2025-09-22
### Fixed
- Se ha arreglado en error a la hora de mandar el ambito de ejecucion en lso asistentes
diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts
index 72f410d..0f1abee 100644
--- a/ogWebconsole/src/app/app-routing.module.ts
+++ b/ogWebconsole/src/app/app-routing.module.ts
@@ -41,6 +41,9 @@ import {
} from "./components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component";
import { roleGuard } from './guards/role.guard';
import { LogoutGuard } from './guards/logout.guard';
+import { HardwareComponent } from './components/hardware/hardware.component';
+import { HardwareProfileComponent } from './components/hardware-profile/hardware-profile.component';
+import { HardwareTypeComponent } from './components/hardware-type/hardware-type.component';
const routes: Routes = [
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
{
@@ -71,6 +74,9 @@ const routes: Routes = [
{ path: 'software-profiles', component: SoftwareProfileComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
{ path: 'operative-systems', component: OperativeSystemComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
{ path: 'menus', component: MenusComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
+ { path: 'hardware', component: HardwareComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
+ { path: 'hardware-profiles', component: HardwareProfileComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
+ { path: 'hardware-types', component: HardwareTypeComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
],
},
{
diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index ef79fbe..1df101c 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -164,6 +164,12 @@ import { CreateTagModalComponent } from './components/repositories/show-git-imag
import { CreateBranchModalComponent } from './components/repositories/show-git-images/create-branch-modal/create-branch-modal.component';
import { ClientLogsModalComponent } from './components/groups/shared/client-logs-modal/client-logs-modal.component';
import { BackupRepositoryModalComponent } from './components/repositories/show-git-images/backup-repository-modal/backup-repository-modal.component';
+import { HardwareComponent } from './components/hardware/hardware.component';
+import { CreateHardwareComponent } from './components/hardware/create-hardware/create-hardware.component';
+import { HardwareProfileComponent } from './components/hardware-profile/hardware-profile.component';
+import { HardwareCollectionModalComponent } from './components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component';
+import { HardwareTypeComponent } from './components/hardware-type/hardware-type.component';
+import { ShowDetailsComponent } from './components/hardware-type/show-details/show-details.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
@@ -284,7 +290,13 @@ registerLocaleData(localeEs, 'es-ES');
CreateBranchModalComponent,
ClientLogsModalComponent,
SafePipe,
- BackupRepositoryModalComponent
+ BackupRepositoryModalComponent,
+ HardwareComponent,
+ CreateHardwareComponent,
+ HardwareProfileComponent,
+ HardwareCollectionModalComponent,
+ HardwareTypeComponent,
+ ShowDetailsComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,
diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
index 27ace1b..e8bf0bf 100644
--- a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
+++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
@@ -35,7 +35,7 @@ export class ExecuteCommandComponent implements OnInit {
{ translationKey: 'executeCommands.deleteImageCache', slug: 'remove-cache-image', disabled: false },
{ translationKey: 'executeCommands.partition', slug: 'partition', disabled: false },
{ translationKey: 'executeCommands.softwareInventory', slug: 'software-inventory', disabled: false },
- { translationKey: 'executeCommands.hardwareInventory', slug: 'hardware-inventory', disabled: true },
+ { translationKey: 'executeCommands.hardwareInventory', slug: 'hardware-inventory', disabled: false },
{ translationKey: 'executeCommands.runScript', slug: 'run-script', disabled: false },
];
@@ -112,7 +112,7 @@ export class ExecuteCommandComponent implements OnInit {
if (states[0] === 'off' || states[0] === 'disconnected') {
command.disabled = !['power-on', 'create-image'].includes(command.slug);
} else {
- command.disabled = !['power-off', 'reboot', 'login', 'create-image', 'deploy-image', 'remove-cache-image', 'partition', 'run-script', 'software-inventory'].includes(command.slug);
+ command.disabled = !['power-off', 'reboot', 'login', 'create-image', 'deploy-image', 'remove-cache-image', 'partition', 'run-script', 'software-inventory', 'hardware-inventory'].includes(command.slug);
}
} else {
if (command.slug === 'software-inventory') {
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.css b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.css
new file mode 100644
index 0000000..594d6b1
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.css
@@ -0,0 +1,57 @@
+.dialog-content {
+ min-width: 600px;
+ max-width: 800px;
+ max-height: 500px;
+ overflow: auto;
+ padding: 24px;
+}
+
+.no-data-message {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 20px;
+ color: #666;
+ text-align: center;
+}
+
+.no-data-message mat-icon {
+ font-size: 48px;
+ width: 48px;
+ height: 48px;
+ margin-bottom: 16px;
+ color: #999;
+}
+
+.hardware-table-container {
+ width: 100%;
+}
+
+.hardware-table-container table {
+ width: 100%;
+}
+
+.action-container {
+ display: flex;
+ justify-content: flex-end;
+ padding: 24px;
+}
+
+.action-container button {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+h2[mat-dialog-title] {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin: 0;
+ padding: 24px 24px 16px 24px;
+}
+
+h2[mat-dialog-title] mat-icon {
+ color: #3f51b5;
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.html b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.html
new file mode 100644
index 0000000..56401a9
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.html
@@ -0,0 +1,31 @@
+
+ {{ profileName }}
+
+
+
+
+
info
+
No hay hardware asociado a este perfil
+
+
+ 0" class="hardware-table-container">
+
+
+ {{ column.header }} |
+
+ {{ column.cell(hardware) }}
+ |
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.spec.ts b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.spec.ts
new file mode 100644
index 0000000..7fc9f49
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.spec.ts
@@ -0,0 +1,27 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { HardwareCollectionModalComponent } from './hardware-collection-modal.component';
+
+describe('HardwareCollectionModalComponent', () => {
+ let component: HardwareCollectionModalComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [HardwareCollectionModalComponent],
+ providers: [
+ { provide: MatDialogRef, useValue: {} },
+ { provide: MAT_DIALOG_DATA, useValue: {} }
+ ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HardwareCollectionModalComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.ts b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.ts
new file mode 100644
index 0000000..00984da
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component.ts
@@ -0,0 +1,64 @@
+import { Component, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatTableDataSource } from '@angular/material/table';
+
+@Component({
+ selector: 'app-hardware-collection-modal',
+ templateUrl: './hardware-collection-modal.component.html',
+ styleUrl: './hardware-collection-modal.component.css'
+})
+export class HardwareCollectionModalComponent {
+ dataSource = new MatTableDataSource();
+ profileName: string = '';
+
+ columns = [
+ {
+ columnDef: 'id',
+ header: 'ID',
+ cell: (hardware: any) => hardware.id || hardware['@id']?.split('/').pop() || 'N/A'
+ },
+ {
+ columnDef: 'name',
+ header: 'Nombre',
+ cell: (hardware: any) => hardware.name || 'N/A'
+ },
+ {
+ columnDef: 'type',
+ header: 'Tipo',
+ cell: (hardware: any) => hardware.type || 'N/A'
+ },
+ {
+ columnDef: 'description',
+ header: 'Descripción',
+ cell: (hardware: any) => hardware.description || 'N/A'
+ }
+ ];
+
+ displayedColumns = this.columns.map(column => column.columnDef);
+
+ constructor(
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: any
+ ) {
+ if (data) {
+ this.profileName = data.profileName || 'Perfil de Hardware';
+
+ // Si se pasa directamente la colección de hardware
+ if (data.hardwareCollection && Array.isArray(data.hardwareCollection)) {
+ this.dataSource.data = data.hardwareCollection;
+ }
+ // Si se pasa el perfil completo
+ else if (data.profile && data.profile.hardwareCollection) {
+ this.dataSource.data = data.profile.hardwareCollection;
+ }
+ // Si no hay datos
+ else {
+ this.dataSource.data = [];
+ }
+ }
+ }
+
+ onClose(): void {
+ this.dialogRef.close();
+ }
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.css b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.css
new file mode 100644
index 0000000..cd19596
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.css
@@ -0,0 +1,80 @@
+.header-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 10px;
+ border-bottom: 1px solid #ddd;
+ }
+
+ .header-container-title {
+ flex-grow: 1;
+ text-align: left;
+ margin-left: 1em;
+ }
+
+ .calendar-button-row {
+ display: flex;
+ gap: 15px;
+ }
+
+ .lists-container {
+ padding: 16px;
+ }
+
+ .card.unidad-card {
+ height: 100%;
+ box-sizing: border-box;
+ }
+
+ table {
+ width: 100%;
+ }
+
+ .search-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin: 1.5rem 0rem 1.5rem 0rem;
+ box-sizing: border-box;
+ }
+
+ .search-string {
+ flex: 2;
+ padding: 5px;
+ }
+
+ .search-boolean {
+ flex: 1;
+ padding: 5px;
+ }
+
+ .mat-elevation-z8 {
+ box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
+ }
+
+ .paginator-container {
+ display: flex;
+ justify-content: end;
+ margin-bottom: 30px;
+}
+
+.client-info {
+ display: flex;
+ flex-direction: column;
+
+.client-name {
+ font-weight: 500;
+ color: #333;
+}
+
+.client-details {
+ display: flex;
+ flex-direction: column;
+ font-size: 0.75em;
+ color: #666;
+}
+
+.client-ip, .client-mac {
+ font-family: monospace;
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.html b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.html
new file mode 100644
index 0000000..6672eff
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+ {{ column.header }} |
+
+
+
+ {{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
+
+
+
+
+
+ {{ column.cell(image).name }}
+
+ {{ column.cell(image).ip }}
+
+
+
+ N/A
+
+
+
+
+ {{ column.cell(image) }}
+
+ |
+
+
+
+ Acciones |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.spec.ts b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.spec.ts
new file mode 100644
index 0000000..fc6d569
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HardwareProfileComponent } from './hardware-profile.component';
+
+describe('HardwareProfileComponent', () => {
+ let component: HardwareProfileComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [HardwareProfileComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HardwareProfileComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.ts b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.ts
new file mode 100644
index 0000000..f16f0dd
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.component.ts
@@ -0,0 +1,206 @@
+import { Component, signal } from '@angular/core';
+import { MatTableDataSource } from "@angular/material/table";
+import { DatePipe } from "@angular/common";
+import { MatDialog } from "@angular/material/dialog";
+import { HttpClient } from "@angular/common/http";
+import { ToastrService } from "ngx-toastr";
+import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
+import { PageEvent } from "@angular/material/paginator";
+import { JoyrideService } from 'ngx-joyride';
+import { ConfigService } from '@services/config.service';
+import { HardwareProfileService } from './hardware-profile.service';
+import { HardwareCollectionModalComponent } from './hardware-collection-modal/hardware-collection-modal.component';
+import { map, Observable, startWith } from 'rxjs';
+import { FormControl } from '@angular/forms';
+
+@Component({
+ selector: 'app-hardware-profile',
+ templateUrl: './hardware-profile.component.html',
+ styleUrl: './hardware-profile.component.css'
+})
+export class HardwareProfileComponent {
+ baseUrl: string;
+ private apiUrl: string;
+ dataSource = new MatTableDataSource();
+ length: number = 0;
+ itemsPerPage: number = 10;
+ page: number = 0;
+ pageSizeOptions: number[] = [5, 10, 20, 40, 100];
+ loading: boolean = false;
+ filters: { [key: string]: string } = {};
+ alertMessage: string | null = null;
+ readonly panelOpenState = signal(false);
+ datePipe: DatePipe = new DatePipe('es-ES');
+ filteredClients!: Observable;
+ clientControl = new FormControl();
+ clients: any[] = [];
+ columns = [
+ {
+ columnDef: 'id',
+ header: 'ID',
+ cell: (hardware: any) => `${hardware.id}`,
+ },
+ {
+ columnDef: 'description',
+ header: 'Descripción',
+ cell: (hardware: any) => hardware.description
+ },
+ {
+ columnDef: 'client',
+ header: 'Cliente',
+ cell: (hardware: any) => hardware.client ? {
+ name: hardware.client.name || 'N/A',
+ ip: hardware.client.ip || 'N/A',
+ mac: hardware.client.mac || 'N/A'
+ } : 'N/A'
+ },
+ {
+ columnDef: 'createdAt',
+ header: 'Fecha de creación',
+ cell: (hardware: any) => `${this.datePipe.transform(hardware.createdAt, 'dd/MM/yyyy hh:mm:ss')}`,
+ }
+ ];
+ displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
+
+ constructor(
+ public dialog: MatDialog,
+ private http: HttpClient,
+ private toastService: ToastrService,
+ private joyrideService: JoyrideService,
+ private configService: ConfigService,
+ private hardwareProfileService: HardwareProfileService
+ ) {
+ this.baseUrl = this.configService.apiUrl;
+ this.apiUrl = `${this.baseUrl}/hardware-profiles`;
+ }
+
+ ngOnInit(): void {
+ this.search();
+ this.loadClients();
+ this.filteredClients = this.clientControl.valueChanges.pipe(
+ startWith(''),
+ map(value => (typeof value === 'string' ? value : value?.name)),
+ map(name => (name ? this._filterClients(name) : this.clients.slice()))
+ );
+ }
+
+ search(): void {
+ this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
+ (data) => {
+ this.dataSource.data = data['hydra:member'];
+ this.length = data['hydra:totalItems'];
+ },
+ (error) => {
+ console.error('Error fetching commands', error);
+ }
+ );
+ }
+
+ displayFnClient(client: any): string {
+ return client && client.name ? client.name : '';
+ }
+
+ onOptionClientSelected(selectedClient: any): void {
+ this.filters['client.id'] = selectedClient.id;
+ this.search();
+ }
+
+ clearClientFilter(event: Event, clientSearchClientInput: any): void {
+ clientSearchClientInput.value = '';
+ this.filters['client.id'] = '';
+ this.search();
+ }
+
+ private _filterClients(value: string): any[] {
+ const filterValue = value.toLowerCase();
+
+ return this.clients.filter(client =>
+ client.name?.toLowerCase().includes(filterValue) ||
+ client.ip?.toLowerCase().includes(filterValue) ||
+ client.mac?.toLowerCase().includes(filterValue)
+ );
+ }
+
+ loadClients() {
+ this.http.get(`${this.baseUrl}/clients`).subscribe(
+ (data) => {
+ this.clients = data['hydra:member'];
+ }
+ );
+ }
+
+ deleteHardware(hardware: any): void {
+ const dialogRef = this.dialog.open(DeleteModalComponent, {
+ width: '400px',
+ data: { name: hardware.name }
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ if (result) {
+ const apiUrl = `${this.baseUrl}${hardware['@id']}`;
+
+ this.http.delete(apiUrl).subscribe({
+ next: () => {
+ this.search();
+ this.toastService.success('Hardware deleted successfully');
+ },
+ error: (error) => {
+ this.toastService.error('Error deleting hardware');
+ }
+ });
+ } else {
+ console.log('hardware deletion cancelled');
+ }
+ });
+ }
+
+ onPageChange(event: PageEvent) {
+ this.page = event.pageIndex;
+ this.itemsPerPage = event.pageSize;
+ this.length = event.length;
+ this.search();
+ }
+
+ iniciarTour(): void {
+ this.joyrideService.startTour({
+ steps: [
+ 'titleStep',
+ 'addHardwareStep',
+ 'searchStep',
+ 'tableStep',
+ ],
+ showPrevButton: true,
+ themeColor: '#3f51b5'
+ });
+ }
+
+ viewHardwareCollection(hardware: any): void {
+ // Si el hardware ya tiene la colección cargada, abrir directamente
+ if (hardware.hardwareCollection) {
+ this.openHardwareCollectionModal(hardware);
+ } else {
+ // Si no, obtener los detalles completos del hardware profile
+ this.hardwareProfileService.getHardwareProfile(hardware['@id']).subscribe({
+ next: (hardwareProfile) => {
+ this.openHardwareCollectionModal(hardwareProfile);
+ },
+ error: (error) => {
+ console.error('Error fetching hardware profile details:', error);
+ this.toastService.error('Error al obtener los detalles del perfil de hardware');
+ }
+ });
+ }
+ }
+
+ private openHardwareCollectionModal(hardwareProfile: any): void {
+ this.dialog.open(HardwareCollectionModalComponent, {
+ width: '800px',
+ maxWidth: '90vw',
+ data: {
+ profile: hardwareProfile,
+ profileName: hardwareProfile.description || `Perfil ID: ${hardwareProfile.id}`
+ }
+ });
+ }
+
+}
diff --git a/ogWebconsole/src/app/components/hardware-profile/hardware-profile.service.ts b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.service.ts
new file mode 100644
index 0000000..6c359cb
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-profile/hardware-profile.service.ts
@@ -0,0 +1,28 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { ConfigService } from '@services/config.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class HardwareProfileService {
+ private baseUrl: string;
+ private apiUrl: string;
+
+ constructor(
+ private http: HttpClient,
+ private configService: ConfigService
+ ) {
+ this.baseUrl = this.configService.apiUrl;
+ this.apiUrl = `${this.baseUrl}/hardware-profiles`;
+ }
+
+ getHardwareProfile(id: string): Observable {
+ return this.http.get(`${this.baseUrl}${id}`);
+ }
+
+ getHardwareProfiles(page: number = 1, itemsPerPage: number = 10, filters: { [key: string]: string } = {}): Observable {
+ return this.http.get(`${this.apiUrl}?page=${page}&itemsPerPage=${itemsPerPage}`, { params: filters });
+ }
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-type/hardware-type.component.css b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.css
new file mode 100644
index 0000000..1d928ca
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.css
@@ -0,0 +1,60 @@
+.header-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 10px;
+ border-bottom: 1px solid #ddd;
+ }
+
+ .header-container-title {
+ flex-grow: 1;
+ text-align: left;
+ margin-left: 1em;
+ }
+
+ .calendar-button-row {
+ display: flex;
+ gap: 15px;
+ }
+
+ .lists-container {
+ padding: 16px;
+ }
+
+ .card.unidad-card {
+ height: 100%;
+ box-sizing: border-box;
+ }
+
+ table {
+ width: 100%;
+ }
+
+ .search-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin: 1.5rem 0rem 1.5rem 0rem;
+ box-sizing: border-box;
+ }
+
+ .search-string {
+ flex: 2;
+ padding: 5px;
+ }
+
+ .search-boolean {
+ flex: 1;
+ padding: 5px;
+ }
+
+ .mat-elevation-z8 {
+ box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
+ }
+
+ .paginator-container {
+ display: flex;
+ justify-content: end;
+ margin-bottom: 30px;
+ }
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-type/hardware-type.component.html b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.html
new file mode 100644
index 0000000..23131fe
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.html
@@ -0,0 +1,69 @@
+
+
+
+
+ Buscar nombre de hardware
+
+ search
+ Pulsar 'enter' para buscar
+
+
+ Buscar por tipo
+
+ Servidor
+ Estación de trabajo
+ Portátil
+ Impresora
+ Escáner
+ Router
+ Switch
+ Firewall
+ Otro
+
+
+
+
+
+ {{ column.header }} |
+
+
+
+ {{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
+
+
+
+
+ {{ column.cell(image) }}
+
+ |
+
+
+
+ Acciones |
+
+
+ |
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware-type/hardware-type.component.spec.ts b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.spec.ts
new file mode 100644
index 0000000..06f26bc
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HardwareTypeComponent } from './hardware-type.component';
+
+describe('HardwareTypeComponent', () => {
+ let component: HardwareTypeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [HardwareTypeComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HardwareTypeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/hardware-type/hardware-type.component.ts b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.ts
new file mode 100644
index 0000000..e615a0b
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/hardware-type.component.ts
@@ -0,0 +1,131 @@
+import { Component, signal } from '@angular/core';
+import { MatTableDataSource } from "@angular/material/table";
+import { DatePipe } from "@angular/common";
+import { MatDialog } from "@angular/material/dialog";
+import { HttpClient } from "@angular/common/http";
+import { ToastrService } from "ngx-toastr";
+import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
+import { PageEvent } from "@angular/material/paginator";
+import { JoyrideService } from 'ngx-joyride';
+import { ConfigService } from '@services/config.service';
+
+@Component({
+ selector: 'app-hardware-type',
+ templateUrl: './hardware-type.component.html',
+ styleUrl: './hardware-type.component.css'
+})
+export class HardwareTypeComponent {
+ baseUrl: string;
+ private apiUrl: string;
+ dataSource = new MatTableDataSource();
+ length: number = 0;
+ itemsPerPage: number = 10;
+ page: number = 0;
+ pageSizeOptions: number[] = [5, 10, 20, 40, 100];
+ loading: boolean = false;
+ filters: { [key: string]: string } = {};
+ alertMessage: string | null = null;
+ readonly panelOpenState = signal(false);
+ datePipe: DatePipe = new DatePipe('es-ES');
+ columns = [
+ {
+ columnDef: 'id',
+ header: 'ID',
+ cell: (hardware: any) => `${hardware.id}`,
+ },
+ {
+ columnDef: 'name',
+ header: 'Nombre',
+ cell: (hardware: any) => hardware.name
+ },
+ {
+ columnDef: 'code',
+ header: 'Código',
+ cell: (hardware: any) => hardware.code
+ },
+ {
+ columnDef: 'description',
+ header: 'Descripción',
+ cell: (hardware: any) => hardware.description
+ },
+ {
+ columnDef: 'createdAt',
+ header: 'Fecha de creación',
+ cell: (hardware: any) => `${this.datePipe.transform(hardware.createdAt, 'dd/MM/yyyy hh:mm:ss')}`,
+ }
+ ];
+ displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
+
+ constructor(
+ public dialog: MatDialog,
+ private http: HttpClient,
+ private toastService: ToastrService,
+ private joyrideService: JoyrideService,
+ private configService: ConfigService
+ ) {
+ this.baseUrl = this.configService.apiUrl;
+ this.apiUrl = `${this.baseUrl}/hardware-types`;
+ }
+
+ ngOnInit(): void {
+ this.search();
+ }
+
+ search(): void {
+ this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
+ (data) => {
+ this.dataSource.data = data['hydra:member'];
+ this.length = data['hydra:totalItems'];
+ },
+ (error) => {
+ console.error('Error fetching commands', error);
+ }
+ );
+ }
+
+ deleteHardware(hardware: any): void {
+ const dialogRef = this.dialog.open(DeleteModalComponent, {
+ width: '400px',
+ data: { name: hardware.name }
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ if (result) {
+ const apiUrl = `${this.baseUrl}${hardware['@id']}`;
+
+ this.http.delete(apiUrl).subscribe({
+ next: () => {
+ this.search();
+ this.toastService.success('Hardware deleted successfully');
+ },
+ error: (error) => {
+ this.toastService.error('Error deleting hardware');
+ }
+ });
+ } else {
+ console.log('hardware deletion cancelled');
+ }
+ });
+ }
+
+ onPageChange(event: PageEvent) {
+ this.page = event.pageIndex;
+ this.itemsPerPage = event.pageSize;
+ this.length = event.length;
+ this.search();
+ }
+
+ iniciarTour(): void {
+ this.joyrideService.startTour({
+ steps: [
+ 'titleStep',
+ 'addHardwareStep',
+ 'searchStep',
+ 'tableStep',
+ ],
+ showPrevButton: true,
+ themeColor: '#3f51b5'
+ });
+ }
+
+}
diff --git a/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.css b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.html b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.html
new file mode 100644
index 0000000..10d4aed
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.html
@@ -0,0 +1 @@
+show-details works!
diff --git a/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.spec.ts b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.spec.ts
new file mode 100644
index 0000000..5d719fb
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ShowDetailsComponent } from './show-details.component';
+
+describe('ShowDetailsComponent', () => {
+ let component: ShowDetailsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ShowDetailsComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ShowDetailsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.ts b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.ts
new file mode 100644
index 0000000..830d6f4
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware-type/show-details/show-details.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-show-details',
+ templateUrl: './show-details.component.html',
+ styleUrl: './show-details.component.css'
+})
+export class ShowDetailsComponent {
+
+}
diff --git a/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.css b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.html b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.html
new file mode 100644
index 0000000..a46ec4c
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.html
@@ -0,0 +1 @@
+create-hardware works!
diff --git a/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.spec.ts b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.spec.ts
new file mode 100644
index 0000000..deb858c
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CreateHardwareComponent } from './create-hardware.component';
+
+describe('CreateHardwareComponent', () => {
+ let component: CreateHardwareComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [CreateHardwareComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(CreateHardwareComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.ts b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.ts
new file mode 100644
index 0000000..8c61d79
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/create-hardware/create-hardware.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-create-hardware',
+ templateUrl: './create-hardware.component.html',
+ styleUrl: './create-hardware.component.css'
+})
+export class CreateHardwareComponent {
+
+}
diff --git a/ogWebconsole/src/app/components/hardware/hardware.component.css b/ogWebconsole/src/app/components/hardware/hardware.component.css
new file mode 100644
index 0000000..1d928ca
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/hardware.component.css
@@ -0,0 +1,60 @@
+.header-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 10px;
+ border-bottom: 1px solid #ddd;
+ }
+
+ .header-container-title {
+ flex-grow: 1;
+ text-align: left;
+ margin-left: 1em;
+ }
+
+ .calendar-button-row {
+ display: flex;
+ gap: 15px;
+ }
+
+ .lists-container {
+ padding: 16px;
+ }
+
+ .card.unidad-card {
+ height: 100%;
+ box-sizing: border-box;
+ }
+
+ table {
+ width: 100%;
+ }
+
+ .search-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin: 1.5rem 0rem 1.5rem 0rem;
+ box-sizing: border-box;
+ }
+
+ .search-string {
+ flex: 2;
+ padding: 5px;
+ }
+
+ .search-boolean {
+ flex: 1;
+ padding: 5px;
+ }
+
+ .mat-elevation-z8 {
+ box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
+ }
+
+ .paginator-container {
+ display: flex;
+ justify-content: end;
+ margin-bottom: 30px;
+ }
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware/hardware.component.html b/ogWebconsole/src/app/components/hardware/hardware.component.html
new file mode 100644
index 0000000..356d9b8
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/hardware.component.html
@@ -0,0 +1,69 @@
+
+
+
+
+ Buscar nombre de hardware
+
+ search
+ Pulsar 'enter' para buscar
+
+
+ Buscar por tipo
+
+ Servidor
+ Estación de trabajo
+ Portátil
+ Impresora
+ Escáner
+ Router
+ Switch
+ Firewall
+ Otro
+
+
+
+
+
+ {{ column.header }} |
+
+
+
+ {{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
+
+
+
+
+ {{ column.cell(image) }}
+
+ |
+
+
+
+ Acciones |
+
+
+ |
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/hardware/hardware.component.spec.ts b/ogWebconsole/src/app/components/hardware/hardware.component.spec.ts
new file mode 100644
index 0000000..b6616bd
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/hardware.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HardwareComponent } from './hardware.component';
+
+describe('HardwareComponent', () => {
+ let component: HardwareComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [HardwareComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HardwareComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ogWebconsole/src/app/components/hardware/hardware.component.ts b/ogWebconsole/src/app/components/hardware/hardware.component.ts
new file mode 100644
index 0000000..be906b3
--- /dev/null
+++ b/ogWebconsole/src/app/components/hardware/hardware.component.ts
@@ -0,0 +1,127 @@
+import { Component, signal } from '@angular/core';
+import { MatTableDataSource } from "@angular/material/table";
+import { DatePipe } from "@angular/common";
+import { MatDialog } from "@angular/material/dialog";
+import { HttpClient } from "@angular/common/http";
+import { ToastrService } from "ngx-toastr";
+import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
+import { PageEvent } from "@angular/material/paginator";
+import { JoyrideService } from 'ngx-joyride';
+import { ConfigService } from '@services/config.service';
+import { CreateHardwareComponent } from './create-hardware/create-hardware.component';
+
+@Component({
+ selector: 'app-hardware',
+ templateUrl: './hardware.component.html',
+ styleUrl: './hardware.component.css'
+})
+export class HardwareComponent {
+ baseUrl: string;
+ private apiUrl: string;
+ dataSource = new MatTableDataSource();
+ length: number = 0;
+ itemsPerPage: number = 10;
+ page: number = 0;
+ pageSizeOptions: number[] = [5, 10, 20, 40, 100];
+ loading: boolean = false;
+ filters: { [key: string]: string } = {};
+ alertMessage: string | null = null;
+ readonly panelOpenState = signal(false);
+ datePipe: DatePipe = new DatePipe('es-ES');
+ columns = [
+ {
+ columnDef: 'id',
+ header: 'ID',
+ cell: (hardware: any) => `${hardware.id}`,
+ },
+ {
+ columnDef: 'name',
+ header: 'Nombre',
+ cell: (hardware: any) => hardware.name
+ },
+ {
+ columnDef: 'type',
+ header: 'Tipo',
+ cell: (hardware: any) => hardware.type?.name || 'N/A'
+ },
+ {
+ columnDef: 'createdAt',
+ header: 'Fecha de creación',
+ cell: (hardware: any) => `${this.datePipe.transform(hardware.createdAt, 'dd/MM/yyyy hh:mm:ss')}`,
+ }
+ ];
+ displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
+
+ constructor(
+ public dialog: MatDialog,
+ private http: HttpClient,
+ private toastService: ToastrService,
+ private joyrideService: JoyrideService,
+ private configService: ConfigService
+ ) {
+ this.baseUrl = this.configService.apiUrl;
+ this.apiUrl = `${this.baseUrl}/hardware`;
+ }
+
+ ngOnInit(): void {
+ this.search();
+ }
+
+ search(): void {
+ this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
+ (data) => {
+ this.dataSource.data = data['hydra:member'];
+ this.length = data['hydra:totalItems'];
+ },
+ (error) => {
+ console.error('Error fetching commands', error);
+ }
+ );
+ }
+
+ deleteHardware(hardware: any): void {
+ const dialogRef = this.dialog.open(DeleteModalComponent, {
+ width: '400px',
+ data: { name: hardware.name }
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ if (result) {
+ const apiUrl = `${this.baseUrl}${hardware['@id']}`;
+
+ this.http.delete(apiUrl).subscribe({
+ next: () => {
+ this.search();
+ this.toastService.success('Hardware deleted successfully');
+ },
+ error: (error) => {
+ this.toastService.error('Error deleting hardware');
+ }
+ });
+ } else {
+ console.log('hardware deletion cancelled');
+ }
+ });
+ }
+
+ onPageChange(event: PageEvent) {
+ this.page = event.pageIndex;
+ this.itemsPerPage = event.pageSize;
+ this.length = event.length;
+ this.search();
+ }
+
+ iniciarTour(): void {
+ this.joyrideService.startTour({
+ steps: [
+ 'titleStep',
+ 'addHardwareStep',
+ 'searchStep',
+ 'tableStep',
+ ],
+ showPrevButton: true,
+ themeColor: '#3f51b5'
+ });
+ }
+
+}
diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html
index 3272552..0d6fc28 100644
--- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html
+++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html
@@ -68,7 +68,6 @@
-
@@ -109,7 +108,6 @@
-
@@ -134,6 +132,40 @@
+
+
+
+ computer
+ {{ 'hardware' | translate }}
+
+
+
+
+
+
+ list
+ {{ 'hardwareList' | translate }}
+
+
+
+
+ folder_shared
+ {{ 'hardwareProfiles' | translate }}
+
+
+
+
+ computer
+ {{ 'hardwareTypes' | translate }}
+
+
+
+
+
diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts b/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts
index 7abcc59..7b8055b 100644
--- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts
+++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.ts
@@ -17,7 +17,8 @@ export class SidebarComponent {
showOgDhcpSub: boolean = false;
showCommandSub: boolean = false;
showSoftwareSub: boolean = false;
-
+ showHardwareSub: boolean = false;
+
toggleOgBootSub() {
this.showOgBootSub = !this.showOgBootSub;
}
@@ -31,6 +32,10 @@ export class SidebarComponent {
this.showSoftwareSub = !this.showSoftwareSub;
}
+ toggleHardwareSub() {
+ this.showHardwareSub = !this.showHardwareSub;
+ }
+
onItemClick() {
if (this.isVisible && this.sidebarMode === 'over') {
this.closeSidebar.emit();
diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json
index b3d5f92..4278ed1 100644
--- a/ogWebconsole/src/locale/en.json
+++ b/ogWebconsole/src/locale/en.json
@@ -582,5 +582,16 @@
"online": "Online",
"busy": "Busy",
"cancelTask": "Cancel task",
- "clickStatsToFilter": "Click on the statistics cards to filter the traces"
+ "clickStatsToFilter": "Click on the statistics cards to filter the traces",
+ "hardware": "Hardware",
+ "manageHardware": "Manage Hardware",
+ "TOOLTIP_HARDWARE": "Manage hardware configurations",
+ "hardwareList": "List",
+ "TOOLTIP_HARDWARE_LIST": "View list of available hardware",
+ "hardwareProfiles": "Profiles",
+ "TOOLTIP_HARDWARE_PROFILES": "Manage hardware profiles",
+ "hardwareTypes": "Types",
+ "TOOLTIP_HARDWARE_TYPES": "Manage hardware types",
+ "manageHardwareProfiles": "Manage hardware profiles",
+ "manageHardwareTypes": "Manage hardware types"
}
diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json
index 3ea8ea5..bfe04d7 100644
--- a/ogWebconsole/src/locale/es.json
+++ b/ogWebconsole/src/locale/es.json
@@ -587,6 +587,17 @@
"online": "Online",
"busy": "Ocupado",
"cancelTask": "Cancelar tarea",
- "clickStatsToFilter": "Haz clic en las tarjetas de estadísticas para filtrar las trazas"
+ "clickStatsToFilter": "Haz clic en las tarjetas de estadísticas para filtrar las trazas",
+ "hardware": "Hardware",
+ "manageHardware": "Administrar Hardware",
+ "TOOLTIP_HARDWARE": "Gestionar configuraciones de hardware",
+ "hardwareList": "Listado",
+ "TOOLTIP_HARDWARE_LIST": "Ver lista de hardware disponible",
+ "hardwareProfiles": "Perfiles",
+ "TOOLTIP_HARDWARE_PROFILES": "Gestionar perfiles de hardware",
+ "hardwareTypes": "Tipos",
+ "TOOLTIP_HARDWARE_TYPES": "Gestionar tipos de hardware",
+ "manageHardwareProfiles": "Administrar perfiles de hardware",
+ "manageHardwareTypes": "Administrar tipos de hardware"
}
\ No newline at end of file