refs #2098. Move clients to different OU

pull/24/head
Manuel Aranda Rosales 2025-05-29 15:31:27 +02:00
parent 4e6dbde59c
commit 1ce50857ac
10 changed files with 170 additions and 12 deletions

View File

@ -153,6 +153,7 @@ import { OutputDialogComponent } from './components/task-logs/output-dialog/outp
import { ClientTaskLogsComponent } from './components/task-logs/client-task-logs/client-task-logs.component';
import { BootSoPartitionComponent } from './components/commands/main-commands/execute-command/boot-so-partition/boot-so-partition.component';
import { RemoveCacheImageComponent } from './components/commands/main-commands/execute-command/remove-cache-image/remove-cache-image.component';
import { ChangeParentComponent } from './components/groups/shared/change-parent/change-parent.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
@ -263,7 +264,8 @@ registerLocaleData(localeEs, 'es-ES');
OutputDialogComponent,
ClientTaskLogsComponent,
BootSoPartitionComponent,
RemoveCacheImageComponent
RemoveCacheImageComponent,
ChangeParentComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -33,15 +33,6 @@
<ng-container *ngIf="column.columnDef !== 'commands'">
{{ column.cell(commandGroup) }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'commands'" joyrideStep="viewCommandsStep" text="{{ 'viewCommandsStepText' | translate }}">
<button class="action-button" [matMenuTriggerFor]="menu">{{ 'viewCommands' | translate }}</button>
<mat-menu #menu="matMenu">
<button mat-menu-item *ngFor="let command of commandGroup.commands">
{{ command.name }}
</button>
</mat-menu>
</ng-container>
</td>
</ng-container>

View File

@ -224,11 +224,16 @@
<strong>{{ selectedNode?.name }}</strong>
</span>
<div class="view-type-container">
<button class="action-button" [disabled]="selection.selected.length === 0" (click)="changeParent($event)"
matTooltip="{{ 'moveClientsTooltip' | translate }}" matTooltipShowDelay="1000">
{{ 'changeOU' | translate }}
</button>
<div joyrideStep="executeCommandStep" text="{{ 'executeCommandStepText' | translate }}">
<app-execute-command [clientData]="selection.selected" [buttonType]="'text'"
[buttonText]="'ejecutarComandos' | translate" [disabled]="selection.selected.length === 0"
[runScriptContext]="getRunScriptContext(selection.selected)"></app-execute-command>
</div>
<mat-button-toggle-group name="viewType" aria-label="View Type" [hideSingleSelectionIndicator]="true"
(change)="toggleView($event.value)" joyrideStep="tabsStep" text="{{ 'tabsStepText' | translate }}">
<mat-button-toggle value="list" [disabled]="currentView === 'list'">
@ -469,4 +474,4 @@
</div>
</ng-template>
</div>
</div>

View File

@ -29,6 +29,7 @@ import { MatMenuTrigger } from '@angular/material/menu';
import { ClientDetailsComponent } from './shared/client-details/client-details.component';
import { PartitionTypeOrganizatorComponent } from './shared/partition-type-organizator/partition-type-organizator.component';
import { ClientTaskLogsComponent } from '../task-logs/client-task-logs/client-task-logs.component';
import {ChangeParentComponent} from "./shared/change-parent/change-parent.component";
enum NodeType {
OrganizationalUnit = 'organizational-unit',
@ -864,6 +865,21 @@ export class GroupsComponent implements OnInit, OnDestroy {
});
}
changeParent(event: MouseEvent, ): void {
event.stopPropagation();
const dialogRef = this.dialog.open(ChangeParentComponent, {
data: { clients: this.selection.selected },
width: '700px',
});
dialogRef.afterClosed().subscribe((result) => {
if (result) {
this.refreshData();
}
});
}
openClientTaskLogs(event: MouseEvent, client: Client): void {
event.stopPropagation();

View File

@ -0,0 +1,39 @@
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
}
mat-form-field {
width: 100%;
}
mat-dialog-actions {
display: flex;
justify-content: flex-end;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.selected-list ul {
list-style: none;
padding: 0;
}
.selected-item {
display: flex;
justify-content: space-between; /* Alinea texto a la izquierda y botón a la derecha */
align-items: center; /* Centra verticalmente */
padding: 8px;
border-bottom: 1px solid #ccc;
}
.selected-item button {
margin-left: 10px;
}

View File

@ -0,0 +1,15 @@
<h2 mat-dialog-title>Mover clientes a:</h2>
<mat-dialog-content>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Seleccione aula</mat-label>
<mat-select [(ngModel)]="newOU">
<mat-option *ngFor="let unit of units" [value]="unit">{{ unit.name }}</mat-option>
</mat-select>
</mat-form-field>
</mat-dialog-content>
<div class="action-container">
<button class="ordinary-button" (click)="close()">Cancelar</button>
<button class="submit-button" (click)="save()" [disabled]="!newOU">Continuar</button>
</div>

View File

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

View File

@ -0,0 +1,63 @@
import {Component, Inject, OnInit} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {ToastrService} from "ngx-toastr";
import {Router} from "@angular/router";
import {ConfigService} from "@services/config.service";
@Component({
selector: 'app-change-parent',
templateUrl: './change-parent.component.html',
styleUrl: './change-parent.component.css'
})
export class ChangeParentComponent implements OnInit {
baseUrl: string;
loading: boolean = true;
units: any[] = [];
newOU: any;
constructor(
private http: HttpClient,
public dialogRef: MatDialogRef<ChangeParentComponent>,
private toastService: ToastrService,
private router: Router,
private configService: ConfigService,
@Inject(MAT_DIALOG_DATA) public data: { clients: any}
) {
this.baseUrl = this.configService.apiUrl;
}
ngOnInit(): void {
this.loading = true;
this.loadUnits();
}
loadUnits() {
this.http.get<any>(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=500`).subscribe(
response => {
this.units = response['hydra:member'];
this.loading = false;
},
error => console.error('Error fetching organizational units:', error)
);
}
save() {
this.http.post<any>(`${this.baseUrl}/clients/change-organizational-unit`, {
clients: this.data.clients.map((client: any) => client['@id']),
organizationalUnit: this.newOU['@id']
}).subscribe({
next: (response) => {
this.toastService.success('Parent changed successfully');
this.dialogRef.close(true);
},
error: error => {
this.toastService.error(error.error['hydra:description']);
}
})
}
close() {
this.dialogRef.close();
}
}

View File

@ -517,6 +517,8 @@
"hardwareInventory": "Hardware Inventory",
"runScript": "Run Script"
},
"changeOU": "Change Organizational Unit",
"moveClientsTooltip": "Move clients to another organizational unit",
"remoteAccess": "Remote access available",
"noRemoteAccess": "Remote access not available",
"capacityWarning": "The capacity cannot be negative",
@ -532,4 +534,4 @@
"filtersStepText": "Here you can see the different filters to apply to the table information.",
"tracesProgressStepText": "Here you can see the execution status updated in real time.",
"tracesInfoStepText": "Here you can consult detailed information about the specific trace."
}
}

View File

@ -518,6 +518,8 @@
"hardwareInventory": "Inventario Hardware",
"runScript": "Ejecutar script"
},
"changeOU": "Mover clientes",
"moveClientsTooltip": "Mover clientes a otra unidad organizativa",
"remoteAccess": "Disponible acceso remoto",
"noRemoteAccess": "No disponible acceso remoto",
"capacityWarning": "El aforo no puede ser",