Compare commits

...

2 Commits

Author SHA1 Message Date
Manuel Aranda Rosales 242f7a374c refs #1972. General styles changed. New 2025
testing/ogGui-multibranch/pipeline/head There was a failure building this commit Details
2025-05-08 12:56:14 +02:00
Manuel Aranda Rosales a5730a1de4 refs #1971. Trace UX changes. Some improvements and fixed filters 2025-05-08 12:54:58 +02:00
17 changed files with 435 additions and 111 deletions

View File

@ -149,6 +149,7 @@ import { ShowTaskScheduleComponent } from './components/commands/commands-task/s
import { ShowTaskScriptComponent } from './components/commands/commands-task/show-task-script/show-task-script.component';
import { CreateTaskScriptComponent } from './components/commands/commands-task/create-task-script/create-task-script.component';
import { ViewParametersModalComponent } from './components/commands/commands-task/show-task-script/view-parameters-modal/view-parameters-modal.component';
import { OutputDialogComponent } from './components/task-logs/output-dialog/output-dialog.component';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
@ -255,7 +256,8 @@ registerLocaleData(localeEs, 'es-ES');
ShowTaskScheduleComponent,
ShowTaskScriptComponent,
CreateTaskScriptComponent,
ViewParametersModalComponent
ViewParametersModalComponent,
OutputDialogComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -0,0 +1,7 @@
<h1 mat-dialog-title>{{ 'inputDetails' | translate }}</h1>
<div mat-dialog-content>
<pre>{{ data.input | json }}</pre>
</div>
<div mat-dialog-actions align="end">
<button class="ordinary-button" (click)="close()">{{ 'closeButton' | translate }}</button>
</div>

View File

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

View File

@ -0,0 +1,18 @@
import {Component, Inject} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
@Component({
selector: 'app-output-dialog',
templateUrl: './output-dialog.component.html',
styleUrl: './output-dialog.component.css'
})
export class OutputDialogComponent {
constructor(
public dialogRef: MatDialogRef<OutputDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { input: any }
) {}
close(): void {
this.dialogRef.close();
}
}

View File

@ -19,38 +19,54 @@
<div class="search-container">
<mat-form-field appearance="fill" class="search-select" joyrideStep="clientSelectStep"
text="{{ 'clientSelectStepText' | translate }}">
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto"
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" #commandClientInput
placeholder="{{ 'filterClientPlaceholder' | translate }}">
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient"
(optionSelected)="onOptionClientSelected($event.option.value)">
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
{{ client.name }}
<div style="display: flex; flex-direction: column;">
<span>{{ client.name }}</span>
<span style="font-size: 0.8rem; color: gray;">
{{ client.ip }} — {{ client.mac }}
</span>
</div>
</mat-option>
</mat-autocomplete>
<button *ngIf="commandClientInput.value" mat-icon-button matSuffix aria-label="Clear input search"
(click)="clearClientFilter($event, commandClientInput)">
<mat-icon>close</mat-icon>
</button>
<mat-hint>Por favor, ingrese el nombre del cliente</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="search-select" joyrideStep="commandSelectStep"
text="{{ 'commandSelectStepText' | translate }}">
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto"
placeholder="{{ 'filterCommandPlaceholder' | translate }}">
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand"
(optionSelected)="onOptionCommandSelected($event.option.value)">
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
{{ command.name }}
<mat-form-field appearance="fill" class="search-select">
<mat-label>{{ 'commandSelectStepText' | translate }}</mat-label>
<mat-select (selectionChange)="onOptionCommandSelected($event.value)" #commandSearchInput>
<mat-option *ngFor="let command of filteredCommands2" [value]="command">
{{ translateCommand(command.name) }}
</mat-option>
</mat-autocomplete>
</mat-select>
<button *ngIf="commandSearchInput.value" mat-icon-button matSuffix aria-label="Clear input search"
(click)="clearCommandFilter($event, commandSearchInput)">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field appearance="fill" class="search-boolean">
<mat-label i18n="@@searchLabel">Estado</mat-label>
<mat-select [(ngModel)]="filters['status']" (selectionChange)="loadTraces()" placeholder="Seleccionar opción">
<mat-option [value]="undefined">Todos</mat-option>
<mat-select (selectionChange)="onOptionStatusSelected($event.value)" placeholder="Seleccionar opción" #commandStatusInput>
<mat-option [value]="'failed'">Fallido</mat-option>
<mat-option [value]="'pending'">Pendiente de ejecutar</mat-option>
<mat-option [value]="'in-progress'">Ejecutando</mat-option>
<mat-option [value]="'success'">Completado con éxito</mat-option>
<mat-option [value]="'cancelled'">Cancelado</mat-option>
</mat-select>
<button *ngIf="commandStatusInput.value" mat-icon-button matSuffix aria-label="Clear input search"
(click)="clearStatusFilter($event, commandStatusInput)">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
@ -82,10 +98,10 @@
'chip-cancelled': trace.status === 'cancelled'
}">
{{
trace.status === 'failed' ? 'Fallido' :
trace.status === 'failed' ? 'Error' :
trace.status === 'in-progress' ? 'En ejecución' :
trace.status === 'success' ? 'Finalizado con éxito' :
trace.status === 'pending' ? 'Pendiente de ejecutar' :
trace.status === 'success' ? 'Completado' :
trace.status === 'pending' ? 'Pendiente' :
trace.status === 'cancelled' ? 'Cancelado' :
trace.status
}}
@ -101,20 +117,59 @@
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="'input'">
<button mat-icon-button (click)="openInputModal(trace.input)">
<mat-icon>info</mat-icon>
</button>
<ng-container *ngSwitchCase="'command'">
<div style="display: flex; flex-direction: column;">
<span>{{ translateCommand(trace.command) }}</span>
<span style="font-size: 0.75rem; color: gray;">{{ trace.jobId }}</span>
</div>
</ng-container>
<ng-container *ngSwitchCase="'client'">
<div style="display: flex; flex-direction: column;">
<span>{{ trace.client?.name }}</span>
<span style="font-size: 0.75rem; color: gray;">{{ trace.client?.ip }}</span>
</div>
</ng-container>
<ng-container *ngSwitchCase="'executedAt'">
<div style="display: flex; flex-direction: column;">
<span style="font-size: 0.8rem;"> {{ trace.executedAt |date: 'dd/MM/yyyy hh:mm:ss'}}</span>
</div>
</ng-container>
<ng-container *ngSwitchCase="'finishedAt'">
<div style="display: flex; flex-direction: column;">
<span style="font-size: 0.8rem;"> {{ trace.finishedAt |date: 'dd/MM/yyyy hh:mm:ss'}}</span>
</div>
</ng-container>
<ng-container *ngSwitchDefault>
{{ column.cell(trace) }}
</ng-container>
</ng-container>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions" joyrideStep="actionsStep" text="{{ 'actionsStepText' | translate }}">
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'columnActions' | translate }}</th>
<td mat-cell *matCellDef="let trace" style="text-align: center;">
<button mat-icon-button color="primary" [disabled]="!trace.input" (click)="openInputModal(trace.input)">
<mat-icon>
<span class="material-symbols-outlined">
mode_comment
</span>
</mat-icon>
</button>
<button mat-icon-button color="primary" [disabled]="!trace.output" (click)="openOutputModal(trace.output)">
<mat-icon>
<span class="material-symbols-outlined">
info
</span>
</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

View File

@ -11,6 +11,9 @@ import { ProgressBarMode } from '@angular/material/progress-bar';
import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
import { ToastrService } from "ngx-toastr";
import { ConfigService } from '@services/config.service';
import {OutputDialogComponent} from "./output-dialog/output-dialog.component";
import {TranslationService} from "@services/translation.service";
import { COMMAND_TYPES } from '../../shared/constants/command-types';
@Component({
selector: 'app-task-logs',
@ -34,6 +37,12 @@ export class TaskLogsComponent implements OnInit {
progress = 0;
bufferValue = 0;
filteredCommands2 = Object.keys(COMMAND_TYPES).map(key => ({
name: key,
value: key,
label: COMMAND_TYPES[key]
}));
columns = [
{
columnDef: 'id',
@ -47,7 +56,7 @@ export class TaskLogsComponent implements OnInit {
},
{
columnDef: 'client',
header: 'Client',
header: 'Cliente',
cell: (trace: any) => trace.client?.name
},
{
@ -55,24 +64,9 @@ export class TaskLogsComponent implements OnInit {
header: 'Estado',
cell: (trace: any) => trace.status
},
{
columnDef: 'jobId',
header: 'Hilo de trabajo',
cell: (trace: any) => trace.jobId
},
{
columnDef: 'input',
header: 'Input',
cell: (trace: any) => trace.input
},
{
columnDef: 'output',
header: 'Logs',
cell: (trace: any) => trace.output
},
{
columnDef: 'executedAt',
header: 'Programación de ejecución',
header: 'Ejecución',
cell: (trace: any) => this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy hh:mm:ss'),
},
{
@ -81,7 +75,7 @@ export class TaskLogsComponent implements OnInit {
cell: (trace: any) => this.datePipe.transform(trace.finishedAt, 'dd/MM/yyyy hh:mm:ss'),
},
];
displayedColumns = [...this.columns.map(column => column.columnDef)];
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
filters: { [key: string]: string } = {};
filteredClients!: Observable<any[]>;
@ -94,7 +88,8 @@ export class TaskLogsComponent implements OnInit {
private dialog: MatDialog,
private cdr: ChangeDetectorRef,
private configService: ConfigService,
private toastService: ToastrService
private toastService: ToastrService,
private translationService: TranslationService
) {
this.baseUrl = this.configService.apiUrl;
this.mercureUrl = this.configService.mercureUrl;
@ -103,7 +98,7 @@ export class TaskLogsComponent implements OnInit {
ngOnInit(): void {
this.loadTraces();
this.loadCommands();
//this.loadClients();
this.loadClients();
this.filteredCommands = this.commandControl.valueChanges.pipe(
startWith(''),
map(value => (typeof value === 'string' ? value : value?.name)),
@ -147,11 +142,17 @@ export class TaskLogsComponent implements OnInit {
}
private _filterClients(name: string): any[] {
const filterValue = name.toLowerCase();
return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
private _filterClients(value: string): any[] {
const filterValue = value.toLowerCase();
return this.clients.filter(client =>
client.name?.toLowerCase().includes(filterValue) ||
client.ip?.toLowerCase().includes(filterValue) ||
client.mac?.toLowerCase().includes(filterValue)
);
}
private _filterCommands(name: string): any[] {
const filterValue = name.toLowerCase();
return this.commands.filter(command => command.name.toLowerCase().includes(filterValue));
@ -161,12 +162,13 @@ export class TaskLogsComponent implements OnInit {
return client && client.name ? client.name : '';
}
displayFnCommand(command: any): string {
return command && command.name ? command.name : '';
onOptionCommandSelected(selectedCommand: any): void {
this.filters['command'] = selectedCommand.name;
this.loadTraces();
}
onOptionCommandSelected(selectedCommand: any): void {
this.filters['command.id'] = selectedCommand.id;
onOptionStatusSelected(selectedStatus: any): void {
this.filters['status'] = selectedStatus;
this.loadTraces();
}
@ -177,11 +179,19 @@ export class TaskLogsComponent implements OnInit {
openInputModal(inputData: any): void {
this.dialog.open(InputDialogComponent, {
width: '700px',
width: '70vw',
height: '60vh',
data: { input: inputData }
});
}
openOutputModal(outputData: any): void {
this.dialog.open(OutputDialogComponent, {
width: '500px',
data: { input: outputData }
});
}
cancelTrace(trace: any): void {
this.dialog.open(DeleteModalComponent, {
width: '300px',
@ -239,28 +249,19 @@ export class TaskLogsComponent implements OnInit {
loadClients() {
this.loading = true;
this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe(
this.http.get<any>(`${this.baseUrl}/clients?page=1&itemsPerPage=10000`).subscribe(
response => {
const clientIds = response['hydra:member'].map((client: any) => client['@id']);
const clientDetailsRequests: Observable<any>[] = clientIds.map((id: string) => this.http.get<any>(`${this.baseUrl}${id}`));
forkJoin(clientDetailsRequests).subscribe(
(clients: any[]) => {
this.clients = clients;
this.loading = false;
},
(error: any) => {
console.error('Error fetching client details:', error);
this.loading = false;
}
);
this.clients = response['hydra:member'];
this.loading = false;
},
(error: any) => {
error => {
console.error('Error fetching clients:', error);
this.loading = false;
}
);
}
resetFilters() {
this.loading = true;
this.filters = {};
@ -291,6 +292,31 @@ export class TaskLogsComponent implements OnInit {
this.loadTraces();
}
translateCommand(command: string): string {
return this.translationService.getCommandTranslation(command);
}
clearCommandFilter(event: Event, clientSearchCommandInput: any): void {
event.stopPropagation();
delete this.filters['command'];
clientSearchCommandInput.value = null;
this.loadTraces()
}
clearStatusFilter(event: Event, clientSearchStatusInput: any): void {
event.stopPropagation();
delete this.filters['status'];
clientSearchStatusInput.value = null;
this.loadTraces()
}
clearClientFilter(event: Event, clientSearchClientInput: any): void {
event.stopPropagation();
delete this.filters['client.id'];
clientSearchClientInput.value = null;
this.loadTraces()
}
iniciarTour(): void {
this.joyrideService.startTour({
steps: [

View File

@ -2,12 +2,8 @@ mat-toolbar {
/*height: 7vh;*/
min-height: 65px;
min-width: 375px;
background-color: #3f51b5;
color: white;
}
.trace-button .mat-icon {
color: #ffffff;
background-color: #e2e8f0;
color: black;
}
.navbar-actions-row {
@ -51,6 +47,10 @@ mat-toolbar {
display: none;
}
.trace-button {
color: #3f51b5;
}
@media (min-width: 1500px) {
.hide-on-small {
display: inline;
@ -80,4 +80,4 @@ mat-toolbar {
.smallScreenMenubutton {
margin-right: 2vh;
}
}
}

View File

@ -1,7 +1,6 @@
<mat-toolbar>
<span class="navbar-title hide-on-small" matTooltip="Consola web de administración de Opengnsys"
matTooltipShowDelay="1000">
Opengnsys webconsole
</span>
<button mat-icon-button (click)="onToggleSidebar()" matTooltip="Abrir o cerrar la barra lateral"
@ -10,8 +9,8 @@
</button>
<div class="navbar-actions-row" *ngIf="!isSmallScreen">
<button class="trace-button" routerLink="/commands-logs" mat-button>
<mat-icon>notifications</mat-icon>
<button routerLink="/commands-logs" mat-button>
<mat-icon class="trace-button" >notifications</mat-icon>
</button>
<div class="navbar-buttons-row">
@ -73,4 +72,4 @@
{{ 'labelEnvVars' | translate }}
</button>
</mat-menu>
</mat-toolbar>
</mat-toolbar>

View File

@ -1,9 +1,30 @@
/* styles.css o en el componente */
html, body {
height: 100%;
margin: 0;
}
.container {
height: calc(100vh - 7vh);
min-width: 375px;
height: 100%;
}
.sidebar {
width: 15vw;
min-width: 250px;
width: 250px;
}
.custom-sidebar {
background-color: #1e293b;
color: white;
}
.custom-sidebar .mat-list-item {
color: white;
}
.mat-icon {
color: white;
}
.mat-list-item:hover {
background-color: #2a2a40;
}

View File

@ -1,12 +1,11 @@
<app-header (toggleSidebar)="toggleSidebar()"></app-header>
<mat-drawer-container class="container" autosize>
<mat-drawer class="sidebar" [mode]="sidebarMode" [opened]="isSidebarVisible" (close)="isSidebarVisible = false">
<mat-drawer class="sidebar custom-sidebar" [mode]="sidebarMode" [opened]="isSidebarVisible" (close)="isSidebarVisible = false">
<app-sidebar [isVisible]="isSidebarVisible" [sidebarMode]="sidebarMode" (closeSidebar)="isSidebarVisible = false">
</app-sidebar>
</mat-drawer>
<mat-drawer-content class="content">
<app-header (toggleSidebar)="toggleSidebar()"></app-header>
<router-outlet></router-outlet>
</mat-drawer-content>
</mat-drawer-container>
</mat-drawer-container>

View File

@ -1,7 +1,7 @@
mat-nav-list {
width: auto;
margin-right: 5px;
overflow-x: hidden;
width: auto;
margin-right: 5px;
overflow-x: hidden;
}
mat-list-item {
@ -23,18 +23,42 @@ mat-icon {
.user-logged {
align-items: center;
height: 70px;
gap: 2rem;
padding: 1rem;
font-size: medium;
}
.sidebar-content {
.user-logged span {
color: #f1f5f9;
}
.custom-sidebar {
display: flex;
flex-direction: column;
height: 100%;
background-color: #1e1e2f;
color: white;
}
.sidebar-content {
flex: 1;
overflow-y: auto;
}
.sidebar-content span {
color: #f1f5f9;
}
button {
color: white;
}
mat-icon {
margin-right: 0.5rem;
}
.admin-link {
margin-top: auto;
}
@ -61,5 +85,35 @@ mat-icon {
.disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
}
.user-info {
justify-content: center;
padding: 12px 0 12px;
}
.user-info-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.user-logo {
width: 48px;
height: 48px;
object-fit: contain;
border-radius: 8px;
}
.user-logged {
color: white;
font-weight: 500;
text-align: center;
}
.white-divider {
border-top-color: rgba(255, 255, 255, 0.2) !important;
margin: 8px 0;
}

View File

@ -1,11 +1,13 @@
<mat-nav-list>
<mat-list-item disabled>
<span class="user-logged" matTooltipShowDelay="1000">
<span>{{ 'welcomeUser' | translate:{username: username} }}</span>
</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-nav-list class="sidebar-content">
<div disabled class="user-info">
<div class="user-info-wrapper" matTooltipShowDelay="1000">
<img ngSrc="assets/images/logo.png" alt="Logo" class="user-logo" height="500" width="500"/>
<span class="user-logged">
{{ 'welcomeUser' | translate:{username: username} }}
</span>
</div>
</div>
<mat-divider class="white-divider"></mat-divider>
<mat-list-item routerLink="/groups" (click)="onItemClick()" matTooltip="{{ 'TOOLTIP_GROUPS' | translate }}"
matTooltipShowDelay="1000">
@ -145,4 +147,4 @@
<span>{{ 'menus' | translate }}</span>
</span>
</mat-list-item>
</mat-nav-list>
</mat-nav-list>

View File

@ -0,0 +1,37 @@
import { Injectable } from '@angular/core';
import { COMMAND_TYPES } from '../shared/constants/command-types';
@Injectable({
providedIn: 'root'
})
export class TranslationService {
private currentLang: string = 'es';
constructor() {}
getCommandTranslation(command: string): string {
switch(command) {
case 'install-oglive':
return COMMAND_TYPES['install-oglive'][this.currentLang];
case 'create-aux-file':
return COMMAND_TYPES['create-aux-file'][this.currentLang];
case 'convert-image-to-virtual':
return COMMAND_TYPES['convert-image-to-virtual'][this.currentLang];
case 'deploy-image':
return COMMAND_TYPES['deploy-image'][this.currentLang];
case 'create-image':
return COMMAND_TYPES['create-image'][this.currentLang];
case 'reboot':
return COMMAND_TYPES.reboot[this.currentLang];
case 'shutdown':
return COMMAND_TYPES.shutdown[this.currentLang];
case 'partition-and-format':
return COMMAND_TYPES['partition-and-format'][this.currentLang];
case 'run-script':
return COMMAND_TYPES['run-script'][this.currentLang];
default:
return command;
}
}
}

View File

@ -0,0 +1,46 @@
export const COMMAND_TYPES: any = {
'create-image': {
en: 'Create Image',
es: 'Crear Imagen'
},
'deploy-image': {
en: 'Deploy Image',
es: 'Desplegar Imagen'
},
'install-oglive': {
en: 'Install OGLive',
es: 'Instalar OGLive'
},
'create-aux-file': {
en: 'Create Auxiliary File',
es: 'Crear archivos auxiliares'
},
'convert-image-to-virtual': {
en: 'Convert Image to Virtual',
es: 'Convertir imagen a virtual'
},
reboot: {
en: 'Reboot',
es: 'Reiniciar'
},
shutdown: {
en: 'Shutdown',
es: 'Apagar'
},
'partition-and-format': {
en: 'Partition and Format',
es: 'Particionar y Formatear'
},
'run-script': {
en: 'Run Script',
es: 'Ejecutar Script'
}
};

View File

@ -51,12 +51,14 @@
"checkboxOrgMinimal": "Minimal Organizational Unit",
"checkboxUserRole": "User",
"groupsTitleStepText": "On this screen, you can manage the main organizational units (Faculties, Classrooms, Classroom Groups, and clients).",
"repositoryTitleStepText": "On this screen, you can manage the repositories.",
"tableDateRepositoryText": "Use the table to view the details of the repositories.",
"titleStepText": "On this screen, you can manage the calendars of remote teams connected to the UDS service",
"groupsAddStepText": "Click to add a new organizational unit or clients.",
"adminCalendarsTitle": "Manage calendars",
"addButtonStepText": "Click here to add a new calendar.",
"addCalendar": "Add calendar",
"searchStepText": "Use this search bar to filter existing calendars.",
"searchStepText": "Use this search bar to filter existing entities.",
"searchCalendarLabel": "Search calendar name",
"tableStepText": "Here are the existing calendars with their characteristics and settings.",
"actionsStepText": "Access the available actions for each calendar here.",
@ -107,7 +109,7 @@
"statusColumn": "Status",
"enabled": "Enabled",
"disabled": "Disabled",
"adminCommandsTitle": "Command and procedure traces",
"adminCommandsTitle": "Trace Commands",
"resetFiltersStepText": "Click to reset the applied filters and see all traces.",
"resetFilters": "Reset filters",
"clientSelectStepText": "Select a client to see the associated traces.",
@ -183,6 +185,7 @@
"editUnitTooltip": "Edit this organizational unit",
"viewUnitTooltip": "View organizational unit details",
"viewUnitMenu": "View data",
"tableDateStep": "Use the table to view the details",
"addInternalUnitTooltip": "Create a new internal organizational unit",
"addClientTooltip": "Register a client in this organizational unit",
"deleteElementTooltip": "Delete this element",
@ -359,10 +362,12 @@
"nameColumnHeader": "Name",
"templateColumnHeader": "Template",
"pxeImageTitle": "Information on ogBoot server",
"tableDatePxeTemplateText": "Use the table to view the details of the PXE templates.",
"searchIsDefaultText": "Search for default images",
"serverInfoDescription": "Access information and synchronization options on the OgBoot server.",
"syncDatabaseButton": "Sync database",
"viewInfoButton": "View Information",
"adminImagesDescription": "From here you can manage the images configured on the OgBoot server.",
"adminImagesDescription": "From here you can manage the ogLive images configured on the OgBoot server.",
"actionsDescription": "Manage each image with options to view, edit, delete, and more.",
"addClientButton": "Add client",
"searchClientNameLabel": "Search client name",
@ -427,6 +432,7 @@
"boot": "Boot",
"TOOLTIP_BOOT": "Configure and manage boot options",
"ogLive": "ogLive",
"viewInfoStepText": "View information about the OgLive server",
"TOOLTIP_PXE_IMAGES": "View available PXE boot images",
"pxeTemplates": "PXE Templates",
"pxeTemplate": "Template",
@ -488,6 +494,9 @@
"processes": "Processes",
"usedPercentageLabel": "Used",
"errorLoadingData": "Error fetching data. Service not available",
"repository": "Repository",
"addRepository": "Add repository",
"addRepositoryStepText": "Click to add a new repository.",
"repositoryTitleStep": "On this screen you can manage image repositories.",
"partitions": "Partitions",
"clientsViewStepText": "Display of the selected organizational unit's clients",
@ -510,5 +519,13 @@
},
"remoteAccess": "Remote access available",
"noRemoteAccess": "Remote access not available",
"capacityWarning": "The capacity cannot be negative"
}
"capacityWarning": "The capacity cannot be negative",
"monolithicImageStep": "Monolithic image",
"monolithicImageStepText": "On this screen you can manage monolithic images.",
"monolithicImage": "Monolithic image",
"gitImage": "Git images",
"gitImageStep": "Git images",
"gitImageStepText": "On this screen you can manage git images.",
"partitions": "Particiones",
"isDefaultLabel": "Default"
}

View File

@ -1,7 +1,7 @@
{
"Administration": "Administración",
"changePassword": "Cambiar contraseña",
"logout": "Salir",
"logout": "Cerrar sesión",
"loginlabelUsername": "Introduce tu usuario",
"loginlabelPassword": "Introduce tu contraseña",
"buttonLogin": "Login",
@ -56,7 +56,7 @@
"adminCalendarsTitle": "Administrar calendarios",
"addButtonStepText": "Haz clic aquí para añadir un nuevo calendario.",
"addCalendar": "Añadir calendario",
"searchStepText": "Utiliza esta barra de búsqueda para filtrar los calendarios existentes.",
"searchStepText": "Utiliza esta barra de búsqueda para filtrar las entidades existentes.",
"searchCalendarLabel": "Buscar nombre de calendario",
"tableStepText": "Aquí se muestran los calendarios existentes con sus características y configuraciones.",
"actionsStepText": "Accede a las acciones disponibles para cada calendario aquí.",
@ -71,6 +71,7 @@
"reasonPlaceholder": "Razón para la excepción",
"startDate": "Fecha de inicio",
"endDate": "Fecha de fin",
"tableDateStep": "Esta tabla muestra los datos asociados al modulo en concreto",
"buttonSave": "Guardar",
"adminCommandGroupsTitle": "Administrar Grupos de Comandos",
"addCommandGroupStepText": "Haz clic para añadir un nuevo grupo de comandos.",
@ -107,13 +108,13 @@
"statusColumn": "Estado",
"enabled": "Habilitado",
"disabled": "Deshabilitado",
"adminCommandsTitle": "Trazas de comandos y procedimientos",
"adminCommandsTitle": "Traza de ejecución",
"CommandsTitle": "Administrar Comandos",
"resetFiltersStepText": "Haz clic para reiniciar los filtros aplicados y ver todas las trazas.",
"resetFilters": "Reiniciar filtros",
"clientSelectStepText": "Selecciona un cliente para ver las trazas asociadas.",
"filterClientPlaceholder": "Filtrar por cliente",
"commandSelectStepText": "Selecciona un comando para ver las trazas específicas de ese comando.",
"commandSelectStepText": "Selecciona un comando para ver las trazas.",
"filterCommandPlaceholder": "Filtrar por comando",
"taskDetailsTitle": "Detalles de la Tarea",
"taskId": "ID de la Tarea",
@ -263,6 +264,7 @@
"ipLabel": "Dirección IP",
"ipError": "Formato de dirección IP inválido. Ejemplo válido: 127.0.0.1",
"templateLabel": "Plantilla PXE",
"templateContent": "Contenido de la plantilla PXE",
"digitalBoard": "Pizarra digital",
"projectorAlt": "Proyector",
"clientAlt": "Cliente",
@ -360,10 +362,12 @@
"nameColumnHeader": "Nombre",
"templateColumnHeader": "Plantilla",
"pxeImageTitle": "Información en servidor ogBoot",
"tableDatePxeTemplateText": "Esta tabla muestra los datos asociados a las plantillas PXE existentes.",
"searchIsDefaultText": "Filtra para mostrar solo las imágenes por defecto o no por defecto.",
"serverInfoDescription": "Accede a información y opciones de sincronización en el servidor OgBoot.",
"syncDatabaseButton": "Sincronizar base de datos",
"viewInfoButton": "Ver Información",
"adminImagesDescription": "Desde aquí puedes gestionar las imágenes configuradas en el servidor OgBoot.",
"adminImagesDescription": "Desde aquí puedes gestionar las imágenes ogLive.",
"actionsDescription": "Administra cada imagen con opciones para ver, editar, eliminar y más.",
"addClientButton": "Añadir cliente",
"searchClientNameLabel": "Buscar nombre de cliente",
@ -429,6 +433,7 @@
"boot": "Boot",
"TOOLTIP_BOOT": "Configurar y administrar opciones de arranque",
"ogLive": "ogLive",
"viewInfoStepText": "Accede a información y opciones en la API de OgBoot.",
"TOOLTIP_PXE_IMAGES": "Ver imágenes disponibles para arranque PXE",
"pxeTemplates": "Plantillas PXE",
"pxeTemplate": "Plantilla",
@ -490,6 +495,9 @@
"processes": "Procesos",
"usedPercentageLabel": "Usado",
"errorLoadingData": "Error al cargar los datos. Servicio inactivo",
"repository": "Repositorio",
"addRepository": "Añadir repositorio",
"addRepositoryStepText": "Haz clic para añadir un nuevo repositorio.",
"repositoryTitleStep": "En esta pantalla se pueden gestionar los repositorios de imágenes.",
"partitions": "Particiones",
"clientsViewStepText": "Visualización de los clientes de la unidad organizativa seleccionada",
@ -512,5 +520,15 @@
},
"remoteAccess": "Disponible acceso remoto",
"noRemoteAccess": "No disponible acceso remoto",
"capacityWarning": "El aforo no puede ser"
}
"capacityWarning": "El aforo no puede ser",
"tableDateRepositoryText": "Esta tabla muestra los datos asociados a los repositorios existentes.",
"repositoryTitleStepText": "En esta pantalla se pueden gestionar los repositorios de imágenes.",
"monolithicImageStep": "Imagen monolítica",
"monolithicImageStepText": "Esta opción permite visualizar las imágenes monolíticas disponibles en el servidor.",
"gitImageStep": "Imágenes Git",
"monolithicImage": "Imagenes monolíticas",
"gitImage": "Imágenes Git",
"gitImageStepText": "Esta opción permite visualizar las imágenes Git disponibles en el servidor.",
"partitions": "Particiones",
"isDefaultLabel": "Por defecto"
}