-
+
+
Añadir Cliente
+
+
+
+
+ Nombre
+
+
-
-
Añadir múltiples clientes
-
-
-
-
o añadelos manualmente:
-
-
-
-
-
+
+ OgLive
+
+
+ {{ oglive.name }}
+
+
+
-
0">Clientes importados:
-
-
+
+ Controlador de red
+
+
+ {{ type.name }}
+
+
+
-
+
+ MAC
+ Ejemplo: 00:11:22:33:44:55
+
+ Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55
+
-
- Añadir un cliente
-
-
-
-
+
+ {{ 'menuLabel' | translate }}
+
+
+ {{ menu.name }}
+
+
+ {{ 'menuError' | translate }}
+
+
-
-
-
+
+
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts
index d7f7e45..dbc6f6b 100644
--- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts
+++ b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts
@@ -5,7 +5,6 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ToastrService } from 'ngx-toastr';
import { DataService } from '../../../services/data.service';
-import * as Papa from 'papaparse';
@Component({
selector: 'app-create-client',
@@ -158,62 +157,6 @@ export class CreateClientComponent implements OnInit {
);
}
- onFileUpload(event: any): void {
- const file = event.target.files[0];
- if (file) {
- const reader = new FileReader();
-
- reader.onload = (e: any) => {
- const textData = e.target.result;
- const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g;
- let match;
- const clients = [];
-
- while ((match = regex.exec(textData)) !== null) {
- clients.push({
- name: match[1],
- mac: match[2],
- ip: match[3]
- });
- }
-
- if (clients.length > 0) {
- this.uploadedClients = clients;
- this.toastService.success('Archivo cargado correctamente, los datos están listos para enviarse.', 'Éxito');
- this.showTextarea = false;
- } else {
- this.toastService.error('No se encontraron datos válidos', 'Error');
- this.showTextarea = true;
- }
- };
-
- reader.readAsText(file);
- }
- }
-
- onTextarea(text: string): void {
- const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g;
- let match;
- const clients = [];
-
- while ((match = regex.exec(text)) !== null) {
- clients.push({
- name: match[1],
- mac: match[2],
- ip: match[3]
- });
- }
-
- if (clients.length > 0) {
- this.uploadedClients = clients;
- this.toastService.success('Datos cargados correctamente, los datos están listos para enviarse.', 'Éxito');
- this.showTextarea = false;
- } else {
- this.toastService.error('No se encontraron datos válidos', 'Error');
- this.showTextarea = true;
- }
- }
-
onSubmit(): void {
if (this.isSingleClientForm) {
if (this.clientForm.valid) {
@@ -266,10 +209,6 @@ export class CreateClientComponent implements OnInit {
}
}
- toggleClientForm(): void {
- this.isSingleClientForm = !this.isSingleClientForm;
- }
-
onNoClick(): void {
this.dialogRef.close();
}
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css
new file mode 100644
index 0000000..64a2fb8
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css
@@ -0,0 +1,158 @@
+.create-client-container {
+ display: flex;
+ flex-direction: column;
+ padding: 16px;
+}
+
+h1, h3, h4 {
+ margin: 0 0 16px;
+ color: #333;
+ font-weight: 600;
+}
+
+h1 {
+ font-size: 20px;
+}
+
+h3 {
+ font-size: 18px;
+}
+
+h4 {
+ font-size: 16px;
+ margin-top: 16px;
+}
+
+.inputs-container {
+ display: flex;
+ gap: 24px;
+ margin-top: 16px;
+}
+
+.mat-dialog-content {
+ flex: 1;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ min-width: 600px;
+ max-width: 90vw;
+ width: 800px;
+}
+
+.create-multiple-client-container {
+ flex: 1;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.client-form {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 16px;
+}
+
+.form-field {
+ width: 100%;
+}
+
+.mat-form-field {
+ width: 100%;
+}
+
+.scrollable-table {
+ max-height: 200px;
+ overflow-y: auto;
+ margin-top: 16px;
+ border: 1px solid #ddd;
+ border-radius: 8px;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+th, td {
+ text-align: left;
+ padding: 8px;
+ border-bottom: 1px solid #ddd;
+}
+
+th {
+ background-color: #f1f1f1;
+ font-weight: bold;
+}
+
+tr:hover {
+ background-color: #f9f9f9;
+}
+
+button {
+ margin-right: 8px;
+}
+
+button:last-child {
+ margin-right: 0;
+}
+
+.mat-dialog-actions {
+ margin-top: 16px;
+ display: flex;
+ justify-content: space-between;
+}
+
+button.mat-raised-button {
+ text-transform: none;
+ font-weight: 600;
+}
+
+.loading-spinner {
+ margin: 16px auto;
+ display: block;
+}
+
+.toggle-button {
+ background: none;
+ border: none;
+ color: #007BFF;
+ cursor: pointer;
+ font-size: 14px;
+ text-decoration: underline;
+}
+
+.toggle-button:hover {
+ text-decoration: none;
+}
+
+.mat-divider {
+ margin: 0 16px;
+}
+
+.upload-container {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+}
+
+input[type="file"] {
+ display: none;
+}
+
+@media (max-width: 768px) {
+ .inputs-container {
+ flex-direction: column;
+ gap: 16px;
+ }
+
+ .mat-dialog-content, .create-multiple-client-container {
+ padding: 12px;
+ }
+
+ .scrollable-table {
+ max-height: 150px;
+ }
+}
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html
new file mode 100644
index 0000000..fd678aa
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html
@@ -0,0 +1,58 @@
+
+
Añadir multiples clientes
+
+
+
+
+
+
+
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts
new file mode 100644
index 0000000..e07eaeb
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts
@@ -0,0 +1,138 @@
+import {Component, Inject, OnInit, Optional} from '@angular/core';
+import {MatDialogRef} from "@angular/material/dialog";
+import {HttpClient} from "@angular/common/http";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {ToastrService} from "ngx-toastr";
+
+@Component({
+ selector: 'app-create-multiple-client',
+ templateUrl: './create-multiple-client.component.html',
+ styleUrl: './create-multiple-client.component.css'
+})
+export class CreateMultipleClientComponent implements OnInit{
+ baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
+ parentUnits: any[] = [];
+ uploadedClients: any[] = [];
+ loading: boolean = false;
+ displayedColumns: string[] = ['name', 'ip', 'mac'];
+ showTextarea: boolean = true;
+ organizationalUnit: any;
+
+ constructor(
+ @Optional() private dialogRef: MatDialogRef
,
+ private http: HttpClient,
+ private snackBar: MatSnackBar,
+ private toastService: ToastrService
+ ) {}
+
+ ngOnInit(): void {
+ this.loadParentUnits();
+ }
+
+ loadParentUnits(): void {
+ this.loading = true;
+ const url = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=10000`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.parentUnits = response['hydra:member'];
+ this.loading = false;
+ },
+ error => {
+ console.error('Error fetching parent units:', error);
+ this.loading = false;
+ }
+ );
+ }
+
+ setOrganizationalUnit(organizationalUnit: any): void {
+ console.log('Organizational unit selected:', organizationalUnit.value);
+ this.organizationalUnit = organizationalUnit.value;
+ }
+
+ onFileUpload(event: any): void {
+ const file = event.target.files[0];
+ if (file) {
+ const reader = new FileReader();
+
+ reader.onload = (e: any) => {
+ const textData = e.target.result;
+ const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g;
+ let match;
+ const clients = [];
+
+ while ((match = regex.exec(textData)) !== null) {
+ clients.push({
+ name: match[1],
+ mac: match[2],
+ ip: match[3]
+ });
+ }
+
+ if (clients.length > 0) {
+ this.uploadedClients = clients;
+ this.toastService.success('Archivo cargado correctamente, los datos están listos para enviarse.', 'Éxito');
+ this.showTextarea = false;
+ } else {
+ this.toastService.error('No se encontraron datos válidos', 'Error');
+ this.showTextarea = true;
+ }
+ };
+
+ reader.readAsText(file);
+ }
+ }
+
+ onTextarea(text: string): void {
+ const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g;
+ let match;
+ const clients = [];
+
+ while ((match = regex.exec(text)) !== null) {
+ clients.push({
+ name: match[1],
+ mac: match[2],
+ ip: match[3]
+ });
+ }
+
+ if (clients.length > 0) {
+ this.uploadedClients = clients;
+ this.toastService.success('Datos cargados correctamente, los datos están listos para enviarse.', 'Éxito');
+ this.showTextarea = false;
+ } else {
+ this.toastService.error('No se encontraron datos válidos', 'Error');
+ this.showTextarea = true;
+ }
+ }
+
+ onSubmit(): void {
+ if (this.uploadedClients.length > 0) {
+ this.uploadedClients.forEach(client => {
+ const formData = {
+ organizationalUnit: this.organizationalUnit,
+ name: client.name || null,
+ mac: client.mac || null,
+ ip: client.ip || null,
+ };
+
+ this.http.post(`${this.baseUrl}/clients`, formData).subscribe(
+ response => {
+ this.toastService.success(`Cliente ${client.name} creado exitosamente`, 'Éxito');
+ },
+ error => {
+ this.toastService.error(error.error['hydra:description'], `Error al crear el cliente ${client.name}`);
+ }
+ );
+ });
+ this.uploadedClients = [];
+ this.dialogRef.close();
+ } else {
+ this.toastService.error('No hay clientes cargados para añadir', 'Error');
+ }
+ }
+
+ onNoClick(): void {
+ this.dialogRef.close();
+ }
+}
diff --git a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.html b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.html
index b423d7b..c15e7a8 100644
--- a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.html
+++ b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.html
@@ -27,7 +27,7 @@
{{ 'defaultMenuLabel' | translate }}
diff --git a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.spec.ts b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.spec.ts
deleted file mode 100644
index ec55d04..0000000
--- a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CreateMenuComponent } from './create-menu.component';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { MatAutocompleteModule } from '@angular/material/autocomplete';
-import { MatButtonModule } from '@angular/material/button';
-import { MatCardModule } from '@angular/material/card';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatDividerModule } from '@angular/material/divider';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatIconModule } from '@angular/material/icon';
-import { MatInputModule } from '@angular/material/input';
-import { MatListModule } from '@angular/material/list';
-import { MatMenuModule } from '@angular/material/menu';
-import { MatPaginatorModule } from '@angular/material/paginator';
-import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
-import { MatSelectModule } from '@angular/material/select';
-import { MatTableModule } from '@angular/material/table';
-import { MatTabsModule } from '@angular/material/tabs';
-import { MatTooltipModule } from '@angular/material/tooltip';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { TranslateModule } from '@ngx-translate/core';
-import { JoyrideModule } from 'ngx-joyride';
-import { ToastrModule } from 'ngx-toastr';
-
-describe('CreateMenuComponent', () => {
- let component: CreateMenuComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [CreateMenuComponent],
- imports: [
- HttpClientTestingModule,
- ToastrModule.forRoot(),
- BrowserAnimationsModule,
- MatDividerModule,
- MatFormFieldModule,
- MatInputModule,
- MatIconModule,
- MatButtonModule,
- MatTableModule,
- MatPaginatorModule,
- MatTooltipModule,
- FormsModule,
- ReactiveFormsModule,
- MatProgressSpinnerModule,
- MatDialogModule,
- MatSelectModule,
- MatTabsModule,
- MatAutocompleteModule,
- MatListModule,
- MatCardModule,
- MatMenuModule,
- TranslateModule.forRoot(),
- JoyrideModule.forRoot(),
- ],
- providers: [
- {
- provide: MatDialogRef,
- useValue: {},
- },
- {
- provide: MAT_DIALOG_DATA,
- useValue: {},
- },
- ],
- }).compileComponents();
-
- fixture = TestBed.createComponent(CreateMenuComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts
index bb33a3c..0a591a1 100644
--- a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts
+++ b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts
@@ -1,9 +1,9 @@
-import {Component, Inject} from '@angular/core';
+import {Component, Inject, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {HttpClient} from "@angular/common/http";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {ToastrService} from "ngx-toastr";
-import {DataService} from "../../images/data.service";
+import {DataService} from "../data.service";
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
@Component({
@@ -11,7 +11,7 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
templateUrl: './create-menu.component.html',
styleUrl: './create-menu.component.css'
})
-export class CreateMenuComponent {
+export class CreateMenuComponent implements OnInit{
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
menuForm: FormGroup;
menuId: string | null = null;
@@ -52,7 +52,7 @@ export class CreateMenuComponent {
}
load(): void {
- this.dataService.getImage(this.data).subscribe({
+ this.dataService.getMenu(this.data).subscribe({
next: (response) => {
this.menuForm = this.fb.group({
name: [response.name, Validators.required],
diff --git a/ogWebconsole/src/app/components/menus/data.service.ts b/ogWebconsole/src/app/components/menus/data.service.ts
new file mode 100644
index 0000000..8973388
--- /dev/null
+++ b/ogWebconsole/src/app/components/menus/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}/menus?page=1&itemsPerPage=1000`;
+
+ constructor(private http: HttpClient) {}
+
+ getMenus(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);
+ })
+ );
+ }
+
+ getMenu(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 menus', error);
+ return throwError(error);
+ })
+ );
+ }
+
+
+}
diff --git a/ogWebconsole/src/app/components/menus/menus.component.ts b/ogWebconsole/src/app/components/menus/menus.component.ts
index 364a4c8..29b1fba 100644
--- a/ogWebconsole/src/app/components/menus/menus.component.ts
+++ b/ogWebconsole/src/app/components/menus/menus.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {MatTableDataSource} from "@angular/material/table";
import {DatePipe} from "@angular/common";
import {MatDialog} from "@angular/material/dialog";
@@ -14,7 +14,7 @@ import {CreateMenuComponent} from "./create-menu/create-menu.component";
templateUrl: './menus.component.html',
styleUrl: './menus.component.css'
})
-export class MenusComponent {
+export class MenusComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
dataSource = new MatTableDataSource();
length: number = 0;
@@ -27,32 +27,32 @@ export class MenusComponent {
{
columnDef: 'id',
header: 'Id',
- cell: (repository: any) => `${repository.id}`
+ cell: (menu: any) => `${menu.id}`
},
{
columnDef: 'name',
header: 'Nombre de menú',
- cell: (repository: any) => `${repository.name}`
+ cell: (menu: any) => `${menu.name}`
},
{
columnDef: 'publicUrl',
header: 'Url pública',
- cell: (repository: any) => `${this.baseUrl}/menu/${repository.publicUrl}`
+ cell: (menu: any) => `${this.baseUrl}/menu/${menu.publicUrl}`
},
{
columnDef: 'isDefault',
header: 'Por defecto',
- cell: (repository: any) => `${repository.isDefault}`
+ cell: (menu: any) => `${menu.isDefault}`
},
{
columnDef: 'resolution',
header: 'Resolución',
- cell: (repository: any) => `${repository.resolution}`
+ cell: (menu: any) => `${menu.resolution}`
},
{
columnDef: 'createdAt',
header: 'Fecha de creación',
- cell: (repository: any) => `${this.datePipe.transform(repository.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
+ cell: (menu: any) => `${this.datePipe.transform(menu.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
}
];
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];