From 49ccdfc4ec171befde0368f8659f48be621ae6c8 Mon Sep 17 00:00:00 2001 From: jmbardallo Date: Thu, 22 Oct 2026 11:17:48 +0200 Subject: [PATCH] fix(plugins): ensure reactive data binding and loading state on page reload --- .../plugin-settings.component.ts | 13 ++++++- .../components/plugins/plugins.component.css | 33 ++++++++++++++++ .../components/plugins/plugins.component.html | 13 +++++-- .../components/plugins/plugins.component.ts | 38 ++++++++++++++++++- 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.ts b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.ts index d1f80fd..552ae19 100644 --- a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.ts +++ b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.ts @@ -1,18 +1,20 @@ -import { Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; import { PluginManifest, PluginService, SchemaField } from '@services/plugin.service'; import { ToastrService } from 'ngx-toastr'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-plugin-settings', templateUrl: './plugin-settings.component.html', styleUrls: ['./plugin-settings.component.css'] }) -export class PluginSettingsComponent implements OnInit, OnChanges { +export class PluginSettingsComponent implements OnInit, OnChanges, OnDestroy { @Input() pluginName: string = ''; plugin?: PluginManifest; schema: SchemaField[] = []; configData: Record = {}; + private pluginSub?: Subscription; constructor( private pluginService: PluginService, @@ -20,9 +22,16 @@ export class PluginSettingsComponent implements OnInit, OnChanges { ) {} ngOnInit(): void { + this.pluginSub = this.pluginService.plugins$.subscribe(() => { + this.loadPluginData(); + }); this.loadPluginData(); } + ngOnDestroy(): void { + this.pluginSub?.unsubscribe(); + } + ngOnChanges(changes: SimpleChanges): void { if (changes['pluginName']) { this.loadPluginData(); diff --git a/ogWebconsole/src/app/components/plugins/plugins.component.css b/ogWebconsole/src/app/components/plugins/plugins.component.css index 80c1d5f..bf2faa3 100644 --- a/ogWebconsole/src/app/components/plugins/plugins.component.css +++ b/ogWebconsole/src/app/components/plugins/plugins.component.css @@ -115,3 +115,36 @@ font-size: 0.95rem; line-height: 1.5; } + +.loading-plugins-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 16px; + padding: 60px 24px; + gap: 16px; +} + +.loading-spinner { + width: 38px; + height: 38px; + border: 3px solid #e2e8f0; + border-top-color: #1976d2; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.loading-text { + color: #64748b; + font-size: 0.95rem; + margin: 0; +} diff --git a/ogWebconsole/src/app/components/plugins/plugins.component.html b/ogWebconsole/src/app/components/plugins/plugins.component.html index 2be5eac..02211d1 100644 --- a/ogWebconsole/src/app/components/plugins/plugins.component.html +++ b/ogWebconsole/src/app/components/plugins/plugins.component.html @@ -4,7 +4,9 @@
extension

Plugins y Extensiones

- {{ plugins.length }} Activo(s) + @if (!isLoading) { + {{ plugins.length }} Activo(s) + }

Gestiona y personaliza los módulos y extensiones instalados en el servidor OpenGnsys. Cada plugin opera de manera desacoplada sin interferir con el núcleo del sistema. @@ -12,7 +14,12 @@ - @if (plugins.length > 0) { + @if (isLoading) { +

+
+

Cargando extensiones y plugins instalados...

+
+ } @else if (plugins.length > 0) {
@for (plugin of plugins; track plugin.name) { @@ -35,7 +42,7 @@

No hay plugins instalados

Actualmente no hay ninguna extensión o plugin registrado en este servidor OpenGnsys. - Cuando instales un paquete compatible (como og-plugin-freeze), aparecerá automáticamente aquí. + Cuando instales un paquete compatible (como ogfreeze), aparecerá automáticamente aquí.

} diff --git a/ogWebconsole/src/app/components/plugins/plugins.component.ts b/ogWebconsole/src/app/components/plugins/plugins.component.ts index 8d82154..92cf793 100644 --- a/ogWebconsole/src/app/components/plugins/plugins.component.ts +++ b/ogWebconsole/src/app/components/plugins/plugins.component.ts @@ -1,18 +1,52 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy, ChangeDetectorRef, inject } from '@angular/core'; import { PluginManifest, PluginService } from '@services/plugin.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-plugins', templateUrl: './plugins.component.html', styleUrls: ['./plugins.component.css'] }) -export class PluginsComponent implements OnInit { +export class PluginsComponent implements OnInit, OnDestroy { plugins: PluginManifest[] = []; selectedTabIndex: number = 0; + isLoading: boolean = true; + private pluginSub?: Subscription; + private cdr = inject(ChangeDetectorRef); constructor(public pluginService: PluginService) {} ngOnInit(): void { + // 1. Asignar estado actual en memoria si ya existe this.plugins = this.pluginService.getPlugins(); + if (this.plugins.length > 0) { + this.isLoading = false; + } + + // 2. Suscribirse reactivamente al flujo de plugins + this.pluginSub = this.pluginService.plugins$.subscribe(plugins => { + this.plugins = plugins || []; + if (this.plugins.length > 0) { + this.isLoading = false; + } + this.cdr.markForCheck(); + }); + + // 3. Forzar refresco desde el backend para garantizar datos actualizados + this.pluginService.refreshPlugins().subscribe({ + next: (plugins) => { + this.plugins = plugins || []; + this.isLoading = false; + this.cdr.markForCheck(); + }, + error: () => { + this.isLoading = false; + this.cdr.markForCheck(); + } + }); + } + + ngOnDestroy(): void { + this.pluginSub?.unsubscribe(); } }