refs #1320. Splice create client logic.
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details

pull/10/head
Manuel Aranda Rosales 2025-01-10 08:26:52 +01:00
parent d689583b56
commit b74f129dbe
9 changed files with 587 additions and 288 deletions

View File

@ -123,6 +123,7 @@ import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component
import { MatSortModule } from '@angular/material/sort';
import { MenusComponent } from './components/menus/menus.component';
import { CreateMenuComponent } from './components/menus/create-menu/create-menu.component';
import { CreateMultipleClientComponent } from './components/groups/shared/clients/create-multiple-client/create-multiple-client.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
}
@ -204,6 +205,7 @@ export function HttpLoaderFactory(http: HttpClient) {
EnvVarsComponent,
MenusComponent,
CreateMenuComponent,
CreateMultipleClientComponent,
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -13,9 +13,12 @@
matTooltip="{{ 'newOrganizationalUnitTooltip' | translate }}" matTooltipShowDelay="1000">
{{ 'newOrganizationalUnitButton' | translate }}
</button>
<button mat-flat-button color="primary" (click)="addClient($event)" matTooltipShowDelay="1000">
{{ 'newClientButton' | translate }}
</button>
<button mat-flat-button color="primary" [matMenuTriggerFor]="menu">{{ 'newClientButton' | translate }}</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="addClient($event)" >Añadir cliente unitario</button>
<button mat-menu-item (click)="addMultipleClients($event)">Añadir clientes masivamente</button>
</mat-menu>
<button mat-flat-button (click)="openBottomSheet()" joyrideStep="keyStep" text="{{ 'keyStepText' | translate }}"
matTooltipShowDelay="1000">
{{ 'legendButton' | translate }}
@ -208,7 +211,7 @@
<strong>{{ selectedNode?.name ? ' ' + selectedNode?.name : ' ' + selectedUnidad?.name }}</strong>
</span>
<mat-divider></mat-divider>
<mat-divider style="margin-bottom: 10px;"></mat-divider>
<div *ngIf="(selectedClients.data?.length || 0) > 0; else noClientsTemplate">
<!-- Cards view -->

View File

@ -23,6 +23,7 @@ import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
import {CreateMultipleClientComponent} from "./shared/clients/create-multiple-client/create-multiple-client.component";
enum NodeType {
OrganizationalUnit = 'organizational-unit',
@ -330,6 +331,20 @@ export class GroupsComponent implements OnInit, OnDestroy {
});
}
addMultipleClients(event: MouseEvent, organizationalUnit: TreeNode | null = null): void {
event.stopPropagation();
const dialogRef = this.dialog.open(CreateMultipleClientComponent, {
data: { organizationalUnit },
width: '900px',
});
dialogRef.afterClosed().subscribe(() => {
this.refreshOrganizationalUnits();
if (organizationalUnit && organizationalUnit['@id']) {
this.refreshClientsForNode(organizationalUnit);
}
});
}
private refreshOrganizationalUnits(): void {
const expandedNodeIds = this.treeControl.dataNodes
? this.treeControl.dataNodes

View File

@ -1,161 +1,63 @@
.create-client-container {
h1 {
text-align: center;
font-family: 'Roboto', sans-serif;
font-weight: 400;
color: #3f51b5;
margin-bottom: 20px;
}
.network-form {
display: flex;
flex-direction: column;
padding: 16px;
font-family: Arial, sans-serif;
font-size: 14px;
align-items: center;
gap: 15px;
}
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;
.form-field {
width: 100%;
margin-top: 10px;
}
.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;
padding: 50px;
}
.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);
button {
text-transform: none;
font-size: 16px;
font-weight: 500;
}
.client-form {
.mat-slide-toggle {
margin-top: 20px;
}
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(auto-fit, minmax(200px, 1fr));
gap: 16px;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.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;
}
}

View File

@ -1,78 +1,39 @@
<div class="create-client-container mat-elevation-z4">
<h1>{{ 'addClientTitle' | translate }}s</h1>
<div class="inputs-container">
<div class="create-client-container">
<h1 mat-dialog-title i18n="@@add-client-dialog-title">Añadir Cliente</h1>
<div class="mat-dialog-content" [ngClass]="{'loading': loading}">
<mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
<form [formGroup]="clientForm" class="client-form grid-form" *ngIf="!loading">
<mat-form-field class="form-field">
<mat-label>{{ 'organizationalUnitLabel' | translate }}</mat-label>
<mat-label i18n="@@organizational-unit-label">Padre</mat-label>
<mat-select formControlName="organizationalUnit">
<mat-option *ngFor="let unit of parentUnits" [value]="unit['@id']">
<div class="unit-name">{{ unit.name }}</div>
<div class="unit-path">{{ unit.path }}</div>
</mat-option>
</mat-select>
</mat-form-field>
</form>
<div *ngIf="!isSingleClientForm; else singleClientForm">
<h3>Añadir múltiples clientes</h3>
<div class="upload-container">
<button mat-raised-button color="primary" (click)="fileInput.click()">Subir fichero</button>
<input #fileInput type="file" (change)="onFileUpload($event)" accept="*" hidden>
<p>o añadelos manualmente:</p>
<div *ngIf="showTextarea">
<textarea #textarea matInput placeholder="Ejemplo: host bbaa-it1-11 { hardware ethernet a0:48:1c:8a:f1:5b; fixed-address 172.17.69.11; };" rows="20" cols="100"></textarea>
<button mat-raised-button color="primary" (click)="onTextarea(textarea.value)">cargar</button>
</div>
</div>
<h4 *ngIf="uploadedClients.length > 0">Clientes importados:</h4>
<div class="scrollable-table">
<table mat-table [dataSource]="uploadedClients" class="mat-elevation-z8" *ngIf="uploadedClients.length > 0">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Nombre </th>
<td mat-cell *matCellDef="let client"> {{ client.name }} </td>
</ng-container>
<ng-container matColumnDef="ip">
<th mat-header-cell *matHeaderCellDef> IP </th>
<td mat-cell *matCellDef="let client"> {{ client.ip }} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
<!-- Añadir uon cliente -->
<ng-template #singleClientForm>
<h3>Añadir un cliente</h3>
<mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
<form [formGroup]="clientForm" class="client-form grid-form" *ngIf="!loading">
<mat-form-field class="form-field">
<mat-label>{{ 'nameLabel' | translate }}</mat-label>
<mat-label i18n="@@name-label">Nombre</mat-label>
<input matInput formControlName="name">
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'ogLiveLabel' | translate }}</mat-label>
<mat-label i18n="@@oglive-label">OgLive</mat-label>
<mat-select formControlName="ogLive">
<mat-option *ngFor="let oglive of ogLives" [value]="oglive['@id']">
{{ oglive.filename }}
{{ oglive.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'serialNumberLabel' | translate }}</mat-label>
<mat-label i18n="@@serial-number-label">Número de Serie</mat-label>
<input matInput formControlName="serialNumber">
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'netifaceLabel' | translate }}</mat-label>
<mat-label i18n="@@netiface-label">Interfaz de red</mat-label>
<mat-select formControlName="netiface">
<mat-option *ngFor="let type of netifaceTypes" [value]="type.value">
{{ type.name }}
@ -81,7 +42,7 @@
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'netDriverLabel' | translate }}</mat-label>
<mat-label i18n="@@net-driver-label">Controlador de red</mat-label>
<mat-select formControlName="netDriver">
<mat-option *ngFor="let type of netDriverTypes" [value]="type.value">
{{ type.name }}
@ -90,21 +51,21 @@
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'macLabel' | translate }}</mat-label>
<mat-hint>{{ 'macHint' | translate }}</mat-hint>
<mat-label i18n="@@mac-label">MAC</mat-label>
<mat-hint i18n="@@mac-hint">Ejemplo: 00:11:22:33:44:55</mat-hint>
<input matInput formControlName="mac">
<mat-error>{{ 'macError' | translate }}</mat-error>
<mat-error i18n="@@mac-error">Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55</mat-error>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'ipLabel' | translate }}</mat-label>
<mat-hint>{{ 'ipHint' | translate }}</mat-hint>
<mat-label i18n="@@ip-label">Dirección IP</mat-label>
<mat-hint i18n="@@ip-hint">Ejemplo: 127.0.0.1</mat-hint>
<input matInput formControlName="ip">
<mat-error>{{ 'ipError' | translate }}</mat-error>
<mat-error i18n="@@ip-error">Formato de dirección IP inválido. Ejemplo válido: 127.0.0.1</mat-error>
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'templateLabel' | translate }}</mat-label>
<mat-label i18n="@@oglive-label">Plantilla PXE</mat-label>
<mat-select formControlName="template">
<mat-option *ngFor="let template of templates" [value]="template['@id']">
{{ template.name }}
@ -113,13 +74,13 @@
</mat-form-field>
<mat-form-field class="form-field">
<mat-label>{{ 'hardwareProfileLabel' | translate }}</mat-label>
<mat-label i18n="@@hardware-profile-label">Perfil de Hardware</mat-label>
<mat-select formControlName="hardwareProfile">
<mat-option *ngFor="let unit of hardwareProfiles" [value]="unit['@id']">
{{ unit.description }}
</mat-option>
</mat-select>
<mat-error>{{ 'hardwareProfileError' | translate }}</mat-error>
<mat-error i18n="@@hardware-profile-error">Formato de URL inválido.</mat-error>
</mat-form-field>
<mat-form-field class="form-field">
@ -141,15 +102,10 @@
<mat-error>{{ 'menuError' | translate }}</mat-error>
</mat-form-field>
</form>
</ng-template>
</div>
</div>
<div mat-dialog-actions align="end">
<button mat-button (click)="toggleClientForm()">
{{ isSingleClientForm ? 'Añadir múltiples clientes' : 'Añadir un único cliente' }}
</button>
<button mat-button color="warn" (click)="onNoClick()">{{ 'cancelButton' | translate }}</button>
<button mat-button color="primary" (click)="onSubmit()">{{ 'addButton' | translate }}</button>
<button mat-button (click)="onNoClick()" i18n="@@cancel-button">Cancelar</button>
<button mat-button [disabled]="!clientForm.valid" (click)="onSubmit()" i18n="@@add-button">Añadir</button>
</div>
</div>

View File

@ -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;
}
}

View File

@ -0,0 +1,58 @@
<div class="create-client-container">
<h1 mat-dialog-title i18n="@@add-client-dialog-title">Añadir multiples clientes</h1>
<div class="inputs-container">
<div class="mat-dialog-content" [ngClass]="{'loading': loading}">
<form [formGroup]="clientForm" class="client-form grid-form" *ngIf="!loading">
<mat-form-field class="form-field">
<mat-label>{{ 'organizationalUnitLabel' | translate }}</mat-label>
<mat-select formControlName="organizationalUnit">
<mat-option *ngFor="let unit of parentUnits" [value]="unit['@id']">
<div class="unit-name">{{ unit.name }}</div>
</mat-option>
</mat-select>
</mat-form-field>
</form>
<div>
<div class="upload-container">
<button mat-raised-button color="primary" (click)="fileInput.click()">Subir fichero</button>
<input #fileInput type="file" (change)="onFileUpload($event)" accept="*" hidden>
<p>o añadelos manualmente:</p>
<div *ngIf="showTextarea">
<textarea #textarea matInput placeholder="Ejemplo: host bbaa-it1-11 { hardware ethernet a0:48:1c:8a:f1:5b; fixed-address 172.17.69.11; };" rows="20" cols="100"></textarea>
<button mat-raised-button color="primary" (click)="onTextarea(textarea.value)">Previsualizar</button>
</div>
</div>
<h4 *ngIf="uploadedClients.length > 0">Clientes importados:</h4>
<div *ngIf="uploadedClients.length > 0" class="scrollable-table">
<table mat-table [dataSource]="uploadedClients" class="mat-elevation-z8" *ngIf="uploadedClients.length > 0">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Nombre </th>
<td mat-cell *matCellDef="let client"> {{ client.name }} </td>
</ng-container>
<ng-container matColumnDef="ip">
<th mat-header-cell *matHeaderCellDef> IP </th>
<td mat-cell *matCellDef="let client"> {{ client.ip }} </td>
</ng-container>
<ng-container matColumnDef="mac">
<th mat-header-cell *matHeaderCellDef> Mac </th>
<td mat-cell *matCellDef="let client"> {{ client.mac }} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</div>
</div>
<div mat-dialog-actions align="end">
<button mat-button color="warn" (click)="onNoClick()">{{ 'cancelButton' | translate }}</button>
<button mat-button color="primary" [disabled]="!clientForm.value.organizationalUnit" (click)="onSubmit()">{{ 'saveButton' | translate }}</button>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateMultipleClientComponent } from './create-multiple-client.component';
describe('CreateMultipleClientComponent', () => {
let component: CreateMultipleClientComponent;
let fixture: ComponentFixture<CreateMultipleClientComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CreateMultipleClientComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CreateMultipleClientComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,182 @@
import {Component, Inject} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {HttpClient} from "@angular/common/http";
import {MatSnackBar} from "@angular/material/snack-bar";
import {ToastrService} from "ngx-toastr";
import {DataService} from "../../../services/data.service";
@Component({
selector: 'app-create-multiple-client',
templateUrl: './create-multiple-client.component.html',
styleUrl: './create-multiple-client.component.css'
})
export class CreateMultipleClientComponent {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
clientForm!: FormGroup;
parentUnits: any[] = [];
hardwareProfiles: any[] = [];
ogLives: any[] = [];
menus: any[] = [];
templates: any[] = [];
uploadedClients: any[] = [];
repositories: any[] = [];
loading: boolean = false;
displayedColumns: string[] = ['name', 'ip', 'mac'];
showTextarea: boolean = true;
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<CreateMultipleClientComponent>,
private http: HttpClient,
private snackBar: MatSnackBar,
private toastService: ToastrService,
private dataService: DataService,
@Inject(MAT_DIALOG_DATA) public data: any
) {}
ngOnInit(): void {
this.initForm();
this.loadParentUnits();
}
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: [
this.data.organizationalUnit?.networkSettings?.hardwareProfile?.['@id'] || 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<any>(url).subscribe(
response => {
this.parentUnits = response['hydra:member'];
this.loading = false;
},
error => {
console.error('Error fetching parent units:', error);
this.loading = false;
}
);
}
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.clientForm.value.organizationalUnit || null,
name: client.name || null,
mac: client.mac || null,
ip: client.ip || null,
template: this.clientForm.value.template || null,
hardwareProfile: this.clientForm.value.hardwareProfile || null,
ogLive: this.clientForm.value.ogLive || null,
repository: this.clientForm.value.repository || null,
serialNumber: null,
netiface: null,
netDriver: null
};
this.http.post(`${this.baseUrl}/clients`, formData).subscribe(
response => {
this.toastService.success(`Cliente ${client.name} creado exitosamente`, 'Éxito');
},
error => {
console.error(`Error al crear el cliente ${client.name}:`, error);
this.toastService.error(`Error al crear el cliente ${client.name}`, 'Error');
}
);
});
this.uploadedClients = [];
this.dialogRef.close();
} else {
this.toastService.error('No hay clientes cargados para añadir', 'Error');
}
}
onNoClick(): void {
this.dialogRef.close();
}
}