Compare commits
2 Commits
2f3e119b54
...
48636d0933
Author | SHA1 | Date |
---|---|---|
|
48636d0933 | |
|
60a96170e1 |
|
@ -54,57 +54,3 @@
|
|||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminCommandsTitle">Trazas de comandos y procedimientos</h2>
|
||||
<div class="images-button-row">
|
||||
<button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div class="search-container">
|
||||
<mat-form-field appearance="fill" class="search-select">
|
||||
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente">
|
||||
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" (optionSelected)="onOptionClientSelected($event.option.value)">
|
||||
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
|
||||
{{ client.name }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="search-select">
|
||||
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="Seleccione un comando">
|
||||
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)">
|
||||
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
|
||||
{{ command.name }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="loading" class="loading-container">
|
||||
<mat-spinner></mat-spinner>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!loading">
|
||||
<table mat-table [dataSource]="traces" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let trace">
|
||||
{{ column.cell(trace) }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="paginator-container">
|
||||
<mat-paginator [length]="length"
|
||||
[pageSize]="itemsPerPage"
|
||||
[pageIndex]="page"
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.client-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -14,10 +21,49 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 120px;
|
||||
min-width: 120px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.row-container {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.charts-wrapper {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.charts-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between; /* Distribuye el espacio entre los gráficos */
|
||||
gap: 20px; /* Añade espacio entre los gráficos */
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
min-width: 200px; /* Ajusta este valor según el tamaño mínimo deseado para cada gráfico */
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
margin: 0 auto; /* Centra el gráfico dentro del contenedor */
|
||||
}
|
||||
|
||||
.chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.icon-pc {
|
||||
font-size: 25px;
|
||||
color: #3b82f6;
|
||||
|
@ -43,7 +89,6 @@
|
|||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.two-column-table {
|
||||
|
@ -53,6 +98,10 @@
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.table-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -72,11 +121,11 @@
|
|||
}
|
||||
|
||||
.mat-tab-group {
|
||||
min-height: 400px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.mat-tab-body-wrapper {
|
||||
min-height: inherit;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
.info-section h2 {
|
||||
|
@ -92,7 +141,6 @@
|
|||
|
||||
.second-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 3fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
|
@ -111,24 +159,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
<div class="header-container">
|
||||
<h2 class="title">Administrar Cliente</h2>
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Datos de cliente</h2>
|
||||
<div class="calendar-button-row">
|
||||
<button mat-flat-button color="primary" [matMenuTriggerFor]="commandMenu">Comandos</button>
|
||||
</div>
|
||||
<mat-menu #commandMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let command of commands" (click)="onCommandSelect(command)">
|
||||
{{ command.name }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
|
||||
<div *ngIf="loading" class="loading-container">
|
||||
|
@ -27,53 +34,56 @@
|
|||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Propiedades del aula">
|
||||
<div class="two-column-table">
|
||||
<div class="table-row" *ngFor="let clientData of classroomData">
|
||||
<div class="column property">{{ clientData?.property }}</div>
|
||||
<div class="column value">{{ clientData?.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<div class="second-section">
|
||||
<div class="buttons-row">
|
||||
<button mat-flat-button color="primary" (click)="togglePartitionAssistant()" [disabled]="isDiskUsageEmpty">Asistente particionado</button>
|
||||
<button mat-flat-button color="primary" (click)="showBootImage()" [disabled]="isDiskUsageEmpty">Restaurar imagen</button>
|
||||
</div>
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Discos/Particiones</h2>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<h2 *ngIf="isDiskUsageVisible">Disco</h2>
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div class="table-container">
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" >
|
||||
<ng-container *ngIf="column.columnDef !== 'size'">
|
||||
{{ column.cell(image) }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="column.columnDef === 'size'">
|
||||
<mat-chip color="primary" >
|
||||
{{ (image.size / 1024).toFixed(2) }} GB
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="charts-wrapper">
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div class="charts-row">
|
||||
<div *ngFor="let disk of diskUsageData" class="disk-usage">
|
||||
<h3>Disco {{ disk.diskNumber }}</h3>
|
||||
<div class="chart-container">
|
||||
<div class="chart">
|
||||
<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" />
|
||||
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"
|
||||
[attr.stroke-dasharray]="disk.percentage + ', 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" />
|
||||
[attr.stroke-dasharray]="disk.percentage + ', 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">{{ disk.percentage }}%</text>
|
||||
</svg>
|
||||
</div>
|
||||
<p>Usado: {{ disk.used }} GB ({{ disk.percentage }}%)</p>
|
||||
<p>Total: {{ disk.total }} GB</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assistants-container" *ngIf="isPartitionAssistantVisible" #assistantContainer>
|
||||
<app-partition-assistant [data]="clientData" [clientUuid]="clientUuid"></app-partition-assistant>
|
||||
</div>
|
||||
<div *ngIf="isBootImageVisible" #assistantContainer>
|
||||
<app-restore-image [data]="clientData"></app-restore-image>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import {DatePipe} from "@angular/common";
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {PartitionAssistantComponent} from "./partition-assistant/partition-assistant.component";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
|
||||
interface ClientInfo {
|
||||
property: string;
|
||||
|
@ -19,14 +23,46 @@ export class ClientMainViewComponent implements OnInit {
|
|||
isPartitionAssistantVisible: boolean = false;
|
||||
isBootImageVisible: boolean = false;
|
||||
isDiskUsageVisible: boolean = true;
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
generalData: ClientInfo[] = [];
|
||||
networkData: ClientInfo[] = [];
|
||||
classroomData: ClientInfo[] = [];
|
||||
diskUsageData: any[] = [];
|
||||
partitions: any[] = [];
|
||||
commands: any[] = [];
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'diskNumber',
|
||||
header: 'Disco',
|
||||
cell: (partition: any) => `${partition.diskNumber}`,
|
||||
},
|
||||
{
|
||||
columnDef: 'partitionNumber',
|
||||
header: 'Particion',
|
||||
cell: (partition: any) => `${partition.partitionNumber}`
|
||||
},
|
||||
{
|
||||
columnDef: 'description',
|
||||
header: 'Sistema de ficheros',
|
||||
cell: (partition: any) => `${partition.filesystem}`
|
||||
},
|
||||
{
|
||||
columnDef: 'size',
|
||||
header: 'Tamaño',
|
||||
cell: (partition: any) => `${(partition.size / 1024).toFixed(2)} GB`
|
||||
},
|
||||
{
|
||||
columnDef: 'memoryUsage',
|
||||
header: 'Uso',
|
||||
cell: (partition: any) => `${partition.memoryUsage} %`
|
||||
},
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef)];
|
||||
isDiskUsageEmpty: boolean = true;
|
||||
loading: boolean = true; // Variable para controlar el estado de carga
|
||||
loading: boolean = true;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
constructor(private http: HttpClient, private dialog: MatDialog) {
|
||||
const url = window.location.href;
|
||||
const segments = url.split('/');
|
||||
this.clientUuid = segments[segments.length - 1];
|
||||
|
@ -34,11 +70,12 @@ export class ClientMainViewComponent implements OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
this.clientData = history.state.clientData;
|
||||
this.loadPartitions()
|
||||
this.updateGeneralData();
|
||||
this.updateNetworkData();
|
||||
this.updateClassroomData();
|
||||
this.loadCommands()
|
||||
this.calculateDiskUsage();
|
||||
this.loading = false; // Desactivar el estado de carga cuando los datos se hayan procesado
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
updateGeneralData() {
|
||||
|
@ -51,55 +88,37 @@ export class ClientMainViewComponent implements OnInit {
|
|||
{ property: 'Netiface', value: this.clientData?.netiface || '' },
|
||||
{ property: 'Perfil hardware', value: this.clientData?.hardwareProfile?.description || '' },
|
||||
{ property: 'Menú', value: this.clientData?.menu?.description || '' },
|
||||
{ property: 'Fecha de creación', value: this.clientData?.createdAt || '' },
|
||||
{ property: 'Creado por', value: this.clientData?.createdBy || '' }
|
||||
];
|
||||
}
|
||||
|
||||
updateNetworkData() {
|
||||
this.networkData = [
|
||||
{ property: 'Perfil hardware', value: this.clientData?.hardwareProfile?.description || '' },
|
||||
{ property: 'Remote Pc', value: this.clientData.remotePc || '' },
|
||||
{ property: 'Subred', value: this.clientData?.subnet || '' },
|
||||
{ property: 'OGlive', value: '' },
|
||||
{ property: 'Autoexec', value: '' },
|
||||
{ property: 'Repositorio', value: '' },
|
||||
{ property: 'Validación', value: this.clientData?.organizationalUnit?.networkSettings?.validation || '' },
|
||||
{ property: 'Fecha de creación', value: this.clientData?.createdAt || '' },
|
||||
{ property: 'Pxe', value: this.clientData?.template.name || '' },
|
||||
{ property: 'Creado por', value: this.clientData?.createdBy || '' }
|
||||
];
|
||||
}
|
||||
|
||||
updateClassroomData() {
|
||||
this.classroomData = [
|
||||
{ property: 'Url servidor proxy', value: this.clientData?.organizationalUnit?.networkSettings?.proxy || '' },
|
||||
{ property: 'IP DNS', value: this.clientData?.organizationalUnit?.networkSettings?.dns || '' },
|
||||
{ property: 'Máscara de red', value: this.clientData?.organizationalUnit?.networkSettings?.netmask || '' },
|
||||
{ property: 'Router', value: this.clientData?.organizationalUnit?.networkSettings?.router || '' },
|
||||
{ property: 'NTP', value: this.clientData?.organizationalUnit?.networkSettings?.ntp || '' },
|
||||
{ property: 'Modo p2p', value: this.clientData?.organizationalUnit?.networkSettings?.p2pMode || '' },
|
||||
{ property: 'Tiempo p2p', value: this.clientData?.organizationalUnit?.networkSettings?.p2pTime || '' },
|
||||
{ property: 'IP multicast', value: this.clientData?.organizationalUnit?.networkSettings?.mcastIp || '' },
|
||||
{ property: 'Modo multicast', value: this.clientData?.organizationalUnit?.networkSettings?.mcastMode || '' },
|
||||
{ property: 'Puerto multicast', value: this.clientData?.organizationalUnit?.networkSettings?.mcastPort || '' },
|
||||
{ property: 'Velocidad multicast', value: this.clientData?.organizationalUnit?.networkSettings?.mcastSpeed || '' },
|
||||
{ property: 'Perfil hardware', value: this.clientData?.organizationalUnit?.networkSettings?.hardwareProfile?.description || '' }
|
||||
];
|
||||
}
|
||||
|
||||
calculateDiskUsage() {
|
||||
const diskUsageMap = new Map<number, { total: number, used: number }>();
|
||||
|
||||
this.clientData.partitions.forEach((partition: any) => {
|
||||
this.partitions.forEach((partition: any) => {
|
||||
const diskNumber = partition.diskNumber;
|
||||
if (!diskUsageMap.has(diskNumber)) {
|
||||
diskUsageMap.set(diskNumber, { total: 0, used: 0 });
|
||||
}
|
||||
|
||||
const diskData = diskUsageMap.get(diskNumber);
|
||||
|
||||
if (partition.partitionNumber === 0) {
|
||||
diskData!.total = partition.size / (1024 * 1024);
|
||||
diskData!.total = Number((partition.size / 1024).toFixed(2));
|
||||
} else {
|
||||
diskData!.used += partition.size / (1024 * 1024);
|
||||
diskData!.used += Number(((partition.size * (partition.memoryUsage / 100))/ 1024).toFixed(2));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -110,23 +129,44 @@ export class ClientMainViewComponent implements OnInit {
|
|||
this.isDiskUsageEmpty = this.diskUsageData.length === 0;
|
||||
}
|
||||
|
||||
togglePartitionAssistant() {
|
||||
this.isPartitionAssistantVisible = !this.isPartitionAssistantVisible;
|
||||
this.isBootImageVisible = false;
|
||||
if (this.isPartitionAssistantVisible) {
|
||||
setTimeout(() => {
|
||||
this.assistantContainer.nativeElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 200);
|
||||
loadPartitions(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData?.id}`).subscribe({
|
||||
next: data => {
|
||||
this.dataSource = data['hydra:member'];
|
||||
this.partitions = data['hydra:member'];
|
||||
this.calculateDiskUsage();
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al obtener las particiones:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadCommands(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/commands?`).subscribe({
|
||||
next: data => {
|
||||
this.commands = data['hydra:member'];
|
||||
},
|
||||
error: error => {
|
||||
console.error('Error al obtener las particiones:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onCommandSelect(command: any): void {
|
||||
if (command.name === 'Particionar y Formatear') {
|
||||
this.openPartitionDialog();
|
||||
}
|
||||
}
|
||||
|
||||
showBootImage() {
|
||||
this.isBootImageVisible = !this.isBootImageVisible;
|
||||
this.isPartitionAssistantVisible = false;
|
||||
if (this.isBootImageVisible) {
|
||||
setTimeout(() => {
|
||||
this.assistantContainer.nativeElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 0);
|
||||
}
|
||||
openPartitionDialog(): void {
|
||||
const dialogRef = this.dialog.open(PartitionAssistantComponent, {
|
||||
width: '1000px',
|
||||
data: this.clientData['@id']
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result: any) => {
|
||||
console.log('El diálogo se cerró', result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
|
@ -13,7 +11,7 @@
|
|||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
background-color: #cecbcb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
@ -52,8 +50,6 @@
|
|||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -68,15 +64,12 @@
|
|||
.partition-table td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.partition-table select,
|
||||
.partition-table input[type="number"],
|
||||
.partition-table input[type="checkbox"] {
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,62 +1,73 @@
|
|||
<div class="partition-assistant" *ngFor="let disk of disks; let i = index">
|
||||
<h2>Asistente a particionado</h2>
|
||||
<div class="header">
|
||||
<label for="disk-number-{{i}}">Disco {{ disk.diskNumber }}:</label>
|
||||
<span class="disk-size">Tamaño: {{ disk.totalDiskSize }} GB</span>
|
||||
</div>
|
||||
<h2 mat-dialog-title>Asistente de particionado</h2>
|
||||
|
||||
<div class="partition-bar">
|
||||
<div
|
||||
*ngFor="let partition of disk.partitions"
|
||||
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
|
||||
class="partition-segment"
|
||||
>
|
||||
{{ partition.type }} ({{ partition.size }} GB)
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="partition-assistant" *ngFor="let disk of disks; let i = index">
|
||||
<div class="header">
|
||||
<label for="disk-number-{{i}}">Disco {{ disk.diskNumber }}:</label>
|
||||
<span class="disk-size">Tamaño: {{ (disk.totalDiskSize / 1024).toFixed(2) }} GB</span>
|
||||
</div>
|
||||
|
||||
<div class="partition-bar">
|
||||
<div
|
||||
*ngFor="let partition of disk.partitions"
|
||||
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
|
||||
class="partition-segment"
|
||||
>
|
||||
{{ partition.type }} ({{ (partition.size / 1024).toFixed(2) }} GB)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button mat-flat-button color="primary" (click)="addPartition(disk.diskNumber)"> + </button>
|
||||
|
||||
<table class="partition-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partición</th>
|
||||
<th>Tipo partición</th>
|
||||
<th>Tamaño (MB)</th>
|
||||
<th>Uso (%)</th>
|
||||
<th>Formatear</th>
|
||||
<th>Eliminar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let partition of disk.partitions; let j = index">
|
||||
<td>{{ partition.partitionNumber }}</td>
|
||||
<td>
|
||||
<select [(ngModel)]="partition.type">
|
||||
<option value="NTFS">NTFS</option>
|
||||
<option value="LINUX">LINUX</option>
|
||||
<option value="CACHE">CACHE</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
[(ngModel)]="partition.size"
|
||||
(input)="updatePartitionSize(disk.diskNumber, j, partition.size)"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
[(ngModel)]="partition.memoryUsage"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" [(ngModel)]="partition.format" />
|
||||
</td>
|
||||
<td>
|
||||
<button (click)="removePartition(disk.diskNumber, partition)" class="remove-btn">X</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button mat-button (click)="addPartition(disk.diskNumber)">Añadir +</button>
|
||||
|
||||
<table class="partition-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partición</th>
|
||||
<th>Tipo partición</th>
|
||||
<th>Tamaño (GB)</th>
|
||||
<th>Formatear</th>
|
||||
<th>Eliminar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let partition of disk.partitions; let j = index">
|
||||
<td>{{ partition.partitionNumber }}</td>
|
||||
<td>
|
||||
<select [(ngModel)]="partition.type">
|
||||
<option value="NTFS">NTFS</option>
|
||||
<option value="LINUX">LINUX</option>
|
||||
<option value="CACHE">CACHE</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
[(ngModel)]="partition.size"
|
||||
(input)="updatePartitionSize(disk.diskNumber, j, partition.size)"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" [(ngModel)]="partition.format" />
|
||||
</td>
|
||||
<td>
|
||||
<button (click)="removePartition(disk.diskNumber, partition)" class="remove-btn">X</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
|
||||
|
||||
<div class="actions">
|
||||
<button mat-flat-button color="primary" (click)="save()">Guardar</button>
|
||||
</div>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="save()">Guardar</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import {Component, EventEmitter, Inject, Input, OnInit, Output} from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import {MAT_DIALOG_DATA} from "@angular/material/dialog";
|
||||
|
||||
interface Partition {
|
||||
uuid?: string;
|
||||
|
@ -8,6 +9,7 @@ interface Partition {
|
|||
size: number;
|
||||
type: string;
|
||||
sizeBytes: number;
|
||||
memoryUsage: number;
|
||||
format: boolean;
|
||||
color: string;
|
||||
percentage: number;
|
||||
|
@ -20,8 +22,6 @@ interface Partition {
|
|||
})
|
||||
export class PartitionAssistantComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
@Input() data: any;
|
||||
@Input() clientUuid: string | undefined;
|
||||
@Output() dataChange = new EventEmitter<any>();
|
||||
|
||||
errorMessage = '';
|
||||
|
@ -30,14 +30,18 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
|
||||
private apiUrl: string = this.baseUrl + '/partitions';
|
||||
|
||||
constructor(private http: HttpClient, private toastService: ToastrService) {}
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private toastService: ToastrService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadClientData();
|
||||
this.loadPartitions();
|
||||
}
|
||||
|
||||
loadClientData() {
|
||||
const url = `${this.baseUrl}/clients/${this.clientUuid}`;
|
||||
loadPartitions() {
|
||||
const url = `${this.baseUrl}${this.data}`;
|
||||
this.http.get(url).subscribe(
|
||||
(response) => {
|
||||
this.data = response;
|
||||
|
@ -68,6 +72,7 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
uuid: partition.uuid,
|
||||
partitionNumber: partition.partitionNumber,
|
||||
size: this.convertBytesToGB(partition.size),
|
||||
memoryUsage: partition.memoryUsage,
|
||||
type: partition.type || partition.filesystem || 'NTFS',
|
||||
sizeBytes: partition.size,
|
||||
format: false,
|
||||
|
@ -88,12 +93,12 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
}
|
||||
|
||||
convertBytesToGB(bytes: number): number {
|
||||
return bytes / 1024;
|
||||
return bytes
|
||||
}
|
||||
|
||||
updatePartitionPercentages(partitions: Partition[], totalDiskSize: number) {
|
||||
partitions.forEach((partition) => {
|
||||
partition.percentage = (partition.size / totalDiskSize) * 100;
|
||||
partition.percentage = (partition.size / totalDiskSize) * 100
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -102,6 +107,7 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
|
||||
if (disk) {
|
||||
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize);
|
||||
console.log(remainingGB)
|
||||
if (remainingGB > 0) {
|
||||
const maxPartitionNumber =
|
||||
disk.partitions.length > 0 ? Math.max(...disk.partitions.map((p) => p.partitionNumber)) : 0;
|
||||
|
@ -111,6 +117,7 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
partitionNumber: newPartitionNumber,
|
||||
size: 0,
|
||||
type: 'NTFS',
|
||||
memoryUsage: 0,
|
||||
sizeBytes: 0,
|
||||
format: false,
|
||||
color: '#' + Math.floor(Math.random() * 16777215).toString(16),
|
||||
|
@ -136,13 +143,14 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
} else {
|
||||
this.errorMessage = '';
|
||||
partition.size = size;
|
||||
partition.sizeBytes = size * 1024;
|
||||
partition.sizeBytes = size;
|
||||
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRemainingGB(partitions: Partition[], totalDiskSize: number): number {
|
||||
console.log(totalDiskSize)
|
||||
const totalUsedGB = partitions.reduce((acc, partition) => acc + partition.size, 0);
|
||||
return Math.max(0, totalDiskSize - totalUsedGB);
|
||||
}
|
||||
|
@ -182,6 +190,18 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
}
|
||||
|
||||
save() {
|
||||
const invalidDisks = this.disks.filter(disk => {
|
||||
const totalPartitionSize = disk.partitions.reduce((sum, partition) => sum + partition.size, 0);
|
||||
return totalPartitionSize > disk.totalDiskSize;
|
||||
});
|
||||
|
||||
if (invalidDisks.length > 0) {
|
||||
this.errorMessage = 'El tamaño total de las particiones en uno o más discos excede el tamaño total del disco.';
|
||||
return;
|
||||
} else {
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
const modifiedPartitions = this.getModifiedOrNewPartitions();
|
||||
|
||||
if (modifiedPartitions.length === 0) {
|
||||
|
@ -193,32 +213,40 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
const payload = {
|
||||
diskNumber: diskNumber,
|
||||
partitionNumber: partitionNumber,
|
||||
memoryUsage: partition.memoryUsage,
|
||||
size: partition.size,
|
||||
filesystem: partition.type,
|
||||
client: `/clients/${this.clientUuid}`
|
||||
client: this.data['@id']
|
||||
};
|
||||
|
||||
if (isNew) {
|
||||
this.http.post(this.apiUrl, payload).subscribe(
|
||||
(response) => {
|
||||
this.toastService.success('Partición creada exitosamente');
|
||||
this.loadClientData();
|
||||
this.loadPartitions();
|
||||
},
|
||||
(error) => {}
|
||||
(error) => {
|
||||
console.error('Error al crear la partición:', error);
|
||||
this.toastService.error('Error al crear la partición');
|
||||
}
|
||||
);
|
||||
} else if (partition.uuid) {
|
||||
const patchUrl = `${this.apiUrl}/${partition.uuid}`;
|
||||
this.http.patch(patchUrl, payload).subscribe(
|
||||
(response) => {
|
||||
this.toastService.success('Partición actualizada exitosamente');
|
||||
this.loadClientData();
|
||||
this.loadPartitions();
|
||||
},
|
||||
(error) => {}
|
||||
(error) => {
|
||||
console.error('Error al actualizar la partición:', error);
|
||||
this.toastService.error('Error al actualizar la partición');
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
removePartition(diskNumber: number, partition: Partition) {
|
||||
const disk = this.disks.find((d) => d.diskNumber === diskNumber);
|
||||
|
||||
|
@ -233,7 +261,7 @@ export class PartitionAssistantComponent implements OnInit {
|
|||
this.http.delete(deleteUrl).subscribe(
|
||||
(response) => {
|
||||
this.toastService.success('Partición eliminada exitosamente');
|
||||
this.loadClientData();
|
||||
this.loadPartitions();
|
||||
},
|
||||
(error) => {}
|
||||
);
|
||||
|
|
|
@ -151,8 +151,6 @@ export class ClientTabViewComponent {
|
|||
|
||||
handleClientClick(event: MouseEvent, client: any): void {
|
||||
event.stopPropagation();
|
||||
/* const dialogRef = this.dialog.open(ClientViewComponent, { data: { client }, width: '800px', height:'700px' }); */
|
||||
|
||||
this.router.navigate(['client', client.uuid], { state: { clientData: client } });
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue