diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d9ea42..f1a0052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,21 +1,37 @@ # Changelog +## [0.10.0] - 2025-3-25 +### Added +- Nuevo componenten de estado global. +- Servicio para que el ogGui obtenga de forma dinamica las variables de entorno. +- Nueva funcionalidad para convertir imagen en imagen virtual. +- Nueva funcionalidad para importar imágenes externas al sistema. +- Despliegue de imangenes sin cache. Cambios en el formulario de "despliegue". + +### Improved +- Mejoras en la internacionalización. +- Nueva UX ogRepository. Ahora se gestionan las imagenes de forma mas sencilla. +- Cambios en ogLive. Mejora en la sincronizacion y obtención de datos en la API + +### Fixed +- Cambios en la expresion regular para la validacion de documentos DHCP en la carga masiva de pc. + ## [0.9.2] - 2025-03-19 ### Changed - Jenkinsfile to pubilsh packages in repo in case og release ## [0.9.1] - 2025-03-12 -### ⚡ Changed +### Changed - Se ha modificado el acceso a Mercure añadiendo nueva variable de entorno. - ## [0.9.0] - 2025-3-4 -### 🔹 Added +### Added - Integracion con Mercure. Subscriber tanto en "Trazas" con en "Clientes". - Nueva funcionalidad para checkear la integridad de una imagen. Boton en apartado "imagenes" dentro del repositorio. - Centralizacion de estilos. - Nueva funcionalidad para realizar backup de imágenes. +- Botón para cancelar despliegues de imagenes. Aparece en "trazas" tan solo para los comendos "deploy" y para el estado "en progreso". -### ⚡ Changed +### Changed - Nueva interfaz en "Grupos". Se ha aprovechado mejor el espacio y acortado el tamaño de las filas, para poder tener mas elementos por pantalla. - Cambios en filtros de "Grupos". Ahora se pueden filtrar por "Centro" y "Unidad Organizativa" y estado. Ahora se busca en base de datos, y no en una lista de clientes dados. - Refactorizados compontentes de crear/editar clientes en uno solo. @@ -23,22 +39,18 @@ - Para gestionar/añadir clientes a subredes ahora tenemos un botón para "añadir todos" y tan solo nos aparecn los equipos que no estén previamente asignados en una subred. ## [0.7.0] - 2024-12-10 - ### Refactored - Refactored the group screen, removing the separate tabs for clients, advanced search, and organizational units. - Added support for partitioning functionality in the client detail view. -- Boton para cancelar despliegues de imagenes. Aparece en "trazas" tan solo para los comendos "deploy" y para el estado "en progreos". ## [0.6.1] - 2024-11-19 - ### Improved - Introduced a new automatic sync mode for the ogdhcp and ogBoot components. - Improve test coverage. - New view for clients inside the classroom on the main page. ## [0.6.0] - 2024-11-19 - -### 🔹 Added +### Added - Added functionality to execute actions from the menu in the general groups screen. - Displayed the selected center on the general screen for better context. - Implemented the option to collapse the sidebar for improved usability. diff --git a/ogWebconsole/.env b/ogWebconsole/.env index 45a28b7..5823a1c 100644 --- a/ogWebconsole/.env +++ b/ogWebconsole/.env @@ -1,2 +1,2 @@ -NG_APP_BASE_API_URL=https://127.0.0.1:8443 -NG_APP_OGCORE_MERCURE_BASE_URL=http://localhost:3000/.well-known/mercure +# NG_APP_BASE_API_URL=https://127.0.0.1:8443 +# NG_APP_OGCORE_MERCURE_BASE_URL=http://localhost:3000/.well-known/mercure diff --git a/ogWebconsole/.env.prod b/ogWebconsole/.env.prod index e69de29..55dfe33 100644 --- a/ogWebconsole/.env.prod +++ b/ogWebconsole/.env.prod @@ -0,0 +1,2 @@ +NG_APP_BASE_API_URL=https://localhost:8443 +NG_APP_OGCORE_MERCURE_BASE_URL=http://localhost:3000/.well-known/mercure diff --git a/ogWebconsole/.gitignore b/ogWebconsole/.gitignore index 03923dd..141af17 100644 --- a/ogWebconsole/.gitignore +++ b/ogWebconsole/.gitignore @@ -41,3 +41,5 @@ testem.log .DS_Store Thumbs.db +test-results/ + diff --git a/ogWebconsole/angular.json b/ogWebconsole/angular.json index 81328fa..b6f4ae3 100644 --- a/ogWebconsole/angular.json +++ b/ogWebconsole/angular.json @@ -4,12 +4,6 @@ "newProjectRoot": "projects", "projects": { "ogWebconsole": { - "i18n": { - "sourceLocale": "es", - "locales": { - "en": "src/locale/en.json" - } - }, "projectType": "application", "schematics": { "@schematics/angular:component": { @@ -30,7 +24,7 @@ "builder": "@ngx-env/builder:application", "options": { "baseHref": "/oggui/", - "localize": true, + "localize": false, "aot": true, "outputPath": "dist/og-webconsole", "index": "src/index.html", @@ -41,20 +35,23 @@ ], "tsConfig": "tsconfig.app.json", "assets": [ - "src/favicon.ico", - "src/assets", - { - "glob": "**/*", - "input": "src/locale", - "output": "/locale" - } -], + "src/favicon.ico", + "src/assets", + { + "glob": "**/*", + "input": "src/locale", + "output": "/locale" + } + ], "styles": [ "src/custom-theme.scss", "src/styles.css", "node_modules/ngx-toastr/toastr.css" ], - "scripts": [] + "scripts": [], + "allowedCommonJsDependencies": [ + "rfdc" + ] }, "configurations": { "production": { @@ -66,7 +63,7 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "4kb", + "maximumWarning": "7kb", "maximumError": "10kb" } ], @@ -76,16 +73,6 @@ "optimization": false, "extractLicenses": false, "sourceMap": false - }, - "es": { - "localize": [ - "es-ES" - ] - }, - "en": { - "localize": [ - "en-US" - ] } }, "defaultConfiguration": "production" @@ -104,29 +91,16 @@ }, "development": { "buildTarget": "ogWebconsole:build:development" - }, - "es": { - "buildTarget": "ogWebconsole:build:es" - }, - "en": { - "buildTarget": "ogWebconsole:build:en" } }, "defaultConfiguration": "development" }, - "extract-i18n": { - "builder": "@ngx-env/builder:extract-i18n", - "options": { - "buildTarget": "ogWebconsole:build" - } - }, "test": { "builder": "@ngx-env/builder:karma", "options": { "polyfills": [ "zone.js", - "zone.js/testing", - "@angular/localize/init" + "zone.js/testing" ], "tsConfig": "tsconfig.spec.json", "assets": [ @@ -146,4 +120,4 @@ "cli": { "analytics": "95fac95c-8936-41a8-8c9c-1fae82fe6912" } -} +} \ No newline at end of file diff --git a/ogWebconsole/docker-compose.yaml b/ogWebconsole/docker-compose.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/ogWebconsole/docker/nginx.conf b/ogWebconsole/docker/nginx.conf deleted file mode 100644 index e69de29..0000000 diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts index 241d28a..c2e3a06 100644 --- a/ogWebconsole/src/app/app-routing.module.ts +++ b/ogWebconsole/src/app/app-routing.module.ts @@ -65,7 +65,6 @@ const routes: Routes = [ { path: 'clients/partition-assistant', component: PartitionAssistantComponent }, { path: 'clients/:id', component: ClientMainViewComponent }, { path: 'clients/:id/create-image', component: CreateClientImageComponent }, - { path: 'images', component: ImagesComponent }, { path: 'repositories', component: RepositoriesComponent }, { path: 'repository/:id', component: MainRepositoryViewComponent }, { path: 'software', component: SoftwareComponent }, diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 277d682..3e5a1e6 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -1,4 +1,5 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, APP_INITIALIZER } from '@angular/core'; +import { ConfigService } from './services/config.service'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -101,7 +102,7 @@ import { OperativeSystemComponent } from './components/operative-system/operativ import { CreateOperativeSystemComponent } from './components/operative-system/create-operative-system/create-operative-system.component'; import { ShowTemplateContentComponent } from './components/ogboot/pxe/show-template-content/show-template-content.component'; import { RepositoriesComponent } from './components/repositories/repositories.component'; -import { CreateRepositoryComponent } from './components/repositories/create-repository/create-repository.component'; +import { ManageRepositoryComponent } from './components/repositories/manage-repository/manage-repository.component'; import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component'; import { DeployImageComponent } from './components/groups/components/client-main-view/deploy-image/deploy-image.component'; import { MainRepositoryViewComponent } from './components/repositories/main-repository-view/main-repository-view.component'; @@ -129,10 +130,24 @@ import { AddClientsToSubnetComponent } from "./components/ogdhcp/add-clients-to- import { ShowClientsComponent } from './components/ogdhcp/show-clients/show-clients.component'; import { OperationResultDialogComponent } from './components/ogdhcp/operation-result-dialog/operation-result-dialog.component'; import { ManageClientComponent } from './components/groups/shared/clients/manage-client/manage-client.component'; +import { ConvertImageComponent } from './components/repositories/convert-image/convert-image.component'; +import { registerLocaleData } from '@angular/common'; +import localeEs from '@angular/common/locales/es'; +import { GlobalStatusComponent } from './components/global-status/global-status.component'; +import { ShowImagesComponent } from './components/repositories/show-images/show-images.component'; +import { StatusTabComponent } from './components/global-status/status-tab/status-tab.component'; +import { ConvertImageToVirtualComponent } from './components/repositories/convert-image-to-virtual/convert-image-to-virtual.component'; + export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './locale/', '.json'); } +export function initializeApp(configService: ConfigService) { + return () => configService.loadConfig(); +} + +registerLocaleData(localeEs, 'es-ES'); + @NgModule({ declarations: [ AppComponent, @@ -195,7 +210,7 @@ export function HttpLoaderFactory(http: HttpClient) { CreateOperativeSystemComponent, ShowTemplateContentComponent, RepositoriesComponent, - CreateRepositoryComponent, + ManageRepositoryComponent, ExecuteCommandComponent, ExecuteCommandOuComponent, DeployImageComponent, @@ -213,7 +228,12 @@ export function HttpLoaderFactory(http: HttpClient) { ManageOrganizationalUnitComponent, BackupImageComponent, ShowClientsComponent, - OperationResultDialogComponent + OperationResultDialogComponent, + ConvertImageComponent, + GlobalStatusComponent, + ShowImagesComponent, + StatusTabComponent, + ConvertImageToVirtualComponent ], bootstrap: [AppComponent], imports: [BrowserModule, @@ -273,8 +293,16 @@ export function HttpLoaderFactory(http: HttpClient) { useClass: CustomInterceptor, multi: true }, + { provide: LOCALE_ID, useValue: 'es-ES' }, provideAnimationsAsync(), - provideHttpClient(withInterceptorsFromDi()) + provideHttpClient(withInterceptorsFromDi()), + ConfigService, + { + provide: APP_INITIALIZER, + useFactory: initializeApp, + deps: [ConfigService], + multi: true + } ], }) export class AppModule { } diff --git a/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.spec.ts b/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.spec.ts index d4199a4..ba67991 100644 --- a/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.spec.ts +++ b/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.spec.ts @@ -13,24 +13,29 @@ import { TranslateModule } from '@ngx-translate/core'; import { ToastrModule, ToastrService } from 'ngx-toastr'; import { DataService } from '../users/users/data.service'; import { MatTableModule } from '@angular/material/table'; +import { ConfigService } from '@services/config.service'; describe('EnvVarsComponent', () => { let component: EnvVarsComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url' + }; + await TestBed.configureTestingModule({ - declarations: [EnvVarsComponent], + declarations: [EnvVarsComponent], imports: [ ReactiveFormsModule, - FormsModule, + FormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatCheckboxModule, MatButtonModule, BrowserAnimationsModule, - MatTableModule, + MatTableModule, ToastrModule.forRoot(), TranslateModule.forRoot() ], @@ -47,6 +52,10 @@ describe('EnvVarsComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} + }, + { + provide: ConfigService, + useValue: mockConfigService } ] }).compileComponents(); diff --git a/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.ts b/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.ts index 3516e13..8b8b79d 100644 --- a/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.ts +++ b/ogWebconsole/src/app/components/admin/env-vars/env-vars.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; -import {HttpClient} from "@angular/common/http"; -import {ToastrService} from "ngx-toastr"; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-env-vars', @@ -8,16 +9,17 @@ import {ToastrService} from "ngx-toastr"; styleUrl: './env-vars.component.css' }) export class EnvVarsComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; envVars: { name: string; value: string }[] = []; displayedColumns: string[] = ['name', 'value']; - - private apiUrl = `${this.baseUrl}/env-vars`; + private apiUrl: string; constructor( private http: HttpClient, private toastService: ToastrService, - ) {} + private configService: ConfigService + ) { + this.apiUrl = `${this.configService.apiUrl}/env-vars`; + } ngOnInit(): void { this.loadEnvVars(); diff --git a/ogWebconsole/src/app/components/admin/roles/roles/add-role-modal/add-role-modal.component.ts b/ogWebconsole/src/app/components/admin/roles/roles/add-role-modal/add-role-modal.component.ts index d8255fc..446a490 100644 --- a/ogWebconsole/src/app/components/admin/roles/roles/add-role-modal/add-role-modal.component.ts +++ b/ogWebconsole/src/app/components/admin/roles/roles/add-role-modal/add-role-modal.component.ts @@ -4,6 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import {DataService} from "../data.service"; import {ToastrService} from "ngx-toastr"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-add-role-modal', @@ -11,9 +12,9 @@ import {ToastrService} from "ngx-toastr"; styleUrls: ['./add-role-modal.component.css'] }) export class AddRoleModalComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; roleForm: FormGroup; roleId: string | null = null; + baseUrl: string; constructor( public dialogRef: MatDialogRef, @@ -21,8 +22,10 @@ export class AddRoleModalComponent { private http: HttpClient, private fb: FormBuilder, private dataService: DataService, - private toastService: ToastrService + private toastService: ToastrService, + private configService: ConfigService ) { + this.baseUrl = this.configService.apiUrl; this.roleForm = this.fb.group({ name: ['', Validators.required], superAdmin: [false], diff --git a/ogWebconsole/src/app/components/admin/roles/roles/data.service.ts b/ogWebconsole/src/app/components/admin/roles/roles/data.service.ts index 1d48441..6ba662e 100644 --- a/ogWebconsole/src/app/components/admin/roles/roles/data.service.ts +++ b/ogWebconsole/src/app/components/admin/roles/roles/data.service.ts @@ -2,15 +2,19 @@ import { Injectable } from '@angular/core'; import {HttpClient, HttpParams} from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/user-groups?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/user-groups?page=1&itemsPerPage=1000`; + } getUserGroups(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/admin/roles/roles/roles.component.spec.ts b/ogWebconsole/src/app/components/admin/roles/roles/roles.component.spec.ts index 651ae73..d283b1c 100644 --- a/ogWebconsole/src/app/components/admin/roles/roles/roles.component.spec.ts +++ b/ogWebconsole/src/app/components/admin/roles/roles/roles.component.spec.ts @@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; import { DataService } from './data.service'; -import { of } from 'rxjs'; +import { ConfigService } from '@services/config.service'; import { MatDivider } from '@angular/material/divider'; import { MatFormField } from '@angular/material/form-field'; import { MatLabel } from '@angular/material/form-field'; @@ -20,12 +20,14 @@ describe('RolesComponent', () => { let mockHttpClient: jasmine.SpyObj; let mockToastrService: jasmine.SpyObj; let mockDataService: jasmine.SpyObj; + let mockConfigService: jasmine.SpyObj; beforeEach(async () => { const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']); const toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']); const dataServiceSpy = jasmine.createSpyObj('DataService', ['getRoles']); + const configServiceSpy = jasmine.createSpyObj('ConfigService', [], { apiUrl: 'http://mock-api-url' }); await TestBed.configureTestingModule({ declarations: [RolesComponent, LoadingComponent], @@ -35,7 +37,8 @@ describe('RolesComponent', () => { { provide: MatDialog, useValue: matDialogSpy }, { provide: HttpClient, useValue: httpClientSpy }, { provide: ToastrService, useValue: toastrServiceSpy }, - { provide: DataService, useValue: dataServiceSpy } + { provide: DataService, useValue: dataServiceSpy }, + { provide: ConfigService, useValue: configServiceSpy } ] }).compileComponents(); }); @@ -47,6 +50,7 @@ describe('RolesComponent', () => { mockHttpClient = TestBed.inject(HttpClient) as jasmine.SpyObj; mockToastrService = TestBed.inject(ToastrService) as jasmine.SpyObj; mockDataService = TestBed.inject(DataService) as jasmine.SpyObj; + mockConfigService = TestBed.inject(ConfigService) as jasmine.SpyObj; }); it('should create', () => { diff --git a/ogWebconsole/src/app/components/admin/roles/roles/roles.component.ts b/ogWebconsole/src/app/components/admin/roles/roles/roles.component.ts index 91a95dc..7e4c266 100644 --- a/ogWebconsole/src/app/components/admin/roles/roles/roles.component.ts +++ b/ogWebconsole/src/app/components/admin/roles/roles/roles.component.ts @@ -7,6 +7,7 @@ import { DataService } from "./data.service"; import { PageEvent } from "@angular/material/paginator"; import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component'; import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-roles', @@ -14,7 +15,7 @@ import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component styleUrls: ['./roles.component.css'] }) export class RolesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string = this.configService.apiUrl; dataSource = new MatTableDataSource(); filters: { [key: string]: string } = {}; loading: boolean = false; @@ -48,7 +49,8 @@ export class RolesComponent implements OnInit { public dialog: MatDialog, private http: HttpClient, private dataService: DataService, - private toastService: ToastrService + private toastService: ToastrService, + private configService: ConfigService ) {} ngOnInit() { diff --git a/ogWebconsole/src/app/components/admin/users/users/add-user-modal/add-user-modal.component.ts b/ogWebconsole/src/app/components/admin/users/users/add-user-modal/add-user-modal.component.ts index f66fa0f..add5556 100644 --- a/ogWebconsole/src/app/components/admin/users/users/add-user-modal/add-user-modal.component.ts +++ b/ogWebconsole/src/app/components/admin/users/users/add-user-modal/add-user-modal.component.ts @@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { ToastrService } from "ngx-toastr"; import { HttpClient } from "@angular/common/http"; import { DataService } from "../data.service"; +import { ConfigService } from '@services/config.service'; interface UserGroup { '@id': string; @@ -17,7 +18,7 @@ interface UserGroup { styleUrls: ['./add-user-modal.component.css'] }) export class AddUserModalComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; @Output() userAdded = new EventEmitter(); @Output() userEdited = new EventEmitter(); userForm: FormGroup; @@ -38,8 +39,10 @@ export class AddUserModalComponent implements OnInit { private http: HttpClient, private fb: FormBuilder, private dataService: DataService, - private toastService: ToastrService + private toastService: ToastrService, + private configService: ConfigService ) { + this.baseUrl = this.configService.apiUrl; this.userForm = this.fb.group({ username: ['', Validators.required], password: ['', Validators.required], diff --git a/ogWebconsole/src/app/components/admin/users/users/data.service.ts b/ogWebconsole/src/app/components/admin/users/users/data.service.ts index 732987e..7346685 100644 --- a/ogWebconsole/src/app/components/admin/users/users/data.service.ts +++ b/ogWebconsole/src/app/components/admin/users/users/data.service.ts @@ -3,15 +3,19 @@ import { Injectable } from '@angular/core'; import {HttpClient, HttpParams} from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/users?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/users?page=1&itemsPerPage=1000`; + } getUsers(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/admin/users/users/users.component.spec.ts b/ogWebconsole/src/app/components/admin/users/users/users.component.spec.ts index ba8906d..cf96cf6 100644 --- a/ogWebconsole/src/app/components/admin/users/users/users.component.spec.ts +++ b/ogWebconsole/src/app/components/admin/users/users/users.component.spec.ts @@ -7,6 +7,7 @@ import { ToastrService } from 'ngx-toastr'; import { of } from 'rxjs'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; +import { ConfigService } from '@services/config.service'; class MockToastrService { success() {} @@ -18,6 +19,11 @@ describe('UsersComponent', () => { let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [UsersComponent], imports: [ @@ -28,6 +34,7 @@ describe('UsersComponent', () => { ], providers: [ { provide: ToastrService, useClass: MockToastrService }, + { provide: ConfigService, useValue: mockConfigService } ], schemas: [NO_ERRORS_SCHEMA], // Ignorar elementos desconocidos }).compileComponents(); diff --git a/ogWebconsole/src/app/components/admin/users/users/users.component.ts b/ogWebconsole/src/app/components/admin/users/users/users.component.ts index 8e758cc..73abe86 100644 --- a/ogWebconsole/src/app/components/admin/users/users/users.component.ts +++ b/ogWebconsole/src/app/components/admin/users/users/users.component.ts @@ -5,7 +5,7 @@ import { AddUserModalComponent } from './add-user-modal/add-user-modal.component import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; -import { DataService } from "./data.service"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-users', @@ -13,7 +13,8 @@ import { DataService } from "./data.service"; styleUrls: ['./users.component.css'] }) export class UsersComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); filters: { [key: string]: string } = {}; loading: boolean = false; @@ -50,14 +51,15 @@ export class UsersComponent implements OnInit { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/users`; - constructor( public dialog: MatDialog, + private configService: ConfigService, private http: HttpClient, - private dataService: DataService, private toastService: ToastrService - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/users`; + } ngOnInit() { this.search(); diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts b/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts index 00ab0f8..28393a0 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts +++ b/ogWebconsole/src/app/components/calendar/calendar.component.spec.ts @@ -16,12 +16,18 @@ import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { JoyrideModule, JoyrideService } from 'ngx-joyride'; import { TranslateModule } from '@ngx-translate/core'; import { LoadingComponent } from '../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; describe('CalendarComponent', () => { let component: CalendarComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [CalendarComponent, LoadingComponent], imports: [ @@ -41,6 +47,9 @@ describe('CalendarComponent', () => { JoyrideModule.forRoot(), TranslateModule.forRoot(), ], + providers: [ + { provide: ConfigService, useValue: mockConfigService } + ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/calendar/calendar.component.ts b/ogWebconsole/src/app/components/calendar/calendar.component.ts index c24d9ea..199d98b 100644 --- a/ogWebconsole/src/app/components/calendar/calendar.component.ts +++ b/ogWebconsole/src/app/components/calendar/calendar.component.ts @@ -9,6 +9,7 @@ import { PageEvent } from "@angular/material/paginator"; import { CreateCalendarComponent } from "./create-calendar/create-calendar.component"; import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component"; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-calendar', @@ -16,7 +17,8 @@ import { JoyrideService } from 'ngx-joyride'; styleUrl: './calendar.component.css' }) export class CalendarComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; images: { downloadUrl: string; name: string; uuid: string }[] = []; dataSource = new MatTableDataSource(); length: number = 0; @@ -52,15 +54,18 @@ export class CalendarComponent implements OnInit { } ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/remote-calendars`; constructor( public dialog: MatDialog, private http: HttpClient, private dataService: DataService, private toastService: ToastrService, + private configService: ConfigService, private joyrideService: JoyrideService - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/remote-calendars`; + } ngOnInit(): void { this.search(); @@ -165,7 +170,7 @@ export class CalendarComponent implements OnInit { this.joyrideService.startTour({ steps: ['titleStep', 'addButtonStep', 'searchStep', 'tableStep', 'actionsStep'], showPrevButton: true, - themeColor: '#3f51b5' + themeColor: '#3f51b5' }); } } diff --git a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts index 7433292..716818f 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts +++ b/ogWebconsole/src/app/components/calendar/create-calendar-rule/create-calendar-rule.component.ts @@ -1,7 +1,8 @@ -import {Component, Inject} from '@angular/core'; -import {ToastrService} from "ngx-toastr"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import { Component, Inject } from '@angular/core'; +import { ToastrService } from "ngx-toastr"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-calendar-rule', @@ -9,7 +10,7 @@ import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; styleUrl: './create-calendar-rule.component.css' }) export class CreateCalendarRuleComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; name: string = ''; remoteCalendarRules: any[] = []; weekDays: string[] = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']; @@ -29,20 +30,23 @@ export class CreateCalendarRuleComponent { constructor( private toastService: ToastrService, private http: HttpClient, + private configService: ConfigService, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, -) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.calendarId = this.data.calendar if (this.data) { this.isEditMode = true; - this.availableFromDate = this.data.rule? this.data.rule.availableFromDate : null; - this.availableToDate = this.data.rule? this.data.rule.availableToDate : null; - this.isRemoteAvailable = this.data.rule? this.data.rule.isRemoteAvailable : false; - this.availableReason = this.data.rule? this.data.rule.availableReason : null; - this.busyFromHour = this.data.rule? this.data.rule.busyFromHour : null; - this.busyToHour = this.data.rule? this.data.rule.busyToHour : null; + this.availableFromDate = this.data.rule ? this.data.rule.availableFromDate : null; + this.availableToDate = this.data.rule ? this.data.rule.availableToDate : null; + this.isRemoteAvailable = this.data.rule ? this.data.rule.isRemoteAvailable : false; + this.availableReason = this.data.rule ? this.data.rule.availableReason : null; + this.busyFromHour = this.data.rule ? this.data.rule.busyFromHour : null; + this.busyToHour = this.data.rule ? this.data.rule.busyToHour : null; if (this.data.rule && this.data.rule.busyWeekDays) { this.busyWeekDays = this.data.rule.busyWeekDays.reduce((acc: { [x: string]: boolean; diff --git a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts index 7b049a2..a3b9a51 100644 --- a/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts +++ b/ogWebconsole/src/app/components/calendar/create-calendar/create-calendar.component.ts @@ -1,10 +1,11 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {ToastrService} from "ngx-toastr"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; -import {CreateCalendarRuleComponent} from "../create-calendar-rule/create-calendar-rule.component"; -import {DataService} from "../data.service"; -import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { Component, Inject, OnInit } from '@angular/core'; +import { ToastrService } from "ngx-toastr"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material/dialog"; +import { CreateCalendarRuleComponent } from "../create-calendar-rule/create-calendar-rule.component"; +import { DataService } from "../data.service"; +import { DeleteModalComponent } from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-calendar', @@ -12,7 +13,7 @@ import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/de styleUrl: './create-calendar.component.css' }) export class CreateCalendarComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; name: string = ''; remoteCalendarRules: any[] = []; isEditMode: boolean = false; @@ -22,11 +23,14 @@ export class CreateCalendarComponent implements OnInit { constructor( private toastService: ToastrService, private http: HttpClient, + private configService: ConfigService, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, public dialog: MatDialog, private dataService: DataService, - ) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { if (this.data) { diff --git a/ogWebconsole/src/app/components/calendar/data.service.ts b/ogWebconsole/src/app/components/calendar/data.service.ts index ba9b568..f6bd68a 100644 --- a/ogWebconsole/src/app/components/calendar/data.service.ts +++ b/ogWebconsole/src/app/components/calendar/data.service.ts @@ -1,16 +1,20 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`; + } getRemoteCalendars(filters: { [key: string]: string }): Observable { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/commands/commands-groups/commands-groups.component.ts b/ogWebconsole/src/app/components/commands/commands-groups/commands-groups.component.ts index 233780b..62fd4ee 100644 --- a/ogWebconsole/src/app/components/commands/commands-groups/commands-groups.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-groups/commands-groups.component.ts @@ -8,6 +8,7 @@ import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/ import { MatTableDataSource } from "@angular/material/table"; import { DatePipe } from "@angular/common"; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-commands-groups', @@ -15,7 +16,8 @@ import { JoyrideService } from 'ngx-joyride'; styleUrls: ['./commands-groups.component.css'] }) export class CommandsGroupsComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); filters: { [key: string]: string | boolean } = {}; length: number = 0; @@ -47,10 +49,12 @@ export class CommandsGroupsComponent implements OnInit { } ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/command-groups`; - constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService, - private joyrideService: JoyrideService) {} + constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService, + private configService: ConfigService, private joyrideService: JoyrideService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/command-groups`; + } ngOnInit(): void { this.search(); @@ -120,17 +124,17 @@ export class CommandsGroupsComponent implements OnInit { iniciarTour(): void { this.joyrideService.startTour({ steps: [ - 'titleStep', - 'addCommandGroupStep', - 'searchStep', - 'tableStep', - 'viewCommandsStep', - 'actionsStep', - 'paginationStep' + 'titleStep', + 'addCommandGroupStep', + 'searchStep', + 'tableStep', + 'viewCommandsStep', + 'actionsStep', + 'paginationStep' ], showPrevButton: true, themeColor: '#3f51b5' }); } - + } diff --git a/ogWebconsole/src/app/components/commands/commands-groups/create-command-group/create-command-group.component.ts b/ogWebconsole/src/app/components/commands/commands-groups/create-command-group/create-command-group.component.ts index 048bf5a..1d897f5 100644 --- a/ogWebconsole/src/app/components/commands/commands-groups/create-command-group/create-command-group.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-groups/create-command-group/create-command-group.component.ts @@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-command-group', @@ -10,21 +11,25 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; styleUrls: ['./create-command-group.component.css'] }) export class CreateCommandGroupComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; availableCommands: any[] = []; selectedCommands: any[] = []; groupName: string = ''; enabled: boolean = true; editing: boolean = false; loading: boolean = false; - private apiUrl = `${this.baseUrl}/commands`; + private apiUrl: string; constructor( private http: HttpClient, private dialogRef: MatDialogRef, private toastService: ToastrService, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/commands`; + } ngOnInit(): void { this.loadAvailableCommands(); diff --git a/ogWebconsole/src/app/components/commands/commands-groups/detail-command-group/detail-command-group.component.ts b/ogWebconsole/src/app/components/commands/commands-groups/detail-command-group/detail-command-group.component.ts index 6d8a326..9d484e1 100644 --- a/ogWebconsole/src/app/components/commands/commands-groups/detail-command-group/detail-command-group.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-groups/detail-command-group/detail-command-group.component.ts @@ -3,6 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-detail-command-group', @@ -10,7 +11,7 @@ import { ToastrService } from 'ngx-toastr'; styleUrls: ['./detail-command-group.component.css'] }) export class DetailCommandGroupComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; form!: FormGroup; clients: any[] = []; showClientSelect = false; @@ -21,9 +22,12 @@ export class DetailCommandGroupComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public data: any, private dialogRef: MatDialogRef, private fb: FormBuilder, + private configService: ConfigService, private http: HttpClient, private toastService: ToastrService - ) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.form = this.fb.group({ diff --git a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.spec.ts b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.spec.ts index e25b28b..c0e518e 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.spec.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.spec.ts @@ -13,11 +13,16 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { LoadingComponent } from '../../../shared/loading/loading.component'; import { JoyrideModule, JoyrideService, JoyrideStepService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; + describe('CommandsTaskComponent', () => { let component: CommandsTaskComponent; let fixture: ComponentFixture; + let mockConfigService: jasmine.SpyObj; beforeEach(async () => { + const configServiceSpy = jasmine.createSpyObj('ConfigService', [], { apiUrl: 'http://mock-api-url' }); + await TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, @@ -33,8 +38,13 @@ describe('CommandsTaskComponent', () => { JoyrideModule.forRoot(), ], declarations: [CommandsTaskComponent, LoadingComponent], + providers: [ + { provide: ConfigService, useValue: configServiceSpy } + ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); + + mockConfigService = TestBed.inject(ConfigService) as jasmine.SpyObj; }); beforeEach(() => { diff --git a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts index ae7904e..a2207e7 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts @@ -6,6 +6,7 @@ import { CreateTaskComponent } from './create-task/create-task.component'; import { DetailTaskComponent } from './detail-task/detail-task.component'; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-commands-task', @@ -13,7 +14,7 @@ import { JoyrideService } from 'ngx-joyride'; styleUrls: ['./commands-task.component.css'] }) export class CommandsTaskComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; tasks: any[] = []; filters: { [key: string]: string | boolean } = {}; length: number = 0; @@ -22,10 +23,14 @@ export class CommandsTaskComponent implements OnInit { pageSizeOptions: number[] = [5, 10, 20, 40, 100]; displayedColumns: string[] = ['taskid', 'notes', 'name', 'scheduledDate', 'enabled', 'actions']; loading: boolean = false; - private apiUrl = `${this.baseUrl}/command-tasks`; + private apiUrl: string; - constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService, - private joyrideService: JoyrideService) {} + constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService, + private configService: ConfigService, + private joyrideService: JoyrideService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/command-tasks`; + } ngOnInit(): void { this.loadTasks(); @@ -110,5 +115,5 @@ export class CommandsTaskComponent implements OnInit { themeColor: '#3f51b5' }); } - + } diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts b/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts index 8a90f7e..4f61c00 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts @@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-task', @@ -10,12 +11,12 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; styleUrls: ['./create-task.component.css'] }) export class CreateTaskComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; taskForm: FormGroup; availableCommandGroups: any[] = []; selectedGroupCommands: any[] = []; availableIndividualCommands: any[] = []; - apiUrl = `${this.baseUrl}/command-tasks`; + apiUrl: string; editing: boolean = false; availableOrganizationalUnits: any[] = []; selectedUnitChildren: any[] = []; @@ -25,10 +26,13 @@ export class CreateTaskComponent implements OnInit { constructor( private fb: FormBuilder, private http: HttpClient, + private configService: ConfigService, private toastr: ToastrService, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/command-tasks`; this.taskForm = this.fb.group({ commandGroup: ['', Validators.required], extraCommands: [[]], diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts index e2ef21b..d2282f5 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Component, OnInit} from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, forkJoin } from 'rxjs'; import { FormControl } from '@angular/forms'; @@ -7,9 +7,10 @@ import { DatePipe } from '@angular/common'; import { JoyrideService } from 'ngx-joyride'; import { MatDialog } from "@angular/material/dialog"; import { InputDialogComponent } from "./input-dialog/input-dialog.component"; -import { ProgressBarMode, MatProgressBarModule } from '@angular/material/progress-bar'; -import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component"; -import {ToastrService} from "ngx-toastr"; +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'; @Component({ selector: 'app-task-logs', @@ -17,8 +18,8 @@ import {ToastrService} from "ngx-toastr"; styleUrls: ['./task-logs.component.css'] }) export class TaskLogsComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - mercureUrl: string = import.meta.env.NG_APP_OGCORE_MERCURE_BASE_URL; + baseUrl: string; + mercureUrl: string; traces: any[] = []; groupedTraces: any[] = []; commands: any[] = []; @@ -89,11 +90,15 @@ export class TaskLogsComponent implements OnInit { commandControl = new FormControl(); constructor(private http: HttpClient, - private joyrideService: JoyrideService, - private dialog: MatDialog, - private cdr: ChangeDetectorRef, - private toastService: ToastrService - ) { } + private joyrideService: JoyrideService, + private dialog: MatDialog, + private cdr: ChangeDetectorRef, + private configService: ConfigService, + private toastService: ToastrService + ) { + this.baseUrl = this.configService.apiUrl; + this.mercureUrl = this.configService.mercureUrl; + } ngOnInit(): void { this.loadTraces(); diff --git a/ogWebconsole/src/app/components/commands/main-commands/commands.component.spec.ts b/ogWebconsole/src/app/components/commands/main-commands/commands.component.spec.ts index 53a40f0..54cefa7 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/commands.component.spec.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/commands.component.spec.ts @@ -20,12 +20,18 @@ import { NgxChartsModule } from '@swimlane/ngx-charts'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; describe('CommandsComponent', () => { let component: CommandsComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [CommandsComponent, LoadingComponent], imports: [ @@ -53,7 +59,8 @@ describe('CommandsComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} } + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); diff --git a/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts b/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts index a2ca53b..460064f 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/commands.component.ts @@ -7,7 +7,7 @@ import { CreateCommandComponent } from './create-command/create-command.componen import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import { MatTableDataSource } from '@angular/material/table'; import { DatePipe } from '@angular/common'; -import { ExecuteCommandComponent } from './execute-command/execute-command.component'; +import { ConfigService } from '@services/config.service'; import { JoyrideService } from 'ngx-joyride'; @Component({ @@ -16,7 +16,8 @@ import { JoyrideService } from 'ngx-joyride'; styleUrls: ['./commands.component.css'] }) export class CommandsComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); filters: { [key: string]: string | boolean } = {}; length: number = 0; @@ -48,10 +49,12 @@ export class CommandsComponent implements OnInit { } ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/commands`; constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService, - private joyrideService: JoyrideService) {} + private joyrideService: JoyrideService, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/commands`; + } ngOnInit(): void { this.search(); diff --git a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.spec.ts b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.spec.ts index 3cd7b9a..0d29638 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.spec.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.spec.ts @@ -13,12 +13,18 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; +import { ConfigService } from '@services/config.service'; describe('CreateCommandComponent', () => { let component: CreateCommandComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [CreateCommandComponent], imports: [ @@ -45,7 +51,8 @@ describe('CreateCommandComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); }); diff --git a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts index 27257e8..d0e74a2 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/create-command/create-command.component.ts @@ -3,7 +3,8 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; -import {DataService} from "../data.service"; +import { DataService } from "../data.service"; +import { ConfigService } from "@services/config.service"; @Component({ selector: 'app-create-command', @@ -11,9 +12,8 @@ import {DataService} from "../data.service"; styleUrls: ['./create-command.component.css'] }) export class CreateCommandComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; createCommandForm: FormGroup; - private apiUrl = `${this.baseUrl}/commands`; commandId: string | null = null; constructor( @@ -21,9 +21,11 @@ export class CreateCommandComponent { private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, + private configService: ConfigService, private dataService: DataService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.createCommandForm = this.fb.group({ name: ['', Validators.required], script: [''], diff --git a/ogWebconsole/src/app/components/commands/main-commands/data.service.ts b/ogWebconsole/src/app/components/commands/main-commands/data.service.ts index d8bd7e1..2333b9d 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/data.service.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/data.service.ts @@ -1,6 +1,6 @@ - +import { ConfigService } from '@services/config.service'; import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -8,10 +8,16 @@ import { catchError, map } from 'rxjs/operators'; providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/commands?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor( + private http: HttpClient, + private configService: ConfigService + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/commands?page=1&itemsPerPage=1000`; + } getCommands(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.ts b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.ts index 7df79bf..f665de9 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/detail-command/command-detail.component.ts @@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dial import { CreateCommandComponent } from '../create-command/create-command.component'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-command-detail', @@ -11,7 +12,7 @@ import { ToastrService } from 'ngx-toastr'; styleUrls: ['./command-detail.component.css'] }) export class CommandDetailComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; form!: FormGroup; clients: any[] = []; showClientSelect = false; @@ -20,12 +21,15 @@ export class CommandDetailComponent implements OnInit { constructor( private fb: FormBuilder, + private configService: ConfigService, private http: HttpClient, public dialogRef: MatDialogRef, private dialog: MatDialog, private toastService: ToastrService, @Inject(MAT_DIALOG_DATA) public data: any - ) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.form = this.fb.group({ @@ -52,7 +56,7 @@ export class CommandDetailComponent implements OnInit { dialogRef.afterClosed().subscribe(result => { if (result) { - this.toastService.success('Comando editado' ); + this.toastService.success('Comando editado'); this.data.command = result; } }); diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.spec.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.spec.ts index df1a797..e4f6853 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.spec.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ExecuteCommandComponent } from './execute-command.component'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -17,12 +16,18 @@ import { ToastrModule, ToastrService } from 'ngx-toastr'; import { DataService } from '../data.service'; import {MatIconModule} from "@angular/material/icon"; import {MatMenu, MatMenuModule} from "@angular/material/menu"; +import { ConfigService } from '@services/config.service'; describe('ExecuteCommandComponent', () => { let component: ExecuteCommandComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [ExecuteCommandComponent], imports: [ @@ -54,7 +59,8 @@ describe('ExecuteCommandComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); 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 72a5f59..bc0fd01 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 @@ -1,9 +1,8 @@ -import {Component, Inject, Input, OnInit, SimpleChanges} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog'; +import { Component, Input, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import {Router} from "@angular/router"; -import {ToastrService} from "ngx-toastr"; +import { Router } from "@angular/router"; +import { ToastrService } from "ngx-toastr"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-execute-command', @@ -16,32 +15,32 @@ export class ExecuteCommandComponent implements OnInit { @Input() buttonText: string = 'Ejecutar Comandos'; @Input() icon: string = 'terminal'; @Input() disabled: boolean = false; - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; loading: boolean = true; arrayCommands: any[] = [ - {name: 'Enceder', slug: 'power-on', disabled: false}, - {name: 'Apagar', slug: 'power-off', disabled: false}, - {name: 'Reiniciar', slug: 'reboot', disabled: false}, - {name: 'Iniciar Sesión', slug: 'login', disabled: true}, - {name: 'Crear imagen', slug: 'create-image', disabled: false}, - {name: 'Clonar/desplegar imagen', slug: 'deploy-image', disabled: false}, - {name: 'Eliminar Imagen Cache', slug: 'delete-image-cache', disabled: true}, - {name: 'Particionar y Formatear', slug: 'partition', disabled: false}, - {name: 'Inventario Software', slug: 'software-inventory', disabled: true}, - {name: 'Inventario Hardware', slug: 'hardware-inventory', disabled: true}, - {name: 'Ejecutar script', slug: 'run-script', disabled: true}, + { name: 'Enceder', slug: 'power-on', disabled: false }, + { name: 'Apagar', slug: 'power-off', disabled: false }, + { name: 'Reiniciar', slug: 'reboot', disabled: false }, + { name: 'Iniciar Sesión', slug: 'login', disabled: true }, + { name: 'Crear imagen', slug: 'create-image', disabled: false }, + { name: 'Clonar/desplegar imagen', slug: 'deploy-image', disabled: false }, + { name: 'Eliminar Imagen Cache', slug: 'delete-image-cache', disabled: true }, + { name: 'Particionar y Formatear', slug: 'partition', disabled: false }, + { name: 'Inventario Software', slug: 'software-inventory', disabled: true }, + { name: 'Inventario Hardware', slug: 'hardware-inventory', disabled: true }, + { name: 'Ejecutar script', slug: 'run-script', disabled: true }, ]; client: any = {}; constructor( - private dialog: MatDialog, private http: HttpClient, - private fb: FormBuilder, private router: Router, + private configService: ConfigService, private toastService: ToastrService ) { + this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { diff --git a/ogWebconsole/src/app/components/global-status/global-status.component.css b/ogWebconsole/src/app/components/global-status/global-status.component.css new file mode 100644 index 0000000..49db94b --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/global-status.component.css @@ -0,0 +1,44 @@ +mat-dialog-content { + height: calc(100% - 64px); + overflow: auto; + padding-top: 0.5em !important; +} + +.content-container { + min-height: 100%; +} + +.action-container { + display: flex; + justify-content: flex-end; + gap: 1em; + padding: 1.5em; +} + +.spinner-container { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1000; +} + +.loading-spinner { + width: 100px; + height: 100px; +} + +.error-card { + margin: 20px auto; + max-width: 600px; + text-align: center; + background-color: rgb(243, 243, 243); + color: rgb(48, 48, 48); +} + +.error-card p { + margin-top: 0; +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/global-status.component.html b/ogWebconsole/src/app/components/global-status/global-status.component.html new file mode 100644 index 0000000..ae4d259 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/global-status.component.html @@ -0,0 +1,94 @@ +
+

{{'GlobalStatus' | translate}}

+
+ +
+ +
+ + +
+ + +
+ + +

{{ 'errorLoadingData' | translate }}

+
+
+
+ + +
+ + +
+ + +

{{ 'errorLoadingData' | translate }}

+
+
+
+ + + + +
+ + +
+ + +

{{ 'errorLoadingData' | translate }}

+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/global-status.component.spec.ts b/ogWebconsole/src/app/components/global-status/global-status.component.spec.ts new file mode 100644 index 0000000..5d993e0 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/global-status.component.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ToastrModule } from 'ngx-toastr'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTabsModule } from '@angular/material/tabs'; +import { GlobalStatusComponent } from './global-status.component'; +import { ConfigService } from '@services/config.service'; +import { LoadingComponent } from '../../shared/loading/loading.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +describe('GlobalStatusComponent', () => { + let component: GlobalStatusComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url' + }; + + await TestBed.configureTestingModule({ + declarations: [GlobalStatusComponent, LoadingComponent], + imports: [ + HttpClientTestingModule, + ToastrModule.forRoot(), + MatDialogModule, + MatTabsModule, + TranslateModule.forRoot(), + NgxChartsModule, + BrowserAnimationsModule + ], + providers: [ + { provide: ConfigService, useValue: mockConfigService } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + + fixture = TestBed.createComponent(GlobalStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/global-status.component.ts b/ogWebconsole/src/app/components/global-status/global-status.component.ts new file mode 100644 index 0000000..9991495 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/global-status.component.ts @@ -0,0 +1,194 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { ConfigService } from '@services/config.service'; +import { MatTabChangeEvent } from '@angular/material/tabs'; + +@Component({ + selector: 'app-global-status', + templateUrl: './global-status.component.html', + styleUrl: './global-status.component.css' +}) +export class GlobalStatusComponent implements OnInit { + baseUrl: string; + loading: boolean = false; + errorOgBoot: boolean = false; + errorDhcp: boolean = false; + errorRepositories: { [key: string]: boolean } = {}; + installedOgLives: any[] = []; + subnets: any[] = []; + showLabels: boolean = true; + isDoughnut: boolean = true; + colorScheme: any = { + domain: ['#df200d', '#26a700'] + }; + view: [number, number] = [400, 220]; + repositoriesUrl: string; + repositories: any[] = []; + repositoryStatuses: { [key: string]: any } = {}; + + ogBootApiUrl: string; + ogBootDiskUsage: any = {}; + ogBootServicesStatus: any = {}; + ogBootDiskUsageChartData: any[] = []; + + dhcpApiUrl: string; + dhcpDiskUsage: any = {}; + dhcpServicesStatus: any = {}; + dhcpDiskUsageChartData: any[] = []; + isDhcp: boolean = false; + isRepository: boolean = false; + + constructor( + private configService: ConfigService, + private http: HttpClient + ) { + this.baseUrl = this.configService.apiUrl; + this.ogBootApiUrl = `${this.baseUrl}/og-boot/status`; + this.dhcpApiUrl = `${this.baseUrl}/og-dhcp/status`; + this.repositoriesUrl = `${this.baseUrl}/image-repositories`; + } + + ngOnInit(): void { + this.loadOgBootStatus(); + } + + [key: string]: any; + + loadStatus(apiUrl: string, diskUsage: any, servicesStatus: any, diskUsageChartData: any[], installedOgLives: any[], isDhcp: boolean, errorState: string): void { + this.loading = true; + this[errorState] = false; + const timeoutId = setTimeout(() => { + this.loading = false; + this[errorState] = true; + }, 3500); + this.http.get(apiUrl).subscribe({ + next: data => { + diskUsage.used = data.message.disk_usage.used; + diskUsage.available = data.message.disk_usage.available; + diskUsage.total = data.message.disk_usage.total; + diskUsage.percentage = data.message.disk_usage.percentage; + + Object.assign(servicesStatus, data.message.services_status); + + if (isDhcp) { + this.subnets.length = 0; + if (data.message.subnets) { + this.subnets.push(...data.message.subnets); + } + } else { + installedOgLives.length = 0; + if (data.message.installed_oglives) { + installedOgLives.push(...data.message.installed_oglives); + } + } + + diskUsageChartData.length = 0; + diskUsageChartData.push( + { name: 'Usado', value: parseFloat(diskUsage.used) }, + { name: 'Disponible', value: parseFloat(diskUsage.available) } + ); + + this.loading = false; + clearTimeout(timeoutId); + }, + error: error => { + console.log(error); + this.loading = false; + this[errorState] = true; + clearTimeout(timeoutId); + } + }); + } + + loadRepositories(): void { + this.loading = true; + this.errorRepositories = {}; + const timeoutId = setTimeout(() => { + this.loading = false; + this.repositories.forEach(repository => { + this.errorRepositories[repository.uuid] = true; + }); + }, 5000); + this.http.get(`${this.repositoriesUrl}?page=1&itemsPerPage=10`).subscribe( + data => { + this.repositories = data['hydra:member']; + let remainingRepositories = this.repositories.length; + this.repositories.forEach(repository => { + this.loadRepositoryStatus(repository.uuid, (errorOccurred: boolean) => { + remainingRepositories--; + if (remainingRepositories === 0) { + this.loading = false; + clearTimeout(timeoutId); + } + if (errorOccurred) { + this.errorRepositories[repository.uuid] = true; + } else { + this.errorRepositories[repository.uuid] = false; + } + }); + }); + }, + error => { + console.error('Error fetching repositories', error); + this.loading = false; + this.repositories.forEach(repository => { + this.errorRepositories[repository.uuid] = true; + }); + clearTimeout(timeoutId); + } + ); + } + + loadRepositoryStatus(repositoryUuid: string, callback: (errorOccurred: boolean) => void): void { + const timeoutId = setTimeout(() => { + callback(true); + }, 5000); + this.http.get(`${this.baseUrl}/image-repositories/server/${repositoryUuid}/status`).subscribe( + data => { + const output = data.output; + this.repositoryStatuses[repositoryUuid] = { + ...output, + disk: { + ...output.disk, + used: parseFloat(output.disk.used), + available: parseFloat(output.disk.available) + }, + ram: { + ...output.ram, + used: parseFloat(output.ram.used), + available: parseFloat(output.ram.available) + } + }; + clearTimeout(timeoutId); + callback(false); + }, + error => { + console.error(`Error fetching status for repository ${repositoryUuid}`, error); + clearTimeout(timeoutId); + callback(true); + } + ); + } + + loadOgBootStatus(): void { + this.isDhcp = false; + this.loadStatus(this.ogBootApiUrl, this.ogBootDiskUsage, this.ogBootServicesStatus, this.ogBootDiskUsageChartData, this.installedOgLives, this.isDhcp, 'errorOgBoot'); + } + + loadDhcpStatus(): void { + this.isDhcp = true; + this.loadStatus(this.dhcpApiUrl, this.dhcpDiskUsage, this.dhcpServicesStatus, this.dhcpDiskUsageChartData, this.installedOgLives, this.isDhcp, 'errorDhcp'); + } + + onTabChange(event: MatTabChangeEvent): void { + if (event.tab.textLabel === 'OgBoot') { + this.loadOgBootStatus(); + } else if (event.tab.textLabel === 'Dhcp') { + this.loadDhcpStatus(); + } else if (event.tab.textLabel === 'Repositorios') { + if (this.repositories.length === 0) { + this.loadRepositories(); + } + } + } +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.css b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.css new file mode 100644 index 0000000..46027d1 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.css @@ -0,0 +1,84 @@ +.dashboard { + display: flex; + flex-direction: column; +} + +.disk-usage-container, +.ram-usage-container { + display: flex; + flex-direction: column; +} + +.disk-usage, +.ram-usage { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +.service-list, +.process-list { + margin-top: 0em; + margin-bottom: 0.5em; +} + +.services-status, +.processes-status { + display: flex; + flex-direction: column; +} + +.services-status li { + margin: 5px 0; + display: flex; + align-items: center; +} + +.processes-status li { + margin: 5px 0; + display: flex; + align-items: center; +} + +.status-led { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; + margin-right: 10px; +} + +.status-led.active { + background-color: green; +} + +.status-led.inactive { + background-color: red; +} + +.disk-title, +.ram-title { + margin-bottom: 0px; +} + +.service-title, +.process-title { + margin-top: 0px; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, +td { + border: 1px solid #ddd; + padding: 8px; +} + +th { + background-color: #f4f4f4; +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.html b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.html new file mode 100644 index 0000000..bfbd735 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.html @@ -0,0 +1,113 @@ + +
+ +
+

{{ 'diskUsageTitle' | translate }}

+
+ + +
+

{{ 'totalLabel' | translate }}: {{ isRepository ? diskUsage.total : formatBytes(diskUsage.total) }}

+

{{ 'usedLabel' | translate }}: {{ isRepository ? diskUsage.used : formatBytes(diskUsage.used) }}

+

{{ 'availableLabel' | translate }}: {{ isRepository ? diskUsage.available : formatBytes(diskUsage.available) }}

+

{{ 'usedPercentageLabel' | translate }}: {{ isRepository ? diskUsage.used_percentage : diskUsage.percentage }}

+
+
+
+ + +
+

{{ 'RamUsage' | translate }}

+
+ + +
+

{{ 'totalLabel' | translate }}: {{ ramUsage.total }}

+

{{ 'usedLabel' | translate }}: {{ ramUsage.used }}

+

{{ 'availableLabel' | translate }}: {{ ramUsage.available }}

+

{{ 'usedPercentageLabel' | translate }}: {{ ramUsage.used_percentage }}

+
+
+
+ + +
+

{{ 'CpuUsage' | translate }}

+
+

{{ 'usedLabel' | translate }}: {{ cpuUsage.used_percentage }}

+
+
+ + +
+

{{ 'servicesTitle' | translate }}

+
    +
  • + + {{ service.name }}: {{ service.status | translate }} +
  • +
+
+ + +
+

{{ 'processes' | translate }}

+
    +
  • + + {{ process.name }}: {{ process.status }} +
  • +
+
+ + +
+

{{ 'InstalledOglivesTitle' | translate }}

+ + + + + + + + + + + + + + + + + +
{{ 'idLabel' | translate }}{{ 'kernelLabel' | translate }}{{ 'architectureLabel' | translate }}{{ 'revisionLabel' | translate }}
{{ item.id }}{{ item.kernel }}{{ item.architecture }}{{ item.revision }}
+
+ +
+

{{ 'subnets' | translate }}

+ + + + + + + + + + + + + + + + + + + +
{{ 'idLabel' | translate }}{{ 'bootFileNameLabel' | translate }}{{ 'nextServerLabel' | translate }}{{ 'ipLabel' | translate }}{{ 'clientsLabel' | translate }}
{{ item.id }}{{ item['boot-file-name'] }}{{ item['next-server'] }}{{ item.subnet }}{{ item.reservations.length }}
+
+
\ No newline at end of file diff --git a/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.spec.ts b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.spec.ts new file mode 100644 index 0000000..c29ac03 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ToastrModule } from 'ngx-toastr'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTabsModule } from '@angular/material/tabs'; +import { ConfigService } from '@services/config.service'; +import { LoadingComponent } from 'src/app/shared/loading/loading.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { StatusTabComponent } from './status-tab.component'; + +describe('StatusTabComponent', () => { + let component: StatusTabComponent; + let fixture: ComponentFixture; + + const mockConfigService = { + apiUrl: 'http://mock-api-url' + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [StatusTabComponent, LoadingComponent], + imports: [ + HttpClientTestingModule, + ToastrModule.forRoot(), + MatDialogModule, + MatTabsModule, + TranslateModule.forRoot(), + NgxChartsModule, + BrowserAnimationsModule + ], + providers: [ + { provide: ConfigService, useValue: mockConfigService } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StatusTabComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.ts b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.ts new file mode 100644 index 0000000..cafcb27 --- /dev/null +++ b/ogWebconsole/src/app/components/global-status/status-tab/status-tab.component.ts @@ -0,0 +1,60 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'app-status-tab', + templateUrl: './status-tab.component.html', + styleUrl: './status-tab.component.css' +}) +export class StatusTabComponent { + @Input() loading: boolean = false; + @Input() diskUsage: any = {}; + @Input() servicesStatus: any = {}; + @Input() installedOgLives: any[] = []; + @Input() subnets: any[] = []; + @Input() diskUsageChartData: any[] = []; + @Input() showLabels: boolean = true; + @Input() isDoughnut: boolean = true; + @Input() colorScheme: any = { + domain: ['#df200d', '#26a700'] + }; + @Input() view: [number, number] = [400, 220]; + @Input() isDhcp: boolean = false; + @Input() processesStatus: any = {}; + @Input() ramUsage: any = {}; + @Input() cpuUsage: any = {}; + @Input() ramUsageChartData: any[] = []; + @Input() isRepository: boolean = false; + + getServices(): { name: string, status: string }[] { + if (!this.servicesStatus) { + return []; + } + const services = Object.keys(this.servicesStatus).map(key => ({ + name: key, + status: this.servicesStatus[key] + })) + return services; + } + + getProcesses(): { name: string, status: string }[] { + if (!this.processesStatus) { + return []; + } + return Object.keys(this.processesStatus).map(key => ({ + name: key, + status: this.processesStatus[key] + })); + } + + formatBytes(bytes: number): string { + if (bytes >= 1e9) { + return (bytes / 1e9).toFixed(2) + ' GB'; + } else if (bytes >= 1e6) { + return (bytes / 1e6).toFixed(2) + ' MB'; + } else if (bytes >= 1e3) { + return (bytes / 1e3).toFixed(2) + ' KB'; + } else { + return bytes + ' B'; + } + } +} 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 index 1c2f62d..c9e5bef 100644 --- 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 @@ -5,6 +5,15 @@ 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; @@ -258,27 +267,72 @@ .disk-container { display: flex; flex-direction: row; - justify-content: space-between; - align-items: flex-start; 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: center; /* Centra contenido verticalmente */ + margin-bottom: 20px; } .table-container { - flex: 3; - overflow-x: auto; + flex: 1; + display: flex; + justify-content: center; /* Centrar la tabla */ + 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; /* Encabezado más moderno */ + color: #333; + font-weight: bold; + text-align: center; +} + +.mat-cell { + text-align: center; +} + +.mat-chip { + font-weight: bold; + color: white; } .charts-container { flex: 1; display: flex; - align-items: center; - gap: 10px; + flex-wrap: wrap; + justify-content: center; /* Centra los gráficos */ + gap: 20px; } .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; @@ -302,4 +356,4 @@ .back-button:disabled { background-color: #ced0df; cursor: not-allowed; -} \ No newline at end of file +} 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 index dcb2d9c..bfda90e 100644 --- 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 @@ -1,68 +1,73 @@ -
-

{{ 'clientDetailsTitle' | translate }}

-
- -
-
- -
-
-
-
-
{{ clientData?.property }}
-
{{ clientData?.value }}
-
+
+
+

{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}

+
+
-
-
-
{{ clientData?.property }}
-
{{ clientData?.value }}
+
+ + + +
+
+
+
+
{{ clientData?.property }}
+
{{ clientData?.value }}
+
+
+
+
+
{{ clientData?.property }}
+
{{ clientData?.value }}
+
-
-
-

Discos/Particiones

-
+
+

Discos/Particiones

+
-
- -
- - - - +
+ +
+
{{ column.header }} - - {{ column.cell(image) }} - - - - {{ (image.size / 1024).toFixed(2) }} GB - - -
+ + + + + + +
{{ column.header }} + + {{ column.cell(image) }} + + + + {{ (image.size / 1024).toFixed(2) }} GB + + +
+
+ + +
+ +
+ + + +

Disco {{ disk.diskNumber }}

+

Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)

+

Total: {{ disk.total }} GB

+
- - - +
+
- -
- -
- - - -

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/components/client-main-view/client-main-view.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts index e302018..51edd4d 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/client-main-view.component.ts @@ -1,12 +1,12 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import {DatePipe} from "@angular/common"; -import {MatTableDataSource} from "@angular/material/table"; -import {PartitionAssistantComponent} from "./partition-assistant/partition-assistant.component"; -import {MatDialog} from "@angular/material/dialog"; -import {Router} from "@angular/router"; -import {ToastrService} from "ngx-toastr"; +import { DatePipe } from "@angular/common"; +import { MatTableDataSource } from "@angular/material/table"; +import { MatDialog } from "@angular/material/dialog"; +import { Router } from "@angular/router"; +import { ToastrService } from "ngx-toastr"; import { ManageClientComponent } from "../../shared/clients/manage-client/manage-client.component"; +import { ConfigService } from '@services/config.service'; interface ClientInfo { property: string; @@ -19,7 +19,7 @@ interface ClientInfo { styleUrl: './client-main-view.component.css' }) export class ClientMainViewComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; @ViewChild('assistantContainer') assistantContainer!: ElementRef; clientUuid: string; clientData: any = {}; @@ -38,17 +38,17 @@ export class ClientMainViewComponent implements OnInit { showLegend: boolean = true; arrayCommands: any[] = [ - {name: 'Enceder', slug: 'power-on'}, - {name: 'Apagar', slug: 'power-off'}, - {name: 'Reiniciar', slug: 'reboot'}, - {name: 'Iniciar Sesión', slug: 'login'}, - {name: 'Crear imagen', slug: 'create-image'}, - {name: 'Clonar/desplegar imagen', slug: 'deploy-image'}, - {name: 'Eliminar Imagen Cache', slug: 'delete-image-cache'}, - {name: 'Particionar y Formatear', slug: 'partition'}, - {name: 'Inventario Software', slug: 'software-inventory'}, - {name: 'Inventario Hardware', slug: 'hardware-inventory'}, - {name: 'Ejecutar script', slug: 'run-script'}, + { name: 'Enceder', slug: 'power-on' }, + { name: 'Apagar', slug: 'power-off' }, + { name: 'Reiniciar', slug: 'reboot' }, + { name: 'Iniciar Sesión', slug: 'login' }, + { name: 'Crear imagen', slug: 'create-image' }, + { name: 'Clonar/desplegar imagen', slug: 'deploy-image' }, + { name: 'Eliminar Imagen Cache', slug: 'delete-image-cache' }, + { name: 'Particionar y Formatear', slug: 'partition' }, + { name: 'Inventario Software', slug: 'software-inventory' }, + { name: 'Inventario Hardware', slug: 'hardware-inventory' }, + { name: 'Ejecutar script', slug: 'run-script' }, ]; datePipe: DatePipe = new DatePipe('es-ES'); @@ -91,9 +91,11 @@ export class ClientMainViewComponent implements OnInit { constructor( private http: HttpClient, private dialog: MatDialog, + private configService: ConfigService, private router: Router, private toastService: ToastrService - ) { + ) { + this.baseUrl = this.configService.apiUrl; const url = window.location.href; const segments = url.split('/'); this.clientUuid = segments[segments.length - 1]; @@ -198,15 +200,16 @@ export class ClientMainViewComponent implements OnInit { onEditClick(event: MouseEvent, uuid: string): void { event.stopPropagation(); - const dialogRef = this.dialog.open(ManageClientComponent, { data: { uuid }, width: '900px' } ); + const dialogRef = this.dialog.open(ManageClientComponent, { data: { uuid }, width: '900px' }); dialogRef.afterClosed().subscribe(); } loadPartitions(): void { this.http.get(`${this.baseUrl}/partitions?client.id=${this.clientData?.id}&order[diskNumber, partitionNumber]=ASC`).subscribe({ next: data => { - this.dataSource = data['hydra:member']; - this.partitions = data['hydra:member']; + const filteredPartitions = data['hydra:member'].filter((partition: any) => partition.partitionNumber !== 0); + this.dataSource = filteredPartitions; + this.partitions = filteredPartitions; this.calculateDiskUsage(); }, error: error => { @@ -215,6 +218,7 @@ export class ClientMainViewComponent implements OnInit { }); } + loadCommands(): void { this.http.get(`${this.baseUrl}/commands?`).subscribe({ next: data => { diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css index fc11805..bafb062 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css @@ -43,8 +43,8 @@ table { box-sizing: border-box; } -.full-width { - width: 100%; +.custom-width { + width: 50%; margin-bottom: 16px; } @@ -62,7 +62,8 @@ table { display: flex; justify-content: space-between; align-items: center; - padding: 10px; + padding: 10px 10px; + border-bottom: 1px solid #ddd; } .mat-elevation-z8 { @@ -74,3 +75,15 @@ table { justify-content: end; margin-bottom: 30px; } + +.header-container-title { + flex-grow: 1; + text-align: left; + padding-left: 1em; +} + +.button-row { + display: flex; + padding-right: 1em; +} + diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html index 189f45d..72a9280 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html @@ -6,14 +6,14 @@ Crear imagen desde {{ clientName }}
-
+
- + Nombre canónico diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts index 05564e6..bf03038 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts @@ -1,16 +1,18 @@ -import {Component, EventEmitter, Output} from '@angular/core'; -import {HttpClient} from "@angular/common/http"; -import {ToastrService} from "ngx-toastr"; -import {ActivatedRoute, Router} from "@angular/router"; -import {MatTableDataSource} from "@angular/material/table"; -import {SelectionModel} from "@angular/cdk/collections"; +import { Component, EventEmitter, Output } from '@angular/core'; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { ActivatedRoute, Router } from "@angular/router"; +import { MatTableDataSource } from "@angular/material/table"; +import { SelectionModel } from "@angular/cdk/collections"; +import { ConfigService } from '@services/config.service'; + @Component({ selector: 'app-create-image', templateUrl: './create-image.component.html', styleUrl: './create-image.component.css' }) export class CreateClientImageComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; @Output() dataChange = new EventEmitter(); errorMessage = ''; @@ -57,10 +59,12 @@ export class CreateClientImageComponent { constructor( private http: HttpClient, private toastService: ToastrService, + private configService: ConfigService, private route: ActivatedRoute, private router: Router, - - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit() { this.clientId = this.route.snapshot.paramMap.get('id'); @@ -91,7 +95,7 @@ export class CreateClientImageComponent { const url = `${this.baseUrl}/images?created=false&page=1&itemsPerPage=1000`; this.http.get(url).subscribe( (response: any) => { - this.images = response['hydra:member']; + this.images = response['hydra:member']; }, (error) => { console.error('Error al cargar las imágenes:', error); @@ -112,16 +116,16 @@ export class CreateClientImageComponent { this.http.post(`${this.baseUrl}/images`, payload) .subscribe({ - next: (response) => { - this.toastService.success('Petición de creación de imagen enviada'); - this.loading = false; - this.router.navigate(['/commands-logs']); - }, - error: (error) => { - this.toastService.error(error.error['hydra:description']); - this.loading = false; - } + next: (response) => { + this.toastService.success('Petición de creación de imagen enviada'); + this.loading = false; + this.router.navigate(['/commands-logs']); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + this.loading = false; } + } ); } } diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css index 227dfc3..06eaade 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css @@ -75,6 +75,8 @@ table { display: flex; justify-content: space-between; align-items: center; + padding: 10px 10px; + border-bottom: 1px solid #ddd; } .mat-elevation-z8 { @@ -130,3 +132,8 @@ table { text-align: left; padding-left: 1em; } + +.button-row { + display: flex; + padding-right: 1em; +} diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html index 9bed9a1..b2e3a4a 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html @@ -6,7 +6,7 @@ {{ 'deployImage' | translate }}
-
+
@@ -41,13 +41,6 @@
-
- - Actualizar cache - Desplegar imagen - -
-
Seleccione imagen @@ -60,7 +53,7 @@ Seleccione método de deploy - {{ method }} + {{ method }}
@@ -90,9 +83,9 @@
-

Opciones multicast

+

Opciones multicast

Opciones torrent

-
+
Puerto diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.spec.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.spec.ts index f38ab00..4d10254 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.spec.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { DeployImageComponent } from './deploy-image.component'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -11,7 +10,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; import { MatDividerModule } from '@angular/material/divider'; -import { MatRadioModule } from '@angular/material/radio'; // Importar MatRadioModule +import { MatRadioModule } from '@angular/material/radio'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { ToastrModule, ToastrService } from 'ngx-toastr'; @@ -19,12 +18,18 @@ import { provideRouter } from '@angular/router'; import { MatSelectModule } from '@angular/material/select'; import {MatExpansionModule} from "@angular/material/expansion"; import {LoadingComponent} from "../../../../../shared/loading/loading.component"; +import { ConfigService } from '@services/config.service'; describe('DeployImageComponent', () => { let component: DeployImageComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [DeployImageComponent, LoadingComponent], imports: [ @@ -57,7 +62,8 @@ describe('DeployImageComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts index 2195ed4..040456a 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts @@ -1,9 +1,10 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {MatTableDataSource} from "@angular/material/table"; -import {SelectionModel} from "@angular/cdk/collections"; -import {HttpClient} from "@angular/common/http"; -import {ToastrService} from "ngx-toastr"; -import {ActivatedRoute, Router} from "@angular/router"; +import { Component, EventEmitter, Output } from '@angular/core'; +import { MatTableDataSource } from "@angular/material/table"; +import { SelectionModel } from "@angular/cdk/collections"; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { ActivatedRoute, Router } from "@angular/router"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-deploy-image', @@ -11,7 +12,7 @@ import {ActivatedRoute, Router} from "@angular/router"; styleUrl: './deploy-image.component.css' }) export class DeployImageComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; @Output() dataChange = new EventEmitter(); errorMessage = ''; @@ -20,7 +21,6 @@ export class DeployImageComponent { images: any[] = []; clientName: string = ''; selectedImage: any = null; - selectedOption: string | null = 'deploy-image'; selectedMethod: string | null = null; selectedPartition: any = null; mcastIp: string = ''; @@ -42,25 +42,19 @@ export class DeployImageComponent { { name: 'Seeder', value: 'seeder' }, ]; protected multicastModeOptions = [ - { name: 'Half duplex', value: "half"}, - { name: 'Full duplex', value: "full"}, + { name: 'Half duplex', value: "half" }, + { name: 'Full duplex', value: "full" }, ]; allMethods = [ 'uftp', 'udpcast', + 'udpcast-direct', 'unicast', 'unicast-direct', 'p2p' ]; - updateCacheMethods = [ - 'uftp', - 'udpcast', - 'unicast', - 'p2p' - ]; - dataSource = new MatTableDataSource(); columns = [ { @@ -96,9 +90,10 @@ export class DeployImageComponent { constructor( private http: HttpClient, private toastService: ToastrService, - private route: ActivatedRoute, + private configService: ConfigService, private router: Router, ) { + this.baseUrl = this.configService.apiUrl; const navigation = this.router.getCurrentNavigation(); this.clientData = navigation?.extras?.state?.['clientData']; this.clientId = this.clientData?.[0]['@id']; @@ -106,10 +101,6 @@ export class DeployImageComponent { this.loadPartitions() } - get deployMethods() { - return this.selectedOption === 'update-cache' ? this.updateCacheMethods : this.allMethods; - } - isMethod(method: string): boolean { return this.selectedMethod === method; } @@ -206,25 +197,25 @@ export class DeployImageComponent { this.http.post(`${this.baseUrl}/image-image-repositories/${this.selectedImage.uuid}/deploy-image`, payload) .subscribe({ - next: (response) => { - this.toastService.success('Petición de despliegue enviada correctamente'); - this.loading = false; - this.router.navigate(['/commands-logs']); - }, - error: (error) => { - console.error('Error:', error); - this.toastService.error(error.error['hydra:description'], 'Se ha detectado un error en el despliegue de imágenes.', { - "closeButton": true, - "newestOnTop": false, - "progressBar": false, - "positionClass": "toast-bottom-right", - "timeOut": 0, - "extendedTimeOut": 0, - "tapToDismiss": false - }); - this.loading = false; - } + next: (response) => { + this.toastService.success('Petición de despliegue enviada correctamente'); + this.loading = false; + this.router.navigate(['/commands-logs']); + }, + error: (error) => { + console.error('Error:', error); + this.toastService.error(error.error['hydra:description'], 'Se ha detectado un error en el despliegue de imágenes.', { + "closeButton": true, + "newestOnTop": false, + "progressBar": false, + "positionClass": "toast-bottom-right", + "timeOut": 0, + "extendedTimeOut": 0, + "tapToDismiss": false + }); + this.loading = false; } + } ); } } diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts index 79251b1..365df59 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts @@ -6,6 +6,7 @@ import {ActivatedRoute, Router} from "@angular/router"; import { PARTITION_TYPES } from '../../../../../shared/constants/partition-types'; import { FILESYSTEM_TYPES } from '../../../../../shared/constants/filesystem-types'; import {toUnredirectedSourceFile} from "@angular/compiler-cli/src/ngtsc/util/src/typescript"; +import { ConfigService } from '@services/config.service'; interface Partition { uuid?: string; @@ -27,7 +28,8 @@ interface Partition { styleUrls: ['./partition-assistant.component.css'] }) export class PartitionAssistantComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; @Output() dataChange = new EventEmitter(); partitionTypes = PARTITION_TYPES; filesystemTypes = FILESYSTEM_TYPES; @@ -42,8 +44,6 @@ export class PartitionAssistantComponent { clientData: any = []; loading: boolean = false; - private apiUrl: string = this.baseUrl + '/partitions'; - view: [number, number] = [400, 300]; showLegend = true; showLabels = true; @@ -53,7 +53,10 @@ export class PartitionAssistantComponent { private toastService: ToastrService, private route: ActivatedRoute, private router: Router, + private configService: ConfigService, ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = this.baseUrl + '/partitions'; const navigation = this.router.getCurrentNavigation(); this.clientData = navigation?.extras?.state?.['clientData']; this.clientId = this.clientData[0]['@id']; diff --git a/ogWebconsole/src/app/components/groups/groups.component.css b/ogWebconsole/src/app/components/groups/groups.component.css index 9858f90..cc550ac 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.css +++ b/ogWebconsole/src/app/components/groups/groups.component.css @@ -258,11 +258,11 @@ mat-tree mat-tree-node.disabled:hover { .client-name { display: block; - font-size: 16px; - font-weight: 600; - color: #333; + font-weight: 500; margin-bottom: 5px; margin-top: 5px; + padding-left: 1rem; + padding-right: 1rem; } .filters-container { @@ -319,11 +319,26 @@ mat-tree mat-tree-node.disabled:hover { box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); } -.client-image { - width: 35px; - height: 35px; +.client-details { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-top: 4px; +} + +.action-icons { + display: flex; + justify-content: center; + align-items: center; + gap: 1px; margin-top: 10px; - margin-bottom: 8px; +} + +.client-status-container { + display: flex; + align-items: center; + gap: 5px; } .client-ip { @@ -338,11 +353,9 @@ mat-tree mat-tree-node.disabled:hover { gap: 4px; } -.action-icons { - display: flex; - justify-content: center; - gap: 1px; - margin-top: 10px; +.sync-spinner { + margin-left: 1em; + margin-right: 1em; } .mat-elevation-z8 { @@ -360,10 +373,6 @@ mat-tree mat-tree-node.disabled:hover { position: relative; } -.client-details { - margin-top: 4px; -} - @media (max-width: 1560px) { .clients-view-header { display: flex; @@ -447,4 +456,4 @@ mat-button-toggle-group { padding: 0.5rem 1rem 1rem 1rem; width: 100%; box-sizing: border-box; -} \ No newline at end of file +} diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index 0c91f2e..3056117 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -60,12 +60,14 @@ - + {{ option.name }} - @@ -226,7 +228,7 @@ - Client Icon
@@ -234,14 +236,6 @@ {{ client.ip }} {{ client.mac }}
- - - @@ -252,6 +246,10 @@ more_vert + + + + +
-
+
\ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/groups.component.spec.ts b/ogWebconsole/src/app/components/groups/groups.component.spec.ts index 1de7160..85978f8 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.spec.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.spec.ts @@ -27,12 +27,18 @@ import { MatTreeModule } from '@angular/material/tree'; import { TreeNode } from './model/model'; import { LoadingComponent } from '../../shared/loading/loading.component'; import { ExecuteCommandComponent } from '../commands/main-commands/execute-command/execute-command.component'; +import { ConfigService } from '@services/config.service'; describe('GroupsComponent', () => { let component: GroupsComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [GroupsComponent, ExecuteCommandComponent, LoadingComponent], imports: [ @@ -63,7 +69,8 @@ describe('GroupsComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: { data: { id: 123 } } } + { provide: MAT_DIALOG_DATA, useValue: { data: { id: 123 } } }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); @@ -128,4 +135,4 @@ describe('GroupsComponent', () => { { params: jasmine.any(Object) } ); }); -}); \ No newline at end of file +}) \ 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 43edcc4..c5b7c46 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -17,12 +17,14 @@ import { DeleteModalComponent } from '../../shared/delete_modal/delete-modal/del import { ClassroomViewDialogComponent } from './shared/classroom-view/classroom-view-modal'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; -import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { PageEvent } from '@angular/material/paginator'; import { CreateMultipleClientComponent } from "./shared/clients/create-multiple-client/create-multiple-client.component"; import { SelectionModel } from "@angular/cdk/collections"; import { ManageClientComponent } from "./shared/clients/manage-client/manage-client.component"; import { debounceTime } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { ConfigService } from '@services/config.service'; +import { GlobalStatusComponent } from '../global-status/global-status.component'; enum NodeType { OrganizationalUnit = 'organizational-unit', @@ -38,8 +40,8 @@ enum NodeType { styleUrls: ['./groups.component.css'], }) export class GroupsComponent implements OnInit, OnDestroy { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - mercureUrl: string = import.meta.env.NG_APP_OGCORE_MERCURE_BASE_URL; + baseUrl: string; + mercureUrl: string; organizationalUnits: UnidadOrganizativa[] = []; selectedUnidad: UnidadOrganizativa | null = null; selectedDetail: UnidadOrganizativa | null = null; @@ -103,8 +105,11 @@ export class GroupsComponent implements OnInit, OnDestroy { public dialog: MatDialog, private bottomSheet: MatBottomSheet, private joyrideService: JoyrideService, - private toastr: ToastrService + private toastr: ToastrService, + private configService: ConfigService ) { + this.baseUrl = this.configService.apiUrl; + this.mercureUrl = this.configService.mercureUrl; this.treeFlattener = new MatTreeFlattener( this.transformer, (node) => node.level, @@ -167,6 +172,7 @@ export class GroupsComponent implements OnInit, OnDestroy { }; this.selectedClients.data = updatedClients; + this.arrayClients = updatedClients; console.log(`Estado actualizado para el cliente ${clientUuid}: ${newStatus}`); } else { diff --git a/ogWebconsole/src/app/components/groups/services/data.service.ts b/ogWebconsole/src/app/components/groups/services/data.service.ts index 8c39ec4..8fb6aa7 100644 --- a/ogWebconsole/src/app/components/groups/services/data.service.ts +++ b/ogWebconsole/src/app/components/groups/services/data.service.ts @@ -3,18 +3,22 @@ import {HttpClient, HttpParams} from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { UnidadOrganizativa } from '../model/model'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; + private clientsUrl: string; - private apiUrl = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=1000`; - private clientsUrl = `${this.baseUrl}/clients`; - - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=1000`; + this.clientsUrl = `${this.baseUrl}/clients`; + } getOrganizationalUnits(search: string = ''): Observable { let url = `${this.apiUrl}&type=organizational-unit`; diff --git a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.ts b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.ts index 33c65f9..6058b57 100644 --- a/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/classroom-view/classroom-view.component.ts @@ -4,6 +4,7 @@ import { ClientViewComponent } from "../client-view/client-view.component"; import { CdkDragMove } from '@angular/cdk/drag-drop'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; interface GroupedClients { organizationalUnitName: string; @@ -16,12 +17,14 @@ interface GroupedClients { styleUrls: ['./classroom-view.component.css'] }) export class ClassroomViewComponent implements OnInit, OnChanges { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; @Input() clients: any[] = []; @Input() pcInTable: number = 5; groupedClients: GroupedClients[] = []; - constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService) { } + constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.groupClientsByOrganizationalUnit(); diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts index a2fe39e..c8eb724 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts @@ -1,18 +1,18 @@ -import {Component, Inject, OnInit, Optional} from '@angular/core'; -import {MatDialogRef} from "@angular/material/dialog"; -import {HttpClient} from "@angular/common/http"; -import {MatSnackBar} from "@angular/material/snack-bar"; -import {ToastrService} from "ngx-toastr"; -import {MAT_DIALOG_DATA} from "@angular/material/dialog"; +import { Component, Inject, OnInit, Optional } from '@angular/core'; +import { MatDialogRef } from "@angular/material/dialog"; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { MAT_DIALOG_DATA } from "@angular/material/dialog"; import { DataService } from '../../../services/data.service'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-multiple-client', templateUrl: './create-multiple-client.component.html', styleUrl: './create-multiple-client.component.css' }) -export class CreateMultipleClientComponent implements OnInit{ - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; +export class CreateMultipleClientComponent implements OnInit { + baseUrl: string = this.configService.apiUrl; parentUnits: any[] = []; parentUnitsWithPaths: { id: string, name: string, path: string }[] = []; uploadedClients: any[] = []; @@ -20,22 +20,24 @@ export class CreateMultipleClientComponent implements OnInit{ displayedColumns: string[] = ['name', 'ip', 'mac']; showTextarea: boolean = true; organizationalUnit: any; - regex: RegExp = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([a-zA-Z0-9]{2}(:[a-zA-Z0-9]{2}){5});\s+fixed-address\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?));\s+\}/g; + regex: RegExp = /host\s+(\S+)\s*\{\s*hardware\s+ethernet\s+([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5});\s*fixed-address\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?));\s*\}/g; constructor( @Optional() private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) private data: any, private http: HttpClient, - private snackBar: MatSnackBar, + private configService: ConfigService, private toastService: ToastrService, private dataService: DataService - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.loadParentUnits(); if (this.data?.organizationalUnit) { - this.organizationalUnit = this.data.organizationalUnit['@id']; + this.organizationalUnit = this.data.organizationalUnit['@id']; } } diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html index 4d90eb2..1a4d246 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html +++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.html @@ -101,6 +101,10 @@ {{ 'menuError' | translate }} + + + {{ 'maintenance' | translate }} +
@@ -110,4 +114,4 @@ {{ isEditMode ? 'Guardar' : 'Crear' }}
-
\ No newline at end of file +
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts index d357706..484b462 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts +++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.spec.ts @@ -8,12 +8,18 @@ import { TranslateModule } from '@ngx-translate/core'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ManageClientComponent } from './manage-client.component'; import { DataService } from '../../../services/data.service'; +import { ConfigService } from '@services/config.service'; describe('ManageClientComponent', () => { let component: ManageClientComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [ManageClientComponent], imports: [ @@ -28,6 +34,7 @@ describe('ManageClientComponent', () => { providers: [ { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: { uuid: '123', organizationalUnit: { '@id': '/units/1' } } }, + { provide: ConfigService, useValue: mockConfigService }, DataService ] }) diff --git a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts index a9d7863..42a1477 100644 --- a/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/clients/manage-client/manage-client.component.ts @@ -2,9 +2,9 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Component, Inject, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { ToastrService } from 'ngx-toastr'; import { DataService } from '../../../services/data.service'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-manage-client', @@ -12,7 +12,7 @@ import { DataService } from '../../../services/data.service'; styleUrls: ['./manage-client.component.css'] }) export class ManageClientComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; clientForm!: FormGroup; parentUnits: any[] = []; parentUnitsWithPaths: { id: string, name: string, path: string }[] = []; @@ -38,11 +38,12 @@ export class ManageClientComponent implements OnInit { private fb: FormBuilder, private dialogRef: MatDialogRef, private http: HttpClient, - private snackBar: MatSnackBar, + private configService: ConfigService, private toastService: ToastrService, private dataService: DataService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.isEditMode = !!data?.uuid; this.dialogTitle = this.isEditMode ? 'editClientDialogTitle' : 'addClientDialogTitle'; } @@ -89,7 +90,8 @@ export class ManageClientComponent implements OnInit { hardwareProfile: [null], ogLive: [null], repository: [null], - menu: [null] + menu: [null], + maintenance: [false] }); } @@ -248,6 +250,7 @@ export class ManageClientComponent implements OnInit { ogLive: data.ogLive ? data.ogLive['@id'] : null, template: data.template ? data.template['@id'] : null, menu: data.menu ? data.menu['@id'] : null, + maintenance: data.maintenance }); resolve(); }, @@ -299,4 +302,4 @@ export class ManageClientComponent implements OnInit { onNoClick(): void { this.dialogRef.close(); } -} \ No newline at end of file +} diff --git a/ogWebconsole/src/app/components/groups/shared/execute-command-ou/execute-command-ou.component.ts b/ogWebconsole/src/app/components/groups/shared/execute-command-ou/execute-command-ou.component.ts index 7831cab..87165a2 100644 --- a/ogWebconsole/src/app/components/groups/shared/execute-command-ou/execute-command-ou.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/execute-command-ou/execute-command-ou.component.ts @@ -3,6 +3,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { FormBuilder, FormGroup } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-execute-command-ou', @@ -14,15 +15,17 @@ export class ExecuteCommandOuComponent implements OnInit { clients: any[] = []; commands: any[] = []; commandGroups: any[] = []; - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; constructor( private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, private http: HttpClient, private fb: FormBuilder, + private configService: ConfigService, private toastService: ToastrService, ) { + this.baseUrl = this.configService.apiUrl; this.form = this.fb.group({ selectedCommand: [null], selectedCommandGroup: [null], diff --git a/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.spec.ts index 1e1ea2a..74e69bd 100644 --- a/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.spec.ts +++ b/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.spec.ts @@ -13,12 +13,18 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { LoadingComponent } from '../../../../../shared/loading/loading.component'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { ConfigService } from '@services/config.service'; describe('ManageOrganizationalUnitComponent', () => { let component: ManageOrganizationalUnitComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [ManageOrganizationalUnitComponent, LoadingComponent], imports: [ @@ -36,7 +42,8 @@ describe('ManageOrganizationalUnitComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} } + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.ts b/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.ts index d7482a1..de6dbf5 100644 --- a/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component.ts @@ -4,6 +4,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DataService } from "../../../services/data.service"; import { ToastrService } from "ngx-toastr"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-manage-organizational-unit', @@ -11,7 +12,7 @@ import { ToastrService } from "ngx-toastr"; styleUrls: ['./manage-organizational-unit.component.css'] }) export class ManageOrganizationalUnitComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; isLinear = true; generalFormGroup: FormGroup; additionalInfoFormGroup: FormGroup; @@ -55,11 +56,12 @@ export class ManageOrganizationalUnitComponent implements OnInit { private dialogRef: MatDialogRef, private http: HttpClient, private dataService: DataService, + private configService: ConfigService, private toastService: ToastrService, @Inject(MAT_DIALOG_DATA) public data: any ) { this.isEditMode = !!data?.uuid; - + this.baseUrl = this.configService.apiUrl; this.generalFormGroup = this._formBuilder.group({ name: [null, Validators.required], parent: [data?.parent ? data.parent['@id'] : null], diff --git a/ogWebconsole/src/app/components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component.ts b/ogWebconsole/src/app/components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component.ts index 2287ff2..417f0d7 100644 --- a/ogWebconsole/src/app/components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component.ts @@ -1,7 +1,8 @@ import { HttpClient } from '@angular/common/http'; -import {Component, Inject, OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {DatePipe} from "@angular/common"; +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { DatePipe } from "@angular/common"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-show-organizational-unit', @@ -9,7 +10,7 @@ import {DatePipe} from "@angular/common"; styleUrl: './show-organizational-unit.component.css' }) export class ShowOrganizationalUnitComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; displayedColumns: string[] = ['property', 'value']; currentCalendar: any; ou: any; @@ -26,8 +27,10 @@ export class ShowOrganizationalUnitComponent implements OnInit { constructor( @Inject(MAT_DIALOG_DATA) public data: any, private dialogRef: MatDialogRef, + private configService: ConfigService, private http: HttpClient ) { + this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.css b/ogWebconsole/src/app/components/images/create-image/create-image.component.css index 8a9bda9..926bd5b 100644 --- a/ogWebconsole/src/app/components/images/create-image/create-image.component.css +++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.css @@ -1,8 +1,25 @@ -.dialog-content { +.create-image-container { display: flex; flex-direction: column; - gap: 16px; - /* Espacio entre los elementos del formulario */ + padding: 1rem; +} + +.loading-spinner { + display: block; + margin: 0 auto; +} + +.mat-dialog-content.loading { + display: flex; + justify-content: center; + align-items: center; + height: 200px; +} + +.mat-dialog-content { + padding-left: 1.5em; + padding-right: 1.5em; + padding-top: 1em; } .image-form { diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.html b/ogWebconsole/src/app/components/images/create-image/create-image.component.html index fcc73de..daf3d74 100644 --- a/ogWebconsole/src/app/components/images/create-image/create-image.component.html +++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.html @@ -1,72 +1,72 @@ - +
+

{{ isEditMode ? 'Editar' : 'Crear' }} imagen

+
+ +
+ + + warning + Ha marcado la casilla "Imagen Global". Se transferirá la imagen al resto de repositorios en + el + caso de que no exista previamente. + + + + {{ 'imageNameLabel' | translate }} + + -

{{ imageId ? 'Editar' : 'Crear' }} imagen

+ + {{ 'repositoryLabel' | translate }} + + + {{ imageRepository.name }} + + + - - - - - warning - Ha marcado la casilla "Imagen Global". Se transferirá la imagen al resto de repositorios en el - caso de que no exista previamente. - - - - {{ 'imageNameLabel' | translate }} - - + + {{ 'descriptionLabel' | translate }} + + - - {{ 'repositoryLabel' | translate }} - - - {{ imageRepository.name }} - - - + + {{ 'commentsLabel' | translate }} + + - - {{ 'descriptionLabel' | translate }} - - + + Perfil de software + + + {{ profile.description }} + + + - - {{ 'commentsLabel' | translate }} - - + + {{ 'remotePcLabel' | translate }} + - - Perfil de software - - - {{ profile.description }} - - - + + {{ 'globalImageLabel' | translate }} + - - {{ 'remotePcLabel' | translate }} - + - - {{ 'globalImageLabel' | translate }} - +
+

Información de Partición de origen

+

Sistema de archivos: {{ partitionInfo['filesystem'] }}

+

Disco: {{ partitionInfo['numDisk'] }}

+

Particion: {{ partitionInfo['numPartition'] }}

+

Nombre del SO: {{ partitionInfo['osName'] }}

+

Código de partición: {{ partitionInfo['partitionCode'] }}

+
+ +
- - -
-

Información de Partición de origen

-

Sistema de archivos: {{ partitionInfo['filesystem'] }}

-

Disco: {{ partitionInfo['numDisk'] }}

-

Particion: {{ partitionInfo['numPartition'] }}

-

Nombre del SO: {{ partitionInfo['osName'] }}

-

Código de partición: {{ partitionInfo['partitionCode'] }}

-
- - - - - - - - + + + + +
\ No newline at end of file diff --git a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts index df6a1e8..68aa01e 100644 --- a/ogWebconsole/src/app/components/images/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/images/create-image/create-image.component.ts @@ -1,9 +1,10 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; -import {HttpClient} from '@angular/common/http'; -import {ToastrService} from 'ngx-toastr'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {DataService} from "../data.service"; +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { HttpClient } from '@angular/common/http'; +import { ToastrService } from 'ngx-toastr'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { DataService } from "../data.service"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-image', @@ -11,12 +12,13 @@ import {DataService} from "../data.service"; styleUrls: ['./create-image.component.css'] }) export class CreateImageComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; imageForm: FormGroup; imageId: string | null = null; softwareProfiles: any[] = []; repositories: any[] = []; loading: boolean = false; + isEditMode: boolean = false; partitionInfo: { [key: string]: string } = {}; showWarning: boolean = false; @@ -25,9 +27,11 @@ export class CreateImageComponent implements OnInit { private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, + private configService: ConfigService, private dataService: DataService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.imageForm = this.fb.group({ name: ['', Validators.required], description: [''], @@ -42,30 +46,34 @@ export class CreateImageComponent implements OnInit { ngOnInit() { this.loading = true; if (this.data) { - this.load() + this.isEditMode = true; + this.load(); + } else { + this.loading = false; } this.fetchSoftwareProfiles(); this.fetchRepositories(); - this.loading = false; } load(): void { this.dataService.getImage(this.data).subscribe({ next: (response) => { - this.imageForm = this.fb.group({ - name: [response.name, Validators.required], - description: [response.description], - comments: [response.comments], - remotePc: [response.remotePc], - isGlobal: [response.isGlobal], - softwareProfile: [response.softwareProfile ? response.softwareProfile['@id'] : null, Validators.required], - imageRepositories: [response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [], Validators.required], + this.imageForm.patchValue({ + name: response.name, + description: response.description, + comments: response.comments, + remotePc: response.remotePc, + isGlobal: response.isGlobal, + softwareProfile: response.softwareProfile ? response.softwareProfile['@id'] : null, + imageRepositories: response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [], }); this.imageId = response['@id']; this.partitionInfo = response.partitionInfo; + this.loading = false; }, error: (err) => { console.error('Error fetching remote calendar:', err); + this.loading = false; } }); } @@ -135,4 +143,4 @@ export class CreateImageComponent implements OnInit { close(): void { this.dialogRef.close(); } -} +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/images/data.service.ts b/ogWebconsole/src/app/components/images/data.service.ts index 1b9debe..1a98166 100644 --- a/ogWebconsole/src/app/components/images/data.service.ts +++ b/ogWebconsole/src/app/components/images/data.service.ts @@ -1,17 +1,20 @@ - import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/images?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/images?page=1&itemsPerPage=1000`; + } getImages(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/images/export-image/export-image.component.spec.ts b/ogWebconsole/src/app/components/images/export-image/export-image.component.spec.ts index e573706..ac35448 100644 --- a/ogWebconsole/src/app/components/images/export-image/export-image.component.spec.ts +++ b/ogWebconsole/src/app/components/images/export-image/export-image.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ExportImageComponent } from './export-image.component'; import {FormBuilder, ReactiveFormsModule} from "@angular/forms"; import {ToastrModule, ToastrService} from "ngx-toastr"; @@ -14,12 +13,18 @@ import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; import {TranslateModule} from "@ngx-translate/core"; import {LoadingComponent} from "../../../shared/loading/loading.component"; import {MatProgressSpinner} from "@angular/material/progress-spinner"; +import { ConfigService } from '@services/config.service'; describe('ExportImageComponent', () => { let component: ExportImageComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [ExportImageComponent, LoadingComponent], imports: [ @@ -46,7 +51,9 @@ describe('ExportImageComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - }] + }, + { provide: ConfigService, useValue: mockConfigService } + ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/images/export-image/export-image.component.ts b/ogWebconsole/src/app/components/images/export-image/export-image.component.ts index 4a4753e..ac21bac 100644 --- a/ogWebconsole/src/app/components/images/export-image/export-image.component.ts +++ b/ogWebconsole/src/app/components/images/export-image/export-image.component.ts @@ -1,8 +1,9 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {Router} from "@angular/router"; +import { Component, Inject, OnInit } from '@angular/core'; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { Router } from "@angular/router"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-export-image', @@ -10,7 +11,7 @@ import {Router} from "@angular/router"; styleUrl: './export-image.component.css' }) export class ExportImageComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; loading: boolean = true; repositories: any[] = []; selectedRepositories: any[] = []; @@ -19,10 +20,11 @@ export class ExportImageComponent implements OnInit { private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, + private configService: ConfigService, private router: Router, @Inject(MAT_DIALOG_DATA) public data: { image: any, imageImageRepository: any } ) { - + this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { diff --git a/ogWebconsole/src/app/components/images/images.component.ts b/ogWebconsole/src/app/components/images/images.component.ts index d59a887..0f9fa4f 100644 --- a/ogWebconsole/src/app/components/images/images.component.ts +++ b/ogWebconsole/src/app/components/images/images.component.ts @@ -1,11 +1,10 @@ -import {Component, Input, OnInit} from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { MatTableDataSource } from '@angular/material/table'; -import { ToastrService } from 'ngx-toastr'; import { DatePipe } from '@angular/common'; import { CreateImageComponent } from './create-image/create-image.component'; -import {Observable} from "rxjs"; +import { ConfigService } from '@services/config.service'; import { JoyrideService } from 'ngx-joyride'; @Component({ @@ -14,7 +13,8 @@ import { JoyrideService } from 'ngx-joyride'; styleUrls: ['./images.component.css'] }) export class ImagesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; @@ -57,16 +57,18 @@ export class ImagesComponent implements OnInit { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/images`; @Input() repositoryUuid: any private repositoryId: any; constructor( public dialog: MatDialog, private http: HttpClient, - private toastService: ToastrService, + private configService: ConfigService, private joyrideService: JoyrideService, - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/images`; + } ngOnInit(): void { if (this.repositoryUuid) { @@ -100,7 +102,7 @@ export class ImagesComponent implements OnInit { search(): void { this.loading = true; - this.http.get(`${this.apiUrl}?page=${this.page +1 }&itemsPerPage=${this.itemsPerPage}&repositoryId=${this.repositoryId}`, { params: this.filters }).subscribe( + this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}&repositoryId=${this.repositoryId}`, { params: this.filters }).subscribe( data => { this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; diff --git a/ogWebconsole/src/app/components/login/login.component.spec.ts b/ogWebconsole/src/app/components/login/login.component.spec.ts index 0406e8b..7073cf7 100644 --- a/ogWebconsole/src/app/components/login/login.component.spec.ts +++ b/ogWebconsole/src/app/components/login/login.component.spec.ts @@ -9,12 +9,18 @@ import { MatInputModule } from '@angular/material/input'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; import { of, throwError } from 'rxjs'; +import { ConfigService } from '@services/config.service'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [LoginComponent], imports: [ @@ -26,7 +32,10 @@ describe('LoginComponent', () => { MatIconModule, TranslateModule.forRoot() ], - providers: [provideHttpClient(withInterceptorsFromDi())] + providers: [ + provideHttpClient(withInterceptorsFromDi()), + { provide: ConfigService, useValue: mockConfigService } + ] }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); diff --git a/ogWebconsole/src/app/components/login/login.component.ts b/ogWebconsole/src/app/components/login/login.component.ts index 088d53e..dc07831 100644 --- a/ogWebconsole/src/app/components/login/login.component.ts +++ b/ogWebconsole/src/app/components/login/login.component.ts @@ -4,6 +4,7 @@ import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { ToastrService } from "ngx-toastr"; import { jwtDecode } from "jwt-decode"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-login', @@ -19,14 +20,16 @@ export class LoginComponent { isLoading: boolean = false; decodedToken: any; - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; constructor( private http: HttpClient, private router: Router, + private configService: ConfigService, private toastService: ToastrService, private translateService: TranslateService ) { + this.baseUrl = this.configService.apiUrl; const savedLanguage = localStorage.getItem('language') || 'es'; this.translateService.use(savedLanguage); } diff --git a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts index 0a591a1..b4dc91d 100644 --- a/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts +++ b/ogWebconsole/src/app/components/menus/create-menu/create-menu.component.ts @@ -1,18 +1,19 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../data.service"; +import { Component, Inject, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { DataService } from "../data.service"; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-menu', templateUrl: './create-menu.component.html', styleUrl: './create-menu.component.css' }) -export class CreateMenuComponent implements OnInit{ - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; +export class CreateMenuComponent implements OnInit { + baseUrl: string; menuForm: FormGroup; menuId: string | null = null; softwareProfiles: any[] = []; @@ -24,9 +25,11 @@ export class CreateMenuComponent implements OnInit{ public dialogRef: MatDialogRef, private toastService: ToastrService, private dataService: DataService, + private configService: ConfigService, private sanitizer: DomSanitizer, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.menuForm = this.fb.group({ name: ['', Validators.required], publicUrl: ['', Validators.required], diff --git a/ogWebconsole/src/app/components/menus/data.service.ts b/ogWebconsole/src/app/components/menus/data.service.ts index 8973388..354afb5 100644 --- a/ogWebconsole/src/app/components/menus/data.service.ts +++ b/ogWebconsole/src/app/components/menus/data.service.ts @@ -1,6 +1,6 @@ - +import { ConfigService } from '@services/config.service'; import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -8,10 +8,13 @@ import { catchError, map } from 'rxjs/operators'; providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/menus?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/menus?page=1&itemsPerPage=1000`; + } getMenus(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/menus/menus.component.spec.ts b/ogWebconsole/src/app/components/menus/menus.component.spec.ts index 4951957..20d535b 100644 --- a/ogWebconsole/src/app/components/menus/menus.component.spec.ts +++ b/ogWebconsole/src/app/components/menus/menus.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MenusComponent } from './menus.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -23,15 +22,21 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import { ToastrModule } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; describe('MenusComponent', () => { let component: MenusComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; await TestBed.configureTestingModule({ declarations: [MenusComponent], imports: [ HttpClientTestingModule, ToastrModule.forRoot(), BrowserAnimationsModule, MatDividerModule, MatFormFieldModule, MatInputModule, MatIconModule, MatButtonModule, MatTableModule, MatPaginatorModule, MatTooltipModule, FormsModule, ReactiveFormsModule, MatProgressSpinnerModule, MatDialogModule, MatSelectModule, MatTabsModule, MatAutocompleteModule, MatListModule, MatCardModule, MatMenuModule, TranslateModule.forRoot(), JoyrideModule.forRoot(), ], + providers: [ { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/menus/menus.component.ts b/ogWebconsole/src/app/components/menus/menus.component.ts index 29b1fba..ebb542a 100644 --- a/ogWebconsole/src/app/components/menus/menus.component.ts +++ b/ogWebconsole/src/app/components/menus/menus.component.ts @@ -1,13 +1,14 @@ -import {Component, OnInit} from '@angular/core'; -import {MatTableDataSource} from "@angular/material/table"; -import {DatePipe} from "@angular/common"; -import {MatDialog} from "@angular/material/dialog"; -import {HttpClient} from "@angular/common/http"; -import {ToastrService} from "ngx-toastr"; -import {JoyrideService} from "ngx-joyride"; -import {Router} from "@angular/router"; -import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; -import {CreateMenuComponent} from "./create-menu/create-menu.component"; +import { Component, OnInit } from '@angular/core'; +import { MatTableDataSource } from "@angular/material/table"; +import { DatePipe } from "@angular/common"; +import { MatDialog } from "@angular/material/dialog"; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { JoyrideService } from "ngx-joyride"; +import { Router } from "@angular/router"; +import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import { CreateMenuComponent } from "./create-menu/create-menu.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-menus', @@ -15,7 +16,8 @@ import {CreateMenuComponent} from "./create-menu/create-menu.component"; styleUrl: './menus.component.css' }) export class MenusComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; @@ -57,15 +59,17 @@ export class MenusComponent implements OnInit { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/menus`; - constructor( public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService, private joyrideService: JoyrideService, - private router: Router - ) {} + private router: Router, + private configService: ConfigService + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/menus`; + } ngOnInit(): void { this.search(); @@ -83,7 +87,7 @@ export class MenusComponent implements OnInit { search(): void { this.loading = true; - this.http.get(`${this.apiUrl}?page=${this.page +1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( data => { this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; diff --git a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.spec.ts b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.spec.ts index 6546320..d0532ad 100644 --- a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.spec.ts +++ b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.spec.ts @@ -19,11 +19,17 @@ import { ToastrModule } from 'ngx-toastr'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import {LoadingComponent} from "../../../shared/loading/loading.component"; +import { ConfigService } from '@services/config.service'; + describe('OgbootStatusComponent', () => { let component: OgbootStatusComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; await TestBed.configureTestingModule({ declarations: [OgbootStatusComponent, LoadingComponent], imports: [ @@ -50,7 +56,8 @@ describe('OgbootStatusComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} } + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); }); diff --git a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts index 42634f5..aefc115 100644 --- a/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts +++ b/ogWebconsole/src/app/components/ogboot/ogboot-status/ogboot-status.component.ts @@ -1,7 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { JoyrideService } from 'ngx-joyride'; -import {ToastrService} from "ngx-toastr"; +import { ToastrService } from "ngx-toastr"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-ogboot-status', @@ -9,7 +10,7 @@ import {ToastrService} from "ngx-toastr"; styleUrls: ['./ogboot-status.component.css'] }) export class OgbootStatusComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; diskUsage: any = {}; servicesStatus: any = {}; installedOglives: any[] = []; @@ -29,8 +30,11 @@ export class OgbootStatusComponent implements OnInit { constructor( private http: HttpClient, private joyrideService: JoyrideService, + private configService: ConfigService, private toastService: ToastrService, - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.loadStatus(); diff --git a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/data.service.ts b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/data.service.ts index c248a52..5ee707f 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/data.service.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/data.service.ts @@ -8,7 +8,7 @@ import { DataService as TemplateDataService } from './../pxe/data.service'; providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string = this.configService.apiUrl; private apiUrl = `${this.baseUrl}/pxe-boot-files?page=1&itemsPerPage=1000`; constructor( diff --git a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.spec.ts b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.spec.ts index 9ab564b..d49b9b4 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.spec.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.spec.ts @@ -14,12 +14,18 @@ import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; describe('PxeBootFilesComponent', () => { let component: PxeBootFilesComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [PxeBootFilesComponent], imports: [ @@ -43,7 +49,7 @@ describe('PxeBootFilesComponent', () => { TranslateModule.forRoot(), JoyrideModule.forRoot(), ], - providers: [NgControl] + providers: [NgControl, { provide: ConfigService, useValue: mockConfigService }] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts index 8fc85f0..95be23e 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-boot-files/pxe-boot-files.component.ts @@ -2,8 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { HttpClient } from '@angular/common/http'; import { ToastrService } from 'ngx-toastr'; -import { PageEvent } from '@angular/material/paginator'; -import {Observable} from "rxjs"; +import { ConfigService } from '@services/config.service'; import { JoyrideService } from 'ngx-joyride'; @Component({ @@ -12,7 +11,7 @@ import { JoyrideService } from 'ngx-joyride'; styleUrls: ['./pxe-boot-files.component.css'] }) export class PxeBootFilesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; availableOrganizationalUnits: any[] = []; selectedUnitChildren: any[] = []; @@ -33,8 +32,10 @@ export class PxeBootFilesComponent implements OnInit { private fb: FormBuilder, private http: HttpClient, private toastService: ToastrService, + private configService: ConfigService, private joyrideService: JoyrideService ) { + this.baseUrl = this.configService.apiUrl; this.taskForm = this.fb.group({ organizationalUnit: ['', Validators.required], selectedChild: ['', Validators.required] @@ -118,13 +119,13 @@ export class PxeBootFilesComponent implements OnInit { iniciarTour(): void { this.joyrideService.startTour({ steps: [ - 'titleStep', - 'selectUnitStep', - 'selectClassStep', - 'applyToAllStep', - 'saveButtonStep', - 'tableStep', - 'selectTemplateStep' + 'titleStep', + 'selectUnitStep', + 'selectClassStep', + 'applyToAllStep', + 'saveButtonStep', + 'tableStep', + 'selectTemplateStep' ], showPrevButton: true, themeColor: '#3f51b5' diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts index 2e7084a..0458adb 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/create-image/create-image/create-image.component.ts @@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-image', @@ -9,7 +10,7 @@ import { ToastrService } from 'ngx-toastr'; styleUrls: ['./create-image.component.css'] }) export class CreatePXEImageComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; downloads: any[] = []; selectedDownload: any; isEditMode: boolean = false; @@ -18,10 +19,13 @@ export class CreatePXEImageComponent implements OnInit { constructor( private toastService: ToastrService, + private configService: ConfigService, private http: HttpClient, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any - ) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.fetchDownloads(); diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/data.service.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/data.service.ts index 89dac83..9ec266f 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/data.service.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/data.service.ts @@ -1,16 +1,20 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/og-lives?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/og-lives?page=1&itemsPerPage=1000`; + } getImages(filters: { [key: string]: string }): Observable { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css index a9062b9..c003fd9 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.css @@ -48,4 +48,24 @@ table { display: flex; justify-content: end; margin-bottom: 30px; -} \ No newline at end of file +} + +.status-active { + background-color: #46c446 !important; + color: white !important; +} + +.status-inactive { + background-color: #e87979 !important; + color: white !important; +} + +.status-installing { + background-color: #f5a623 !important; + color: white !important; +} + +.status-failed { + background-color: #9e9e9e !important; + color: white !important; +} diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html index bf06c2f..72cce0c 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.html @@ -1,3 +1,5 @@ + +
@@ -39,12 +41,13 @@ - {{ 'searchInstalledLabel' | translate }} - {{ 'status' | translate }} + - {{ 'allOption' | translate }} - {{ 'yesOption' | translate }} - {{ 'noOption' | translate }} + {{ 'inactiveOption' | translate }} + {{ 'activeOption' | translate }} + {{ 'failedOption' | translate }} + {{ 'pendingOption' | translate }} @@ -55,7 +58,7 @@ {{ column.header }} - + {{ 'checkCircle' | translate }} @@ -79,13 +82,13 @@ - - {{ column.cell(image) }} + + {{ getStatusLabel(image.status).label }} + *ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'downloadUrl' && column.columnDef !== 'status' && column.columnDef !== 'name'"> {{ column.cell(image) }} @@ -108,12 +111,12 @@ menu - - - @@ -128,4 +131,4 @@ - \ No newline at end of file + diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.spec.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.spec.ts index 64ad107..d9a820e 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.spec.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.spec.ts @@ -16,6 +16,7 @@ import { MatTableModule } from '@angular/material/table'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; describe('PXEimagesComponent', () => { let component: PXEimagesComponent; @@ -25,6 +26,11 @@ describe('PXEimagesComponent', () => { beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + mockToastrService = jasmine.createSpyObj('ToastrService', ['success', 'error']); await TestBed.configureTestingModule({ declarations: [PXEimagesComponent, LoadingComponent], @@ -47,7 +53,8 @@ describe('PXEimagesComponent', () => { JoyrideModule.forRoot(), ], providers: [ - { provide: ToastrService, useValue: mockToastrService } + { provide: ToastrService, useValue: mockToastrService }, + { provide: ConfigService, useValue: mockConfigService } ] }) diff --git a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts index 5b1ea3a..8b2a37f 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe-images/pxe-images.component.ts @@ -1,17 +1,18 @@ -import {Component, OnInit, signal} from '@angular/core'; +import { Component, OnInit, signal } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { CreatePXEImageComponent } from './create-image/create-image/create-image.component'; import { InfoImageComponent } from './info-image/info-image/info-image.component'; import { MatTableDataSource } from "@angular/material/table"; -import {PageEvent} from "@angular/material/paginator"; -import {ToastrService} from "ngx-toastr"; +import { PageEvent } from "@angular/material/paginator"; +import { ToastrService } from "ngx-toastr"; import { DatePipe } from "@angular/common"; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; -import {DataService} from "./data.service"; -import {Observable} from "rxjs"; +import { DataService } from "./data.service"; +import { Observable } from "rxjs"; import { JoyrideService } from 'ngx-joyride'; -import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { ServerInfoDialogComponent } from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-pxe-images', @@ -19,7 +20,8 @@ import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server- styleUrls: ['./pxe-images.component.css'] }) export class PXEimagesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; images: { downloadUrl: string; name: string; uuid: string }[] = []; dataSource = new MatTableDataSource(); length: number = 0; @@ -27,7 +29,7 @@ export class PXEimagesComponent implements OnInit { page: number = 1; pageSizeOptions: number[] = [5, 10, 20, 40, 100]; selectedElements: string[] = []; - loading:boolean = false; + loading: boolean = false; filters: { [key: string]: string } = {}; alertMessage: string | null = null; readonly panelOpenState = signal(false); @@ -48,11 +50,6 @@ export class PXEimagesComponent implements OnInit { header: 'Imagen por defecto', cell: (user: any) => `${user.isDefault}` }, - { - columnDef: 'installed', - header: 'Imagen instalada', - cell: (user: any) => `${user.installed}` - }, { columnDef: 'status', header: 'Estado', @@ -66,15 +63,17 @@ export class PXEimagesComponent implements OnInit { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/og-lives`; - constructor( public dialog: MatDialog, private http: HttpClient, private dataService: DataService, + private configService: ConfigService, private toastService: ToastrService, private joyrideService: JoyrideService - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/og-lives`; + } ngOnInit(): void { this.loading = true; @@ -106,14 +105,19 @@ export class PXEimagesComponent implements OnInit { ); } - showInfo(image: any): void { - const dialogRef = this.dialog.open(InfoImageComponent, { - width: '700px', - data: image - }); + getStatusLabel(status: string): { label: string; class: string } { + const statusMap: { [key: string]: { label: string; class: string } } = { + active: { label: 'Instalada', class: 'status-active' }, + inactive: { label: 'Sin instalar', class: 'status-inactive' }, + pending: { label: 'Instalando...', class: 'status-installing' }, + failed: { label: 'Fallido', class: 'status-failed' } + }; + + return statusMap[status] || { label: 'Desconocido', class: 'status-default' }; } - toggleAction(image: any, action:string): void { + + toggleAction(image: any, action: string): void { switch (action) { case 'set-default': this.http.post(`${this.apiUrl}/server/${image.uuid}/set-default`, {}).subscribe({ @@ -197,7 +201,7 @@ export class PXEimagesComponent implements OnInit { showOgLive(event: MouseEvent, data: any): void { event.stopPropagation(); - const dialogRef = this.dialog.open(InfoImageComponent, { data: { data }, width: '700px'}); + const dialogRef = this.dialog.open(InfoImageComponent, { data: { data }, width: '700px' }); } applyFilter() { @@ -255,14 +259,14 @@ export class PXEimagesComponent implements OnInit { iniciarTour(): void { this.joyrideService.startTour({ steps: [ - 'titleStep', - 'addImageStep', - 'searchNameStep', - 'searchDefaultImageStep', - 'searchInstalledStep', - 'tableStep', - 'actionsStep', - 'paginationStep' + 'titleStep', + 'addImageStep', + 'searchNameStep', + 'searchDefaultImageStep', + 'searchInstalledStep', + 'tableStep', + 'actionsStep', + 'paginationStep' ], showPrevButton: true, themeColor: '#3f51b5' diff --git a/ogWebconsole/src/app/components/ogboot/pxe/create-pxeTemplate/create-pxe-template.component.ts b/ogWebconsole/src/app/components/ogboot/pxe/create-pxeTemplate/create-pxe-template.component.ts index 2cded87..e2f7ee6 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/create-pxeTemplate/create-pxe-template.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe/create-pxeTemplate/create-pxe-template.component.ts @@ -4,6 +4,7 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { DeleteModalComponent } from "../../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-pxe-template', @@ -11,7 +12,7 @@ import { DeleteModalComponent } from "../../../../shared/delete_modal/delete-mod styleUrls: ['./create-pxe-template.component.css'] }) export class CreatePxeTemplateComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; templateForm!: FormGroup; previewContent: string = ''; isEditMode: boolean = false; @@ -54,10 +55,13 @@ exit` public dialogRef: MatDialogRef, public dialog: MatDialog, private http: HttpClient, + private configService: ConfigService, private fb: FormBuilder, private toastService: ToastrService, @Inject(MAT_DIALOG_DATA) public data: any - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit() { this.isEditMode = !!this.data; diff --git a/ogWebconsole/src/app/components/ogboot/pxe/data.service.ts b/ogWebconsole/src/app/components/ogboot/pxe/data.service.ts index 5354d14..8bd5637 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/data.service.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe/data.service.ts @@ -1,17 +1,21 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/pxe-templates?page=1&itemsPerPage=1000`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/pxe-templates?page=1&itemsPerPage=1000`; + } getPxeTemplates(filters: { [key: string]: string }): Observable { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.html b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.html index ca58141..b16e300 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.html +++ b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.html @@ -74,4 +74,4 @@ - \ No newline at end of file + diff --git a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.spec.ts b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.spec.ts index a27a4ef..e7762e7 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.spec.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.spec.ts @@ -17,18 +17,25 @@ import { MatHint } from '@angular/material/form-field'; import { MatSelect } from '@angular/material/select'; import { MatOption } from '@angular/material/select'; import { MatPaginator } from '@angular/material/paginator'; -import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; + describe('PxeComponent', () => { let component: PxeComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [PxeComponent, LoadingComponent], imports: [ @@ -55,6 +62,7 @@ describe('PxeComponent', () => { providers: [ DatePipe, DataService, + { provide: ConfigService, useValue: mockConfigService }, provideHttpClient(), provideHttpClientTesting() ] @@ -130,4 +138,4 @@ describe('PxeComponent', () => { it('should have a defined selectedItem', () => { expect(component.selectedItem).toBeDefined(); }); -}); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.ts b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.ts index b9ed0d4..a07895f 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe/pxe.component.ts @@ -1,17 +1,18 @@ import { HttpClient } from '@angular/common/http'; import { Component } from '@angular/core'; -import { CreatePxeTemplateComponent } from './create-pxeTemplate/create-pxe-template.component'; // Asegúrate de que el path sea correcto +import { CreatePxeTemplateComponent } from './create-pxeTemplate/create-pxe-template.component'; import { MatDialog } from '@angular/material/dialog'; import { MatTableDataSource } from '@angular/material/table'; import { PageEvent } from '@angular/material/paginator'; import { ToastrService } from 'ngx-toastr'; import { DatePipe } from '@angular/common'; import { DataService } from './data.service'; -import {ShowTemplateContentComponent} from "./show-template-content/show-template-content.component"; -import {Observable} from "rxjs"; +import { ShowTemplateContentComponent } from "./show-template-content/show-template-content.component"; +import { Observable } from "rxjs"; import { JoyrideService } from 'ngx-joyride'; -import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; -import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { DeleteModalComponent } from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { ServerInfoDialogComponent } from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-pxe', @@ -19,7 +20,8 @@ import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server- styleUrls: ['./pxe.component.css'] }) export class PxeComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; pxeTemplates: any[] = []; currentPage: number = 1; dataSource = new MatTableDataSource(); @@ -28,7 +30,7 @@ export class PxeComponent { page: number = 1; pageSizeOptions: number[] = [5, 10, 20, 40, 100]; selectedElements: string[] = []; - loading:boolean = false; + loading: boolean = false; filters: { [key: string]: string } = {}; alertMessage: string | null = null; datePipe: DatePipe = new DatePipe('es-ES'); @@ -58,15 +60,17 @@ export class PxeComponent { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/pxe-templates`; - constructor( public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService, private dataService: DataService, - private joyrideService: JoyrideService - ) { } + private joyrideService: JoyrideService, + private configService: ConfigService + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/pxe-templates`; + } ngOnInit(): void { this.loading = true; @@ -140,7 +144,7 @@ export class PxeComponent { showTemplate(event: MouseEvent, data: any): void { event.stopPropagation(); - const dialogRef = this.dialog.open(ShowTemplateContentComponent, { data: { data }, width: '700px'}); + const dialogRef = this.dialog.open(ShowTemplateContentComponent, { data: { data }, width: '700px' }); } syncTemplates() { diff --git a/ogWebconsole/src/app/components/ogboot/pxe/show-template-content/show-template-content.component.ts b/ogWebconsole/src/app/components/ogboot/pxe/show-template-content/show-template-content.component.ts index 6d7588a..5ae59ac 100644 --- a/ogWebconsole/src/app/components/ogboot/pxe/show-template-content/show-template-content.component.ts +++ b/ogWebconsole/src/app/components/ogboot/pxe/show-template-content/show-template-content.component.ts @@ -1,6 +1,7 @@ -import {Component, Inject} from '@angular/core'; -import {MAT_DIALOG_DATA} from "@angular/material/dialog"; -import {HttpClient} from "@angular/common/http"; +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { HttpClient } from "@angular/common/http"; +import { ConfigService } from "@services/config.service"; @Component({ selector: 'app-show-template-content', @@ -8,12 +9,14 @@ import {HttpClient} from "@angular/common/http"; styleUrl: './show-template-content.component.css' }) export class ShowTemplateContentComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; displayedColumns: string[] = ['property', 'value']; generalData: any[] = []; constructor( @Inject(MAT_DIALOG_DATA) public data: any, - private http: HttpClient + private http: HttpClient, + private configService: ConfigService ) { + this.baseUrl = this.configService.apiUrl; } } diff --git a/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts b/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts index 6699e4d..aaa8e8e 100644 --- a/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import {MatDialogRef, MAT_DIALOG_DATA, MatDialog} from '@angular/material/dialog'; +import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; import { FormControl } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; -import {OperationResultDialogComponent} from "../operation-result-dialog/operation-result-dialog.component"; +import { OperationResultDialogComponent } from "../operation-result-dialog/operation-result-dialog.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-add-clients-to-subnet', @@ -11,7 +12,7 @@ import {OperationResultDialogComponent} from "../operation-result-dialog/operati styleUrls: ['./add-clients-to-subnet.component.css'] }) export class AddClientsToSubnetComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; units: any[] = []; clients: any[] = []; selectedClients: string[] = []; @@ -25,8 +26,11 @@ export class AddClientsToSubnetComponent implements OnInit { public dialogRef: MatDialogRef, private toastService: ToastrService, public dialog: MatDialog, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: { subnetUuid: string, subnetName: string } - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.loading = true; @@ -34,7 +38,7 @@ export class AddClientsToSubnetComponent implements OnInit { } loadUnits() { - this.http.get(`${this.baseUrl}/organizational-units?type=classroom&page=1&itemsPerPage=50`).subscribe( + this.http.get(`${this.baseUrl}/organizational-units?page=1&itemsPerPage=500`).subscribe( response => { this.units = response['hydra:member']; this.loading = false; diff --git a/ogWebconsole/src/app/components/ogdhcp/create-subnet/create-subnet.component.ts b/ogWebconsole/src/app/components/ogdhcp/create-subnet/create-subnet.component.ts index bf37323..cd4d368 100644 --- a/ogWebconsole/src/app/components/ogdhcp/create-subnet/create-subnet.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/create-subnet/create-subnet.component.ts @@ -3,6 +3,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-subnet', @@ -10,7 +11,7 @@ import { ToastrService } from 'ngx-toastr'; styleUrls: ['./create-subnet.component.css'] }) export class CreateSubnetComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; subnetForm: FormGroup; isEditMode: boolean = false; subnetId: string | null = null; @@ -19,10 +20,12 @@ export class CreateSubnetComponent implements OnInit { private toastService: ToastrService, private http: HttpClient, private fb: FormBuilder, + private configService: ConfigService, public dialogRef: MatDialogRef, public dialog: MatDialog, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.subnetForm = this.fb.group({ name: ['', Validators.required], netmask: ['', Validators.required], diff --git a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.spec.ts b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.spec.ts index 0144ed2..e39ef39 100644 --- a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.spec.ts +++ b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.spec.ts @@ -17,6 +17,7 @@ import { MatTableModule } from '@angular/material/table'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import { LoadingComponent } from '../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; describe('OgDhcpSubnetsComponent', () => { let component: OgDhcpSubnetsComponent; @@ -26,6 +27,10 @@ describe('OgDhcpSubnetsComponent', () => { let mockToastrService: jasmine.SpyObj; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; mockDialog = jasmine.createSpyObj('MatDialog', ['open']); mockHttpClient = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']); mockToastrService = jasmine.createSpyObj('ToastrService', ['success', 'error']); @@ -52,6 +57,7 @@ describe('OgDhcpSubnetsComponent', () => { { provide: MatDialog, useValue: mockDialog }, { provide: HttpClient, useValue: mockHttpClient }, { provide: ToastrService, useValue: mockToastrService }, + { provide: ConfigService, useValue: mockConfigService } ], }).compileComponents(); }); diff --git a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.ts b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.ts index 4932267..33dfb12 100644 --- a/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/og-dhcp-subnets.component.ts @@ -9,8 +9,9 @@ import { AddClientsToSubnetComponent } from './add-clients-to-subnet/add-clients import { ServerInfoDialogComponent } from "./server-info-dialog/server-info-dialog.component"; import { Observable } from "rxjs"; import { JoyrideService } from 'ngx-joyride'; -import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; -import {ShowClientsComponent} from "./show-clients/show-clients.component"; +import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import { ShowClientsComponent } from "./show-clients/show-clients.component"; +import { ConfigService } from '@services/config.service'; export interface Subnet { '@id': string; @@ -36,7 +37,7 @@ export interface Subnet { styleUrls: ['./og-dhcp-subnets.component.css'] }) export class OgDhcpSubnetsComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; displayedColumns: string[] = ['id', 'name', 'netmask', 'ipAddress', 'synchronized', 'serverId', 'clients', 'actions']; dataSource = new MatTableDataSource([]); length = 0; @@ -45,7 +46,7 @@ export class OgDhcpSubnetsComponent implements OnInit { filters: { [key: string]: string } = {}; pageSizeOptions: number[] = [5, 10, 20]; alertMessage: string | null = null; - loading:boolean = false; + loading: boolean = false; @ViewChild(MatPaginator) paginator: MatPaginator | undefined; @@ -59,10 +60,13 @@ export class OgDhcpSubnetsComponent implements OnInit { { columnDef: 'clients', header: 'Lista de clientes', cell: (subnet: Subnet) => `${subnet.clients}` }, ]; - private apiUrl = `${this.baseUrl}/subnets`; + private apiUrl: string; constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService, - private joyrideService: JoyrideService) { } + private joyrideService: JoyrideService, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/subnets`; + } ngOnInit() { this.loading = true; diff --git a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.html b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.html index 9167cb1..2fba37e 100644 --- a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.html +++ b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.html @@ -97,5 +97,5 @@ - + diff --git a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.spec.ts b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.spec.ts index 16d442b..aafcfad 100644 --- a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.spec.ts +++ b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.spec.ts @@ -18,7 +18,8 @@ import { HttpClient } from '@angular/common/http'; import { ToastrService } from "ngx-toastr"; import { of } from "rxjs"; import { LoadingComponent } from '../../../shared/loading/loading.component'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; // Add this import +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { ConfigService } from '@services/config.service'; describe('ShowClientsComponent', () => { let component: ShowClientsComponent; @@ -28,6 +29,10 @@ describe('ShowClientsComponent', () => { let mockToastrService: jasmine.SpyObj; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; mockDialog = jasmine.createSpyObj('MatDialog', ['open']); mockToastrService = jasmine.createSpyObj('ToastrService', ['success', 'error']); @@ -48,13 +53,14 @@ describe('ShowClientsComponent', () => { TranslateModule.forRoot(), JoyrideModule.forRoot(), HttpClientTestingModule, - MatProgressSpinnerModule // Add this import + MatProgressSpinnerModule ], providers: [ { provide: MatDialog, useValue: mockDialog }, { provide: ToastrService, useValue: mockToastrService }, { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ], }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.ts b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.ts index 11affe5..322c90a 100644 --- a/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/show-clients/show-clients.component.ts @@ -1,10 +1,11 @@ import { Component, Inject, OnInit } from '@angular/core'; import { ToastrService } from "ngx-toastr"; import { HttpClient } from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material/dialog"; import { MatTableDataSource } from "@angular/material/table"; import { Client } from "../../groups/model/model"; -import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { DeleteModalComponent } from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-show-clients', @@ -12,7 +13,7 @@ import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/de styleUrl: './show-clients.component.css' }) export class ShowClientsComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; dataSource = new MatTableDataSource([]); length = 0; itemsPerPage: number = 10; @@ -22,13 +23,13 @@ export class ShowClientsComponent implements OnInit { filters: { [key: string]: string } = {}; protected status = [ - {value: 'off', name: 'Apagado'}, - {value: 'initializing', name: 'Inicializando'}, - {value: 'og-live', name: 'Og Live'}, - {value: 'linux', name: 'Linux'}, - {value: 'linux-session', name: 'Linux Session'}, - {value: 'windows', name: 'Windows'}, - {value: 'mac', name: 'Mac'}, + { value: 'off', name: 'Apagado' }, + { value: 'initializing', name: 'Inicializando' }, + { value: 'og-live', name: 'Og Live' }, + { value: 'linux', name: 'Linux' }, + { value: 'linux-session', name: 'Linux Session' }, + { value: 'windows', name: 'Windows' }, + { value: 'mac', name: 'Mac' }, ]; columns = [ @@ -48,8 +49,11 @@ export class ShowClientsComponent implements OnInit { private http: HttpClient, public dialogRef: MatDialogRef, public dialog: MatDialog, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any - ) { } + ) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { if (this.data) { @@ -69,7 +73,6 @@ export class ShowClientsComponent implements OnInit { this.loading = false; } ); - console.log(this.dataSource.data) } deleteClient(client: any): void { @@ -89,7 +92,8 @@ export class ShowClientsComponent implements OnInit { this.toastService.error(error.error['hydra:description']); } }); - }}) + } + }) } onNoClick(): void { diff --git a/ogWebconsole/src/app/components/ogdhcp/status/status.component.spec.ts b/ogWebconsole/src/app/components/ogdhcp/status/status.component.spec.ts index 6633f82..7621b97 100644 --- a/ogWebconsole/src/app/components/ogdhcp/status/status.component.spec.ts +++ b/ogWebconsole/src/app/components/ogdhcp/status/status.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { StatusComponent } from './status.component'; import { MatInputModule } from '@angular/material/input'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -21,12 +20,18 @@ import { NgxChartsModule } from '@swimlane/ngx-charts'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; import {LoadingComponent} from "../../../shared/loading/loading.component"; +import { ConfigService } from '@services/config.service'; describe('StatusComponent', () => { let component: StatusComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [StatusComponent, LoadingComponent], imports: [ @@ -53,7 +58,8 @@ describe('StatusComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} } + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/ogdhcp/status/status.component.ts b/ogWebconsole/src/app/components/ogdhcp/status/status.component.ts index db2630d..79316bc 100644 --- a/ogWebconsole/src/app/components/ogdhcp/status/status.component.ts +++ b/ogWebconsole/src/app/components/ogdhcp/status/status.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; -import {HttpClient} from "@angular/common/http"; +import { HttpClient } from "@angular/common/http"; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-status', @@ -8,7 +9,7 @@ import { JoyrideService } from 'ngx-joyride'; styleUrl: './status.component.css' }) export class StatusComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; diskUsage: any = {}; servicesStatus: any = {}; subnets: any[] = []; @@ -25,8 +26,9 @@ export class StatusComponent { domain: ['#FF6384', '#3f51b5'] }; - constructor(private http: HttpClient, - private joyrideService: JoyrideService) {} + constructor(private http: HttpClient, private joyrideService: JoyrideService, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + } ngOnInit(): void { this.loadStatus(); diff --git a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts index ae21c97..6556fb8 100644 --- a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { CreateOperativeSystemComponent } from './create-operative-system.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -15,12 +14,17 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ToastrModule } from 'ngx-toastr'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { ConfigService } from '@services/config.service'; describe('CreateOperativeSystemComponent', () => { let component: CreateOperativeSystemComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; await TestBed.configureTestingModule({ declarations: [CreateOperativeSystemComponent], imports: [ @@ -42,8 +46,9 @@ describe('CreateOperativeSystemComponent', () => { ReactiveFormsModule ], providers: [ - { provide: MatDialogRef, useValue: {} }, // Proporciona un mock de MatDialogRef - { provide: MAT_DIALOG_DATA, useValue: {} } // Proporciona un mock de MAT_DIALOG_DATA si es necesario + { provide: MatDialogRef, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts index f731306..0a8d4df 100644 --- a/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts +++ b/ogWebconsole/src/app/components/operative-system/create-operative-system/create-operative-system.component.ts @@ -1,9 +1,10 @@ -import {Component, Inject} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../../software/data.service"; +import { Component, Inject } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { DataService } from "../../software/data.service"; +import { ConfigService } from "@services/config.service"; @Component({ selector: 'app-create-operative-system', @@ -11,9 +12,8 @@ import {DataService} from "../../software/data.service"; styleUrl: './create-operative-system.component.css' }) export class CreateOperativeSystemComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; formGroup: FormGroup; - private apiUrl = `${this.baseUrl}/software`; operativeSystemId: string | null = null; constructor( @@ -22,8 +22,10 @@ export class CreateOperativeSystemComponent { public dialogRef: MatDialogRef, private toastService: ToastrService, private dataService: DataService, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.formGroup = this.fb.group({ name: ['', Validators.required], }); diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts b/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts index b38248d..0b83f63 100644 --- a/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { OperativeSystemComponent } from './operative-system.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormsModule } from '@angular/forms'; @@ -16,12 +15,18 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ToastrModule } from 'ngx-toastr'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; describe('OperativeSystemComponent', () => { let component: OperativeSystemComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [OperativeSystemComponent], imports: [ @@ -40,6 +45,9 @@ describe('OperativeSystemComponent', () => { MatProgressSpinner, TranslateModule.forRoot(), JoyrideModule.forRoot(), + ], + providers: [ + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/operative-system/operative-system.component.ts b/ogWebconsole/src/app/components/operative-system/operative-system.component.ts index e4324cd..5066707 100644 --- a/ogWebconsole/src/app/components/operative-system/operative-system.component.ts +++ b/ogWebconsole/src/app/components/operative-system/operative-system.component.ts @@ -1,15 +1,15 @@ -import {Component, signal} from '@angular/core'; -import {MatTableDataSource} from "@angular/material/table"; -import {DatePipe} from "@angular/common"; -import {MatDialog} from "@angular/material/dialog"; -import {HttpClient} from "@angular/common/http"; -import {DataService} from "../software/data.service"; -import {ToastrService} from "ngx-toastr"; -import {CreateSoftwareComponent} from "../software/create-software/create-software.component"; -import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; -import {PageEvent} from "@angular/material/paginator"; -import {CreateOperativeSystemComponent} from "./create-operative-system/create-operative-system.component"; +import { Component, signal } from '@angular/core'; +import { MatTableDataSource } from "@angular/material/table"; +import { DatePipe } from "@angular/common"; +import { MatDialog } from "@angular/material/dialog"; +import { HttpClient } from "@angular/common/http"; +import { DataService } from "../software/data.service"; +import { ToastrService } from "ngx-toastr"; +import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import { PageEvent } from "@angular/material/paginator"; +import { CreateOperativeSystemComponent } from "./create-operative-system/create-operative-system.component"; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-operative-system', @@ -17,14 +17,15 @@ import { JoyrideService } from 'ngx-joyride'; styleUrl: './operative-system.component.css' }) export class OperativeSystemComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; images: { downloadUrl: string; name: string; uuid: string }[] = []; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; page: number = 0; pageSizeOptions: number[] = [5, 10, 20, 40, 100]; - loading:boolean = false; + loading: boolean = false; filters: { [key: string]: string } = {}; alertMessage: string | null = null; readonly panelOpenState = signal(false); @@ -48,15 +49,16 @@ export class OperativeSystemComponent { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/operative-systems`; - constructor( public dialog: MatDialog, private http: HttpClient, - private dataService: DataService, + private configService: ConfigService, private toastService: ToastrService, private joyrideService: JoyrideService - ) {} + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/operative-systems`; + } ngOnInit(): void { this.search(); @@ -73,7 +75,7 @@ export class OperativeSystemComponent { } search(): void { - this.http.get(`${this.apiUrl}?page=${this.page + 1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( (data) => { this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; diff --git a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.html b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.html index cc15162..de3cfe1 100644 --- a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.html +++ b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.html @@ -15,7 +15,8 @@ -
+
- +
+ diff --git a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts index 648e229..ad64655 100644 --- a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { BackupImageComponent } from './backup-image.component'; import {ImportImageComponent} from "../import-image/import-image.component"; import {FormBuilder, ReactiveFormsModule} from "@angular/forms"; @@ -14,12 +13,18 @@ import {TranslateModule} from "@ngx-translate/core"; import {provideHttpClient} from "@angular/common/http"; import {provideHttpClientTesting} from "@angular/common/http/testing"; import {LoadingComponent} from "../../../shared/loading/loading.component"; +import { ConfigService } from '@services/config.service'; describe('BackupImageComponent', () => { let component: BackupImageComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [BackupImageComponent, LoadingComponent], imports: [ @@ -45,7 +50,9 @@ describe('BackupImageComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - }] + }, + { provide: ConfigService, useValue: mockConfigService } + ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.ts b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.ts index 12a9ed0..d721940 100644 --- a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.ts +++ b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.ts @@ -1,9 +1,10 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {Router} from "@angular/router"; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; +import { Component, Inject, OnInit } from '@angular/core'; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { Router } from "@angular/router"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { ConfigService } from "@services/config.service"; @Component({ selector: 'app-backup-image', @@ -11,7 +12,7 @@ import {FormBuilder, FormGroup, Validators} from "@angular/forms"; styleUrl: './backup-image.component.css' }) export class BackupImageComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; loading: boolean = false; form!: FormGroup; @@ -21,9 +22,10 @@ export class BackupImageComponent implements OnInit { public dialogRef: MatDialogRef, private toastService: ToastrService, private router: Router, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: { imageImageRepository: any } ) { - + this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { diff --git a/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.css b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.css new file mode 100644 index 0000000..bdecbfa --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.css @@ -0,0 +1,39 @@ +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100px; +} + +mat-form-field { + width: 100%; +} + +mat-dialog-actions { + display: flex; + justify-content: flex-end; +} + +.checkbox-group { + display: flex; + flex-direction: column; + gap: 10px; +} + +.selected-list ul { + list-style: none; + padding: 0; +} + +.selected-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + border-bottom: 1px solid #ccc; +} + +.selected-item button { + margin-left: 10px; +} diff --git a/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.html b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.html new file mode 100644 index 0000000..c055d3a --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.html @@ -0,0 +1,14 @@ +

Convertir imagen en virtual

+ + + + Extension + + + + +
+ + +
diff --git a/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.spec.ts b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.spec.ts new file mode 100644 index 0000000..6f25a05 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.spec.ts @@ -0,0 +1,52 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { ConvertImageToVirtualComponent } from './convert-image-to-virtual.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ToastrModule } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; + +describe('ConvertImageToVirtualComponent', () => { + let component: ConvertImageToVirtualComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url' + }; + await TestBed.configureTestingModule({ + declarations: [ConvertImageToVirtualComponent], + imports: [ + MatDialogModule, + ToastrModule.forRoot() + ], + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + { + provide: MatDialogRef, + useValue: {} + }, + { + provide: MAT_DIALOG_DATA, + useValue: {} + }, + { + provide: ConfigService, + useValue: mockConfigService + } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConvertImageToVirtualComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.ts b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.ts new file mode 100644 index 0000000..bd5e923 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image-to-virtual/convert-image-to-virtual.component.ts @@ -0,0 +1,51 @@ +import {Component, Inject} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {ToastrService} from "ngx-toastr"; +import {Router} from "@angular/router"; +import {ConfigService} from "@services/config.service"; + +@Component({ + selector: 'app-convert-image-to-virtual', + templateUrl: './convert-image-to-virtual.component.html', + styleUrl: './convert-image-to-virtual.component.css' +}) +export class ConvertImageToVirtualComponent { + baseUrl: string; + loading: boolean = true; + extension: string = ''; + + constructor( + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private router: Router, + private configService: ConfigService, + @Inject(MAT_DIALOG_DATA) public data: { imageImageRepository: any } + ) { + this.baseUrl = this.configService.apiUrl; + } + + ngOnInit(): void { + this.loading = true; + } + + save() { + this.http.post(`${this.baseUrl}${this.data.imageImageRepository['@id']}/convert-image-to-virtual`, { + extension: this.extension + }).subscribe({ + next: (response) => { + this.toastService.success('Peticion de conversion de imagen enviada correctamente'); + this.dialogRef.close(); + this.router.navigate(['/commands-logs']); + }, + error: error => { + this.toastService.error(error.error['hydra:description']); + } + }); + } + + close() { + this.dialogRef.close(); + } +} diff --git a/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.css b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.css new file mode 100644 index 0000000..bdecbfa --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.css @@ -0,0 +1,39 @@ +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100px; +} + +mat-form-field { + width: 100%; +} + +mat-dialog-actions { + display: flex; + justify-content: flex-end; +} + +.checkbox-group { + display: flex; + flex-direction: column; + gap: 10px; +} + +.selected-list ul { + list-style: none; + padding: 0; +} + +.selected-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + border-bottom: 1px solid #ccc; +} + +.selected-item button { + margin-left: 10px; +} diff --git a/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.html b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.html new file mode 100644 index 0000000..df4bff5 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.html @@ -0,0 +1,21 @@ +

Convertir imagen virtual

+ + +

Repositorio destino: {{ data.name }}

+ + + Imagen + + El nombre de la imagen tiene que ir con la extensión. + + + Sistema de archivos + + +
+ +
+ + +
diff --git a/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.spec.ts b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.spec.ts new file mode 100644 index 0000000..d5d56d8 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ToastrModule } from 'ngx-toastr'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { FormsModule } from '@angular/forms'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ConvertImageComponent } from './convert-image.component'; +import { ConfigService } from '@services/config.service'; + +describe('ConvertImageComponent', () => { + let component: ConvertImageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + + await TestBed.configureTestingModule({ + declarations: [ConvertImageComponent], + imports: [ + HttpClientTestingModule, + MatDialogModule, + ToastrModule.forRoot(), + MatFormFieldModule, + MatInputModule, + FormsModule, + BrowserAnimationsModule + ], + providers: [ + { provide: MatDialogRef, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConvertImageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.ts b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.ts new file mode 100644 index 0000000..5142905 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/convert-image/convert-image.component.ts @@ -0,0 +1,54 @@ +import { Component, Inject } from '@angular/core'; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { Router } from "@angular/router"; +import { ConfigService } from "@services/config.service"; + +@Component({ + selector: 'app-convert-image', + templateUrl: './convert-image.component.html', + styleUrl: './convert-image.component.css' +}) +export class ConvertImageComponent { + baseUrl: string; + loading: boolean = true; + imageName: string = ''; + filesystem: string = ''; + + constructor( + private http: HttpClient, + public dialogRef: MatDialogRef, + private toastService: ToastrService, + private router: Router, + private configService: ConfigService, + @Inject(MAT_DIALOG_DATA) public data: { repositoryUuid: any, name: string } + ) { + this.baseUrl = this.configService.apiUrl; + } + + ngOnInit(): void { + this.loading = true; + } + + save() { + console.log(this.data?.repositoryUuid) + this.http.post(`${this.baseUrl}/image-repositories/${this.data?.repositoryUuid}/convert-image`, { + name: this.imageName, + filesystem: this.filesystem + }).subscribe({ + next: (response) => { + this.toastService.success('Peticion de conversion de imagen enviada correctamente'); + this.dialogRef.close(); + this.router.navigate(['/commands-logs']); + }, + error: error => { + this.toastService.error(error.error['hydra:description']); + } + }); + } + + close() { + this.dialogRef.close(); + } +} diff --git a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.html b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.html index 82bc9fd..a9760f5 100644 --- a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.html +++ b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.html @@ -1,28 +1,15 @@ -

Importar imagenes a {{data.repository?.name}}

+

Importar imagenes a {{ data.name }}

- Seleccione imagenes a importar - - {{ image.name }} - + Imagen + + El nombre de la imagen tiene que ir sin la extensión. - -
-

Imágenes seleccionadas:

-
    -
  • - {{ getImageName(imageId) }} - -
  • -
-
-
- +
- +
diff --git a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.spec.ts b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.spec.ts index 03aa01d..19f4ef5 100644 --- a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.spec.ts @@ -1,27 +1,32 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { ImportImageComponent } from './import-image.component'; -import {FormBuilder, ReactiveFormsModule} from "@angular/forms"; -import {ToastrModule, ToastrService} from "ngx-toastr"; -import {DataService} from "../../calendar/data.service"; -import {provideHttpClient} from "@angular/common/http"; -import {provideHttpClientTesting} from "@angular/common/http/testing"; -import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog"; -import {MatFormFieldModule} from "@angular/material/form-field"; -import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; -import {TranslateModule} from "@ngx-translate/core"; -import {MatButtonModule} from "@angular/material/button"; -import {MatInputModule} from "@angular/material/input"; -import {MatSelectModule} from "@angular/material/select"; +import { FormBuilder, FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { ToastrModule, ToastrService } from "ngx-toastr"; +import { provideHttpClient } from "@angular/common/http"; +import { provideHttpClientTesting } from "@angular/common/http/testing"; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from "@angular/material/dialog"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { TranslateModule } from "@ngx-translate/core"; +import { MatButtonModule } from "@angular/material/button"; +import { MatInputModule } from "@angular/material/input"; +import { MatSelectModule } from "@angular/material/select"; +import { ConfigService } from '@services/config.service'; describe('ImportImageComponent', () => { let component: ImportImageComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [ImportImageComponent], imports: [ + FormsModule, ReactiveFormsModule, MatDialogModule, MatFormFieldModule, @@ -41,6 +46,7 @@ describe('ImportImageComponent', () => { provide: MatDialogRef, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService }, { provide: MAT_DIALOG_DATA, useValue: {} @@ -56,4 +62,4 @@ describe('ImportImageComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); -}); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.ts b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.ts index 8db695a..6d41dc6 100644 --- a/ogWebconsole/src/app/components/repositories/import-image/import-image.component.ts +++ b/ogWebconsole/src/app/components/repositories/import-image/import-image.component.ts @@ -1,57 +1,39 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {Router} from "@angular/router"; +import { Component, Inject, OnInit } from '@angular/core'; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { Router } from "@angular/router"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-import-image', templateUrl: './import-image.component.html', styleUrl: './import-image.component.css' }) -export class ImportImageComponent implements OnInit{ - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; +export class ImportImageComponent implements OnInit { + baseUrl: string; loading: boolean = true; - images: any[] = []; - selectedClients: any[] = []; + imageName: string = ''; constructor( private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, private router: Router, - @Inject(MAT_DIALOG_DATA) public data: { repository: any } + private configService: ConfigService, + @Inject(MAT_DIALOG_DATA) public data: { repositoryUuid: any, name: string } ) { - + this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { this.loading = true; - this.loadImages(); - } - - loadImages() { - this.http.get(`${this.baseUrl}/images?page=1&itemsPerPage=50`).subscribe( - response => { - this.images = response['hydra:member']; - this.loading = false; - }, - error => console.error('Error fetching organizational units:', error) - ); - } - - getImageName(imageId: string): string { - const image = this.images.find(img => img['@id'] === imageId); - return image ? image.name : 'Desconocido'; - } - - removeImage(imageId: string) { - this.selectedClients = this.selectedClients.filter(id => id !== imageId); } save() { - this.http.post(`${this.baseUrl}${this.data.repository['@id']}/import-image`, { - images: this.selectedClients + console.log(this.data?.repositoryUuid) + this.http.post(`${this.baseUrl}/image-repositories/${this.data?.repositoryUuid}/import-image`, { + name: this.imageName }).subscribe({ next: (response) => { this.toastService.success('Peticion de importacion de imagen enviada correctamente'); @@ -59,7 +41,7 @@ export class ImportImageComponent implements OnInit{ this.router.navigate(['/commands-logs']); }, error: error => { - this.toastService.error('Error al importar imagenes'); + this.toastService.error(error.error['hydra:description']); } }); } diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.css b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.css index 4fa0077..c9f5f5c 100644 --- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.css +++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.css @@ -382,4 +382,4 @@ table { .top-row .card { max-width: 100%; } -} \ No newline at end of file +} diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html index c2b6da9..e545a96 100644 --- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html +++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.html @@ -1,37 +1,10 @@ +

OgRepository Server Status

-
-
-

Uso de Disco

- - -
-

Total: {{ diskUsage.total }}

-

Ocupado: {{ diskUsage.used }}

-

Disponible: {{ diskUsage.available }}

-

Ocupado (%): {{ diskUsage.percentage }}

-
-
- -
-

Uso de RAM

- - -
-

Total: {{ ramUsage.total }}

-

Ocupado: {{ ramUsage.used }}

-

Disponible: {{ ramUsage.available }}

-

Ocupado (%): {{ ramUsage.percentage }}

-
-
-
-

Uso de CPU

@@ -74,6 +47,36 @@
+ +
+
+

Uso de Disco

+ +
+ + +

Total: {{ diskUsage.total }}

+

Ocupado: {{ diskUsage.used }}

+

Disponible: {{ diskUsage.available }}

+

Ocupado (%): {{ diskUsage.percentage }}

+
+
+ +
+

Uso de RAM

+ +
+ + +

Total: {{ ramUsage.total }}

+

Ocupado: {{ ramUsage.used }}

+

Disponible: {{ ramUsage.available }}

+

Ocupado (%): {{ ramUsage.percentage }}

+
+
+
diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.spec.ts b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.spec.ts index 3c518d4..609dee6 100644 --- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MainRepositoryViewComponent } from './main-repository-view.component'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -17,12 +16,18 @@ import { CommonModule } from '@angular/common'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { provideRouter } from '@angular/router'; import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ConfigService } from '@services/config.service'; describe('MainRepositoryViewComponent', () => { let component: MainRepositoryViewComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [MainRepositoryViewComponent, LoadingComponent], imports: [ @@ -52,7 +57,8 @@ describe('MainRepositoryViewComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -65,4 +71,4 @@ describe('MainRepositoryViewComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); -}); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts index 6d2433c..a3c5d30 100644 --- a/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts +++ b/ogWebconsole/src/app/components/repositories/main-repository-view/main-repository-view.component.ts @@ -1,14 +1,15 @@ -import {Component, Inject, OnInit} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {HttpClient} from "@angular/common/http"; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../../images/data.service"; -import {ActivatedRoute} from "@angular/router"; -import {MatTableDataSource} from "@angular/material/table"; -import {DatePipe} from "@angular/common"; -import {Observable} from "rxjs"; -import {MatDialog} from "@angular/material/dialog"; -import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { Component, Inject, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { ToastrService } from "ngx-toastr"; +import { DataService } from "../../images/data.service"; +import { ActivatedRoute } from "@angular/router"; +import { MatTableDataSource } from "@angular/material/table"; +import { DatePipe } from "@angular/common"; +import { Observable } from "rxjs"; +import { MatDialog } from "@angular/material/dialog"; +import { ServerInfoDialogComponent } from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-main-repository-view', @@ -16,7 +17,8 @@ import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server- styleUrl: './main-repository-view.component.css' }) export class MainRepositoryViewComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; repositoryForm: FormGroup; repositoryId: string | null = null; dataSource = new MatTableDataSource(); @@ -32,7 +34,6 @@ export class MainRepositoryViewComponent implements OnInit { length: number = 0; itemsPerPage: number = 10; page: number = 0; - view: [number, number] = [800, 500]; gradient: boolean = true; showLabels: boolean = true; isDoughnut: boolean = true; @@ -82,7 +83,6 @@ export class MainRepositoryViewComponent implements OnInit { } ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/images`; constructor( private fb: FormBuilder, @@ -91,7 +91,10 @@ export class MainRepositoryViewComponent implements OnInit { private dataService: DataService, private route: ActivatedRoute, public dialog: MatDialog, + private configService: ConfigService ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/images`; this.repositoryForm = this.fb.group({ name: ['', Validators.required], ip: [''], diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.css b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.css similarity index 100% rename from ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.css rename to ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.css diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.html similarity index 93% rename from ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html rename to ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.html index 84ce0db..1edb619 100644 --- a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.html +++ b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.html @@ -19,7 +19,7 @@ - +
- \ No newline at end of file +
diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.spec.ts similarity index 76% rename from ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts rename to ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.spec.ts index 6fe6029..fe4be8b 100644 --- a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.spec.ts @@ -1,6 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CreateRepositoryComponent } from './create-repository.component'; +import { ManageRepositoryComponent } from './manage-repository.component'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ReactiveFormsModule, FormBuilder } from '@angular/forms'; @@ -13,12 +12,18 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { ToastrModule, ToastrService } from 'ngx-toastr'; import { DataService } from '../../calendar/data.service'; +import { ConfigService } from '@services/config.service'; -describe('CreateRepositoryComponent', () => { - let component: CreateRepositoryComponent; - let fixture: ComponentFixture; +describe('ManageRepositoryComponent', () => { + let component: ManageRepositoryComponent; + let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ imports: [ ReactiveFormsModule, @@ -44,11 +49,12 @@ describe('CreateRepositoryComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); - fixture = TestBed.createComponent(CreateRepositoryComponent); + fixture = TestBed.createComponent(ManageRepositoryComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.ts similarity index 62% rename from ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts rename to ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.ts index 634dd4f..d570f90 100644 --- a/ogWebconsole/src/app/components/repositories/create-repository/create-repository.component.ts +++ b/ogWebconsole/src/app/components/repositories/manage-repository/manage-repository.component.ts @@ -1,17 +1,18 @@ -import {Component, Inject} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../../images/data.service"; +import {Component, Inject, OnInit} from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { DataService } from "../../images/data.service"; +import { ConfigService } from "@services/config.service"; @Component({ - selector: 'app-create-repository', - templateUrl: './create-repository.component.html', - styleUrl: './create-repository.component.css' + selector: 'app-manage-repository', + templateUrl: './manage-repository.component.html', + styleUrl: './manage-repository.component.css' }) -export class CreateRepositoryComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; +export class ManageRepositoryComponent implements OnInit { + baseUrl: string; imageForm: FormGroup; repositoryId: string | null = null; softwareProfiles: any[] = []; @@ -19,15 +20,17 @@ export class CreateRepositoryComponent { constructor( private fb: FormBuilder, private http: HttpClient, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private toastService: ToastrService, private dataService: DataService, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; this.imageForm = this.fb.group({ - name: ['', Validators.required], - ip: [''], - comments: [''], + name: [null, Validators.required], + ip: [null], + comments: [null], }); } @@ -63,23 +66,21 @@ export class CreateRepositoryComponent { if (this.repositoryId) { this.http.put(`${this.baseUrl}${this.repositoryId}`, payload).subscribe( (response) => { - this.toastService.success('Imagen editada correctamente'); + this.toastService.success('Repositorio editada correctamente'); this.dialogRef.close(); }, (error) => { this.toastService.error(error['error']['hydra:description']); - console.error('Error al editar la imagen', error); } ); } else { this.http.post(`${this.baseUrl}/image-repositories`, payload).subscribe( (response) => { - this.toastService.success('Imagen añadida correctamente'); + this.toastService.success('Repositorio añadido correctamente'); this.dialogRef.close(); }, (error) => { this.toastService.error(error['error']['hydra:description']); - console.error('Error al añadir la imagen', error); } ); } diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.css b/ogWebconsole/src/app/components/repositories/repositories.component.css index d7f88b1..75b57f7 100644 --- a/ogWebconsole/src/app/components/repositories/repositories.component.css +++ b/ogWebconsole/src/app/components/repositories/repositories.component.css @@ -50,13 +50,13 @@ table { width: 100%; } -.search-container { +.search-container { display: flex; justify-content: space-between; align-items: center; width: 100%; - margin: 1.5rem 0rem 1.5rem 0rem; box-sizing: border-box; + margin: 1.5rem 0rem 1.5rem 0rem; } .search-string { diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.html b/ogWebconsole/src/app/components/repositories/repositories.component.html index 6ad69e6..2f33a45 100644 --- a/ogWebconsole/src/app/components/repositories/repositories.component.html +++ b/ogWebconsole/src/app/components/repositories/repositories.component.html @@ -37,19 +37,12 @@ {{ column.header }} - - - - {{ repository.status === true ? 'Conectado' : 'Fallido' }} - - - - + {{ column.cell(repository) }} - + + + + diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts b/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts index 4cd0696..f9bd956 100644 --- a/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/repositories.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { RepositoriesComponent } from './repositories.component'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -19,14 +18,20 @@ import { ToastrModule, ToastrService } from 'ngx-toastr'; import { DataService } from '../calendar/data.service'; import { JoyrideModule } from 'ngx-joyride'; import { CommonModule } from '@angular/common'; -import {LoadingComponent} from "../../shared/loading/loading.component"; -import {MatProgressSpinner} from "@angular/material/progress-spinner"; +import { LoadingComponent } from "../../shared/loading/loading.component"; +import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; +import { ConfigService } from '@services/config.service'; describe('RepositoriesComponent', () => { let component: RepositoriesComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [RepositoriesComponent, LoadingComponent], imports: [ @@ -42,7 +47,7 @@ describe('RepositoriesComponent', () => { MatDividerModule, MatIconModule, BrowserAnimationsModule, - MatProgressSpinner, + MatProgressSpinnerModule, ToastrModule.forRoot(), TranslateModule.forRoot(), JoyrideModule.forRoot(), @@ -61,7 +66,8 @@ describe('RepositoriesComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); @@ -73,4 +79,4 @@ describe('RepositoriesComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); -}); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/repositories.component.ts b/ogWebconsole/src/app/components/repositories/repositories.component.ts index eedb828..6443e59 100644 --- a/ogWebconsole/src/app/components/repositories/repositories.component.ts +++ b/ogWebconsole/src/app/components/repositories/repositories.component.ts @@ -6,8 +6,11 @@ import {HttpClient} from "@angular/common/http"; import {ToastrService} from "ngx-toastr"; import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; import { JoyrideService } from 'ngx-joyride'; -import {CreateRepositoryComponent} from "./create-repository/create-repository.component"; import { Router } from '@angular/router'; +import { ConfigService } from '@services/config.service'; +import {Subnet} from "../ogdhcp/og-dhcp-subnets.component"; +import {ShowImagesComponent} from "./show-images/show-images.component"; +import {ManageRepositoryComponent} from "./manage-repository/manage-repository.component"; @Component({ selector: 'app-repositories', @@ -15,7 +18,8 @@ import { Router } from '@angular/router'; styleUrl: './repositories.component.css' }) export class RepositoriesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; @@ -40,9 +44,9 @@ export class RepositoriesComponent implements OnInit { cell: (repository: any) => `${repository.ip}` }, { - columnDef: 'status', - header: 'Estado', - cell: (repository: any) => `${repository.status}` + columnDef: 'images', + header: 'Imágenes', + cell: (repository: any) => `${repository.images}` }, { columnDef: 'createdAt', @@ -50,24 +54,26 @@ export class RepositoriesComponent implements OnInit { cell: (repository: any) => `${this.datePipe.transform(repository.createdAt, 'dd/MM/yyyy hh:mm:ss')}` } ]; - displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - - private apiUrl = `${this.baseUrl}/image-repositories`; + displayedColumns: string[] = ['id', 'name', 'ip', 'images', 'createdAt', 'actions']; constructor( public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService, private joyrideService: JoyrideService, - private router: Router - ) {} + private router: Router, + private configService: ConfigService + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/image-repositories`; + } ngOnInit(): void { this.search(); } addImage(): void { - const dialogRef = this.dialog.open(CreateRepositoryComponent, { + const dialogRef = this.dialog.open(ManageRepositoryComponent, { width: '600px' }); @@ -93,7 +99,10 @@ export class RepositoriesComponent implements OnInit { editRepository(event: MouseEvent, repository: any): void { event.stopPropagation(); - this.router.navigate(['repository', repository.uuid]); + this.dialog.open(ManageRepositoryComponent, { + width: '600px', + data: repository['@id'] + }).afterClosed().subscribe(() => this.search()); } deleteRepository(event: MouseEvent,command: any): void { @@ -116,6 +125,21 @@ export class RepositoriesComponent implements OnInit { }); } + openShowImagesDialog(repository: Subnet) { + const dialogRef = this.dialog.open(ShowImagesComponent, { + width: '100vw', + height: '100vh', + maxWidth: '100vw', + maxHeight: '100vh', + data: { repositoryId: repository.id, repositoryName: repository.name, repositoryUuid: repository.uuid } + }); + + dialogRef.afterClosed().subscribe(result => { + this.search(); + }); + } + + onPageChange(event: any): void { this.page = event.pageIndex; this.itemsPerPage = event.pageSize; diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css index 5dd3535..ecb91dd 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.css @@ -1,8 +1,3 @@ -.images-button-row { - display: flex; - justify-content: flex-start; - margin-top: 16px; -} table { width: 100%; @@ -94,3 +89,9 @@ table { margin-left: 1em; } +.images-button-row { + display: flex; + gap: 15px; + padding: 5px; +} + diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html index b59ce8d..e1f3c60 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html @@ -9,6 +9,16 @@ {{ 'imagesTitle' | translate }} en {{ repository?.name }}
+
+ +
+
+ +
@@ -36,9 +46,9 @@ {{ column.header }} - - - {{ image[column.columnDef] ? 'check_circle' : 'cancel' }} + + + {{ image.image[column.columnDef] ? 'check_circle' : 'cancel' }} @@ -73,8 +83,9 @@ + - + diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts index c18a2a3..064bad7 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ReactiveFormsModule, FormsModule, FormBuilder } from '@angular/forms'; @@ -22,12 +21,18 @@ import {RepositoryImagesComponent} from "./repository-images.component"; import {LoadingComponent} from "../../../shared/loading/loading.component"; import {MatOptionModule} from "@angular/material/core"; import {MatSelectModule} from "@angular/material/select"; +import { ConfigService } from '@services/config.service'; describe('RepositoriesComponent', () => { let component: RepositoryImagesComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [RepositoryImagesComponent, LoadingComponent], imports: [ @@ -63,7 +68,8 @@ describe('RepositoriesComponent', () => { { provide: MAT_DIALOG_DATA, useValue: {} - } + }, + { provide: ConfigService, useValue: mockConfigService } ] }).compileComponents(); diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts index 170ff11..5fde29f 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, Input, isDevMode, OnInit} from '@angular/core'; import {MatTableDataSource} from "@angular/material/table"; import {DatePipe} from "@angular/common"; import {MatDialog} from "@angular/material/dialog"; @@ -10,6 +10,10 @@ import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/de import {ExportImageComponent} from "../../images/export-image/export-image.component"; import {BackupImageComponent} from "../backup-image/backup-image.component"; import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import {ImportImageComponent} from "../import-image/import-image.component"; +import {ConvertImageComponent} from "../convert-image/convert-image.component"; +import { ConfigService } from '@services/config.service'; +import {Router} from "@angular/router"; @Component({ selector: 'app-repository-images', @@ -17,7 +21,8 @@ import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server- styleUrl: './repository-images.component.css' }) export class RepositoryImagesComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; @@ -34,14 +39,19 @@ export class RepositoryImagesComponent implements OnInit { cell: (image: any) => `${image.id}` }, { - columnDef: 'image.image.name', + columnDef: 'name', header: 'Nombre de imagen', cell: (image: any) => `${image.image.name}` }, { columnDef: 'remotePc', header: 'Remote Pc', - cell: (image: any) => `${image.remotePc}` + cell: (image: any) => `${image.image?.remotePc}` + }, + { + columnDef: 'isGlobal', + header: 'Imagen global', + cell: (image: any) => `${image.image?.isGlobal}` }, { columnDef: 'status', @@ -61,7 +71,6 @@ export class RepositoryImagesComponent implements OnInit { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/image-image-repositories`; @Input() repositoryUuid: any private repositoryId: any; @@ -70,7 +79,12 @@ export class RepositoryImagesComponent implements OnInit { private http: HttpClient, private toastService: ToastrService, private joyrideService: JoyrideService, - ) {} + private configService: ConfigService, + private router: Router, + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/image-image-repositories`; + } ngOnInit(): void { if (this.repositoryUuid) { @@ -162,11 +176,39 @@ export class RepositoryImagesComponent implements OnInit { ); } + importImage(): void { + this.dialog.open(ImportImageComponent, { + width: '600px', + data: { + repositoryUuid: this.repositoryUuid, + name: this.repository.name + } + }).afterClosed().subscribe((result) => { + if (result) { + this.search(); + } + }); + } + + convertImage(): void { + this.dialog.open(ConvertImageComponent, { + width: '600px', + data: { + repositoryUuid: this.repositoryUuid, + name: this.repository.name + } + }).afterClosed().subscribe((result) => { + if (result) { + this.search(); + } + }); + } + toggleAction(image: any, action:string): void { switch (action) { case 'get-aux': this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/create-aux-files`, {}).subscribe({ - next: () => { + next: (message) => { this.toastService.success('Petición de creación de archivos auxiliares enviada'); this.search() }, @@ -239,8 +281,8 @@ export class RepositoryImagesComponent implements OnInit { break; case 'status': this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/status`, {}).subscribe({ - next: () => { - this.toastService.success('Integridad de la imagen comprobada con éxito'); + next: (response: any) => { + this.toastService.info(response?.output); this.search() }, error: (error) => { @@ -264,6 +306,20 @@ export class RepositoryImagesComponent implements OnInit { } }); break; + case 'transfer-global': + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/transfer-global`, { + }).subscribe({ + next: (response) => { + this.toastService.success('Petición de exportación de imagen realizada correctamente'); + this.loading = false; + this.router.navigate(['/commands-logs']); + }, + error: error => { + this.loading = false; + this.toastService.error('Error en la petición de exportación de imagen'); + } + }); + break; case 'backup': this.http.get(`${this.baseUrl}${image.image['@id']}`).subscribe({ next: (response) => { @@ -301,4 +357,6 @@ export class RepositoryImagesComponent implements OnInit { themeColor: '#3f51b5' }); } + + protected readonly isDevMode = isDevMode; } diff --git a/ogWebconsole/src/app/components/repositories/show-images/show-images.component.css b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.css new file mode 100644 index 0000000..01712ec --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.css @@ -0,0 +1,202 @@ + +table { + width: 100%; +} + +.search-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin: 1.5rem 0rem 1.5rem 0rem; + box-sizing: border-box; +} + +.search-string { + flex: 2; + padding: 5px; +} + +.search-boolean { + flex: 1; + padding: 5px; +} + +.header-container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 10px; + border-bottom: 1px solid #ddd; +} + +.mat-elevation-z8 { + box-shadow: 0px 0px 0px rgba(0,0,0,0.2); +} + +.paginator-container { + display: flex; + justify-content: end; + margin-bottom: 30px; +} + +.example-headers-align .mat-expansion-panel-header-description { + justify-content: space-between; + align-items: center; +} + +.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field { + margin-left: 8px; +} + +.button-row { + display: table-cell; + max-width: 600px; +} + +.button-row .mat-mdc-button-base { + margin: 8px 8px 8px 0; +} + +.chip-failed { + background-color: #e87979 !important; + color: white; +} + +.chip-success { + background-color: #46c446 !important; + color: white; +} + +.chip-pending { + background-color: lightgrey !important; + color: black; +} + +.chip-in-progress { + background-color: #f5a623 !important; + color: white; +} + +.chip-transferring { + background-color: #f5a623 !important; + color: white; +} + +.header-container-title { + flex-grow: 1; + text-align: left; + margin-left: 1em; +} + +.images-button-row { + display: flex; + gap: 15px; + padding: 5px; +} + + +table { + width: 100%; +} + +.search-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin: 1.5rem 0rem 1.5rem 0rem; + box-sizing: border-box; +} + +.search-string { + flex: 2; + padding: 5px; +} + +.search-boolean { + flex: 1; + padding: 5px; +} + +.header-container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 10px; + border-bottom: 1px solid #ddd; +} + +.mat-elevation-z8 { + box-shadow: 0px 0px 0px rgba(0,0,0,0.2); +} + +.paginator-container { + display: flex; + justify-content: end; + margin-bottom: 30px; +} + +.example-headers-align .mat-expansion-panel-header-description { + justify-content: space-between; + align-items: center; +} + +.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field { + margin-left: 8px; +} + +.button-row { + display: table-cell; + max-width: 600px; +} + +.button-row .mat-mdc-button-base { + margin: 8px 8px 8px 0; +} + +.chip-failed { + background-color: #e87979 !important; + color: white; +} + +.chip-success { + background-color: #46c446 !important; + color: white; +} + +.chip-pending { + background-color: lightgrey !important; + color: black; +} + +.chip-in-progress { + background-color: #f5a623 !important; + color: white; +} + +.chip-transferring { + background-color: #f5a623 !important; + color: white; +} + +.action-container { + display: flex; + justify-content: flex-end; + gap: 1em; + padding: 1.5em; +} + +.header-container-title { + flex-grow: 1; + text-align: left; + margin-left: 1em; +} + +.images-button-row { + display: flex; + gap: 15px; + padding: 5px; +} + + diff --git a/ogWebconsole/src/app/components/repositories/show-images/show-images.component.html b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.html new file mode 100644 index 0000000..fc28fe2 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.html @@ -0,0 +1,117 @@ + + +

Gestionar imágenes en {{data.repositoryName}}

+ + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + {{ 'searchLabel' | translate }} + + search + {{ 'searchHint' | translate }} + + + Estado + + Fallido + Pendiente + Transfiriendo + Creado con éxito + En progreso + Papelera + Creando archivos auxiliares + + +
+ + + + + + + + + + + + + +
{{ column.header }} + + + {{ image.image[column.columnDef] ? 'check_circle' : 'cancel' }} + + + + + {{ getStatusLabel(image[column.columnDef]) }} + + + + {{ column.cell(image) }} + + Acciones + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + diff --git a/ogWebconsole/src/app/components/repositories/show-images/show-images.component.spec.ts b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.spec.ts new file mode 100644 index 0000000..c7cc4a1 --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.spec.ts @@ -0,0 +1,63 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ShowImagesComponent } from './show-images.component'; +import { ToastrModule } from 'ngx-toastr'; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; +import { JoyrideModule } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; +import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { MatIconModule } from '@angular/material/icon'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTableModule } from '@angular/material/table'; +import { FormsModule } from '@angular/forms'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; + +describe('ShowImagesComponent', () => { + let component: ShowImagesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url' + }; + + await TestBed.configureTestingModule({ + declarations: [ShowImagesComponent, LoadingComponent], + imports: [ + HttpClientTestingModule, + ToastrModule.forRoot(), + MatDialogModule, + TranslateModule.forRoot(), + JoyrideModule.forRoot(), + MatIconModule, + MatFormFieldModule, + MatSelectModule, + MatPaginatorModule, + MatTableModule, + FormsModule, + NoopAnimationsModule, + MatInputModule, + MatProgressSpinnerModule + ], + providers: [ + { provide: MatDialogRef, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ShowImagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/repositories/show-images/show-images.component.ts b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.ts new file mode 100644 index 0000000..270c73b --- /dev/null +++ b/ogWebconsole/src/app/components/repositories/show-images/show-images.component.ts @@ -0,0 +1,402 @@ +import {Component, Inject, Input, isDevMode, OnInit} from '@angular/core'; +import {MatTableDataSource} from "@angular/material/table"; +import {DatePipe} from "@angular/common"; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; +import {HttpClient} from "@angular/common/http"; +import {ToastrService} from "ngx-toastr"; +import {JoyrideService} from "ngx-joyride"; +import {ConfigService} from "@services/config.service"; +import {Router} from "@angular/router"; +import {Observable} from "rxjs"; +import {ServerInfoDialogComponent} from "../../ogdhcp/server-info-dialog/server-info-dialog.component"; +import {ImportImageComponent} from "../import-image/import-image.component"; +import {ConvertImageComponent} from "../convert-image/convert-image.component"; +import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; +import {ExportImageComponent} from "../../images/export-image/export-image.component"; +import {BackupImageComponent} from "../backup-image/backup-image.component"; +import {ConvertImageToVirtualComponent} from "../convert-image-to-virtual/convert-image-to-virtual.component"; + +@Component({ + selector: 'app-show-images', + templateUrl: './show-images.component.html', + styleUrl: './show-images.component.css' +}) +export class ShowImagesComponent implements OnInit { + baseUrl: string; + private apiUrl: string; + dataSource = new MatTableDataSource(); + length: number = 0; + itemsPerPage: number = 10; + page: number = 0; + loading: boolean = false; + filters: { [key: string]: string } = {}; + alertMessage: string | null = null; + repository: any = {}; + datePipe: DatePipe = new DatePipe('es-ES'); + columns = [ + { + columnDef: 'id', + header: 'Id', + cell: (image: any) => `${image.id}` + }, + { + columnDef: 'name', + header: 'Nombre de imagen', + cell: (image: any) => `${image.image.name}` + }, + { + columnDef: 'remotePc', + header: 'Remote Pc', + cell: (image: any) => `${image.image?.remotePc}` + }, + { + columnDef: 'isGlobal', + header: 'Imagen global', + cell: (image: any) => `${image.image?.isGlobal}` + }, + { + columnDef: 'status', + header: 'Estado', + cell: (image: any) => `${image.status}` + }, + { + columnDef: 'imageFullsum', + header: 'Fullsum', + cell: (image: any) => `${image.imageFullsum}` + }, + { + columnDef: 'createdAt', + header: 'Fecha de creación', + cell: (image: any) => `${this.datePipe.transform(image.createdAt, 'dd/MM/yyyy hh:mm:ss')}` + } + ]; + displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; + + constructor( + public dialog: MatDialog, + private http: HttpClient, + private toastService: ToastrService, + private joyrideService: JoyrideService, + private configService: ConfigService, + private router: Router, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/image-image-repositories`; + } + + ngOnInit(): void { + console.error() + if (this.data) { + this.loadData(); + } + } + + loadData(): void { + this.loading = true; + this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}&repository.id=${this.data.repositoryId}`, { params: this.filters }).subscribe( + data => { + this.dataSource.data = data['hydra:member']; + this.length = data['hydra:totalItems']; + this.loading = false; + }, + error => { + console.error('Error fetching image repositories', error); + } + ) + } + + getStatusLabel(status: string): string { + switch (status) { + case 'pending': + return 'Pendiente'; + case 'in-progress': + return 'En progreso'; + case 'aux-files-pending': + return 'Archivos auxiliares pendientes'; + case 'success': + return 'Creado con éxito'; + case 'trash': + return 'Papelera temporal'; + case 'failed': + return 'Fallido'; + case 'transferring': + return 'Transfiriendo'; + default: + return 'Estado desconocido'; + } + } + + onPageChange(event: any): void { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.length = event.length; + this.loadData(); + } + + loadImageAlert(image: any): Observable { + return this.http.get(`${this.apiUrl}/server/${image.uuid}/get`, {}); + } + + showImageInfo(event: MouseEvent, image:any) { + event.stopPropagation(); + this.loading = true; + this.loadImageAlert(image).subscribe( + response => { + this.alertMessage = response; + + this.dialog.open(ServerInfoDialogComponent, { + width: '800px', + data: { + message: this.alertMessage + } + }); + this.loading = false; + }, + error => { + this.toastService.error(error.error['hydra:description']); + this.loading = false; + } + ); + } + + importImage(): void { + console.log(this.data) + this.dialog.open(ImportImageComponent, { + width: '600px', + data: { + repositoryUuid: this.data.repositoryUuid, + name: this.data.repositoryName + } + }).afterClosed().subscribe((result) => { + if (result) { + this.loadData(); + } + }); + } + + convertImage(): void { + this.dialog.open(ConvertImageComponent, { + width: '600px', + data: { + repositoryUuid: this.data.repositoryUuid, + name: this.data.repositoryName + } + }).afterClosed().subscribe((result) => { + if (result) { + this.loadData(); + } + }); + } + + toggleAction(image: any, action:string): void { + switch (action) { + case 'get-aux': + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/create-aux-files`, {}).subscribe({ + next: (message) => { + this.toastService.success('Petición de creación de archivos auxiliares enviada'); + this.loadData() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + case 'delete-trash': + if (!image.imageFullsum) { + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { name: image.name }, + }); + + dialogRef.afterClosed().subscribe((result) => { + this.http.delete(`${this.baseUrl}${image['@id']}`).subscribe({ + next: () => { + this.toastService.success('Image deleted successfully'); + this.loadData() + }, + error: (error) => { + this.toastService.error('Error deleting image'); + } + }); + }); + } else { + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-trash`, + { repository: `/image-repositories/${this.data.repositoryUuid}` }) + .subscribe({ + next: () => { + this.toastService.success('Petición de eliminación de la papelera temporal enviada'); + this.loadData() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + } + + break; + case 'delete-permanent': + this.dialog.open(DeleteModalComponent, { + width: '300px', + data: { name: image.name }, + }).afterClosed().subscribe((result) => { + if (result) { + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-permanent`, {}).subscribe({ + next: () => { + this.toastService.success('Petición de eliminación de la papelera temporal enviada'); + this.loadData() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + } + }); + break; + case 'recover': + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/recover`, {}).subscribe({ + next: () => { + this.toastService.success('Petición de recuperación de la imagen enviada'); + this.loadData() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + case 'status': + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/status`, {}).subscribe({ + next: (response: any) => { + this.toastService.info(response?.output); + this.loadData() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + case 'transfer': + this.http.get(`${this.baseUrl}${image.image['@id']}`).subscribe({ + next: (response) => { + this.dialog.open(ExportImageComponent, { + width: '600px', + data: { + image: response, + imageImageRepository: image + } + }); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + case 'transfer-global': + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/transfer-global`, { + }).subscribe({ + next: (response) => { + this.toastService.success('Petición de exportación de imagen realizada correctamente'); + this.loading = false; + this.router.navigate(['/commands-logs']); + }, + error: error => { + this.loading = false; + this.toastService.error('Error en la petición de exportación de imagen'); + } + }); + break; + case 'backup': + this.http.get(`${this.baseUrl}${image.image['@id']}`).subscribe({ + next: (response) => { + this.dialog.open(BackupImageComponent, { + width: '600px', + data: { + image: response, + imageImageRepository: image + } + }); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + case 'convert-image-to-virtual': + this.http.get(`${this.baseUrl}${image.image['@id']}`).subscribe({ + next: (response) => { + this.dialog.open(ConvertImageToVirtualComponent, { + width: '600px', + data: { + image: response, + imageImageRepository: image + } + }); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; + default: + console.error('Acción no soportada:', action); + break; + } + } + + iniciarTour(): void { + this.joyrideService.startTour({ + steps: [ + 'imagesTitleStep', + 'addImageButton', + 'searchImagesField', + 'imagesTable', + 'actionsHeader', + 'editImageButton', + 'deleteImageButton', + 'imagesPagination' + ], + showPrevButton: true, + themeColor: '#3f51b5' + }); + } + + loadAlert(): Observable { + return this.http.post(`${this.baseUrl}/image-repositories/server/${this.data.repositoryUuid}/get-collection`, {}); + } + + syncRepository() { + this.http.post(`${this.baseUrl}/image-repositories/server/${this.data.repositoryUuid}/sync`, {}) + .subscribe(response => { + this.toastService.success('Sincronización completada'); + this.loadData() + }, error => { + console.error('Error al sincronizar', error); + this.toastService.error('Error al sincronizar'); + }); + } + + openImageInfoDialog() { + this.loadAlert().subscribe( + response => { + this.alertMessage = response.output; + + this.dialog.open(ServerInfoDialogComponent, { + width: '800px', + data: { + message: this.alertMessage + } + }); + }, + error => { + console.error('Error al cargar la información del alert', error); + } + ); + } + + onNoClick(): void { + this.dialogRef.close(); + } + + protected readonly isDevMode = isDevMode; +} diff --git a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts index 592f088..58dd86c 100644 --- a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.spec.ts @@ -19,12 +19,17 @@ import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/materia import { MatTabsModule } from '@angular/material/tabs'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatListModule } from '@angular/material/list'; +import { ConfigService } from '@services/config.service'; describe('CreateSoftwareProfileComponent', () => { let component: CreateSoftwareProfileComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; await TestBed.configureTestingModule({ declarations: [CreateSoftwareProfileComponent], imports: [ @@ -50,7 +55,8 @@ describe('CreateSoftwareProfileComponent', () => { ], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} } + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts index 22440a6..24b85f5 100644 --- a/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts +++ b/ogWebconsole/src/app/components/software-profile/create-software-profile/create-software-profile.component.ts @@ -7,6 +7,7 @@ import { DataService as SoftwareService } from '../../software/data.service'; import { DataService } from '../data.service'; import { Observable, startWith } from 'rxjs'; import { debounceTime, switchMap, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-software-profile', @@ -14,9 +15,9 @@ import { debounceTime, switchMap, map } from 'rxjs/operators'; styleUrls: ['./create-software-profile.component.css'] }) export class CreateSoftwareProfileComponent implements OnInit { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; formGroup: FormGroup; - private apiUrl = `${this.baseUrl}/software-profiles`; softwareCollection: any[] = []; organizationalUnits: any[] = []; operativeSystems: any[] = []; @@ -33,8 +34,11 @@ export class CreateSoftwareProfileComponent implements OnInit { private toastService: ToastrService, private softwareDataService: SoftwareService, private dataService: DataService, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/software-profiles`; this.formGroup = this.fb.group({ description: [''], comments: [''], diff --git a/ogWebconsole/src/app/components/software-profile/data.service.ts b/ogWebconsole/src/app/components/software-profile/data.service.ts index b631756..e7456a5 100644 --- a/ogWebconsole/src/app/components/software-profile/data.service.ts +++ b/ogWebconsole/src/app/components/software-profile/data.service.ts @@ -1,16 +1,20 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/software-profiles`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/software-profiles`; + } getSoftwareProfiles(filters: { [key: string]: string }): Observable { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts b/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts index 5ff4e1d..21af624 100644 --- a/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.spec.ts @@ -18,12 +18,16 @@ import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform import { ToastrModule } from 'ngx-toastr'; import { TranslateModule } from '@ngx-translate/core'; import { JoyrideModule } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; describe('SoftwareProfileComponent', () => { let component: SoftwareProfileComponent; let fixture: ComponentFixture; + let mockConfigService: jasmine.SpyObj; beforeEach(async () => { + const configServiceSpy = jasmine.createSpyObj('ConfigService', [], { apiUrl: 'http://mock-api-url' }); + Object.defineProperty(window, 'getComputedStyle', { value: () => ({ getPropertyValue: (prop: string) => '', @@ -51,10 +55,17 @@ describe('SoftwareProfileComponent', () => { MatSelectModule, TranslateModule.forRoot(), JoyrideModule.forRoot(), + ], + providers: [ + { provide: ConfigService, useValue: configServiceSpy } ] }) .compileComponents(); + mockConfigService = TestBed.inject(ConfigService) as jasmine.SpyObj; + }); + + beforeEach(() => { fixture = TestBed.createComponent(SoftwareProfileComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -63,4 +74,4 @@ describe('SoftwareProfileComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); -}); +}); \ No newline at end of file diff --git a/ogWebconsole/src/app/components/software-profile/software-profile.component.ts b/ogWebconsole/src/app/components/software-profile/software-profile.component.ts index ddf8d88..86d94ed 100644 --- a/ogWebconsole/src/app/components/software-profile/software-profile.component.ts +++ b/ogWebconsole/src/app/components/software-profile/software-profile.component.ts @@ -1,14 +1,15 @@ -import {Component, signal} from '@angular/core'; -import {MatTableDataSource} from "@angular/material/table"; -import {DatePipe} from "@angular/common"; -import {MatDialog} from "@angular/material/dialog"; -import {HttpClient} from "@angular/common/http"; -import {DataService} from "../software/data.service"; -import {ToastrService} from "ngx-toastr"; -import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component"; -import {PageEvent} from "@angular/material/paginator"; -import {CreateSoftwareProfileComponent} from "./create-software-profile/create-software-profile.component"; +import { Component, signal } from '@angular/core'; +import { MatTableDataSource } from "@angular/material/table"; +import { DatePipe } from "@angular/common"; +import { MatDialog } from "@angular/material/dialog"; +import { HttpClient } from "@angular/common/http"; +import { DataService } from "../software/data.service"; +import { ToastrService } from "ngx-toastr"; +import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component"; +import { PageEvent } from "@angular/material/paginator"; +import { CreateSoftwareProfileComponent } from "./create-software-profile/create-software-profile.component"; import { JoyrideService } from 'ngx-joyride'; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-software-profile', @@ -16,14 +17,15 @@ import { JoyrideService } from 'ngx-joyride'; styleUrl: './software-profile.component.css' }) export class SoftwareProfileComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; + private apiUrl: string; images: { downloadUrl: string; name: string; uuid: string }[] = []; dataSource = new MatTableDataSource(); length: number = 0; itemsPerPage: number = 10; page: number = 0; pageSizeOptions: number[] = [5, 10, 20, 40, 100]; - loading:boolean = false; + loading: boolean = false; filters: { [key: string]: string } = {}; alertMessage: string | null = null; readonly panelOpenState = signal(false); @@ -52,15 +54,17 @@ export class SoftwareProfileComponent { ]; displayedColumns = [...this.columns.map(column => column.columnDef), 'actions']; - private apiUrl = `${this.baseUrl}/software-profiles`; - constructor( public dialog: MatDialog, private http: HttpClient, private dataService: DataService, private toastService: ToastrService, - private joyrideService: JoyrideService - ) {} + private joyrideService: JoyrideService, + private configService: ConfigService + ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/software-profiles`; + } ngOnInit(): void { this.search(); @@ -77,7 +81,7 @@ export class SoftwareProfileComponent { } search(): void { - this.http.get(`${this.apiUrl}?page=${this.page + 1 }&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( + this.http.get(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( (data) => { this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; @@ -133,9 +137,9 @@ export class SoftwareProfileComponent { iniciarTour(): void { this.joyrideService.startTour({ - steps: ['titleStep','addStep', 'filterStep', 'tableStep', 'actionsStep'], + steps: ['titleStep', 'addStep', 'filterStep', 'tableStep', 'actionsStep'], showPrevButton: true, - themeColor: '#3f51b5' + themeColor: '#3f51b5' }); } } diff --git a/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts b/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts index 3408cf8..de2d528 100644 --- a/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts +++ b/ogWebconsole/src/app/components/software/create-software/create-software.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { CreateSoftwareComponent } from './create-software.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -17,12 +16,18 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ToastrModule } from 'ngx-toastr'; import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ConfigService } from '@services/config.service'; describe('CreateSoftwareComponent', () => { let component: CreateSoftwareComponent; let fixture: ComponentFixture; beforeEach(async () => { + const mockConfigService = { + apiUrl: 'http://mock-api-url', + mercureUrl: 'http://mock-mercure-url' + }; + await TestBed.configureTestingModule({ declarations: [CreateSoftwareComponent], imports: [ @@ -45,8 +50,9 @@ describe('CreateSoftwareComponent', () => { MatSelectModule ], providers: [ - { provide: MatDialogRef, useValue: {} }, // Proporciona un mock de MatDialogRef - { provide: MAT_DIALOG_DATA, useValue: {} } // Proporciona un mock de MAT_DIALOG_DATA si es necesario + { provide: MatDialogRef, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: ConfigService, useValue: mockConfigService } ] }) .compileComponents(); diff --git a/ogWebconsole/src/app/components/software/create-software/create-software.component.ts b/ogWebconsole/src/app/components/software/create-software/create-software.component.ts index ea8c629..a6f09fa 100644 --- a/ogWebconsole/src/app/components/software/create-software/create-software.component.ts +++ b/ogWebconsole/src/app/components/software/create-software/create-software.component.ts @@ -1,9 +1,10 @@ -import {Component, Inject} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; -import {HttpClient} from "@angular/common/http"; -import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {ToastrService} from "ngx-toastr"; -import {DataService} from "../data.service"; +import { Component, Inject } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { ToastrService } from "ngx-toastr"; +import { DataService } from "../data.service"; +import { ConfigService } from '@services/config.service'; @Component({ selector: 'app-create-software', @@ -11,9 +12,9 @@ import {DataService} from "../data.service"; styleUrl: './create-software.component.css' }) export class CreateSoftwareComponent { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + baseUrl: string; formGroup: FormGroup; - private apiUrl = `${this.baseUrl}/software`; + private apiUrl: string; softwareId: string | null = null; constructor( @@ -22,8 +23,11 @@ export class CreateSoftwareComponent { public dialogRef: MatDialogRef, private toastService: ToastrService, private dataService: DataService, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/software`; this.formGroup = this.fb.group({ name: ['', Validators.required], description: [''], diff --git a/ogWebconsole/src/app/components/software/data.service.ts b/ogWebconsole/src/app/components/software/data.service.ts index 68d4574..f1f2295 100644 --- a/ogWebconsole/src/app/components/software/data.service.ts +++ b/ogWebconsole/src/app/components/software/data.service.ts @@ -1,16 +1,20 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { ConfigService } from '@services/config.service'; @Injectable({ providedIn: 'root' }) export class DataService { - baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; - private apiUrl = `${this.baseUrl}/software`; + baseUrl: string; + private apiUrl: string; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private configService: ConfigService) { + this.baseUrl = this.configService.apiUrl; + this.apiUrl = `${this.baseUrl}/software`; + } getSoftwareCollection(filters: { [key: string]: string }): Observable { const params = new HttpParams({ fromObject: filters }); diff --git a/ogWebconsole/src/app/components/software/software.component.html b/ogWebconsole/src/app/components/software/software.component.html index 8ee4f5e..24b4f26 100644 --- a/ogWebconsole/src/app/components/software/software.component.html +++ b/ogWebconsole/src/app/components/software/software.component.html @@ -4,7 +4,7 @@

Administrar Software

+ text="Administra el software deisponible desde este componente.">{{ 'manageSoftware' | translate }}
- + \ No newline at end of file diff --git a/ogWebconsole/src/app/layout/header/header.component.ts b/ogWebconsole/src/app/layout/header/header.component.ts index e926157..7e50f1d 100644 --- a/ogWebconsole/src/app/layout/header/header.component.ts +++ b/ogWebconsole/src/app/layout/header/header.component.ts @@ -1,7 +1,8 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core'; -import {jwtDecode} from 'jwt-decode'; +import { jwtDecode } from 'jwt-decode'; import { ChangePasswordModalComponent } from '../../components/admin/users/users/change-password-modal/change-password-modal.component'; -import {MatDialog} from "@angular/material/dialog"; +import { MatDialog } from "@angular/material/dialog"; +import { GlobalStatusComponent } from 'src/app/components/global-status/global-status.component'; @Component({ selector: 'app-header', @@ -20,7 +21,7 @@ export class HeaderComponent implements OnInit { this.toggleSidebar.emit(); } - constructor(public dialog: MatDialog) {} + constructor(public dialog: MatDialog) { } ngOnInit(): void { const token = localStorage.getItem('loginToken'); @@ -47,4 +48,12 @@ export class HeaderComponent implements OnInit { }); } + showGlobalStatus() { + this.dialog.open(GlobalStatusComponent, { + width: '45vw', + height: '80vh', + // maxWidth: '60vw', + // maxHeight: '60vh' + }) + } } diff --git a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html index bf0e82a..cde15ee 100644 --- a/ogWebconsole/src/app/layout/sidebar/sidebar.component.html +++ b/ogWebconsole/src/app/layout/sidebar/sidebar.component.html @@ -43,29 +43,13 @@ - + - settings_ethernet - {{ 'dhcp' | translate }} + lan + {{ 'subnets' | translate }} - - - - - analytics - {{ 'status' | translate }} - - - - - lan - {{ 'subnets' | translate }} - - - - desktop_windows @@ -75,12 +59,6 @@ - - - analytics - {{ 'status' | translate }} - - album @@ -137,13 +115,6 @@ - - - photo - {{ 'images' | translate }} - - - warehouse diff --git a/ogWebconsole/src/app/services/config.service.ts b/ogWebconsole/src/app/services/config.service.ts new file mode 100644 index 0000000..e6c0e1b --- /dev/null +++ b/ogWebconsole/src/app/services/config.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ConfigService { + private config: any; + + constructor(private http: HttpClient) {} + + loadConfig() { + return this.http.get('/assets/config.json').pipe( + catchError((error) => { + console.error('Error loading config.json', error); + return of({}); + }) + ).toPromise().then(config => { + this.config = config; + }); + } + + get apiUrl(): string { + return this.config.apiUrl; + } + + get mercureUrl(): string { + return this.config.mercureUrl; + } +} \ No newline at end of file diff --git a/ogWebconsole/src/assets/config.json b/ogWebconsole/src/assets/config.json new file mode 100644 index 0000000..2a53caa --- /dev/null +++ b/ogWebconsole/src/assets/config.json @@ -0,0 +1,4 @@ +{ + "apiUrl": "https://127.0.0.1:8443", + "mercureUrl": "http://localhost:3000/.well-known/mercure" +} diff --git a/ogWebconsole/src/assets/images/og_logo.png b/ogWebconsole/src/assets/images/og_logo.png new file mode 100644 index 0000000..9beadc3 Binary files /dev/null and b/ogWebconsole/src/assets/images/og_logo.png differ diff --git a/ogWebconsole/src/locale/en-US.json b/ogWebconsole/src/locale/en-US.json deleted file mode 100644 index 0340a4d..0000000 --- a/ogWebconsole/src/locale/en-US.json +++ /dev/null @@ -1,466 +0,0 @@ -{ - "loginlabelUsername": "Enter your username", - "loginlabelPassword": "Enter your password", - "buttonLogin": "Login", - "welcomeMessage": "Welcome {{username}}", - "loginError": "Login error: {{error}}", - "labelUsers": "Users", - "labelRoles": "Roles", - "adminImagesTitle": "Manage images", - "addUser": "Add users", - "searchLabel": "Search image name", - "searchPlaceholder": "Search", - "searchHint": "Press 'enter' to search", - "columnActions": "Actions", - "dialogTitleAddUser": "Add User", - "addUserlabelUsername": "Username", - "addUserlabelPassword": "Password", - "labelRole": "Role", - "labelOrganizationalUnit": "Organizational Unit", - "buttonCancel": "Cancel", - "buttonAdd": "Add", - "addButton": "Add", - "addClientDialogTitle": "Add Client", - "dialogTitleEditUser": "Editar usuario", - "dialogTitleChangePassword": "Change password", - "labelCurrentPassword": "Current password", - "labelNewPassword": "New password", - "labelRepeatPassword": "Repeat password", - "errorPasswordMismatch": "Passwords do not match", - "buttonEdit": "Edit", - "adminRolesTitle": "Manage Roles", - "addRole": "Add role", - "searchRoleLabel": "Search role name", - "dialogTitleAddRole": "Add Role", - "labelRoleName": "Name", - "sectionTitlePermissions": "Permissions:", - "checkboxSuperAdmin": "Super Admin", - "checkboxOrgAdmin": "Organizational Unit Admin", - "checkboxOrgOperator": "Organizational Unit Operator", - "checkboxOrgMinimal": "Minimal Organizational Unit", - "checkboxUserRole": "User", - "groupsTitleStepText": "On this screen, you can manage the main organizational units (Faculties, Classrooms, Classroom Groups, and clients).", - "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 client.", - "adminCalendarsTitle": "Manage calendars", - "addButtonStepText": "Click here to add a new calendar.", - "addCalendar": "Add calendar", - "searchStepText": "Use this search bar to filter existing calendars.", - "searchCalendarLabel": "Search calendar name", - "tableStepText": "Here are the existing calendars with their characteristics and settings.", - "actionsStepText": "Access the available actions for each calendar here.", - "editCalendar": "Edit calendar", - "remoteAvailability": "Remote availability?", - "selectWeekDays": "Select the days of the week", - "startTime": "Start time", - "startTimePlaceholder": "Select start time", - "endTime": "End time", - "endTimePlaceholder": "Select end time", - "reasonLabel": "Reason", - "reasonPlaceholder": "Reason for the exception", - "startDate": "Start date", - "endDate": "End date", - "buttonSave": "Save", - "adminCommandGroupsTitle": "Manage Command Groups", - "addCommandGroupStepText": "Click to add a new command group.", - "addCommandGroup": "Add Command Group", - "searchGroupNameLabel": "Search group name", - "loadingStepText": "Wait while the command groups are loading.", - "viewCommands": "View commands", - "paginationStepText": "Navigate between command group pages using the paginator.", - "commandGroupDetailsTitle": "Command Group Details", - "createdBy": "Created by", - "groupId": "Group ID", - "creationDate": "Creation Date", - "includedCommands": "Included Commands", - "nameColumn": "Name", - "selectClients": "Select clients:", - "clientsLabel": "Clients", - "selectAtLeastOneClient": "You must select at least one client.", - "execute": "Execute", - "scheduleExecution": "Schedule Execution", - "editCommandGroup": "Edit command group", - "createCommandGroup": "Create command group", - "groupNameLabel": "Group Name", - "enabledToggle": "Enabled", - "availableCommandsTitle": "Available Commands", - "selectedCommandsTitle": "Selected Commands", - "manageTasksTitle": "Manage Tasks", - "addTaskStepText": "Click to add a new task.", - "addTask": "Add Task", - "searchTaskLabel": "Search task", - "idColumn": "Id", - "infoColumn": "Info", - "createdByColumn": "Created by", - "executionDateColumn": "Execution Date", - "statusColumn": "Status", - "enabled": "Enabled", - "disabled": "Disabled", - "adminCommandsTitle": "Command and procedure traces", - "resetFiltersStepText": "Click to reset the applied filters and see all traces.", - "resetFilters": "Reset filters", - "clientSelectStepText": "Select a client to see the associated traces.", - "filterClientPlaceholder": "Filter by client", - "commandSelectStepText": "Select a command to see the specific traces of that command.", - "filterCommandPlaceholder": "Filter by command", - "taskDetailsTitle": "Task Details", - "taskId": "Task ID", - "status": "Status", - "notes": "Notes", - "includedCommandGroups": "Included Command Groups", - "commandGroupColumn": "Command Group", - "commandsToExecute": "Commands to execute", - "group": "Group", - "commandColumn": "Command", - "editTask": "Edit Task", - "createTask": "Create Task", - "informationSectionTitle": "Information", - "informationLabel": "Information", - "notesPlaceholder": "Enter your notes here", - "commandSelectionSectionTitle": "Command selection", - "selectCommandsLabel": "Select Commands", - "requiredFieldError": "This field is required", - "executionDateTimeSectionTitle": "Execution date and time", - "executionDateLabel": "Execution Date", - "selectDatePlaceholder": "Select a date", - "executionTimeLabel": "Execution Time", - "selectTimePlaceholder": "Select a time", - "destinationSelectionSectionTitle": "Select destination", - "selectOrganizationalUnitLabel": "Select Organizational Unit", - "selectClassroomLabel": "Select Classroom", - "selectAllClients": "Select all", - "addCommand": "Add Command", - "searchCommandLabel": "Search command name", - "executeCommandTitle": "Execute Command", - "subOrganizationalUnitLabel": "Sub-organizational Unit", - "noClientsAvailable": "No clients available", - "buttonExecute": "Execute", - "commandDetailsTitle": "Command Details", - "nameLabel": "Name", - "commentsLabel": "Comments", - "createdByLabel": "Created by", - "creationDateLabel": "Creation Date", - "scriptLabel": "Script", - "selectClientsTitle": "Select clients:", - "selectAtLeastOneClientError": "You must select at least one client.", - "editCommandTitle": "Edit Command", - "createCommandTitle": "Create Command", - "commandNamePlaceholder": "Command name", - "commandScriptPlaceholder": "Command script", - "readOnlyLabel": "Read only", - "enabledLabel": "Enabled", - "cancelButton": "Cancel", - "saveButton": "Save", - "generalTabLabel": "General", - "tabsStepText": "Use the tabs to access different viewing and search options for organizational units and clients.", - "adminGroupsTitle": "Manage groups", - "newOrganizationalUnitTooltip": "Open modal to create organizational units of any type (Center, Classroom, Classroom Group, or Client Group)", - "newOrganizationalUnitButton": "New Organizational Unit", - "newClientButton": "New Client", - "newSingleClientButton": "Add single client", - "newMultipleClientButton": "Add numerous clients", - "keyStepText": "The legend will show you the types of organizational units and their corresponding icons", - "legendButton": "Legend", - "unitStepText": "This is the section where 'Center' type organizational units will be displayed", - "organizationalUnitTitle": "Centers", - "elementsStepText": "This is the section to view internal units of the selected center and navigate through them.", - "internalElementsTitle": "Internal elements", - "noInternalElementsMessage": "No internal elements", - "viewTreeTooltip": "View unit as a tree", - "viewTreeMenu": "View organizational chart", - "editUnitTooltip": "Edit this organizational unit", - "viewUnitTooltip": "View organizational unit details", - "viewUnitMenu": "View data", - "addInternalUnitTooltip": "Create a new internal organizational unit", - "addClientTooltip": "Register a client in this organizational unit", - "deleteElementTooltip": "Delete this element", - "deleteElementMenu": "Delete element", - "executeCommandTooltip": "Execute command on this element", - "advancedSearchTabLabel": "Advanced search", - "clientsTabLabel": "Clients", - "organizationalUnitsTabLabel": "Organizational units", - "viewTreeTitle": "View organizational unit tree", - "toggleNodeAriaLabel": "Toggle node", - "closeButton": "Close", - "inputDetails": "Input details", - "excludeParentChanges": "Exclude parent changes", - "orgUnitPropertiesTitle": "Organizational unit properties", - "generalDataTab": "General data", - "propertyHeader": "Property", - "valueHeader": "Value", - "classroomNetworkPropertiesTab": "Classroom and network properties", - "editOrgUnitTitle": "Edit Organizational Unit", - "generalStepLabel": "General", - "typeLabel": "Type", - "editOrgUnitParentLabel": "Parent", - "descriptionLabel": "Description", - "nextButton": "Next", - "classroomInfoStepLabel": "Classroom Information", - "locationLabel": "Location", - "projectorToggle": "Projector", - "boardToggle": "Board", - "capacityLabel": "Capacity", - "associatedCalendarLabel": "Associated Calendar", - "backButton": "Back", - "additionalInfoStepLabel": "Additional Information", - "networkSettingsStepLabel": "Network Settings", - "proxyUrlLabel": "Proxy server URL", - "dnsIpLabel": "DNS server IP", - "netmaskLabel": "Netmask", - "routerLabel": "Router", - "ntpIpLabel": "NTP server IP", - "p2pModeLabel": "P2P Mode", - "p2pTimeLabel": "P2P Time", - "mcastIpLabel": "Multicast IP", - "mcastSpeedLabel": "Multicast Speed", - "mcastPortLabel": "Multicast Port", - "mcastModeLabel": "Multicast Mode", - "menuUrlLabel": "Menu URL", - "menuLabel": "Menu", - "hardwareProfileLabel": "Hardware Profile", - "urlFormatError": "Invalid URL format.", - "validationToggle": "Validation", - "addOUSubmitButton": "Add", - "editOUSubmitButton": "Edit", - "addOrgUnitTitle": "Add Organizational Unit", - "createOrgUnitparentLabel": "Parent organizational unit", - "noParentOption": "--", - "nextServerLabel": "NextServer", - "bootFileNameLabel": "bootFileName", - "orgUnitTitle": "Organizational unit", - "classroomGroupsTitle": "Classroom groups", - "classroomTitle": "Classroom", - "clientGroupsTitle": "Client groups", - "clientTitle": "Client", - "executeCommandOrGroupTitle": "Execute Command or Command Group", - "selectCommandLabel": "Select Command", - "selectCommandGroupLabel": "Select Command Group", - "noClientsMessage": "No clients available", - "editClientDialogTitle": "Edit Client", - "organizationalUnitLabel": "Parent", - "ogLiveLabel": "OgLive", - "serialNumberLabel": "Serial Number", - "netifaceLabel": "Network interface", - "netDriverLabel": "Network driver", - "macLabel": "MAC", - "macError": "Invalid MAC format. Valid example: 00:11:22:33:44:55", - "ipLabel": "IP Address", - "ipError": "Invalid IP address format. Valid example: 127.0.0.1", - "templateLabel": "PXE Template", - "digitalBoard": "Digital board", - "projectorAlt": "Projector", - "clientAlt": "Client", - "saveDispositionButton": "Save disposition", - "actionsModalTitle": "Actions", - "adminOuTitle": "Manage organizational units", - "resetFiltersButton": "Reset filters", - "addOUButton": "Add OU", - "searchLabelOu": "Search OU name", - "macHint": "Example: 00:11:22:33:44:55", - "ipHint": "Example: 123.1.1.1", - "allOption": "All", - "centerOption": "Center", - "classroomsGroupOption": "Classroom Groups", - "classroomOption": "Classroom", - "clientsGroupOption": "PC Groups", - "roomMapOption": "Classroom map", - "clientDetailsTitle": "Client details", - "commandsButton": "Commands", - "networkPropertiesTab": "Network properties", - "disksPartitionsTitle": "Disks/Partitions", - "diskTitle": "Disk", - "diskUsedLabel": "Used", - "diskTotalLabel": "Total", - "diskImageAssistantTitle": "Disk image assistant", - "deployImage": "Deploy image", - "partitionColumn": "Partition", - "isoImageColumn": "ISO Image", - "ogliveColumn": "OgLive", - "selectImageOption": "Select image", - "selectOgLiveOption": "Select OgLive", - "repositoryTitle": "Admin Repository", - "saveAssociationsButton": "Save Associations", - "partitionAssistantTitle": "Partition assistant", - "diskSizeLabel": "Size", - "partitionTypeColumn": "Partition type", - "partitionSizeColumn": "Size (MB)", - "usageColumn": "Usage (%)", - "formatColumn": "Format", - "remotePcLabel": "Remote PC", - "globalImageLabel": "Global image", - "ntfsOption": "NTFS", - "linuxOption": "LINUX", - "cacheOption": "CACHE", - "deleteButton": "Delete", - "searchTitle": "Advanced search", - "selectFilterLabel": "Select filter", - "gridViewButton": "Grid", - "listViewButton": "List", - "selectOptionLabel": "Select an option", - "namePlaceholder": "Organizational unit", - "selectAllButton": "Select/Deselect All", - "saveFiltersButton": "Save Filters", - "sendFiltersButton": "Send Action", - "addPxeButton": "Add PXE file", - "repositoryLabel": "Repositories", - "internalUnits": "Internal units", - "imageNameLabel": "Image name", - "noResultsMessage": "No results to display.", - "imagesTitle": "Manage images", - "addImageButton": "Add image", - "searchNameDescription": "Search images by name to quickly find a specific image.", - "searchDefaultDescription": "Filter images to show only default or non-default images.", - "searchDefaultLabel": "Default image", - "searchInstalledDescription": "Filter images to show only those installed on the OgBoot server.", - "searchInstalledLabel": "Installed on OgBoot server", - "tableDescription": "Here is the list of available images to manage.", - "actionsColumnHeader": "Actions", - "viewIcon": "visibility", - "editIcon": "edit", - "installOption": "Install", - "uninstallOption": "Uninstall", - "setDefaultOption": "Set as default image", - "paginationDescription": "Navigate between image pages using the paginator.", - "detailsTitle": "Details of {{ name }}", - "editTemplateTitle": "Edit template", - "addTemplateTitle": "Add template", - "templateNameLabel": "Template Name", - "templateNamePlaceholder": "Enter the template name", - "templateContentPlaceholder": "Enter the template content", - "loadTemplateModelButton": "Load model template", - "diskModel": "Disk boot", - "createButton": "Create", - "manageClientsTitle": "Manage clients", - "syncIcon": "sync", - "addClientsTitle": "Add clients to {{ subnetName }}", - "selectedClientsTitle": "Selected clients:", - "editClientTitle": "Edit Client", - "addClientTitle": "Add Client", - "advancedNetbootTitle": "Advanced netboot", - "selectUnitLabel": "Select Organizational Unit", - "loadingUnitsOption": "Loading units...", - "selectClassLabel": "Select classroom", - "applyToAllLabel": "Select template to apply to all clients", - "saveButtonLabel": "Save", - "idColumnHeader": "Id", - "nameColumnHeader": "Name", - "templateColumnHeader": "Template", - "pxeImageTitle": "Information on ogBoot server", - "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.", - "actionsDescription": "Manage each image with options to view, edit, delete, and more.", - "addClientButton": "Add client", - "searchClientNameLabel": "Search client name", - "searchIPLabel": "Search IP", - "searchMACLabel": "Search MAC", - "diskUsageTitle": "Disk Usage", - "ogBootServerStatus": "OgBoot Server Status", - "servicesTitle": "Services", - "Legend": "Legend", - "totalLabel": "Total", - "usedLabel": "Used", - "freeLabel": "Free", - "availableLabel": "Available", - "InstalledOglivesTitle": "Installed OgLives", - "idLabel": "ID", - "KernelLabel": "Kernel", - "architectureLabel": "Architecture", - "revisionLabel": "Revision", - "serverInfoTitle": "Server Information", - "adminPxeTitle": "Manage PXE Files", - "createdOgBootLabel": "Created in OgBoot", - "selectOptionPlaceholder": "Select an option", - "yesOption": "Yes", - "noOption": "No", - "actionsColumn": "Actions", - "createServerButton": "Create Server", - "labelName": "Name", - "diskUsageDescription": "Here is the disk usage of the server.", - "servicesStatusDescription": "Here is the status of the server services.", - "oglivesDescription": "Here are the OgLives installed on the server.", - "addImageButtonDescription": "Click to add a new image.", - "adminPxeDescription": "From here you can manage the PXE templates of the server.", - "addTemplateButtonDescription": "Add PXE Template", - "addTemplateButton": "Add Template", - "searchSyncDescription": "Filter PXE templates to show only synchronized or unsynchronized ones.", - "advancedNetbootDescription": "From here you can configure the advanced netboot of the clients.", - "selectUnitDescription": "Select the organizational unit to which the clients belong.", - "selectClassDescription": "Select the classroom to which the clients belong.", - "applyToAllDescription": "Select a template to apply to all clients.", - "saveButtonDescription": "Save the changes made.", - "welcomeUser": "Welcome {{username}}", - "TOOLTIP_WELCOME_USER": "Welcome, {{username}}", - "groups": "Groups", - "TOOLTIP_GROUPS": "Manage user groups", - "actions": "Actions", - "TOOLTIP_ACTIONS": "View and execute predefined actions", - "commands": "Commands", - "TOOLTIP_COMMANDS": "List of available commands", - "commandGroups": "Groups", - "TOOLTIP_COMMAND_GROUPS": "Manage command groups", - "tasks": "Tasks", - "TOOLTIP_TASKS": "View and manage scheduled tasks", - "dhcp": "DHCP", - "TOOLTIP_DHCP": "Configure and manage DHCP", - "TOOLTIP_DHCP_STATUS": "Current status of the DHCP service", - "subnets": "Subnets", - "TOOLTIP_SUBNETS": "Manage and create subnets", - "boot": "Boot", - "TOOLTIP_BOOT": "Configure and manage boot options", - "ogLive": "ogLive", - "TOOLTIP_PXE_IMAGES": "View available PXE boot images", - "pxeTemplates": "PXE Templates", - "pxeTemplate" : "Plantilla", - "TOOLTIP_PXE_TEMPLATES": "Manage PXE boot templates", - "pxeBootFiles": "PXE Boot Files", - "TOOLTIP_PXE_BOOT_FILES": "Configure PXE boot files", - "calendars": "Calendars", - "TOOLTIP_CALENDARS": "Manage remotePC calendars", - "software": "Software", - "TOOLTIP_SOFTWARE": "Manage software configurations", - "softwareList": "List", - "TOOLTIP_SOFTWARE_LIST": "View list of available software", - "softwareProfiles": "Profiles", - "TOOLTIP_SOFTWARE_PROFILES": "Manage software profiles", - "operativeSystems": "Operating Systems", - "TOOLTIP_OPERATIVE_SYSTEMS": "Configure operating systems", - "images": "Images", - "TOOLTIP_IMAGES": "Manage system images", - "repositories": "Repositories", - "TOOLTIP_REPOSITORIES": "View and manage software repositories", - "menus": "Menus", - "TOOLTIP_MENUS": "Menu management (option disabled)", - "search": "Search", - "TOOLTIP_SEARCH": "Search function (option disabled)", - "detailsOf": "Details of", - "editUnitMenu": "Edit", - "addInternalUnitMenu": "Add", - "addClientMenu": "Add client", - "filters": "Filters", - "searchClient": "Search client", - "searchTree": "Search organizational unit", - "filterByType": "Filter by type", - "all": "All", - "classroomsGroup": "Classroom groups", - "classrooms": "Classrooms", - "computerGroups": "PC groups", - "executeCommand": "Execute command", - "roomMap": "Classroom map", - "addOrganizationalUnit": "Add organizational unit", - "edit": "Edit", - "delete": "Delete", - "clients": "Clients", - "sync": "Sync", - "viewDetails": "View details", - "name": "Name", - "maintenance": "Maintenance", - "subnet": "Subnet", - "parent": "Parent", - "adminUsersTitle": "Manage users", - "filtersPanelStepText": "Use these filters to search or load configurations.", - "organizationalUnitsStepText": "List of Organizational Units. Click on one to view details.", - "defaultMenuLabel": "Main menu", - "noClients": "No clients" -} diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json index 0340a4d..afdbe80 100644 --- a/ogWebconsole/src/locale/en.json +++ b/ogWebconsole/src/locale/en.json @@ -1,11 +1,13 @@ { + "Administration": "Administration", + "changePassword": "Change password", + "logout": "Logout", "loginlabelUsername": "Enter your username", "loginlabelPassword": "Enter your password", "buttonLogin": "Login", - "welcomeMessage": "Welcome {{username}}", - "loginError": "Login error: {{error}}", "labelUsers": "Users", "labelRoles": "Roles", + "labelEnvVars": "Environment variables", "adminImagesTitle": "Manage images", "addUser": "Add users", "searchLabel": "Search image name", @@ -19,7 +21,10 @@ "labelOrganizationalUnit": "Organizational Unit", "buttonCancel": "Cancel", "buttonAdd": "Add", - "addButton": "Add", + "addRule": "Add rule", + "rulesHeader": "Rules", + "statusUnavailable": "Unavailable", + "statusAvailable": "Available", "addClientDialogTitle": "Add Client", "dialogTitleEditUser": "Editar usuario", "dialogTitleChangePassword": "Change password", @@ -298,7 +303,6 @@ "listViewButton": "List", "selectOptionLabel": "Select an option", "namePlaceholder": "Organizational unit", - "selectAllButton": "Select/Deselect All", "saveFiltersButton": "Save Filters", "sendFiltersButton": "Send Action", "addPxeButton": "Add PXE file", @@ -308,6 +312,8 @@ "noResultsMessage": "No results to display.", "imagesTitle": "Manage images", "addImageButton": "Add image", + "importImageButton": "Import image", + "convertImageButton": "Convert virtual image", "searchNameDescription": "Search images by name to quickly find a specific image.", "searchDefaultDescription": "Filter images to show only default or non-default images.", "searchDefaultLabel": "Default image", @@ -338,7 +344,6 @@ "addClientTitle": "Add Client", "advancedNetbootTitle": "Advanced netboot", "selectUnitLabel": "Select Organizational Unit", - "loadingUnitsOption": "Loading units...", "selectClassLabel": "Select classroom", "applyToAllLabel": "Select template to apply to all clients", "saveButtonLabel": "Save", @@ -370,10 +375,13 @@ "revisionLabel": "Revision", "serverInfoTitle": "Server Information", "adminPxeTitle": "Manage PXE Files", - "createdOgBootLabel": "Created in OgBoot", "selectOptionPlaceholder": "Select an option", "yesOption": "Yes", "noOption": "No", + "inactiveOption": "Uninstalled", + "activeOption": "Installed", + "pendingOption": "Pending", + "failedOption": "Failed", "actionsColumn": "Actions", "createServerButton": "Create Server", "labelName": "Name", @@ -381,6 +389,8 @@ "servicesStatusDescription": "Here is the status of the server services.", "oglivesDescription": "Here are the OgLives installed on the server.", "addImageButtonDescription": "Click to add a new image.", + "addOgLiveButtonDescription": "Click to add a new OgLive.", + "addOgLiveButton": "Add OgLive", "adminPxeDescription": "From here you can manage the PXE templates of the server.", "addTemplateButtonDescription": "Add PXE Template", "addTemplateButton": "Add Template", @@ -391,7 +401,6 @@ "applyToAllDescription": "Select a template to apply to all clients.", "saveButtonDescription": "Save the changes made.", "welcomeUser": "Welcome {{username}}", - "TOOLTIP_WELCOME_USER": "Welcome, {{username}}", "groups": "Groups", "TOOLTIP_GROUPS": "Manage user groups", "actions": "Actions", @@ -412,13 +421,14 @@ "ogLive": "ogLive", "TOOLTIP_PXE_IMAGES": "View available PXE boot images", "pxeTemplates": "PXE Templates", - "pxeTemplate" : "Plantilla", + "pxeTemplate": "Plantilla", "TOOLTIP_PXE_TEMPLATES": "Manage PXE boot templates", "pxeBootFiles": "PXE Boot Files", "TOOLTIP_PXE_BOOT_FILES": "Configure PXE boot files", "calendars": "Calendars", "TOOLTIP_CALENDARS": "Manage remotePC calendars", "software": "Software", + "manageSoftware": "Manage Software", "TOOLTIP_SOFTWARE": "Manage software configurations", "softwareList": "List", "TOOLTIP_SOFTWARE_LIST": "View list of available software", @@ -462,5 +472,11 @@ "filtersPanelStepText": "Use these filters to search or load configurations.", "organizationalUnitsStepText": "List of Organizational Units. Click on one to view details.", "defaultMenuLabel": "Main menu", - "noClients": "No clients" + "noClients": "No clients", + "GlobalStatus": "Global Status", + "RamUsage": "RAM Usage", + "CpuUsage": "CPU Usage", + "processes": "Processes", + "usedPercentageLabel": "Used", + "errorLoadingData": "Error fetching data. Service not available" } diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json index 470fe4f..314061a 100644 --- a/ogWebconsole/src/locale/es.json +++ b/ogWebconsole/src/locale/es.json @@ -1,11 +1,13 @@ { + "Administration": "Administración", + "changePassword": "Cambiar contraseña", + "logout": "Salir", "loginlabelUsername": "Introduce tu usuario", "loginlabelPassword": "Introduce tu contraseña", "buttonLogin": "Login", - "welcomeMessage": "Bienvenido {{username}}", - "loginError": "Login error: {{error}}", "labelUsers": "Usuarios", "labelRoles": "Roles", + "labelEnvVars": "Variables de entorno", "adminImagesTitle": "Administrar OgLives", "addUser": "Añadir usuarios", "searchLabel": "Buscar nombre de imagen", @@ -19,7 +21,7 @@ "labelOrganizationalUnit": "Unidad organizativa", "buttonCancel": "Cancelar", "buttonAdd": "Añadir", - "addButton": "Añadir", + "back": "Atrás", "addClientDialogTitle": "Añadir Cliente", "dialogTitleEditUser": "Editar usuario", "dialogTitleChangePassword": "Change password", @@ -32,6 +34,10 @@ "addRole": "Añadir rol", "searchRoleLabel": "Buscar nombre de rol", "dialogTitleAddRole": "Añadir Rol", + "addRule": "Añadir regla", + "rulesHeader": "Reglas", + "statusUnavailable": "No disponible", + "statusAvailable": "Disponible", "labelRoleName": "Nombre", "sectionTitlePermissions": "Permisos:", "checkboxSuperAdmin": "Super Admin", @@ -238,6 +244,8 @@ "organizationalUnitLabel": "Padre", "ogLiveLabel": "OgLive", "imageNameLabel": "Nombre de la imagen", + "importImageButton": "Importar imagen", + "convertImageButton": "Convertir imagen virtual", "repositoryLabel": "Repositorios", "serialNumberLabel": "Número de Serie", "netifaceLabel": "Interfaz de red", @@ -297,7 +305,6 @@ "listViewButton": "Lista", "selectOptionLabel": "Selecciona una opción", "namePlaceholder": "Unidad organizativa", - "selectAllButton": "Seleccionar/Deseleccionar Todos", "saveFiltersButton": "Guardar Filtros", "sendFiltersButton": "Enviar Acción", "addPxeButton": "Añadir fichero PXE", @@ -338,7 +345,6 @@ "addClientTitle": "Añadir Cliente", "advancedNetbootTitle": "Netboot avanzado", "selectUnitLabel": "Selecciona Unidad Organizacional", - "loadingUnitsOption": "Cargando unidades...", "selectClassLabel": "Selecciona aula", "applyToAllLabel": "Seleccione plantilla para aplicar a todos los clientes", "saveButtonLabel": "Guardar", @@ -370,10 +376,15 @@ "revisionLabel": "Revisión", "serverInfoTitle": "Información del servidor", "adminPxeTitle": "Administrar ficheros PXE", - "createdOgBootLabel": "Creado en OgBoot", "selectOptionPlaceholder": "Selecciona una opción", "yesOption": "Sí", "noOption": "No", + "inactiveOption": "No instalada", + "activeOption": "Instalada", + "pendingOption": "Instalando", + "failedOption": "Fallida", + "addOgLiveButtonDescription": "Haz clic para añadir un nuevo OgLive.", + "addOgLiveButton": "Añadir OgLive", "menuLabel": "Menu", "actionsColumn": "Acciones", "createServerButton": "Crear servidor", @@ -392,7 +403,6 @@ "applyToAllDescription": "Selecciona una plantilla para aplicar a todos los clientes.", "saveButtonDescription": "Guarda los cambios realizados.", "welcomeUser": "Bienvenido {{username}}", - "TOOLTIP_WELCOME_USER": "Bienvenido, {{username}}", "groups": "Grupos", "TOOLTIP_GROUPS": "Gestionar grupos de usuarios", "actions": "Acciones", @@ -420,6 +430,7 @@ "calendars": "Calendarios", "TOOLTIP_CALENDARS": "Gestionar calendarios de remotePC", "software": "Software", + "manageSoftware": "Administrar Software", "TOOLTIP_SOFTWARE": "Administrar configuraciones de software", "softwareList": "Listado", "TOOLTIP_SOFTWARE_LIST": "Ver lista de software disponible", @@ -463,5 +474,12 @@ "filtersPanelStepText": "Utiliza estos filtros para buscar o cargar configuraciones.", "organizationalUnitsStepText": "Lista de Unidades Organizacionales. Haz clic en una para ver detalles.", "defaultMenuLabel": "Menú por defecto", - "noClients": "No hay clientes" + "noClients": "No hay clientes", + "GlobalStatus": "Estado Global", + "RamUsage": "Uso de RAM", + "CpuUsage": "Uso de CPU", + "processes": "Procesos", + "usedPercentageLabel": "Usado", + "errorLoadingData": "Error al cargar los datos. Servicio inactivo" + } diff --git a/ogWebconsole/src/main.ts b/ogWebconsole/src/main.ts index be6bfab..fbf76ea 100644 --- a/ogWebconsole/src/main.ts +++ b/ogWebconsole/src/main.ts @@ -1,9 +1,5 @@ -/// - import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - import { AppModule } from './app/app.module'; - platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.error(err)); diff --git a/ogWebconsole/src/styles.css b/ogWebconsole/src/styles.css index 9f78cfb..1612d8e 100644 --- a/ogWebconsole/src/styles.css +++ b/ogWebconsole/src/styles.css @@ -93,4 +93,11 @@ body { .action-button:disabled { background-color: #ced0df; cursor: not-allowed; -} \ No newline at end of file +} + +.action-container { + display: flex; + justify-content: flex-end; + gap: 1em; + padding: 1.5em; +} diff --git a/ogWebconsole/tsconfig.app.json b/ogWebconsole/tsconfig.app.json index ec26f70..374cc9d 100644 --- a/ogWebconsole/tsconfig.app.json +++ b/ogWebconsole/tsconfig.app.json @@ -3,9 +3,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", - "types": [ - "@angular/localize" - ] + "types": [] }, "files": [ "src/main.ts" diff --git a/ogWebconsole/tsconfig.json b/ogWebconsole/tsconfig.json index 4b332f9..ed22091 100644 --- a/ogWebconsole/tsconfig.json +++ b/ogWebconsole/tsconfig.json @@ -21,7 +21,11 @@ "lib": [ "ES2022", "dom" - ] + ], + "baseUrl": "./", + "paths": { + "@services/*": ["src/app/services/*"], + } }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false,