diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts
index cbd353a..3a0ca83 100644
--- a/ogWebconsole/src/app/app-routing.module.ts
+++ b/ogWebconsole/src/app/app-routing.module.ts
@@ -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 },
diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index 2bd3f99..0203339 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -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,
diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
index 90af7b0..e459e66 100644
--- a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
+++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts
@@ -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);
});
}
-
-
}
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.css
deleted file mode 100644
index 664c4d0..0000000
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.css
+++ /dev/null
@@ -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;
-}
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html
deleted file mode 100644
index f0c7970..0000000
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.html
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
{{ clientData?.property }}
-
{{ clientData?.value }}
-
-
-
-
-
{{ clientData?.property }}
-
{{ clientData?.value }}
-
-
-
-
-
-
-
-
-
-
- {{ column.header }} |
-
-
- {{ column.cell(image) }}
-
-
-
- {{ (image.size / 1024).toFixed(2) }} GB
-
-
- |
-
-
-
-
-
-
-
-
0">
-
-
-
-
-
Disco {{ disk.diskNumber }}
-
Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)
-
Total: {{ disk.total }} GB
-
-
-
-
-
-
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html
index e41eea9..0ad4fbf 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html
+++ b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html
@@ -3,7 +3,7 @@
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts
index 0235ab9..677b65e 100644
--- a/ogWebconsole/src/app/components/groups/groups.component.ts
+++ b/ogWebconsole/src/app/components/groups/groups.component.ts
@@ -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
+ });
+ }
}
diff --git a/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.css b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.css
new file mode 100644
index 0000000..902cb8f
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.css
@@ -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;
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html
new file mode 100644
index 0000000..9e8edc0
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
{{ data?.property }}
+
{{ data?.value || '--' }}
+
+
+
+
+
{{ data?.property }}
+
{{ data?.value || '--' }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ column.header }} |
+
+
+ {{ column.cell(image) }}
+
+
+
+ {{ (image.size / 1024).toFixed(2) }} GB
+
+
+ |
+
+
+
+
+
+
+
+
0">
+
+
+
+
Disco {{ disk.diskNumber }}
+
Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)
+
Total: {{ disk.total }} GB
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.spec.ts
new file mode 100644
index 0000000..bd363f4
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.spec.ts
@@ -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;
+
+ 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();
+ });
+});
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.ts
similarity index 90%
rename from ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts
rename to ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.ts
index bf77392..919e84e 100644
--- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts
+++ b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.ts
@@ -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(`${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(`${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(`${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;
diff --git a/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.css b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.css
new file mode 100644
index 0000000..aa2921c
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.css
@@ -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;
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.html b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.html
new file mode 100644
index 0000000..6f25123
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.html
@@ -0,0 +1,42 @@
+
+
+
{{ client.name }}
+
+
+
+
+ Disco |
+ {{ element.diskNumber }} |
+
+
+
+ Partición |
+ {{ element.partitionNumber }} |
+
+
+
+ Tipo |
+ {{ element.partitionCode }} |
+
+
+
+ Tamaño |
+ {{ element.size }} |
+
+
+
+ Fyle System |
+ {{ element.filesystem }} |
+
+
+
+ Memoria |
+ {{ element.memoryUsage }} |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.spec.ts
new file mode 100644
index 0000000..aba862d
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.spec.ts
@@ -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();
+ });
+});
\ No newline at end of file
diff --git a/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.ts b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.ts
new file mode 100644
index 0000000..2f47bd9
--- /dev/null
+++ b/ogWebconsole/src/app/components/groups/shared/partition-type-organizator/partition-type-organizator.component.ts
@@ -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
+ }))
+ }));
+ }
+
+
+}
diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json
index 6bc649a..95a731c 100644
--- a/ogWebconsole/src/locale/en.json
+++ b/ogWebconsole/src/locale/en.json
@@ -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"
}
diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json
index fb6d793..28a8640 100644
--- a/ogWebconsole/src/locale/es.json
+++ b/ogWebconsole/src/locale/es.json
@@ -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"
}