Solve conflics
commit
b237bf4786
|
@ -1,2 +1 @@
|
|||
#NG_APP_BASE_API_URL=http://127.0.0.1:8090
|
||||
NG_APP_BASE_API_URL=http://127.0.0.1:8080
|
||||
NG_APP_BASE_API_URL=http://127.0.0.1:8001
|
||||
|
|
|
@ -102,6 +102,7 @@ import { TaskLogsComponent } from './components/commands/commands-task/task-logs
|
|||
import { OrganizationalUnitTabViewComponent } from './components/groups/components/organizational-unit-tab-view/organizational-unit-tab-view.component';
|
||||
import { ServerInfoDialogComponent } from './components/ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component';
|
||||
import { StatusComponent } from './components/ogdhcp/og-dhcp-subnets/status/status.component';
|
||||
import {MatSliderModule} from '@angular/material/slider';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -188,6 +189,7 @@ import { StatusComponent } from './components/ogdhcp/og-dhcp-subnets/status/stat
|
|||
NgxChartsModule,
|
||||
MatDatepickerModule,
|
||||
MatNativeDateModule,
|
||||
MatSliderModule,
|
||||
ToastrModule.forRoot(
|
||||
{
|
||||
timeOut: 5000,
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</mat-step>
|
||||
|
||||
<!-- Paso 4: Selecciona Unidad Organizacional, Aula y Clientes -->
|
||||
<mat-step label="Selecciona Unidad Organizacional, Aula y Clientes">
|
||||
<mat-step label="Selecciona Unidad Organizacional, Aula y Clientes">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Selecciona Unidad Organizacional</mat-label>
|
||||
<mat-select formControlName="organizationalUnit" (selectionChange)="onOrganizationalUnitChange()">
|
||||
|
@ -88,6 +88,9 @@
|
|||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Selecciona Clientes</mat-label>
|
||||
<mat-select formControlName="selectedClients" multiple>
|
||||
<mat-option (click)="toggleSelectAll()" [selected]="areAllSelected()">
|
||||
Seleccionar todos
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let client of selectedClients" [value]="client.uuid">
|
||||
{{ client.name }} ({{ client.ip }})
|
||||
</mat-option>
|
||||
|
@ -98,7 +101,8 @@
|
|||
<button mat-button matStepperPrevious>Atrás</button>
|
||||
<button mat-raised-button color="primary" (click)="saveTask()">Guardar</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
</mat-step>
|
||||
|
||||
</mat-horizontal-stepper>
|
||||
</mat-dialog-content>
|
||||
</form>
|
||||
|
|
|
@ -20,6 +20,7 @@ export class CreateTaskComponent implements OnInit {
|
|||
availableOrganizationalUnits: any[] = [];
|
||||
selectedUnitChildren: any[] = [];
|
||||
selectedClients: any[] = [];
|
||||
selectedClientIds: Set<string> = new Set();
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
|
@ -124,6 +125,7 @@ export class CreateTaskComponent implements OnInit {
|
|||
(data) => {
|
||||
this.selectedClients = data.clients;
|
||||
this.taskForm.patchValue({ selectedClients: [] });
|
||||
this.selectedClientIds.clear();
|
||||
},
|
||||
(error) => {
|
||||
this.toastr.error('Error al cargar los detalles del aula seleccionada');
|
||||
|
@ -131,6 +133,20 @@ export class CreateTaskComponent implements OnInit {
|
|||
);
|
||||
}
|
||||
|
||||
toggleSelectAll() {
|
||||
const allSelected = this.areAllSelected();
|
||||
if (allSelected) {
|
||||
this.selectedClientIds.clear();
|
||||
} else {
|
||||
this.selectedClients.forEach(client => this.selectedClientIds.add(client.uuid));
|
||||
}
|
||||
this.taskForm.get('selectedClients')!.setValue(Array.from(this.selectedClientIds));
|
||||
}
|
||||
|
||||
areAllSelected(): boolean {
|
||||
return this.selectedClients.length > 0 && this.selectedClients.every(client => this.selectedClientIds.has(client.uuid));
|
||||
}
|
||||
|
||||
saveTask(): void {
|
||||
if (this.taskForm.invalid) {
|
||||
this.toastr.error('Por favor, rellene todos los campos obligatorios');
|
||||
|
@ -147,7 +163,7 @@ export class CreateTaskComponent implements OnInit {
|
|||
commandGroups: formData.commandGroup ? [`/command-groups/${formData.commandGroup}`] : null,
|
||||
dateTime: dateTime,
|
||||
notes: formData.notes || '',
|
||||
clients: this.selectedClients.map((client: any) => client['@id']),
|
||||
clients: Array.from(this.selectedClientIds).map((uuid: string) => `/clients/${uuid}`),
|
||||
};
|
||||
|
||||
if (selectedCommands) {
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
<ng-container *ngIf="filteredResults && filteredResults.length > 0; else noResults">
|
||||
<mat-grid-list cols="4" rowHeight="1:1">
|
||||
<mat-grid-tile *ngFor="let result of filteredResults">
|
||||
<mat-card class="result-card">
|
||||
<mat-card class="result-card" (dblclick)="onDobleClick($event, result.uuid, result.type)">
|
||||
<mat-checkbox
|
||||
[checked]="isSelected(result.name)"
|
||||
(change)="onCheckboxChange($event, result.name, result.uuid)">
|
||||
|
|
|
@ -19,6 +19,7 @@ import { SaveFiltersDialogComponent } from '../../shared/save-filters-dialog/sav
|
|||
import { AcctionsModalComponent } from '../../shared/acctions-modal/acctions-modal.component';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {DatePipe} from "@angular/common";
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -62,7 +63,8 @@ export class AdvancedSearchComponent {
|
|||
public dialog: MatDialog,
|
||||
private toastService: ToastrService,
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
private http: HttpClient
|
||||
private http: HttpClient,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -406,9 +408,21 @@ export class AdvancedSearchComponent {
|
|||
|
||||
}
|
||||
|
||||
|
||||
sendActions() {
|
||||
const dialogRef = this.dialog.open(AcctionsModalComponent, { data: { selectedElements: this.selectedElements }, width: '700px'});
|
||||
}
|
||||
|
||||
|
||||
|
||||
onDobleClick(event: MouseEvent, data: any, type: string): void {
|
||||
console.log('Doble click en:', data);
|
||||
|
||||
if (type === 'client') {
|
||||
this.router.navigate(['client', data]);
|
||||
}
|
||||
else {
|
||||
console.error('ADD VIEW FOR OU');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/* Global Container */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 40px;
|
||||
background-color: #f4f6f9;
|
||||
font-family: 'Arial', sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Header - Title and Icon */
|
||||
.client-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.client-icon {
|
||||
flex-shrink: 0; /* Prevent shrinking of the icon */
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 120px; /* Ensure the icon has enough space */
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.icon-pc {
|
||||
font-size: 100px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.client-title h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.client-title p {
|
||||
margin: 2px 0;
|
||||
font-size: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Information Section */
|
||||
.client-info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.info-section h2 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 10px;
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.info-section p {
|
||||
font-size: 1rem;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
/* Disk Usage Section */
|
||||
.disk-usage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.circle-bg {
|
||||
fill: none;
|
||||
stroke: #eee;
|
||||
stroke-width: 3.8;
|
||||
}
|
||||
|
||||
.circle {
|
||||
fill: none;
|
||||
stroke-width: 3.8;
|
||||
stroke: #00bfa5;
|
||||
stroke-linecap: round;
|
||||
animation: progress 1s ease-out forwards;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
fill: #333;
|
||||
font-size: 0.5rem;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.client-footer {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
@keyframes progress {
|
||||
0% {
|
||||
stroke-dasharray: 0, 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<div class="container">
|
||||
<div class="client-header">
|
||||
<div class="client-icon">
|
||||
<mat-icon class="icon-pc">computer</mat-icon>
|
||||
</div>
|
||||
<div class="client-title">
|
||||
<h1>{{ clientData?.name }}</h1>
|
||||
<p><strong>UUID:</strong> {{ clientData?.uuid }}</p>
|
||||
<p><strong>IP Address:</strong> {{ clientData?.ip }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="client-info">
|
||||
<!-- General Information -->
|
||||
<div class="info-section">
|
||||
<h2>General Information</h2>
|
||||
<p><strong>Type:</strong> {{ clientData?.type }}</p>
|
||||
<p><strong>MAC Address:</strong> {{ clientData?.mac }}</p>
|
||||
<p><strong>Serial Number:</strong> {{ clientData?.serialNumber }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Disk Space Usage -->
|
||||
<div class="info-section">
|
||||
<h2>Disk Space</h2>
|
||||
<div class="disk-usage">
|
||||
<div class="chart-container">
|
||||
<svg viewBox="0 0 36 36" class="circular-chart green">
|
||||
<path class="circle-bg"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<path class="circle"
|
||||
stroke-dasharray="75, 100"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<text x="18" y="20.35" class="percentage">75%</text>
|
||||
</svg>
|
||||
</div>
|
||||
<p>Used: 75% (375GB)</p>
|
||||
<p>Total: 500GB</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Organizational Unit -->
|
||||
<div class="info-section">
|
||||
<h2>Organizational Unit</h2>
|
||||
<p><strong>Name:</strong> {{ clientData?.organizationalUnit?.name }}</p>
|
||||
<p><strong>Type:</strong> {{ clientData?.organizationalUnit?.type }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Network Settings -->
|
||||
<div class="info-section">
|
||||
<h2>Network Settings</h2>
|
||||
<p><strong>Next Server:</strong> {{ clientData?.organizationalUnit?.networkSettings?.nextServer }}</p>
|
||||
<p><strong>Boot File Name:</strong> {{ clientData?.organizationalUnit?.networkSettings?.bootFileName }}</p>
|
||||
<p><strong>DNS:</strong> {{ clientData?.organizationalUnit?.networkSettings?.dns }}</p>
|
||||
<p><strong>Router:</strong> {{ clientData?.organizationalUnit?.networkSettings?.router }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="client-footer">
|
||||
<p><strong>Created At:</strong> {{ clientData?.createdAt | date }}</p>
|
||||
<p><strong>Created By:</strong> {{ clientData?.createdBy }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ClientMainViewComponent } from './client-main-view.component';
|
||||
|
||||
describe('ClientMainViewComponent', () => {
|
||||
let component: ClientMainViewComponent;
|
||||
let fixture: ComponentFixture<ClientMainViewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ClientMainViewComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ClientMainViewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-client-main-view',
|
||||
templateUrl: './client-main-view.component.html',
|
||||
styleUrl: './client-main-view.component.css'
|
||||
})
|
||||
export class ClientMainViewComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
clientUuid: string;
|
||||
clientData: any;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
const url = window.location.href;
|
||||
const segments = url.split('/');
|
||||
this.clientUuid = segments[segments.length - 1];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadClientData();
|
||||
}
|
||||
|
||||
loadClientData() {
|
||||
this.http.get(`${this.baseUrl}/clients/${this.clientUuid}`).subscribe(
|
||||
data => {
|
||||
this.clientData = data;
|
||||
},
|
||||
error => {
|
||||
console.error('Error loading client data:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ import { ToastrService } from 'ngx-toastr';
|
|||
import { CreatePxeBootFileComponent } from '../../../ogboot/pxe-boot-files/create-pxeBootFile/create-pxe-boot-file/create-pxe-boot-file.component';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CommandDetailComponent } from '../../../commands/main-commands/detail-command/command-detail.component';
|
||||
import { RouterLink } from '@angular/router';
|
||||
@Component({
|
||||
selector: 'app-acctions-modal',
|
||||
templateUrl: './acctions-modal.component.html',
|
||||
|
@ -52,24 +53,9 @@ export class AcctionsModalComponent {
|
|||
});
|
||||
}
|
||||
|
||||
onCommandClick(command: any): void {
|
||||
const payload = {
|
||||
clients: this.selectedElements.map((uuid: any) => `/clients/${uuid}`)
|
||||
};
|
||||
onCommandClick(command: any): void {
|
||||
|
||||
const apiUrl = `${this.baseUrl}/commands/${command.uuid}/execute`;
|
||||
|
||||
this.http.post(apiUrl, payload).subscribe({
|
||||
next: () => {
|
||||
console.log('Command executed successfully');
|
||||
this.loadCommands();
|
||||
this.toastService.success('Command executed successfully');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error executing command:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
chunkArray(arr: any[], chunkSize: number): any[] {
|
||||
const chunks = [];
|
||||
|
|
|
@ -27,8 +27,6 @@ export class CreatePxeBootFileComponent implements OnInit {
|
|||
this.selectedElements = this.data.clients;
|
||||
this.clientes = this.selectedElements.map((client: { uuid: any }) => `/clients/${client.uuid}`);
|
||||
this.loadPxeTemplates();
|
||||
|
||||
// Configura el modo de edición si se proporciona bootFile
|
||||
if (this.data.bootFile) {
|
||||
this.isEditMode = true;
|
||||
this.selectedPxeTemplate = this.data.bootFile.template.uuid;
|
||||
|
@ -60,7 +58,6 @@ export class CreatePxeBootFileComponent implements OnInit {
|
|||
};
|
||||
|
||||
if (this.isEditMode && this.data.bootFile) {
|
||||
// Edit mode: Actualizar boot file existente
|
||||
this.http.put(`${this.baseUrl}/pxe-boot-files/${this.data.bootFile.uuid}`, payload)
|
||||
.subscribe({
|
||||
next: response => {
|
||||
|
@ -72,7 +69,6 @@ export class CreatePxeBootFileComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
// Create mode: Crear nuevo boot file
|
||||
this.http.post(`${this.baseUrl}/pxe-boot-files`, payload)
|
||||
.subscribe({
|
||||
next: response => {
|
||||
|
|
Loading…
Reference in New Issue