diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index 009b857..277d682 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -35,9 +35,7 @@ import { GroupsComponent } from './components/groups/groups.component';
import { MatDividerModule } from '@angular/material/divider';
import { MatStepperModule } from '@angular/material/stepper';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
-import { CreateClientComponent } from './components/groups/shared/clients/create-client/create-client.component';
import { DeleteModalComponent } from './shared/delete_modal/delete-modal/delete-modal.component';
-import { EditClientComponent } from './components/groups/shared/clients/edit-client/edit-client.component';
import { ClassroomViewComponent } from './components/groups/shared/classroom-view/classroom-view.component';
import { MatProgressSpinner } from "@angular/material/progress-spinner";
import { MatProgressBarModule } from '@angular/material/progress-bar';
@@ -130,6 +128,7 @@ import { CreateSubnetComponent } from "./components/ogdhcp/create-subnet/create-
import { AddClientsToSubnetComponent } from "./components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component";
import { ShowClientsComponent } from './components/ogdhcp/show-clients/show-clients.component';
import { OperationResultDialogComponent } from './components/ogdhcp/operation-result-dialog/operation-result-dialog.component';
+import { ManageClientComponent } from './components/groups/shared/clients/manage-client/manage-client.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
}
@@ -150,9 +149,8 @@ export function HttpLoaderFactory(http: HttpClient) {
AddRoleModalComponent,
ChangePasswordModalComponent,
GroupsComponent,
- CreateClientComponent,
+ ManageClientComponent,
DeleteModalComponent,
- EditClientComponent,
ClassroomViewComponent,
ClientViewComponent,
ShowOrganizationalUnitComponent,
@@ -215,7 +213,7 @@ export function HttpLoaderFactory(http: HttpClient) {
ManageOrganizationalUnitComponent,
BackupImageComponent,
ShowClientsComponent,
- OperationResultDialogComponent,
+ OperationResultDialogComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts
index 2865375..e302018 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts
+++ b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts
@@ -5,8 +5,8 @@ import {MatTableDataSource} from "@angular/material/table";
import {PartitionAssistantComponent} from "./partition-assistant/partition-assistant.component";
import {MatDialog} from "@angular/material/dialog";
import {Router} from "@angular/router";
-import {EditClientComponent} from "../../shared/clients/edit-client/edit-client.component";
import {ToastrService} from "ngx-toastr";
+import { ManageClientComponent } from "../../shared/clients/manage-client/manage-client.component";
interface ClientInfo {
property: string;
@@ -198,7 +198,7 @@ export class ClientMainViewComponent implements OnInit {
onEditClick(event: MouseEvent, uuid: string): void {
event.stopPropagation();
- const dialogRef = this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' } );
+ const dialogRef = this.dialog.open(ManageClientComponent, { data: { uuid }, width: '900px' } );
dialogRef.afterClosed().subscribe();
}
diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts
index e98f94c..c953296 100644
--- a/ogWebconsole/src/app/components/groups/groups.component.ts
+++ b/ogWebconsole/src/app/components/groups/groups.component.ts
@@ -10,9 +10,7 @@ import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'
import { Subscription } from 'rxjs';
import { DataService } from './services/data.service';
import { UnidadOrganizativa, Client, TreeNode, FlatNode, Command } from './model/model';
-import { CreateClientComponent } from './shared/clients/create-client/create-client.component';
import { ManageOrganizationalUnitComponent } from './shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component';
-import { EditClientComponent } from './shared/clients/edit-client/edit-client.component';
import { ShowOrganizationalUnitComponent } from './shared/organizational-units/show-organizational-unit/show-organizational-unit.component';
import { LegendComponent } from './shared/legend/legend.component';
import { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/delete-modal.component';
@@ -22,6 +20,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { CreateMultipleClientComponent } from "./shared/clients/create-multiple-client/create-multiple-client.component";
import { SelectionModel } from "@angular/cdk/collections";
+import { ManageClientComponent } from "./shared/clients/manage-client/manage-client.component";
enum NodeType {
OrganizationalUnit = 'organizational-unit',
@@ -392,7 +391,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
addClient(event: MouseEvent, organizationalUnit: TreeNode | null = null): void {
event.stopPropagation();
const targetNode = organizationalUnit || this.selectedNode;
- const dialogRef = this.dialog.open(CreateClientComponent, {
+ const dialogRef = this.dialog.open(ManageClientComponent, {
data: { organizationalUnit: targetNode },
width: '900px',
});
@@ -443,7 +442,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
const dialogRef = node?.type !== NodeType.Client
? this.dialog.open(ManageOrganizationalUnitComponent, { data: { uuid }, width: '900px' })
- : this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' });
+ : this.dialog.open(ManageClientComponent, { data: { uuid }, width: '900px' });
dialogRef.afterClosed().subscribe(() => {
if (node) {
@@ -510,7 +509,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
const selectedClientsBeforeEdit = this.selection.selected.map(client => client.uuid);
const dialogRef = type !== NodeType.Client
? this.dialog.open(ManageOrganizationalUnitComponent, { data: { uuid }, width: '900px' })
- : this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' });
+ : this.dialog.open(ManageClientComponent, { data: { uuid }, width: '900px' });
dialogRef.afterClosed().subscribe(() => {
this.refreshData(this.selectedNode?.id, selectedClientsBeforeEdit);
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css
deleted file mode 100644
index b3c6559..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.css
+++ /dev/null
@@ -1,51 +0,0 @@
-h1 {
- text-align: center;
- font-family: 'Roboto', sans-serif;
- font-weight: 400;
- color: #3f51b5;
- margin-bottom: 20px;
- margin-top: 20px;
-}
-
-.form-field {
- width: 100%;
-}
-
-.mat-dialog-content {
- padding: 15px 50px 15px 50px;
-}
-
-mat-option .unit-name {
- display: block;
-}
-
-mat-option .unit-path {
- display: block;
- font-size: 0.8em;
- color: gray;
-}
-
-.loading-spinner {
- display: block;
- margin: 0 auto;
- align-items: center;
- justify-content: center;
-}
-
-.create-client-container {
- position: relative;
-}
-
-.grid-form {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- column-gap: 20px;
- row-gap: 20px;
-}
-
-.action-container {
- display: flex;
- justify-content: flex-end;
- gap: 1em;
- padding: 1.5em;
-}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html
deleted file mode 100644
index 3cbfd51..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.html
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
Añadir Cliente
-
-
-
-
-
-
-
\ No newline at end of file
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
deleted file mode 100644
index c807b1c..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/create-client/create-client.component.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Component, Inject, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-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';
-
-@Component({
- selector: 'app-create-client',
- templateUrl: './create-client.component.html',
- styleUrls: ['./create-client.component.css']
-})
-export class CreateClientComponent implements OnInit {
- baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
- clientForm!: FormGroup;
- parentUnits: any[] = [];
- parentUnitsWithPaths: { id: string, name: string, path: string }[] = [];
- hardwareProfiles: any[] = [];
- ogLives: any[] = [];
- menus: any[] = [];
- templates: any[] = [];
- repositories: any[] = [];
- loading: boolean = false;
- displayedColumns: string[] = ['name', 'ip'];
- protected netifaceTypes = [
- { name: 'Eth0', value: 'eth0' },
- { name: 'Eth1', value: 'eth1' },
- { name: 'Eth2', value: 'eth2' }
- ];
- protected netDriverTypes = [
- { name: 'Generic', value: 'generic' }
- ];
-
- constructor(
- private fb: FormBuilder,
- private dialogRef: MatDialogRef,
- private http: HttpClient,
- private snackBar: MatSnackBar,
- private toastService: ToastrService,
- private dataService: DataService,
- @Inject(MAT_DIALOG_DATA) public data: any
- ) {}
-
- ngOnInit(): void {
- this.loadParentUnits();
- this.loadHardwareProfiles();
- this.loadOgLives();
- this.loadPxeTemplates();
- this.loadRepositories();
- this.loadMenus()
- this.initForm();
- }
-
- initForm(): void {
- this.clientForm = this.fb.group({
- organizationalUnit: [
- this.data.organizationalUnit ? this.data.organizationalUnit['@id'] : null,
- Validators.required
- ],
- name: ['', Validators.required],
- serialNumber: [''],
- netiface: null,
- netDriver: null,
- mac: ['', Validators.required],
- ip: ['', Validators.required],
- template: [null],
- hardwareProfile: [null],
- ogLive: [null],
- repository: [null],
- menu: [null]
- });
- }
-
- 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.parentUnitsWithPaths = this.parentUnits.map(unit => ({
- id: unit['@id'],
- name: unit.name,
- path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits),
- repository: unit.networkSettings?.repository?.['@id'],
- hardwareProfile: unit.networkSettings?.hardwareProfile?.['@id'],
- ogLive: unit.networkSettings?.ogLive?.['@id'],
- menu: unit.networkSettings?.menu?.['@id']
- }));
-
- // 🚀 Ahora que los datos están listos, aplicamos la configuración inicial
- const initialUnitId = this.clientForm.get('organizationalUnit')?.value;
- if (initialUnitId) {
- this.setOrganizationalUnitDefaults(initialUnitId);
- }
-
- this.loading = false;
- },
- error => {
- console.error('Error fetching parent units:', error);
- this.loading = false;
- }
- );
- }
-
- getSelectedParentName(): string | undefined {
- const parentId = this.clientForm.get('organizationalUnit')?.value;
- return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;
- }
-
- loadHardwareProfiles(): void {
- this.dataService.getHardwareProfiles().subscribe(
- (data: any[]) => {
- this.hardwareProfiles = data;
- this.loading = false;
- },
- error => {
- console.error('Error fetching hardware profiles:', error);
- this.loading = false;
- }
- );
- }
-
- loadOgLives(): void {
- const url = `${this.baseUrl}/og-lives?page=1&itemsPerPage=30`;
-
- this.http.get(url).subscribe(
- response => {
- this.ogLives = response['hydra:member'];
- },
- error => {
- console.error('Error fetching ogLives:', error);
- }
- );
- }
-
- loadPxeTemplates(): void {
- const url = `${this.baseUrl}/pxe-templates?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.templates = response['hydra:member'];
- },
- error => {
- console.error('Error fetching PXE templates:', error);
- }
- );
- }
-
- loadMenus(): void {
- const url = `${this.baseUrl}/menus?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.menus = response['hydra:member'];
- },
- error => {
- console.error('Error fetching menus:', error);
- }
- );
- }
-
- loadRepositories(): void {
- const url = `${this.baseUrl}/image-repositories?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.repositories = response['hydra:member'];
- },
- error => {
- console.error('Error fetching ogLives:', error);
- }
- );
- }
-
- onParentChange(event: any): void {
- this.setOrganizationalUnitDefaults(event.value);
- }
-
- setOrganizationalUnitDefaults(unitId: string): void {
- const selectedUnit: any = this.parentUnitsWithPaths.find(unit => unit.id === unitId);
- if (selectedUnit) {
- this.clientForm.patchValue({
- repository: selectedUnit.repository || null,
- hardwareProfile: selectedUnit.hardwareProfile || null,
- ogLive: selectedUnit.ogLive || null,
- menu: selectedUnit.menu || null
- });
- }
- }
-
- onSubmit(): void {
- if (this.clientForm.valid) {
- const formData = this.clientForm.value;
-
- this.http.post(`${this.baseUrl}/clients`, formData).subscribe(
- (response) => {
- this.toastService.success('Cliente creado exitosamente', 'Éxito');
- this.dialogRef.close({
- client: response,
- organizationalUnit: formData.organizationalUnit,
- });
- },
- (error) => {
- this.toastService.error(error.error['hydra:description'], 'Error al crear el cliente');
- }
- );
- } else {
- this.toastService.error('Formulario inválido. Por favor, revise los campos obligatorios.', 'Error');
- }
- }
-
- onNoClick(): void {
- this.dialogRef.close();
- }
-}
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.css
deleted file mode 100644
index b3c6559..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.css
+++ /dev/null
@@ -1,51 +0,0 @@
-h1 {
- text-align: center;
- font-family: 'Roboto', sans-serif;
- font-weight: 400;
- color: #3f51b5;
- margin-bottom: 20px;
- margin-top: 20px;
-}
-
-.form-field {
- width: 100%;
-}
-
-.mat-dialog-content {
- padding: 15px 50px 15px 50px;
-}
-
-mat-option .unit-name {
- display: block;
-}
-
-mat-option .unit-path {
- display: block;
- font-size: 0.8em;
- color: gray;
-}
-
-.loading-spinner {
- display: block;
- margin: 0 auto;
- align-items: center;
- justify-content: center;
-}
-
-.create-client-container {
- position: relative;
-}
-
-.grid-form {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- column-gap: 20px;
- row-gap: 20px;
-}
-
-.action-container {
- display: flex;
- justify-content: flex-end;
- gap: 1em;
- padding: 1.5em;
-}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.html
deleted file mode 100644
index ba47bb9..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.html
+++ /dev/null
@@ -1,112 +0,0 @@
-{{ 'editClientDialogTitle' | translate }}
-
-
-
-
-
-
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.ts
deleted file mode 100644
index e53dd47..0000000
--- a/ogWebconsole/src/app/components/groups/shared/clients/edit-client/edit-client.component.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-import { HttpClient, HttpHeaders } from '@angular/common/http';
-import { Component, Inject } from '@angular/core';
-import { FormGroup, FormBuilder, Validators } from '@angular/forms';
-import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
-import { CreateClientComponent } from '../create-client/create-client.component';
-import { DataService } from '../../../services/data.service';
-import { ToastrService } from 'ngx-toastr';
-
-@Component({
- selector: 'app-edit-client',
- templateUrl: './edit-client.component.html',
- styleUrls: ['./edit-client.component.css']
-})
-export class EditClientComponent {
- baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
- clientForm!: FormGroup;
- parentUnits: any[] = [];
- parentUnitsWithPaths: { id: string, name: string, path: string }[] = [];
- hardwareProfiles: any[] = [];
- repositories: any[] = [];
- ogLives: any[] = [];
- templates: any[] = [];
- menus: any[] = [];
- isEditMode: boolean;
- protected netifaceTypes = [
- { "name": 'Eth0', "value": "eth0" },
- { "name": 'Eth1', "value": "eth1" },
- { "name": 'Eth2', "value": "eth2" },
- ];
- protected netDriverTypes = [
- { "name": 'Generic', "value": "generic" },
- ];
- loading: boolean = false;
-
- constructor(
- private fb: FormBuilder,
- private dialogRef: MatDialogRef,
- private http: HttpClient,
- private dataService: DataService,
- private toastService: ToastrService,
- @Inject(MAT_DIALOG_DATA) public data: any
- ) {
- this.isEditMode = !!data?.uuid;
- if (this.isEditMode) {
- this.loadData(data.uuid);
- }
- }
-
- ngOnInit(): void {
- this.loadParentUnits();
- this.loadHardwareProfiles();
- this.loadOgLives();
- this.loadPxeTemplates()
- this.loadRepositories();
- this.loadMenus()
- this.clientForm = this.fb.group({
- organizationalUnit: [null, Validators.required],
- name: ['', Validators.required],
- serialNumber: null,
- netiface: null,
- netDriver: null,
- mac: null,
- ip: null,
- template: null,
- hardwareProfile: null,
- ogLive: null,
- repository: null,
- menu: null,
- });
- }
-
- 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.parentUnitsWithPaths = this.parentUnits.map(unit => ({
- id: unit['@id'],
- name: unit.name,
- path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits)
- }));
- this.loading = false;
- },
- error => {
- console.error('Error fetching parent units:', error);
- this.loading = false;
- }
- );
- }
-
- getSelectedParentName(): string | undefined {
- const parentId = this.clientForm.get('organizationalUnit')?.value;
- return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;
- }
-
- loadHardwareProfiles(): void {
- this.dataService.getHardwareProfiles().subscribe(
- (data: any[]) => {
- this.hardwareProfiles = data;
- },
- (error: any) => {
- console.error('Error fetching hardware profiles', error);
- }
- );
- }
-
- loadOgLives(): void {
- const url = `${this.baseUrl}/og-lives?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.ogLives = response['hydra:member'];
- },
- error => {
- console.error('Error fetching ogLives:', error);
- }
- );
- }
-
- loadMenus(): void {
- const url = `${this.baseUrl}/menus?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.menus = response['hydra:member'];
- },
- error => {
- console.error('Error fetching menus:', error);
- }
- );
- }
-
- loadRepositories(): void {
- const url = `${this.baseUrl}/image-repositories?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.repositories = response['hydra:member'];
- },
- error => {
- console.error('Error fetching ogLives:', error);
- }
- );
- }
-
- loadPxeTemplates(): void {
- const url = `${this.baseUrl}/pxe-templates?page=1&itemsPerPage=10000`;
-
- this.http.get(url).subscribe(
- response => {
- this.templates = response['hydra:member'];
- },
- error => {
- console.error('Error fetching ogLives:', error);
- }
- );
- }
-
- loadData(uuid: string) {
- this.loading = true;
- const url = `${this.baseUrl}/clients/${uuid}`;
-
- this.http.get(url).subscribe(
- data => {
- this.clientForm.patchValue({
- name: data.name,
- ip: data.ip,
- mac: data.mac,
- netiface: data.netiface,
- netDriver: data.netDriver,
- serialNumber: data.serialNumber,
- hardwareProfile: data.hardwareProfile ? data.hardwareProfile['@id'] : null,
- organizationalUnit: data.organizationalUnit ? data.organizationalUnit['@id'] : null,
- repository: data.repository ? data.repository['@id'] : null,
- ogLive: data.ogLive ? data.ogLive['@id'] : null,
- template: data.template ? data.template['@id'] : null,
- menu: data.menu ? data.menu['@id'] : null,
- });
- this.loading = false;
- },
- error => {
- console.error('Error al cargar datos del cliente:', error);
- this.loading = false;
- }
- );
- }
-
- onSubmit() {
- if (this.clientForm.valid) {
- const formData = this.clientForm.value;
-
- if (this.isEditMode) {
- // Edit mode: Send PUT request to update the unit
- const putUrl = `${this.baseUrl}/clients/${this.data.uuid}`;
- const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
-
- this.http.patch(putUrl, formData, { headers }).subscribe(
- response => {
- this.dialogRef.close();
- this.openSnackBar(false, 'Cliente actualizado exitosamente');
-
- },
- error => {
- console.error('Error al realizar PUT:', error);
- this.openSnackBar(true, 'Error al actualizar el cliente: ' + error.error['hydra:description']);
- }
- );
- } else {
- // Create mode: Send POST request to create a new unit
- const postUrl = `${this.baseUrl}/clients`;
- const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
-
- this.http.post(postUrl, formData, { headers }).subscribe(
- response => {
- this.dialogRef.close();
- this.openSnackBar(false, 'Cliente creado exitosamente');
- },
- error => {
- console.error('Error al realizar POST:', error);
- this.openSnackBar(true, 'Error al crear el cliente: ' + error.error['hydra:description']);
- }
- );
- }
- }
- }
-
- onNoClick(): void {
- this.dialogRef.close();
- }
-
- openSnackBar(isError: boolean, message: string) {
- if (isError) {
- this.toastService.error(' Error al crear el cliente: ' + message, 'Error');
- } else
- this.toastService.success(message, 'Éxito');
- }
-}
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.css
new file mode 100644
index 0000000..55a0232
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.css
@@ -0,0 +1,51 @@
+h1 {
+ text-align: center;
+ font-family: 'Roboto', sans-serif;
+ font-weight: 400;
+ color: #3f51b5;
+ margin-bottom: 20px;
+ margin-top: 20px;
+}
+
+.form-field {
+ width: 100%;
+}
+
+.mat-dialog-content {
+ padding: 15px 50px 15px 50px;
+}
+
+mat-option .unit-name {
+ display: block;
+}
+
+mat-option .unit-path {
+ display: block;
+ font-size: 0.8em;
+ color: gray;
+}
+
+.loading-spinner {
+ display: block;
+ margin: 0 auto;
+ align-items: center;
+ justify-content: center;
+}
+
+.create-client-container {
+ position: relative;
+}
+
+.grid-form {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ column-gap: 20px;
+ row-gap: 20px;
+}
+
+.action-container {
+ display: flex;
+ justify-content: flex-end;
+ gap: 1em;
+ padding: 1.5em;
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html
new file mode 100644
index 0000000..4d90eb2
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html
@@ -0,0 +1,113 @@
+
+
{{ dialogTitle | translate }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts
new file mode 100644
index 0000000..d357706
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts
@@ -0,0 +1,69 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { ToastrModule } from 'ngx-toastr';
+import { TranslateModule } from '@ngx-translate/core';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { ManageClientComponent } from './manage-client.component';
+import { DataService } from '../../../services/data.service';
+
+describe('ManageClientComponent', () => {
+ let component: ManageClientComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ManageClientComponent],
+ imports: [
+ HttpClientTestingModule,
+ ReactiveFormsModule,
+ MatDialogModule,
+ MatSnackBarModule,
+ ToastrModule.forRoot(),
+ TranslateModule.forRoot(),
+ MatProgressSpinnerModule
+ ],
+ providers: [
+ { provide: MatDialogRef, useValue: {} },
+ { provide: MAT_DIALOG_DATA, useValue: { uuid: '123', organizationalUnit: { '@id': '/units/1' } } },
+ DataService
+ ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ManageClientComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should initialize form', () => {
+ expect(component.clientForm).toBeDefined();
+ expect(component.clientForm.controls['name']).toBeDefined();
+ expect(component.clientForm.controls['mac']).toBeDefined();
+ expect(component.clientForm.controls['ip']).toBeDefined();
+ });
+
+ it('should set dialog title for edit mode', () => {
+ component.isEditMode = true;
+ component.data = { uuid: '123', organizationalUnit: { '@id': '/units/1' } };
+ component.ngOnInit();
+ expect(component.dialogTitle).toBe('editClientDialogTitle');
+ });
+
+ it('should call onSubmit when form is valid', () => {
+ spyOn(component, 'onSubmit');
+ component.clientForm.controls['name'].setValue('Test Client');
+ component.clientForm.controls['mac'].setValue('00:11:22:33:44:55');
+ component.clientForm.controls['ip'].setValue('192.168.1.1');
+ fixture.detectChanges();
+ const button = fixture.debugElement.nativeElement.querySelector('.submit-button');
+ button.click();
+ expect(component.onSubmit).toHaveBeenCalled();
+ });
+});
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts
new file mode 100644
index 0000000..a9d7863
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts
@@ -0,0 +1,302 @@
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Component, Inject, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+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';
+
+@Component({
+ selector: 'app-manage-client',
+ templateUrl: './manage-client.component.html',
+ styleUrls: ['./manage-client.component.css']
+})
+export class ManageClientComponent implements OnInit {
+ baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
+ clientForm!: FormGroup;
+ parentUnits: any[] = [];
+ parentUnitsWithPaths: { id: string, name: string, path: string }[] = [];
+ hardwareProfiles: any[] = [];
+ ogLives: any[] = [];
+ menus: any[] = [];
+ templates: any[] = [];
+ repositories: any[] = [];
+ loading: boolean = false;
+ displayedColumns: string[] = ['name', 'ip'];
+ dialogTitle: string;
+ protected netifaceTypes = [
+ { name: 'Eth0', value: 'eth0' },
+ { name: 'Eth1', value: 'eth1' },
+ { name: 'Eth2', value: 'eth2' }
+ ];
+ protected netDriverTypes = [
+ { name: 'Generic', value: 'generic' }
+ ];
+ isEditMode: boolean;
+
+ constructor(
+ private fb: FormBuilder,
+ private dialogRef: MatDialogRef,
+ private http: HttpClient,
+ private snackBar: MatSnackBar,
+ private toastService: ToastrService,
+ private dataService: DataService,
+ @Inject(MAT_DIALOG_DATA) public data: any
+ ) {
+ this.isEditMode = !!data?.uuid;
+ this.dialogTitle = this.isEditMode ? 'editClientDialogTitle' : 'addClientDialogTitle';
+ }
+
+ ngOnInit(): void {
+ this.loading = true;
+ this.initForm();
+ const observables = [
+ this.loadParentUnits(),
+ this.loadHardwareProfiles(),
+ this.loadOgLives(),
+ this.loadPxeTemplates(),
+ this.loadRepositories(),
+ this.loadMenus()
+ ];
+
+ Promise.all(observables).then(() => {
+ if (this.isEditMode) {
+ this.loadData(this.data.uuid).then(() => {
+ this.loading = false;
+ });
+ } else {
+ this.loading = false;
+ }
+ }).catch(error => {
+ console.error('Error loading data:', error);
+ this.loading = false;
+ });
+ }
+
+ initForm(): void {
+ this.clientForm = this.fb.group({
+ organizationalUnit: [
+ this.data.organizationalUnit ? this.data.organizationalUnit['@id'] : null,
+ Validators.required
+ ],
+ name: ['', Validators.required],
+ serialNumber: [''],
+ netiface: null,
+ netDriver: null,
+ mac: ['', Validators.required],
+ ip: ['', Validators.required],
+ template: [null],
+ hardwareProfile: [null],
+ ogLive: [null],
+ repository: [null],
+ menu: [null]
+ });
+ }
+
+ loadParentUnits(): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=10000`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.parentUnits = response['hydra:member'];
+ this.parentUnitsWithPaths = this.parentUnits.map(unit => ({
+ id: unit['@id'],
+ name: unit.name,
+ path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits),
+ repository: unit.networkSettings?.repository?.['@id'],
+ hardwareProfile: unit.networkSettings?.hardwareProfile?.['@id'],
+ ogLive: unit.networkSettings?.ogLive?.['@id'],
+ menu: unit.networkSettings?.menu?.['@id']
+ }));
+
+ const initialUnitId = this.clientForm.get('organizationalUnit')?.value;
+ if (initialUnitId) {
+ this.setOrganizationalUnitDefaults(initialUnitId);
+ }
+
+ resolve();
+ },
+ error => {
+ console.error('Error fetching parent units:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ getSelectedParentName(): string | undefined {
+ const parentId = this.clientForm.get('organizationalUnit')?.value;
+ return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;
+ }
+
+ loadHardwareProfiles(): Promise {
+ return new Promise((resolve, reject) => {
+ this.dataService.getHardwareProfiles().subscribe(
+ (data: any[]) => {
+ this.hardwareProfiles = data;
+ resolve();
+ },
+ error => {
+ console.error('Error fetching hardware profiles:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ loadOgLives(): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/og-lives?page=1&itemsPerPage=30`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.ogLives = response['hydra:member'];
+ resolve();
+ },
+ error => {
+ console.error('Error fetching ogLives:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ loadPxeTemplates(): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/pxe-templates?page=1&itemsPerPage=10000`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.templates = response['hydra:member'];
+ resolve();
+ },
+ error => {
+ console.error('Error fetching PXE templates:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ loadMenus(): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/menus?page=1&itemsPerPage=10000`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.menus = response['hydra:member'];
+ resolve();
+ },
+ error => {
+ console.error('Error fetching menus:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ loadRepositories(): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/image-repositories?page=1&itemsPerPage=10000`;
+
+ this.http.get(url).subscribe(
+ response => {
+ this.repositories = response['hydra:member'];
+ resolve();
+ },
+ error => {
+ console.error('Error fetching ogLives:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ onParentChange(event: any): void {
+ this.setOrganizationalUnitDefaults(event.value);
+ }
+
+ setOrganizationalUnitDefaults(unitId: string): void {
+ const selectedUnit: any = this.parentUnitsWithPaths.find(unit => unit.id === unitId);
+ if (selectedUnit) {
+ this.clientForm.patchValue({
+ repository: selectedUnit.repository || null,
+ hardwareProfile: selectedUnit.hardwareProfile || null,
+ ogLive: selectedUnit.ogLive || null,
+ menu: selectedUnit.menu || null
+ });
+ }
+ }
+
+ loadData(uuid: string): Promise {
+ return new Promise((resolve, reject) => {
+ const url = `${this.baseUrl}/clients/${uuid}`;
+
+ this.http.get(url).subscribe(
+ data => {
+ this.clientForm.patchValue({
+ name: data.name,
+ ip: data.ip,
+ mac: data.mac,
+ netiface: data.netiface,
+ netDriver: data.netDriver,
+ serialNumber: data.serialNumber,
+ hardwareProfile: data.hardwareProfile ? data.hardwareProfile['@id'] : null,
+ organizationalUnit: data.organizationalUnit ? data.organizationalUnit['@id'] : null,
+ repository: data.repository ? data.repository['@id'] : null,
+ ogLive: data.ogLive ? data.ogLive['@id'] : null,
+ template: data.template ? data.template['@id'] : null,
+ menu: data.menu ? data.menu['@id'] : null,
+ });
+ resolve();
+ },
+ error => {
+ console.error('Error al cargar datos del cliente:', error);
+ reject(error);
+ }
+ );
+ });
+ }
+
+ onSubmit(): void {
+ if (this.clientForm.valid) {
+ const formData = this.clientForm.value;
+
+ if (this.isEditMode) {
+ const putUrl = `${this.baseUrl}/clients/${this.data.uuid}`;
+ const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
+
+ this.http.patch(putUrl, formData, { headers }).subscribe(
+ response => {
+ this.dialogRef.close();
+ this.toastService.success('Cliente actualizado exitosamente', 'Éxito');
+ },
+ error => {
+ console.error('Error al realizar PUT:', error);
+ this.toastService.error('Error al actualizar el cliente: ' + error.error['hydra:description'], 'Error');
+ }
+ );
+ } else {
+ this.http.post(`${this.baseUrl}/clients`, formData).subscribe(
+ (response) => {
+ this.toastService.success('Cliente creado exitosamente', 'Éxito');
+ this.dialogRef.close({
+ client: response,
+ organizationalUnit: formData.organizationalUnit,
+ });
+ },
+ (error) => {
+ this.toastService.error(error.error['hydra:description'], 'Error al crear el cliente');
+ }
+ );
+ }
+ } else {
+ this.toastService.error('Formulario inválido. Por favor, revise los campos obligatorios.', 'Error');
+ }
+ }
+
+ onNoClick(): void {
+ this.dialogRef.close();
+ }
+}
\ No newline at end of file