Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
commit
d47eaf49a7
|
@ -18,8 +18,6 @@ import { CommandsComponent } from './components/commands/main-commands/commands.
|
|||
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
||||
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
||||
import { ImagesComponent } from './components/images/images.component';
|
||||
import {SoftwareComponent} from "./components/software/software.component";
|
||||
import {SoftwareProfileComponent} from "./components/software-profile/software-profile.component";
|
||||
import {OperativeSystemComponent} from "./components/operative-system/operative-system.component";
|
||||
|
@ -67,7 +65,6 @@ const routes: Routes = [
|
|||
{ path: 'clients/deploy-image', component: DeployImageComponent },
|
||||
{ path: 'clients/partition-assistant', component: PartitionAssistantComponent },
|
||||
{ path: 'clients/run-script', component: RunScriptAssistantComponent },
|
||||
{ path: 'clients/:id', component: ClientMainViewComponent },
|
||||
{ path: 'clients/:id/create-image', component: CreateClientImageComponent },
|
||||
{ path: 'repositories', component: RepositoriesComponent },
|
||||
{ path: 'repository/:id', component: MainRepositoryViewComponent },
|
||||
|
|
|
@ -89,7 +89,6 @@ import { CreateTaskComponent } from './components/commands/commands-task/create-
|
|||
import { DetailTaskComponent } from './components/commands/commands-task/detail-task/detail-task.component';
|
||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
||||
import { ImagesComponent } from './components/images/images.component';
|
||||
import { CreateImageComponent } from './components/images/create-image/create-image.component';
|
||||
import { CreateClientImageComponent } from './components/groups/components/client-main-view/create-image/create-image.component';
|
||||
|
@ -143,6 +142,8 @@ import {
|
|||
import { EditImageComponent } from './components/repositories/edit-image/edit-image.component';
|
||||
import { ShowGitImagesComponent } from './components/repositories/show-git-images/show-git-images.component';
|
||||
import { RenameImageComponent } from './components/repositories/rename-image/rename-image.component';
|
||||
import { ClientDetailsComponent } from './components/groups/shared/client-details/client-details.component';
|
||||
import { PartitionTypeOrganizatorComponent } from './components/groups/shared/partition-type-organizator/partition-type-organizator.component';
|
||||
|
||||
export function HttpLoaderFactory(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, './locale/', '.json');
|
||||
|
@ -204,7 +205,6 @@ registerLocaleData(localeEs, 'es-ES');
|
|||
TaskLogsComponent,
|
||||
ServerInfoDialogComponent,
|
||||
StatusComponent,
|
||||
ClientMainViewComponent,
|
||||
ImagesComponent,
|
||||
CreateImageComponent,
|
||||
PartitionAssistantComponent,
|
||||
|
@ -243,7 +243,9 @@ registerLocaleData(localeEs, 'es-ES');
|
|||
SaveScriptComponent,
|
||||
EditImageComponent,
|
||||
ShowGitImagesComponent,
|
||||
RenameImageComponent
|
||||
RenameImageComponent,
|
||||
ClientDetailsComponent,
|
||||
PartitionTypeOrganizatorComponent
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [BrowserModule,
|
||||
|
|
|
@ -10,11 +10,13 @@ import { ConfigService } from '@services/config.service';
|
|||
styleUrls: ['./execute-command.component.css']
|
||||
})
|
||||
export class ExecuteCommandComponent implements OnInit {
|
||||
@Input() clientState: string = 'off';
|
||||
@Input() clientData: any[] = [];
|
||||
@Input() buttonType: 'icon' | 'text' | 'menu-item' = 'icon';
|
||||
@Input() buttonText: string = 'Ejecutar Comandos';
|
||||
@Input() icon: string = 'terminal';
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() runScriptContext: string = '';
|
||||
baseUrl: string;
|
||||
loading: boolean = true;
|
||||
|
||||
|
@ -45,6 +47,48 @@ export class ExecuteCommandComponent implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.clientData = this.clientData || [];
|
||||
this.updateCommandStates();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.updateCommandStates();
|
||||
}
|
||||
|
||||
private updateCommandStates(): void {
|
||||
let states: string[] = [];
|
||||
|
||||
if (this.clientData.length > 0) {
|
||||
states = this.clientData.map(client => client.status);
|
||||
} else if (this.clientState) {
|
||||
states = [this.clientState];
|
||||
}
|
||||
|
||||
const allOffOrDisconnected = states.every(state => state === 'off' || state === 'disconnected');
|
||||
const allSameState = states.every(state => state === states[0]);
|
||||
const multipleClients = this.clientData.length > 1;
|
||||
|
||||
this.arrayCommands = this.arrayCommands.map(command => {
|
||||
if (allOffOrDisconnected) {
|
||||
command.disabled = command.slug !== 'power-on';
|
||||
} else if (allSameState) {
|
||||
if (states[0] === 'off' || states[0] === 'disconnected') {
|
||||
command.disabled = command.slug !== 'power-on';
|
||||
} else {
|
||||
command.disabled = !['power-off', 'reboot', 'login', 'create-image', 'deploy-image', 'partition', 'run-script'].includes(command.slug);
|
||||
}
|
||||
} else {
|
||||
if (command.slug === 'create-image') {
|
||||
command.disabled = multipleClients;
|
||||
} else if (
|
||||
['power-on', 'power-off', 'reboot', 'login', 'deploy-image', 'partition', 'run-script'].includes(command.slug)
|
||||
) {
|
||||
command.disabled = false;
|
||||
} else {
|
||||
command.disabled = true;
|
||||
}
|
||||
}
|
||||
return command;
|
||||
});
|
||||
}
|
||||
|
||||
onCommandSelect(action: any): void {
|
||||
|
@ -137,7 +181,7 @@ export class ExecuteCommandComponent implements OnInit {
|
|||
const clientDataToSend = this.clientData.map(client => ({
|
||||
name: client.name,
|
||||
mac: client.mac,
|
||||
uuid: '/clients/'+client.uuid,
|
||||
uuid: '/clients/' + client.uuid,
|
||||
status: client.status,
|
||||
partitions: client.partitions,
|
||||
firmwareType: client.firmwareType,
|
||||
|
@ -161,7 +205,7 @@ export class ExecuteCommandComponent implements OnInit {
|
|||
const clientDataToSend = this.clientData.map(client => ({
|
||||
name: client.name,
|
||||
mac: client.mac,
|
||||
uuid: '/clients/'+client.uuid,
|
||||
uuid: '/clients/' + client.uuid,
|
||||
status: client.status,
|
||||
partitions: client.partitions,
|
||||
ip: client.ip
|
||||
|
@ -178,18 +222,19 @@ export class ExecuteCommandComponent implements OnInit {
|
|||
const clientDataToSend = this.clientData.map(client => ({
|
||||
name: client.name,
|
||||
mac: client.mac,
|
||||
uuid: '/clients/'+client.uuid,
|
||||
uuid: '/clients/' + client.uuid,
|
||||
status: client.status,
|
||||
partitions: client.partitions,
|
||||
ip: client.ip
|
||||
}));
|
||||
|
||||
this.router.navigate(['/clients/run-script'], {
|
||||
queryParams: { clientData: JSON.stringify(clientDataToSend) }
|
||||
queryParams: {
|
||||
clientData: JSON.stringify(clientDataToSend) ,
|
||||
runScriptContext: this.runScriptContext
|
||||
}
|
||||
}).then(() => {
|
||||
console.log('Navigated to run script with data:', clientDataToSend);
|
||||
console.log('Navigated to run script with data:', clientDataToSend, 'runScriptContext:', this.runScriptContext);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,351 +0,0 @@
|
|||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.client-container {
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0rem 1rem 0rem 0.5rem;
|
||||
}
|
||||
|
||||
.client-icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
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;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon-pc {
|
||||
font-size: 25px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.client-title h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.client-title p {
|
||||
margin: 2px 0;
|
||||
font-size: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.client-info {
|
||||
margin: 20px 0;
|
||||
border-radius: 12px;
|
||||
background-color: #f5f7fa;
|
||||
padding: 20px;
|
||||
border: 2px solid #d1d9e6;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.two-column-table {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
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;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.column.property {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.column.value {
|
||||
text-align: right;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.mat-tab-group {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.mat-tab-body-wrapper {
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
.info-section h2 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 10px;
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.info-section p {
|
||||
font-size: 1rem;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.second-section {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.client-button-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.buttons-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.buttons-row button {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.circle-bg {
|
||||
fill: none;
|
||||
stroke: #f0f0f0;
|
||||
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.7rem;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.disk-usage h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 1.2rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@keyframes progress {
|
||||
0% {
|
||||
stroke-dasharray: 0, 100;
|
||||
}
|
||||
}
|
||||
|
||||
.assistants-container {
|
||||
background-color: #fff;
|
||||
margin-top: 10px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.circle-bg {
|
||||
fill: none;
|
||||
stroke: #f0f0f0;
|
||||
stroke-width: 3.8;
|
||||
}
|
||||
|
||||
.circle {
|
||||
fill: none;
|
||||
stroke-width: 3.8;
|
||||
stroke-linecap: round;
|
||||
animation: progress 1s ease-out forwards;
|
||||
}
|
||||
|
||||
.partition-0 {
|
||||
stroke: #00bfa5;
|
||||
}
|
||||
|
||||
.partition-1 {
|
||||
stroke: #ff6f61;
|
||||
}
|
||||
|
||||
.partition-2 {
|
||||
stroke: #ffb400;
|
||||
}
|
||||
|
||||
.partition-3 {
|
||||
stroke: #3498db;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
fill: #333;
|
||||
font-size: 0.7rem;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.disk-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
background-color: #f5f7fa;
|
||||
border: 2px solid #d1d9e6;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 3;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
table.mat-elevation-z8 {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mat-header-cell {
|
||||
background-color: #d1d9e6 !important;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mat-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mat-chip {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
background-color: white;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
justify-self: center;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-left: 0.5em;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
padding: 8px 18px 8px 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: transform 0.3s ease;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.back-button:hover:not(:disabled) {
|
||||
background-color: #485ac0d7;
|
||||
}
|
||||
|
||||
.back-button:disabled {
|
||||
background-color: #ced0df;
|
||||
cursor: not-allowed;
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<app-loading [isLoading]="loading"></app-loading>
|
||||
|
||||
<div class="client-container">
|
||||
<div class="header-container">
|
||||
<h2 class="title">{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}</h2>
|
||||
<div class="client-button-row">
|
||||
<button class="back-button" (click)="navigateToGroups()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
{{ 'back' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div *ngIf="!loading" class="client-info">
|
||||
<div class="info-section">
|
||||
<div class="two-column-table">
|
||||
<div class="table-row" *ngFor="let clientData of generalData">
|
||||
<div class="column property">{{ clientData?.property }}</div>
|
||||
<div class="column value">{{ clientData?.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two-column-table">
|
||||
<div class="table-row" *ngFor="let clientData of networkData">
|
||||
<div class="column property">{{ clientData?.property }}</div>
|
||||
<div class="column value">{{ clientData?.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Discos/Particiones</h2>
|
||||
</div>
|
||||
|
||||
<div class="disk-container">
|
||||
<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-container">
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div *ngFor="let disk of chartDisk" class="disk-usage">
|
||||
<ngx-charts-pie-chart class="chart" [view]="view" [results]="disk.chartData" [doughnut]="true">
|
||||
</ngx-charts-pie-chart>
|
||||
|
||||
<h3>Disco {{ disk.diskNumber }}</h3>
|
||||
<p>Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)</p>
|
||||
<p>Total: {{ disk.total }} GB</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="header-container">
|
||||
<div class="header-container-title">
|
||||
<h2>
|
||||
{{ 'runScript' | translate }}
|
||||
{{ 'runScript' | translate }} {{this.runScriptContext}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
|
|
|
@ -32,6 +32,7 @@ export class RunScriptAssistantComponent {
|
|||
newScript: string = '';
|
||||
selection = new SelectionModel(true, []);
|
||||
parameterNames: string[] = Object.keys(this.parameters);
|
||||
runScriptContext: string = '';
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
|
@ -46,6 +47,9 @@ export class RunScriptAssistantComponent {
|
|||
if (params['clientData']) {
|
||||
this.clientData = JSON.parse(params['clientData']);
|
||||
}
|
||||
if (params['runScriptContext']) {
|
||||
this.runScriptContext = params['runScriptContext'];
|
||||
}
|
||||
});
|
||||
this.clientId = this.clientData?.length ? this.clientData[0]['@id'] : null;
|
||||
this.clientData.forEach((client: { selected: boolean; status: string}) => {
|
||||
|
|
|
@ -198,11 +198,16 @@
|
|||
<mat-icon>delete</mat-icon>
|
||||
<span>{{ 'delete' | translate }}</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="openPartitionTypeModal($event, selectedNode)">
|
||||
<mat-icon>storage</mat-icon>
|
||||
<span>{{ 'partitions' | translate }}</span>
|
||||
</button>
|
||||
<app-execute-command [clientData]="selectedNode?.clients || []" [buttonType]="'menu-item'"
|
||||
[buttonText]="'Ejecutar comandos'" [icon]="'terminal'" [disabled]="!((selectedNode?.clients ?? []).length > 0)">
|
||||
[buttonText]="'Ejecutar comandos'" [icon]="'terminal'"
|
||||
[disabled]="!((selectedNode?.clients ?? []).length > 0)" [runScriptContext]="selectedNode?.name || ''"
|
||||
[runScriptContext]="getRunScriptContext(selectedNode?.clients || [])">
|
||||
</app-execute-command>
|
||||
</mat-menu>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-divider [vertical]="true"></mat-divider>
|
||||
|
@ -218,7 +223,8 @@
|
|||
</span>
|
||||
<div class="view-type-container">
|
||||
<app-execute-command [clientData]="selection.selected" [buttonType]="'text'"
|
||||
[buttonText]="'Ejecutar comandos'" [disabled]="selection.selected.length === 0"></app-execute-command>
|
||||
[buttonText]="'Ejecutar comandos'" [disabled]="selection.selected.length === 0"
|
||||
[runScriptContext]="getRunScriptContext(selection.selected)"></app-execute-command>
|
||||
<mat-button-toggle-group name="viewType" aria-label="View Type" [hideSingleSelectionIndicator]="true"
|
||||
(change)="toggleView($event.value)">
|
||||
<mat-button-toggle value="list" [disabled]="currentView === 'list'">
|
||||
|
@ -260,8 +266,10 @@
|
|||
<span class="client-ip">{{ client.mac }}</span>
|
||||
<div class="action-icons">
|
||||
|
||||
<app-execute-command [clientData]="[client]" [buttonType]="'icon'" [icon]="'terminal'"
|
||||
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"></app-execute-command>
|
||||
<app-execute-command [clientState]="client.status" [clientData]="[client]"
|
||||
[buttonType]="'icon'" [icon]="'terminal'"
|
||||
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"
|
||||
[runScriptContext]="getRunScriptContext([client])"></app-execute-command>
|
||||
|
||||
<button
|
||||
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"
|
||||
|
@ -395,8 +403,10 @@
|
|||
mat-icon-button [matMenuTriggerFor]="clientMenu" color="primary">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<app-execute-command [clientData]="[client]" [buttonType]="'icon'" [icon]="'terminal'"
|
||||
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))">
|
||||
<app-execute-command [clientState]="client.status" [clientData]="[client]" [buttonType]="'icon'"
|
||||
[icon]="'terminal'"
|
||||
[disabled]="selection.selected.length > 1 || (selection.selected.length === 1 && !selection.isSelected(client))"
|
||||
[runScriptContext]="getRunScriptContext([client])">
|
||||
</app-execute-command>
|
||||
<mat-menu #clientMenu="matMenu">
|
||||
<button mat-menu-item (click)="onEditClick($event, client.type, client.uuid)">
|
||||
|
@ -442,4 +452,4 @@
|
|||
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, OnInit, OnDestroy, ViewChild, QueryList, ViewChildren, ChangeDetectorRef} from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ViewChild, QueryList, ViewChildren, ChangeDetectorRef } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
@ -26,6 +26,8 @@ import { Subject } from 'rxjs';
|
|||
import { ConfigService } from '@services/config.service';
|
||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||
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';
|
||||
|
||||
enum NodeType {
|
||||
OrganizationalUnit = 'organizational-unit',
|
||||
|
@ -85,6 +87,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||
{ value: 'windows-session', name: 'Windows Session' },
|
||||
{ value: 'busy', name: 'Ocupado' },
|
||||
{ value: 'mac', name: 'Mac' },
|
||||
{ value: 'disconnected', name: 'Desconectado' }
|
||||
];
|
||||
|
||||
displayedColumns: string[] = ['select', 'status', 'ip', 'firmwareType', 'name', 'oglive', 'subnet', 'pxeTemplate', 'actions'];
|
||||
|
@ -181,7 +184,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||
|
||||
const index = this.arrayClients.findIndex(client => client['@id'] === clientUuid);
|
||||
if (index !== -1) {
|
||||
const updatedClient = {...this.arrayClients[index], status};
|
||||
const updatedClient = { ...this.arrayClients[index], status };
|
||||
this.arrayClients = [
|
||||
...this.arrayClients.slice(0, index),
|
||||
updatedClient,
|
||||
|
@ -608,7 +611,10 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||
|
||||
onShowClientDetail(event: MouseEvent, client: Client): void {
|
||||
event.stopPropagation();
|
||||
this.router.navigate(['clients', client.uuid], { state: { clientData: client } });
|
||||
this.dialog.open(ClientDetailsComponent, {
|
||||
width: '1200px',
|
||||
data: { clientData: client },
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
@ -823,4 +829,36 @@ export class GroupsComponent implements OnInit, OnDestroy {
|
|||
clientSearchStatusInput.value = null;
|
||||
this.fetchClientsForNode(this.selectedNode);
|
||||
}
|
||||
|
||||
getRunScriptContext(clientData: any[]): string {
|
||||
const selectedClientNames = clientData.map(client => client.name);
|
||||
|
||||
if (clientData.length === 1) {
|
||||
return selectedClientNames[0];
|
||||
} else if (
|
||||
clientData.length === this.selectedClients.data.length &&
|
||||
selectedClientNames.every(name => this.selectedClients.data.some(c => c.name === name))
|
||||
) {
|
||||
return this.selectedNode?.name || '';
|
||||
} else if (clientData.length > 1) {
|
||||
return selectedClientNames.join(', ');
|
||||
} else if (this.selectedNode?.name && clientData.length === 0) {
|
||||
return this.selectedNode.name;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
openPartitionTypeModal(event: MouseEvent, node: TreeNode | null = null): void {
|
||||
event.stopPropagation();
|
||||
|
||||
const simplifiedClientsData = node?.clients?.map((client: any) => ({
|
||||
name: client.name,
|
||||
partitions: client.partitions
|
||||
}));
|
||||
|
||||
this.dialog.open(PartitionTypeOrganizatorComponent, {
|
||||
width: '1200px',
|
||||
data: simplifiedClientsData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
.modal-content {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em 4em 2em 4em;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.client-info-card {
|
||||
background-color: #f1f1f1;
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.info-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.info-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.info-pair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 550;
|
||||
color: #3f51b5;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 1rem;
|
||||
color: #222;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.disk-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.disk-layout {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 32px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1 1 500px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
flex: 1 1 300px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 2px solid #f3f3f3;
|
||||
border-radius: 0px !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.charts-container.single-disk {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.charts-container.single-disk .disk-usage {
|
||||
max-width: 400px;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
flex: 1 1 260px;
|
||||
min-width: 240px;
|
||||
padding: 16px;
|
||||
background: #f9f9f9;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 160px;
|
||||
margin-bottom: 12px;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<mat-dialog-content class="modal-content">
|
||||
<app-loading [isLoading]="loading"></app-loading>
|
||||
|
||||
<div class="client-container" *ngIf="!loading">
|
||||
<div class="header-container">
|
||||
<h2 class="title">{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="client-info-card">
|
||||
<div class="info-columns">
|
||||
<div class="info-column">
|
||||
<div class="info-pair" *ngFor="let data of generalData">
|
||||
<div class="label">{{ data?.property }}</div>
|
||||
<div class="value">{{ data?.value || '--' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<div class="info-pair" *ngFor="let data of networkData">
|
||||
<div class="label">{{ data?.property }}</div>
|
||||
<div class="value">{{ data?.value || '--' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-header">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Discos/Particiones</h2>
|
||||
</div>
|
||||
|
||||
<div class="disk-container">
|
||||
<div class="disk-layout">
|
||||
<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-container" [ngClass]="{'single-disk': chartDisk.length === 1}">
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div *ngFor="let disk of chartDisk" class="disk-usage">
|
||||
<ngx-charts-pie-chart class="chart" [view]="view" [results]="disk.chartData" [doughnut]="true">
|
||||
</ngx-charts-pie-chart>
|
||||
<h3>Disco {{ disk.diskNumber }}</h3>
|
||||
<p>Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)</p>
|
||||
<p>Total: {{ disk.total }} GB</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
|
@ -0,0 +1,52 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { ClientDetailsComponent } from './client-details.component';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { LoadingComponent } from 'src/app/shared/loading/loading.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
|
||||
describe('ClientDetailsComponent', () => {
|
||||
let component: ClientDetailsComponent;
|
||||
let fixture: ComponentFixture<ClientDetailsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const mockConfigService = {
|
||||
apiUrl: 'http://mock-api-url',
|
||||
mercureUrl: 'http://mock-mercure-url'
|
||||
};
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ClientDetailsComponent, LoadingComponent],
|
||||
imports: [
|
||||
MatDialogModule,
|
||||
HttpClientTestingModule,
|
||||
ToastrModule.forRoot(),
|
||||
MatDividerModule,
|
||||
TranslateModule.forRoot(),
|
||||
CommonModule,
|
||||
MatTableModule,
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: {} },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: { data: { id: 123 } } },
|
||||
{ provide: ConfigService, useValue: mockConfigService }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ClientDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { MatTableDataSource } from "@angular/material/table";
|
||||
|
@ -14,11 +15,11 @@ interface ClientInfo {
|
|||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-client-main-view',
|
||||
templateUrl: './client-main-view.component.html',
|
||||
styleUrl: './client-main-view.component.css'
|
||||
selector: 'app-client-details',
|
||||
templateUrl: './client-details.component.html',
|
||||
styleUrl: './client-details.component.css'
|
||||
})
|
||||
export class ClientMainViewComponent implements OnInit {
|
||||
export class ClientDetailsComponent {
|
||||
baseUrl: string;
|
||||
@ViewChild('assistantContainer') assistantContainer!: ElementRef;
|
||||
clientUuid: string;
|
||||
|
@ -34,7 +35,7 @@ export class ClientMainViewComponent implements OnInit {
|
|||
partitions: any[] = [];
|
||||
commands: any[] = [];
|
||||
chartDisk: any[] = [];
|
||||
view: [number, number] = [300, 200];
|
||||
view: [number, number] = [260, 160];
|
||||
showLegend: boolean = true;
|
||||
|
||||
arrayCommands: any[] = [
|
||||
|
@ -93,7 +94,8 @@ export class ClientMainViewComponent implements OnInit {
|
|||
private dialog: MatDialog,
|
||||
private configService: ConfigService,
|
||||
private router: Router,
|
||||
private toastService: ToastrService
|
||||
private toastService: ToastrService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { clientData: any }
|
||||
) {
|
||||
this.baseUrl = this.configService.apiUrl;
|
||||
const url = window.location.href;
|
||||
|
@ -102,14 +104,18 @@ export class ClientMainViewComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.clientData = history.state.clientData['@id'];
|
||||
this.loadClient(this.clientData)
|
||||
this.loadCommands()
|
||||
this.calculateDiskUsage();
|
||||
this.loading = false;
|
||||
if (this.data && this.data.clientData) {
|
||||
this.clientData = this.data.clientData;
|
||||
this.updateGeneralData();
|
||||
this.updateNetworkData();
|
||||
this.calculateDiskUsage();
|
||||
this.loadPartitions();
|
||||
this.loading = false;
|
||||
} else {
|
||||
console.error('No se recibieron datos del cliente.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
loadClient = (uuid: string) => {
|
||||
this.http.get<any>(`${this.baseUrl}${uuid}`).subscribe({
|
||||
next: data => {
|
||||
|
@ -125,10 +131,6 @@ export class ClientMainViewComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
navigateToGroups() {
|
||||
this.router.navigate(['/groups']);
|
||||
}
|
||||
|
||||
updateGeneralData() {
|
||||
this.generalData = [
|
||||
{ property: 'Nombre', value: this.clientData?.name || '' },
|
||||
|
@ -205,7 +207,12 @@ export class ClientMainViewComponent implements OnInit {
|
|||
}
|
||||
|
||||
loadPartitions(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData?.id}&order[diskNumber, partitionNumber]=ASC`).subscribe({
|
||||
if (!this.clientData?.id) {
|
||||
console.error('El ID del cliente no está disponible.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData.id}&order[diskNumber, partitionNumber]=ASC`).subscribe({
|
||||
next: data => {
|
||||
const filteredPartitions = data['hydra:member'].filter((partition: any) => partition.partitionNumber !== 0);
|
||||
this.dataSource = filteredPartitions;
|
|
@ -0,0 +1,46 @@
|
|||
.modal-content {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em 4em 2em 4em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.client-section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.client-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: 500;
|
||||
color: #3f51b5;
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.partition-table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.partition-table th,
|
||||
.partition-table td {
|
||||
padding: 0.75em 1em;
|
||||
text-align: left;
|
||||
font-size: 0.95em;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.partition-table th {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: 500;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.partition-table tr:hover td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<mat-dialog-content class="modal-content">
|
||||
<div *ngFor="let client of simplifiedData" class="client-section">
|
||||
<h3 class="client-title">{{ client.name }}</h3>
|
||||
|
||||
<table mat-table [dataSource]="client.partitions" class="mat-elevation-z1 partition-table">
|
||||
|
||||
<ng-container matColumnDef="diskNumber">
|
||||
<th mat-header-cell *matHeaderCellDef>Disco</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.diskNumber }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="partitionNumber">
|
||||
<th mat-header-cell *matHeaderCellDef>Partición</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.partitionNumber }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="partitionCode">
|
||||
<th mat-header-cell *matHeaderCellDef>Tipo</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.partitionCode }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="size">
|
||||
<th mat-header-cell *matHeaderCellDef>Tamaño</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.size }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="filesystem">
|
||||
<th mat-header-cell *matHeaderCellDef>Fyle System</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.filesystem }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="memoryUsage">
|
||||
<th mat-header-cell *matHeaderCellDef>Memoria</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.memoryUsage }}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</mat-dialog-content>
|
|
@ -0,0 +1,42 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { PartitionTypeOrganizatorComponent } from './partition-type-organizator.component';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
|
||||
describe('PartitionTypeOrganizatorComponent', () => {
|
||||
let component: PartitionTypeOrganizatorComponent;
|
||||
let fixture: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PartitionTypeOrganizatorComponent],
|
||||
imports: [
|
||||
MatDialogModule,
|
||||
MatTableModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: {} },
|
||||
{
|
||||
provide: MAT_DIALOG_DATA,
|
||||
useValue: [
|
||||
{
|
||||
name: 'Client 1',
|
||||
partitions: [
|
||||
{ diskNumber: 1, partitionNumber: 1, partitionCode: 'EXT4', size: 1024, filesystem: 'ext4', memoryUsage: 50 },
|
||||
{ diskNumber: 1, partitionNumber: 2, partitionCode: 'NTFS', size: 2048, filesystem: 'ntfs', memoryUsage: 75 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PartitionTypeOrganizatorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-partition-type-organizator',
|
||||
templateUrl: './partition-type-organizator.component.html',
|
||||
styleUrl: './partition-type-organizator.component.css'
|
||||
})
|
||||
export class PartitionTypeOrganizatorComponent implements OnInit {
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any) { }
|
||||
|
||||
public simplifiedData: any[] = [];
|
||||
displayedColumns: string[] = ['diskNumber', 'partitionNumber', 'partitionCode', 'size', 'filesystem', 'memoryUsage'];
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log('Data recibida en modal:', this.data);
|
||||
|
||||
this.simplifiedData = this.data.map((client: any) => ({
|
||||
name: client.name,
|
||||
partitions: client.partitions.map((p: any) => ({
|
||||
diskNumber: p.diskNumber,
|
||||
partitionNumber: p.partitionNumber,
|
||||
partitionCode: p.partitionCode,
|
||||
size: p.size,
|
||||
filesystem: p.filesystem,
|
||||
memoryUsage: p.memoryUsage
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -272,7 +272,7 @@
|
|||
"classroomOption": "Classroom",
|
||||
"clientsGroupOption": "PC Groups",
|
||||
"roomMapOption": "Classroom map",
|
||||
"clientDetailsTitle": "Client details",
|
||||
"clientDetailsTitle": "Details of",
|
||||
"commandsButton": "Commands",
|
||||
"networkPropertiesTab": "Network properties",
|
||||
"disksPartitionsTitle": "Disks/Partitions",
|
||||
|
@ -482,5 +482,6 @@
|
|||
"processes": "Processes",
|
||||
"usedPercentageLabel": "Used",
|
||||
"errorLoadingData": "Error fetching data. Service not available",
|
||||
"repositoryTitleStep": "On this screen you can manage image repositories."
|
||||
"repositoryTitleStep": "On this screen you can manage image repositories.",
|
||||
"partitions": "Particiones"
|
||||
}
|
||||
|
|
|
@ -276,7 +276,7 @@
|
|||
"classroomOption": "Aula",
|
||||
"clientsGroupOption": "Grupos de PCs",
|
||||
"roomMapOption": "Plano de aula",
|
||||
"clientDetailsTitle": "Datos de cliente",
|
||||
"clientDetailsTitle": "Datos de",
|
||||
"commandsButton": "Comandos",
|
||||
"excludeParentChanges": "Excluir cambios de las unidades organizativas superiores",
|
||||
"networkPropertiesTab": "Propiedades de red",
|
||||
|
@ -484,5 +484,6 @@
|
|||
"processes": "Procesos",
|
||||
"usedPercentageLabel": "Usado",
|
||||
"errorLoadingData": "Error al cargar los datos. Servicio inactivo",
|
||||
"repositoryTitleStep": "En esta pantalla se pueden gestionar los repositorios de imágenes."
|
||||
"repositoryTitleStep": "En esta pantalla se pueden gestionar los repositorios de imágenes.",
|
||||
"partitions": "Particiones"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue